
POC для CVE-2026-39816, который позволяет пользователям NiFi без разрешений на выполнение кода запускать произвольные скрипты
Демонстрация proof-of-concept для CVE-2026-39816 — обход разрешения EXECUTE_CODE
в Apache NiFi 2.8.0, который позволяет разработчику потоков, которому
было намеренно отказано в привилегиях выполнения кода, запускать произвольный Groovy
в JVM NiFi через процессор ExecuteGraphQuery из graph bundle.
@Restricted в TinkerpopClientService (CVE-2026-39816, CWE-95)NiFi ограничивает процессоры, выполняющие код, разрешением EXECUTE_CODE
через аннотацию @Restricted(requiredPermission = RequiredPermission.EXECUTE_CODE).
Все 16 выделенных компонентов для написания скриптов в NiFi —
ExecuteScript, InvokeScriptedProcessor, ExecuteGroovyScript,
ScriptedTransformRecord и т. д. — несут эту аннотацию, поэтому разработчику
потоков должно быть явно предоставлено разрешение EXECUTE_CODE, прежде чем
уровень авторизации позволит ему создавать или настраивать эти
процессоры.
Graph bundle (nifi-graph-nar + nifi-other-graph-services-nar)
поставляет третий путь выполнения кода, в котором отсутствует эта аннотация:
// nifi-extension-bundles/nifi-graph-bundle/nifi-other-graph-services/
// src/main/java/org/apache/nifi/graph/TinkerpopClientService.java:451
protected Map<String, String> bytecodeSubmission(
String s, Map<String, Object> map, GraphQueryResultCallback cb) {
...
compiled = groovyShell.parse(s); // строка 463 — компиляция строки атакующего
compiledCode.put(s, compiled);
...
compiled.setBinding(bindings);
Object result = compiled.run(); // строка 477 — выполнение в JVM NiFi
Параметр s — это строка запроса из свойства "Graph Query" процессора
ExecuteGraphQuery, передаваемая без изменений. Здесь нет
санитизации, нет белого списка и нет песочницы — любой допустимый Groovy
компилируется и выполняется с полными привилегиями процесса NiFi.
Ни TinkerpopClientService, ни процессоры ExecuteGraphQuery /
ExecuteGraphQueryRecord, которые им управляют, не несут аннотацию
@Restricted. Поэтому уровень авторизации NiFi рассматривает их
как обычные компоненты, и разработчик потоков, которому было
явно отказано в EXECUTE_CODE, всё равно может их создавать.
Сервер NiFi 2.8.0 эксплуатируем, когда оба следующих условия истинны:
Установлен опциональный graph bundle. Конкретно
NAR nifi-other-graph-services-nar — это пакет, который поставляет
TinkerpopClientService. Серверы без этого NAR не затронуты.
Хотя бы один пользователь имеет разрешения уровня разработчика потоков
(read/write на группе процессов и на /controller) без
EXECUTE_CODE. Это распространённая форма политики: организация
хочет, чтобы пользователи могли создавать и изменять конвейеры, но не запускать
произвольный код. Разрешение EXECUTE_CODE — это именно та
граница безопасности, которую такая политика призвана обеспечивать.
Если оба условия выполняются, пользователь может настроить
TinkerpopClientService в режиме "ByteCode Submission", указать ему любой
достижимый Gremlin-сервер (Gremlin-сервер используется только для
инициализации сервиса — выполнение происходит локально) и создать
процессор ExecuteGraphQuery с кодом Groovy в свойстве "Graph Query".
Запуск процессора компилирует и выполняет Groovy в JVM NiFi.
Произвольное выполнение кода от имени сервисной учётной записи NiFi. Далее:
flow.json.gz, размещения NAR
в каталоге автозагрузки или изменения сервиса контроллера.Эксплойт обходит разрешение EXECUTE_CODE, поэтому он работает даже
когда оператор явно удалил права выполнения кода у
пользователя — именно тот сценарий, для предотвращения которого существует это разрешение.
Этот репозиторий содержит один POC, покрывающий наиболее прямой триггер:
управляемый атакующим Groovy, размещённый непосредственно в свойстве процессора
Graph Query. Не требуется ни внешнего подключения, ни Expression Language,
ни содержимого FlowFile — только процессор, работающий по собственному
таймеру.
Существуют два дополнительных пути выполнения кода (содержимое тела FlowFile, используемое как
запрос, когда Graph Query пуст, и интерполяция Expression Language
влияющих на атакующего атрибутов FlowFile в шаблон запроса). Они не
продемонстрированы здесь, потому что достигают того же приёмника с дополнительными
предварительными условиями; прямой путь достаточен
для доказательства обхода EXECUTE_CODE.
setup/ — окружение Docker Compose. Поднимает экземпляр NiFi 2.8.0
(официальный образ apache/nifi:2.8.0) с автоматически загруженными NAR
graph bundle, LDAP-сервер, предоставляющий двух тестовых пользователей (admin
и flow_designer), и Gremlin-сервер, на который будет указывать
TinkerpopClientService. setup.sh настраивает
политики так, чтобы flow_designer имел права редактирования потоков, но
не EXECUTE_CODE, а затем проверяет конфигурацию.
pocs/flow_designer_groovy_rce.py — автономный эксплойт.
Аутентифицируется как flow_designer, демонстрирует, что ExecuteScript
запрещён (шлюз EXECUTE_CODE работает), затем создаёт
+ с полезной нагрузкой
Groovy, которая запускает обратный bash-shell на прослушиватель, который POC
запускает локально. После установления соединения POC
повышает shell до полностью интерактивного bash с PTY-поддержкой через
util-linux , с мостом stdio в raw-режиме и динамическим
изменением размера окна — оператор получает настоящий терминал внутри контейнера NiFi.
Предварительные требования: Docker, Python 3.10+ и uv.
cd setup
./setup.sh
Первый запуск загружает NAR graph bundle из Maven Central и стягивает Docker-образы (~2-3 мин). По завершении настройки выводится готовая к вставке команда запуска POC.
Запуск POC:
uv run --no-project --with requests \
pocs/flow_designer_groovy_rce.py \
--base-url https://localhost:8443 \
--username flow_designer \
--password 'flowDesigner123!' \
--gremlin-host gremlin-server
POC открывает оператору интерактивную bash-сессию внутри
контейнера NiFi, работающую от имени сервисной учётной записи nifi. Введите exit или
нажмите Ctrl-D для отключения — после этого POC очистит процессор
и сервис контроллера, которые он создал.
В Linux Docker (где host.docker.internal не разрешается по
умолчанию) передайте --shell-host <host-bridge-ip> или добавьте
extra_hosts: ["host.docker.internal:host-gateway"] в сервис nifi
в setup/docker-compose.yml.
Завершение работы:
cd setup
./teardown.sh
TinkerpopClientServiceExecuteGraphQueryscript