Кому можно давать права на ExportTemplate и ConfigTemplate в NetBox после CVE-2026-29514
Право «создавать шаблон экспорта» в NetBox от 4.3.5 и до выхода 4.6.1 фактически означало право выполнить код на сервере — хватало обычного object permission, без staff и superuser. CVE-2026-29514 закрыли в версии 4.6.1, но патч не удаляет уже созданные вредоносные шаблоны. Разбираю механизм, кому реально давать права на ExportTemplate и ConfigTemplate и как проверить свою инсталляцию.
Что такое CVE-2026-29514 и почему «просто экспорт» — не безобидная функция
В NetBox шаблоны ExportTemplate и ConfigTemplate обычно воспринимают как бухгалтерскую мелочь: выгрузить список оборудования в CSV, собрать конфиг для свитча по шаблону. Весной 2026 вышло исследование, которое показало, что через них можно выполнить произвольный код от имени сервисного процесса NetBox. Уязвимость получила номер CVE-2026-29514 — я сверил его по записи в NVD, по GitHub Advisory GHSA-f249-4qx6-hg68 и по release notes NetBox 4.6.1: номер и механизм совпадают везде, а вот с диапазоном версий есть подвох, о нём ниже. Когда клиент спрашивает «зачем вообще разграничивать права на шаблоны, это же не база данных» — показываю именно этот кейс, и обычно с этого начинается аудит уязвимостей и рисков информационной безопасности, которым мы закрываем подобные дыры у клиентов после внедрения NetBox.
Причина — в методе RenderTemplateMixin.get_environment_params() (netbox/extras/models/mixins.py), общем для моделей ExportTemplate и ConfigTemplate. У обеих есть поле environment_params — JSON, который пробрасывается в конструктор Jinja2 Environment при рендере. Ключ finalize — валидная опция Jinja2: функция, которая применяется к результату каждого {{ выражения }} перед выводом. До патча NetBox резолвил значение этого ключа как путь до python-объекта через Django import_string() без единого ограничения. Указав в environment_params {"finalize": "subprocess.getoutput«}, атакующий заставлял Jinja вызывать subprocess.getoutput на каждое выражение шаблона — а finalize выполняется вне механизма перехвата вызовов SandboxedEnvironment, то есть песочница Jinja2 его попросту не видит. Дальше — обычный RCE: шаблон вида {{ »id" }} возвращает вывод команды id, выполненной от имени пользователя, под которым крутится процесс NetBox — как правило, системного юзера с доступом к NETBOX_CONFIGURATION, подключению к БД и секретам плагинов.
В NVD и в GitHub Advisory (CNA — VulnCheck, исследователь — Chocapikk, issue #22079) указан диапазон 4.3.5–4.5.4. Но верхняя граница — это просто версия, на которой исследователь проверял эксплойт. Я открыл исходники: в 4.5.5–4.5.10 и в 4.6.0 метод get_environment_params() всё так же вызывает import_string() без ограничений, а исправление появилось только в 4.6.1 и в ветку 4.5 не бэкпортировалось. Уязвимый код жил с коммита 063d1fef от 29.07.2025, то есть с релиза 4.3.5, почти десять месяцев. И вот ключевой момент именно для темы прав доступа: эксплуатация не требует ни флага staff, ни superuser. Хватает узкого набора object-based permissions на модель ExportTemplate или ConfigTemplate — ровно того набора, который админы NetBox обычно выдают «безобидным» ролям вроде инженера или монтажника, чтобы те сами выгружали список портов или собирали конфиг коммутатора без похода в консоль.
Как устроена модель прав в NetBox и что реально проверяется при рендере
NetBox начиная с версии 2.9 не использует плоские права Django, а строит объектные permissions (документация — netboxlabs.com/docs/netbox/administration/permissions/): право — это набор типов объектов, пользователи или группы, действия (actions) и необязательные JSON-констрейнты в синтаксисе Django QuerySet-фильтра. Если в одном объекте констрейнта несколько условий — они работают по И, если задать список из нескольких объектов — по ИЛИ; есть токен $user для динамических условий вроде {"created_by": "$user"}. Базовых действий четыре: view, add, change, delete. Но есть и кастомные — например, sync у источников данных или render_config у устройств и виртуальных машин. Именно на этой возможности «добавить нестандартное действие под конкретную модель» и построен риск с шаблонами: у моделей ExportTemplate и ConfigTemplate есть собственные действия сверх обычного CRUD.
Чтобы отрендерить ExportTemplate через API-экспорт (GET /api/dcim/sites/?export=ИмяШаблона), нужны extras.view_exporttemplate плюс view на сам экспортируемый тип объекта — например dcim.site. Чтобы СОЗДАТЬ шаблон — отдельное действие extras.add_exporttemplate. Для ConfigTemplate путь другой: создание требует extras.add_configtemplate, а рендер — отдельного действия render (codename extras.render_configtemplate), которое NetBox также использует, когда конфиг собирается для устройства или ВМ через действие render_config уже на самом устройстве (dcim.render_config / virtualization.render_config). То есть в системе четыре разных «выключателя», а не один общий «доступ к шаблонам» — и именно этим разделением я пользуюсь при настройке ролей.
Есть и ещё один слой: переопределение шаблона конфигурации прямо на карточке устройства или ВМ (поле config_template можно задать индивидуально, в обход роли или платформы). По документации это отдельно требует view-права на Extras > Config Template плюс тот же render_config. Разработчики NetBox осознанно разделили «кто может просто сгенерировать конфиг по готовому шаблону» и «кто может создавать и менять сами шаблоны» — но на практике почти в каждом аудите NetBox я вижу, что это разделение стёрто одной широкой ролью вроде «Инженеры» с правом change на весь app extras целиком.
Кому давать add_exporttemplate и render_configtemplate: таблица ролей
После CVE-2026-29514 я стал относиться к правам на шаблоны так же, как к правам на выполнение произвольного кода — потому что формально это и есть то же самое, просто через рендер Jinja2, а не через SSH. Значит, действует тот же принцип наименьших привилегий, что и для любой административной функции: право должно совпадать с реальной задачей сотрудника, а не выдаваться «на всякий случай, чтобы не дёргали каждый раз».
Ниже — таблица ролей, которую я обычно предлагаю клиентам при первой настройке NetBox, независимо от того, разбирали мы CVE-2026-29514 конкретно или нет. | Роль | view (шаблоны) | render / render_config | add / change (шаблоны) | |---|---|---|---| | Читатель (дашборды, только просмотр) | да | нет | нет | | Инженер / монтажник (собирает конфиг по готовому шаблону) | да | да | нет | | Аналитик (выгружает отчёты и CSV) | да | да (только export, на нужные типы объектов) | нет | | Администратор NetBox (владелец шаблонов) | да | да | да | Единственная роль, у которой реально должно быть add/change на ExportTemplate и ConfigTemplate, — администратор NetBox, и таких людей в компании до 50 рабочих мест обычно один-два.
Тот же принцип я формулирую в статье про учётные записи с расширенными правами: право «создать объект, который потом выполняется сервером» логически ближе к administrative privilege, чем к обычному CRUD над карточкой устройства, и выдавать его нужно так же редко и так же журналируемо, как доступ к самой ОС сервера NetBox.
Как закрыть CVE-2026-29514: обновление и аудит того, что уже накопилось
NetBox 4.6.1 вышел 19 мая 2026, и в release notes прямо перечислены фикс issue #22079 и CVE-2026-29514. Исправление — не флаг в конфиге, а внутренний allowlist JINJA_ENV_PARAMS_ALLOWED в netbox/extras/constants.py: разрешены только перечисленные ключи Jinja2-окружения (trim_blocks, lstrip_blocks, autoescape, строки-разделители и т. п.), остальные отбрасываются при рендере; ключ undefined теперь резолвится не произвольным import_string(), а маппингом на четыре класса из самого jinja2 (Undefined, ChainableUndefined, DebugUndefined, StrictUndefined); ключ finalize объявлен устаревшим и запрещён в clean() модели — сохранить новый или изменённый шаблон с ним через форму или API больше нельзя.
Но есть нюанс, который я обязательно проверяю на апгрейде: уже сохранённые до патча шаблоны с finalize в environment_params продолжают резолвиться через старый import_string() — это осознанное решение ради обратной совместимости, оно зафиксировано в коде метода _resolve_finalize(). Значит, само по себе обновление версии не закрывает дыру, если в базе уже лежит вредоносный шаблон: его нужно найти и удалить руками, апгрейд от этого не освобождает.
Проверка версии и поиск подозрительных шаблонов делаются штатными средствами NetBox, без сторонних инструментов.
# версия установленного NetBox
curl -s https://netbox.example.com/api/status/ | jq '.["netbox-version"]'
# поиск шаблонов с непустым environment_params через nbshell (на сервере NetBox)
cd /opt/netbox && source venv/bin/activate
python3 netbox/manage.py nbshell
>>> from extras.models import ExportTemplate, ConfigTemplate
>>> [(t.pk, t.name, t.environment_params) for t in ExportTemplate.objects.all() if t.environment_params]
>>> [(t.pk, t.name, t.environment_params) for t in ConfigTemplate.objects.all() if t.environment_params]Если доступа к консоли сервера нет, то же самое — через REST API с токеном, у которого есть право на просмотр этих моделей:
curl -s -H "Authorization: Token $NETBOX_TOKEN" \
"https://netbox.example.com/api/extras/export-templates/?limit=0" \
| jq '.results[] | select((.environment_params // {}) != {}) | {id, name, environment_params}'
# то же для /api/extras/config-templates/Любой шаблон, где в environment_params есть ключ finalize (или вообще что-то, чего вы туда не клали сами), — кандидат на немедленное удаление, а не на «разобраться потом».
Кейс: садовый центр «Огород и двор», 28 рабочих мест
У «Огород и двор» сеть из офиса, трёх точек продаж и двух теплиц с автоматикой полива — 28 рабочих мест. NetBox 4.5.2 подняли годом раньше как раз для документирования: в нём держат стойки в серверной офиса, IP-план для касс, камер и контроллеров теплиц, плюс шаблоны конфигов для коммутаторов Cisco на точках продаж, которые выездной инженер собирает под новое оборудование без похода в консоль. О том, зачем вообще заводить NetBox для сети такого размера, я писал в статье про документирование инфраструктуры в NetBox для среднего бизнеса — здесь разбираю другую сторону той же системы: что происходит, когда права на неё настроены на скорую руку.
При плановом аудите прав (часть нашего сопровождения по ИБ) я проверил object permissions и нашёл группу «Выездные инженеры» (3 человека, доступ по VPN), у которой было право extras: change — на весь app extras целиком, а не на отдельные модели. Выдал его предыдущий подрядчик восемь месяцев назад «для удобства, чтобы сами правили конфиги». В это широкое право входили и add_exporttemplate, и add_configtemplate, и render на обеих моделях. Версия NetBox была 4.5.2 — внутри уязвимого диапазона (всё от 4.3.5 до 4.6.0). Признаков компрометации аудит не нашёл: проверили changelog NetBox (таблицу ObjectChange) за всё время — новые ExportTemplate и ConfigTemplate создавались только известными администраторами, а environment_params у всех существующих объектов были пустыми. Но дыра была открыта восемь месяцев на группу из трёх учётных записей с VPN-доступом снаружи.
Исправляли в три шага за один вечер. Сначала обновили NetBox 4.5.2 → 4.6.1 штатным upgrade.sh с бэкапом PostgreSQL перед миграциями и проверкой двух установленных плагинов на совместимость. Затем разбили общее право extras: change на явные действия: инженерам оставили view и render на ConfigTemplate плюс render_config на Device и VirtualMachine, а add/change/delete на ExportTemplate и ConfigTemplate оставили только за собой как администратором NetBox. Прогнали nbshell-запрос по всем существующим шаблонам — environment_params пустые везде, чистить было нечего. И добавили в еженедельный чек-лист по NetBox (тот же, что я веду по Zabbix у других клиентов) проверку версии через /api/status/ и списка групп с правом add_exporttemplate или add_configtemplate.
- Было: 3 инженера с правом add/change на весь app extras, версия 4.5.2 (уязвимая), 8 месяцев без пересмотра прав.
- Стало: add/change на шаблоны только у одного администратора NetBox, версия 4.6.1, ежемесячный пересмотр групп.
- Аудит + обновление + разбор прав — один вечер, без простоя (активных рендеров в момент апгрейда не было).
- Компрометации по changelog не найдено — окно уязвимости закрыто превентивно, а не по факту инцидента.
Что делать, если обновиться прямо сейчас нельзя
Не у всех апгрейд — вопрос одного вечера. Если на NetBox стоят плагины, ещё не проверенные под 4.6, или обновление запланировано на окно техобслуживания через месяц, закрывать дыру всё равно нужно сегодня — компенсирующим контролем на уровне прав, а не ожиданием патча. Поскольку эксплуатация CVE-2026-29514 не требует ни staff, ни superuser, а только object permission на конкретную модель, закрыть основной путь атаки можно без единой строчки апгрейда: отозвать add и change на ExportTemplate и ConfigTemplate (extras.add_exporttemplate, extras.change_exporttemplate и их пары для configtemplate) у всех, кроме реальных администраторов NetBox — правом change вредоносный environment_params точно так же вписывается в уже существующий шаблон. Право render (extras.render_configtemplate) и render_config на устройствах при этом можно оставить как было — само по себе создание вредоносного шаблона оно не даёт, риск именно в возможности ЗАПИСАТЬ шаблон с произвольным environment_params.
Важная оговорка: отзыв права на создание шаблонов не защищает задним числом от уже существующих. Если в базе до вашей чистки прав уже лежал шаблон со злонамеренным finalize — например, оставленный кем-то «на попробовать» ещё до того, как про уязвимость стало известно, — достаточно права render на этот конкретный шаблон, чтобы его отработать, и право на создание новых тут вообще ни при чём. Поэтому компенсирующий контроль обязательно нужно сочетать с тем же nbshell-аудитом из раздела про обновление: сначала ищете подозрительные environment_params среди уже существующих ExportTemplate и ConfigTemplate, потом закрываете право на создание новых, и только затем, по готовности, обновляете саму версию.
NetBox сам пишет ObjectChange на каждое создание и изменение ExportTemplate и ConfigTemplate — это фиксируется в changelog, и на него удобно повесить простой еженедельный отчёт: если в группе с широкими правами появился новый шаблон, а автора нет в списке доверенных администраторов, это повод разбираться немедленно, а не в конце месяца. Похожая история с чрезмерно широкими правами была у меня на токенах API у Proxmox Backup Server: там тоже «один токен на всё» вместо узкого набора прав на конкретный datastore создавал лишний риск, хотя и без прямого RCE. Принцип один и тот же в любой системе с ролевой моделью: чем ближе право к «выполнить код или скрипт», тем меньше людей оно должно касаться — независимо от того, насколько безобидно называется кнопка в интерфейсе.
Частые вопросы
В NVD написано «4.3.5–4.5.4», а у меня 4.5.8 — я в безопасности?
Нет. 4.5.4 — лишь версия, на которой исследователь проверил эксплойт. Исправление вошло только в NetBox 4.6.1 (19.05.2026), в ветку 4.5 его не переносили, поэтому 4.5.5–4.5.10 и 4.6.0 тоже уязвимы. Проверьте версию через /api/status/ и обновляйтесь до 4.6.1 или новее.
Обязательно ли быть суперпользователем, чтобы проэксплуатировать CVE-2026-29514?
Нет — и это главная причина писать про права, а не только про версию. По issue #22079 хватало extras.add_exporttemplate и extras.view_exporttemplate плюс view на экспортируемый тип объекта; для ConfigTemplate — права на создание или изменение шаблона и действие render. Флаги staff и superuser не нужны.
Что делать с уже существующими шаблонами после обновления до 4.6.1?
Проверить их через nbshell или REST API на непустой environment_params. Старые шаблоны с finalize ради обратной совместимости по-прежнему резолвятся через import_string() (метод _resolve_finalize), патч их не удаляет — ключ нужно убрать вручную, иначе такой шаблон к тому же нельзя будет пересохранить.
Какое право безопаснее оставить инженерам, которые просто собирают конфиг устройства по готовому шаблону?
render_config на Device и VirtualMachine плюс view на Extras > Config Template. Право add/change на сами шаблоны им для этой задачи не нужно — и именно оно является вектором эксплуатации CVE-2026-29514.
Где официально подтверждён этот CVE?
Запись CVE-2026-29514 в NVD (CNA — VulnCheck), GitHub Advisory GHSA-f249-4qx6-hg68, issue #22079 в netbox-community/netbox и release notes NetBox 4.6.1 от 19.05.2026. По механизму они совпадают; диапазон 4.3.5–4.5.4 в NVD и GHSA отражает проверенную исследователем версию, а фактически без исправления всё до 4.6.0 включительно.
Можно ли доверять разбору уязвимости в блоге исследователя (chocapikk.com)?
Это неофициальный источник, автор которого нашёл баг и репортил его через VulnCheck. Я использовал его пост только для контекста атаки и примеров эксплуатации, а не для цифр версий или дат — их я брал из NVD, release notes NetBox и исходников на GitHub.
Источники
- GitHub Advisory GHSA-f249-4qx6-hg68 — Карточка CVE-2026-29514: заявленный диапазон NetBox 4.3.5–4.5.4, CVSS v4 8.7 (High), CWE-183, описание обхода песочницы через finalize: https://github.com/advisories/GHSA-f249-4qx6-hg68
- netbox-community/netbox, issue #22079 — Первоисточник: механизм в RenderTemplateMixin.get_environment_params(), права для эксплуатации (extras.add_exporttemplate + extras.view_exporttemplate + view на тип объекта, без staff/superuser), коммит 063d1fef от 29.07.2025: https://github.com/netbox-community/netbox/issues/22079
- NetBox — Release v4.6.1 (2026-05-19) — Официальные release notes: строка «#22079 — Fix security vulnerability allowing arbitrary code execution via ExportTemplate environment_params (CVE-2026-29514)»; исходники v4.6.1 (extras/constants.py — JINJA_ENV_PARAMS_ALLOWED, extras/models/mixins.py — clean() и _resolve_finalize()): https://github.com/netbox-community/netbox/releases/tag/v4.6.1
- NVD — CVE-2026-29514 — Запись в National Vulnerability Database: опубликована 04.05.2026, CVSS 3.1 8.8 / CVSS 4.0 8.7, CWE-183, ссылки на коммит d124c5f, PR #22078 и #22170: https://nvd.nist.gov/vuln/detail/CVE-2026-29514
- NetBox Labs Docs — Object-Based Permissions — Модель прав: типы объектов, actions (view/add/change/delete + кастомные вроде render_config), JSON-констрейнты, токен $user: https://netboxlabs.com/docs/netbox/administration/permissions/
- NetBox Labs Docs — Export Templates — Официальная рекомендация «Only grant permission to create or modify export templates to trusted users», механизм рендера через ?export=, поле environment_params: https://netboxlabs.com/docs/netbox/customization/export-templates/



