
CVE-2026-39816のPoC。これにより、コード実行権限を持たないNiFiユーザーが任意のスクリプトを実行できるようになります。
CVE-2026-39816 の概念実証デモ。Apache NiFi 2.8.0 における EXECUTE_CODE
権限バイパスで、コード実行権限を意図的に拒否されたフローデザイナーが、
グラフバンドルの ExecuteGraphQuery プロセッサを介して NiFi JVM 内で
任意の Groovy を実行できるようになります。
@Restricted アノテーションの欠落 (CVE-2026-39816, CWE-95)NiFi はコード実行プロセッサを @Restricted(requiredPermission = RequiredPermission.EXECUTE_CODE)
アノテーションを介して EXECUTE_CODE 権限の背後にゲートしています。NiFi の
16 個すべての専用スクリプトコンポーネント —
ExecuteScript、InvokeScriptedProcessor、ExecuteGroovyScript、
ScriptedTransformRecord など — にはこのアノテーションが付与されているため、
フローデザイナーは認可レイヤーがこれらのプロセッサの作成や設定を許可する前に、
明示的な EXECUTE_CODE 権限を付与されている必要があります。
グラフバンドル (nifi-graph-nar + nifi-other-graph-services-nar)
には、このアノテーションが欠落した 3 つ目のコード実行パスが含まれています:
// 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); // line 463 — compile attacker string
compiledCode.put(s, compiled);
...
compiled.setBinding(bindings);
Object result = compiled.run(); // line 477 — execute in NiFi JVM
s パラメータは ExecuteGraphQuery プロセッサの「Graph Query」プロパティからの
クエリ文字列で、変更されずに渡されます。サニタイズも、許可リストも、
サンドボックスもありません — 有効な Groovy はすべて NiFi プロセスの
完全な権限でコンパイルおよび実行されます。
TinkerpopClientService も、それを駆動する ExecuteGraphQuery /
ExecuteGraphQueryRecord プロセッサも、@Restricted アノテーションを
保持していません。そのため NiFi の認可レイヤーはこれらを通常のコンポーネントとして
扱い、EXECUTE_CODE を明示的に拒否されたフローデザイナーでも
これらを作成できます。
NiFi 2.8.0 サーバーは、以下の 両方 が真の場合に悪用可能です:
オプションのグラフバンドルがインストールされている。 具体的には
nifi-other-graph-services-nar NAR — これは TinkerpopClientService を
同梱するバンドルです。この NAR がないサーバーは影響を受けません。
少なくとも 1 人のユーザーが EXECUTE_CODE なしでフローデザイナー
スタイルの権限 (プロセスグループと /controller に対する read/write) を
持っている。 これは一般的なポリシー構成です: 組織はユーザーが
パイプラインを構築・変更できるようにしたいが、任意のコードを実行させたくない
と考えます。EXECUTE_CODE 権限はまさにそのポリシーが強制しようとする
セキュリティ境界です。
両方の条件が満たされる場合、ユーザーは TinkerpopClientService を
「ByteCode Submission」モードで設定し、到達可能な任意の Gremlin サーバーを
指定して (Gremlin サーバーはサービス初期化にのみ使用され、実行はローカルで行われます)、
「Graph Query」プロパティに Groovy コードを設定した ExecuteGraphQuery
プロセッサを作成できます。プロセッサを開始すると、NiFi JVM 内で
Groovy がコンパイルおよび実行されます。
NiFi サービスアカウントとしての任意のコード実行。そこから:
flow.json.gz への書き込み、autoload ディレクトリへの NAR の配置、
またはコントローラサービスの変更による永続化の確立。このエクスプロイトは EXECUTE_CODE 権限をバイパスするため、
オペレーターがユーザーからコード実行権限を明示的に削除している場合でも
機能します — まさにこの権限が防ぐことを目的としたシナリオです。
このリポジトリには 1 つ の POC が含まれており、最も直接的なトリガーを
カバーしています: 攻撃者が制御する Groovy を Graph Query プロセッサの
プロパティに直接配置します。上流接続も、Expression Language も、
FlowFile コンテンツも不要です — プロセッサが独自のタイマーで実行されるだけです。
さらに 2 つのコードパスが存在します (Graph Query が空の場合に FlowFile
ボディコンテンツがクエリとして使用されるパスと、攻撃者が影響を与えた FlowFile
属性の Expression Language 補間がクエリテンプレートに使用されるパス)。
これらは同じシンクに追加の前提条件付きで到達するため、ここでは実証されていません。
直接パスで EXECUTE_CODE バイパスを証明するのに十分です。
setup/ — Docker Compose 環境。グラフバンドル NAR が自動ロードされた
NiFi 2.8.0 インスタンス (公式 apache/nifi:2.8.0 イメージ)、2 人のテストユーザー
(admin と flow_designer) を提供する LDAP サーバー、および
TinkerpopClientService が接続する Gremlin サーバーを起動します。
setup.sh は flow_designer がフロー編集権限を持つが EXECUTE_CODE は
持たない ようにポリシーをブートストラップし、設定を検証します。
pocs/flow_designer_groovy_rce.py — 自己完結型エクスプロイト。
flow_designer として認証し、ExecuteScript が拒否されること
(EXECUTE_CODE ゲートが機能していること) を実証してから、
POC がローカルで開始するリスナーへの bash リバースシェルを生成する
Groovy ペイロードを持つ TinkerpopClientService + を
作成します。接続が確立されると、POC は util-linux を介して
シェルを完全にインタラクティブな PTY バックアップ bash にアップグレードし、
raw モードの stdio ブリッジとライブウィンドウリサイズを備えます —
オペレーターは NiFi コンテナ内で実際のターミナルを取得できます。
前提条件: Docker、Python 3.10+、および uv。
cd setup
./setup.sh
初回実行時は Maven Central からグラフバンドル NAR をダウンロードし、 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 はオペレーターを NiFi コンテナ内の nifi サービスアカウントとして実行される
インタラクティブな bash セッションにドロップします。exit と入力するか
Ctrl-D を押すと切断され、POC は作成したプロセッサとコントローラサービスを
クリーンアップします。
Linux Docker (host.docker.internal がデフォルトで解決されない場合) では、
--shell-host <host-bridge-ip> を渡すか、setup/docker-compose.yml の
nifi サービスに extra_hosts: ["host.docker.internal:host-gateway"] を
追加してください。
終了:
cd setup
./teardown.sh
ExecuteGraphQueryscript