
POC für CVE-2026-39816, der es NiFi-Benutzern ohne Ausführungsberechtigung für Code ermöglicht, beliebige Skripte auszuführen.
Proof-of-Concept-Demonstration für CVE-2026-39816, eine EXECUTE_CODE-
Berechtigungsumgehung in Apache NiFi 2.8.0, die es einem Flow-Designer, dem
Code-Ausführungsrechte bewusst verweigert wurden, ermöglicht, beliebiges Groovy
in der NiFi-JVM über den ExecuteGraphQuery-Prozessor des Graph-Bundles auszuführen.
@Restricted-Annotation auf TinkerpopClientService (CVE-2026-39816, CWE-95)NiFi beschränkt codeausführende Prozessoren hinter der EXECUTE_CODE-Berechtigung
über die @Restricted(requiredPermission = RequiredPermission.EXECUTE_CODE)-
Annotation. Alle 16 dedizierten Skriptkomponenten in NiFi –
ExecuteScript, InvokeScriptedProcessor, ExecuteGroovyScript,
ScriptedTransformRecord usw. – tragen diese Annotation, sodass ein Flow-
Designer die explizite EXECUTE_CODE-Berechtigung erhalten muss, bevor
die Autorisierungsebene ihm erlaubt, diese Prozessoren zu erstellen oder zu konfigurieren.
Das Graph-Bundle (nifi-graph-nar + nifi-other-graph-services-nar)
enthält einen dritten Code-Ausführungspfad, dem diese Annotation fehlt:
// 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); // Zeile 463 – Angreifer-String kompilieren
compiledCode.put(s, compiled);
...
compiled.setBinding(bindings);
Object result = compiled.run(); // Zeile 477 – in NiFi-JVM ausführen
Der Parameter s ist die Abfragezeichenfolge aus der Eigenschaft „Graph Query"
des ExecuteGraphQuery-Prozessors, die unverändert durchgereicht wird. Es gibt
keine Bereinigung, keine Whitelist und keine Sandbox – jedes gültige Groovy wird
mit den vollen Rechten des NiFi-Prozesses kompiliert und ausgeführt.
Weder TinkerpopClientService noch die Prozessoren ExecuteGraphQuery /
ExecuteGraphQueryRecord, die ihn steuern, tragen die
@Restricted-Annotation. Die Autorisierungsebene von NiFi behandelt sie daher
als gewöhnliche Komponenten, und ein Flow-Designer, dem EXECUTE_CODE
ausdrücklich verweigert wurde, kann sie weiterhin erstellen.
Ein NiFi-2.8.0-Server ist ausnutzbar, wenn beide der folgenden Bedingungen zutreffen:
Das optionale Graph-Bundle ist installiert. Insbesondere die
nifi-other-graph-services-nar-NAR – dies ist das Bundle, das
TinkerpopClientService enthält. Server ohne diese NAR sind nicht betroffen.
Mindestens ein Benutzer hat Flow-Designer-ähnliche Berechtigungen
(read/write auf eine Prozessgruppe und auf /controller) ohne
EXECUTE_CODE. Dies ist eine häufige Richtlinienform: Eine Organisation
möchte, dass Benutzer Pipelines erstellen und ändern können, aber kein
beliebiges Code ausführen. Die EXECUTE_CODE-Berechtigung ist genau die
Sicherheitsgrenze, die diese Richtlinie durchsetzen soll.
Wenn beide Bedingungen erfüllt sind, kann der Benutzer einen
TinkerpopClientService im Modus „ByteCode Submission" konfigurieren, ihn auf einen
beliebigen erreichbaren Gremlin-Server ausrichten (der Gremlin-Server wird nur für
die Dienstinitialisierung verwendet – die Ausführung erfolgt lokal) und einen
ExecuteGraphQuery-Prozessor mit Groovy-Code in der Eigenschaft „Graph Query"
erstellen. Das Starten des Prozessors kompiliert und führt das Groovy in der
NiFi-JVM aus.
Beliebige Code-Ausführung als NiFi-Dienstkonto. Von dort aus:
flow.json.gz, Ablegen einer NAR
im Autoload-Verzeichnis oder Ändern eines Controller-Service.Der Exploit umgeht die EXECUTE_CODE-Berechtigung, sodass dies selbst dann
funktioniert, wenn der Betreiber dem Benutzer die Code-Ausführungsrechte
ausdrücklich entzogen hat – genau das Szenario, das die Berechtigung verhindern soll.
Dieses Repository enthält einen POC, der den direktesten Auslöser abdeckt: Angreiferkontrolliertes Groovy, das direkt in die Prozessoreigenschaft „Graph Query" eingefügt wird. Keine vorgelagerte Verbindung, keine Expression Language, kein FlowFile-Inhalt erforderlich – nur der Prozessor, der über seinen eigenen Timer läuft.
Zwei zusätzliche Codepfade existieren (FlowFile-Body-Inhalt als Abfrage, wenn
„Graph Query" leer ist, und Expression-Language-Interpolation von
angreiferbeeinflussten FlowFile-Attributen in die Abfragevorlage). Sie werden hier
nicht demonstriert, da sie dieselbe Senke mit zusätzlichen Vorbedingungen
erreichen; der direkte Pfad reicht aus, um die EXECUTE_CODE-Umgehung zu beweisen.
setup/ – Docker-Compose-Umgebung. Startet eine NiFi-2.8.0-
Instanz (offizielles apache/nifi:2.8.0-Image) mit automatisch geladenen
Graph-Bundle-NARs, einen LDAP-Server mit zwei Testbenutzern (admin
und flow_designer) sowie einen Gremlin-Server, auf den der
TinkerpopClientService zeigen kann. setup.sh richtet die
Richtlinien so ein, dass flow_designer Flow-Bearbeitungsrechte, aber
nicht EXECUTE_CODE hat, und überprüft anschließend die Konfiguration.
pocs/flow_designer_groovy_rce.py – Eigenständiger Exploit.
Authentifiziert sich als flow_designer, zeigt, dass ExecuteScript
verweigert wird (das EXECUTE_CODE-Gate funktioniert), und erstellt dann
einen + mit einer Groovy-
Nutzlast, die eine Bash-Reverse-Shell zu einem Listener startet, den der POC
lokal startet. Sobald die Verbindung hergestellt ist, stuft der POC
die Shell zu einer vollständig interaktiven PTY-gestützten Bash über
util-linux hoch, mit Raw-Mode-Stdio-Brücke und Live-Fenster-
Größenanpassung – der Bediener erhält ein echtes Terminal im NiFi-Container.
Voraussetzungen: Docker, Python 3.10+ und uv.
cd setup
./setup.sh
Der erste Lauf lädt die Graph-Bundle-NARs von Maven Central herunter und zieht Docker-Images (~2-3 Min.). Wenn das Setup abgeschlossen ist, wird der bereit zum Einfügen POC-Aufruf ausgegeben.
POC ausführen:
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
Der POC bringt den Bediener in eine interaktive Bash-Sitzung im
NiFi-Container, die als nifi-Dienstkonto läuft. Geben Sie exit ein oder
drücken Sie Ctrl-D, um die Verbindung zu trennen – der POC räumt dann den
Prozessor und den Controller-Service auf, die er erstellt hat.
Unter Linux-Docker (wo host.docker.internal standardmäßig nicht aufgelöst wird),
übergeben Sie --shell-host <host-bridge-ip> oder fügen Sie
extra_hosts: ["host.docker.internal:host-gateway"] zum nifi-
Dienst in setup/docker-compose.yml hinzu.
Abbau:
cd setup
./teardown.sh
TinkerpopClientServiceExecuteGraphQueryscript