LiteLLM Guardrail Custom-Code Sandbox Escape to Root RCE (CVE-2026-40217)
by Markus Vervier (X41 D-Sec GmbH); PoC packaged by learner202649 · 2026-07-05
- Severity
- Critical
- CVE
- CVE-2026-40217 (X41-2026-001, GHSA-3926-2jvf-fg29)
- Category
- web
- Affected product
- LiteLLM (open-source LLM proxy/gateway), POST /guardrails/test_custom_code endpoint
- Affected versions
- LiteLLM through the 2026-04-08 build (pre-v1.83.11); fixed in v1.83.11+
- Disclosed
- 2026-07-05
- Patch status
- patched
Tags
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-05 |
| Last Updated | 2026-05 |
| Author / Researcher | Markus Vervier (X41 D-Sec GmbH); PoC packaged by learner202649 |
| CVE / Advisory | CVE-2026-40217 (X41-2026-001, GHSA-3926-2jvf-fg29) |
| Category | web |
| Severity | Critical |
| CVSS Score | 8.8 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, per source advisory) |
| Status | PoC |
| Tags | litellm, llm-proxy, sandbox-escape, bytecode-manipulation, cwe-913, docker, root-rce, authenticated |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | LiteLLM (open-source LLM proxy/gateway), POST /guardrails/test_custom_code endpoint |
| Versions Affected | LiteLLM through the 2026-04-08 build (pre-v1.83.11); fixed in v1.83.11+ |
| Language / Platform | Python 3 exploit against the LiteLLM proxy’s HTTP API, targeting its default Docker deployment (runs as root) |
| Authentication Required | Yes (a valid LiteLLM API/master key with access to the guardrail-testing endpoint) |
| Network Access Required | Yes |
Summary
LiteLLM’s guardrail-testing endpoint lets authenticated users submit custom Python code that is checked with a regex-based source-code filter meant to block dangerous identifiers such as __globals__, __builtins__, and __import__. Because the filter only inspects source text and not runtime behavior, an attacker can build the blocked identifiers via string concatenation and then use CPython’s code.replace()/object.__setattr__() to rewrite a generator function’s bytecode name table (co_names) at runtime, recovering access to real builtins and __import__ without the blocked strings ever appearing literally in the submitted code. This bypass lets the submitted “guardrail” code execute arbitrary OS commands inside the proxy process, which in LiteLLM’s default Docker image runs as root, giving full container compromise from what is nominally a code-testing sandbox.
Vulnerability Details
Root Cause
Improper control of dynamically-managed code resources (CWE-913): the guardrail sandbox relies solely on regex filtering of submitted source text rather than restricting the actual runtime capabilities available to executed code, so bytecode-level tricks (code.replace, object.__setattr__, generator gi_code) recover full builtins access that the regex was meant to block.
Attack Vector
- Attacker authenticates to a LiteLLM proxy instance using a valid API/master key.
- Attacker submits a crafted
apply_guardrailfunction toPOST /guardrails/test_custom_codein which forbidden identifiers (__globals__,__builtins__,__import__) are built via string concatenation to dodge the regex filter. - The payload obtains the base
objectclass viastr.mro()[1], creates a generator, and usesobject.__setattr__pluscode.replace(co_names=...)to rewrite the generator’s bytecode so itsco_namestable references__globals__. - Iterating the rewritten generator yields the real global namespace of an existing function, from which
__builtins__["__import__"]is recovered. - The recovered
__import__is used to import theosmodule and run an arbitrary shell command (e.g.,os.popen("id").read()), with the result returned in the guardrail-test HTTP response. - Because the default Docker image runs the LiteLLM proxy as root, the attacker’s command executes with root privileges inside the container.
Impact
Authenticated remote code execution as root inside the LiteLLM proxy container, allowing full compromise of the proxy, exposure of upstream LLM API keys/secrets, and a pivot point into any network the container can reach.
Environment / Lab Setup
Target: LiteLLM proxy Docker image pinned to the vulnerable pre-fix digest (per docker-compose.yml)
Attacker: Python 3, requests (see requirements.txt), Docker/Docker Compose
Proof of Concept
PoC Script
See
exploit/exploit.pyandexploit/payload.pyin this folder, withdocker-compose.ymlto stand up a vulnerable target.
| |
The exploit script builds the bytecode-rewriting payload from payload.py, submits it to the guardrail-testing endpoint with an attacker-chosen shell command, and prints the command’s output as returned by the vulnerable LiteLLM instance — demonstrating root-level command execution inside the container.
Detection & Indicators of Compromise
Signs of compromise:
- Guardrail-test API calls whose
custom_codefield avoids literal__globals__/__import__strings but reconstructs them via concatenation - Unexpected root-owned shell processes spawned from the LiteLLM proxy container
- Outbound connections or file reads (e.g.,
/etc/shadow) originating from the LiteLLM proxy shortly after a guardrail-test call
Remediation
| Action | Detail |
|---|---|
| Primary fix | Upgrade to LiteLLM v1.83.11 or later, which replaces the hand-rolled regex sandbox with RestrictedPython |
| Interim mitigation | Block or restrict access to /guardrails/test_custom_code at the reverse proxy/gateway, limit master-key distribution to trusted admins, and run the LiteLLM container as a non-root user |
References
Notes
Mirrored from https://github.com/learner202649/CVE-2026-40217-PoC on 2026-07-05.