PoC Archive PoC Archive
Critical CVE-2026-40217 (X41-2026-001, GHSA-3926-2jvf-fg29) patched

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

CVSS 8.8/10
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

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-05
Author / ResearcherMarkus Vervier (X41 D-Sec GmbH); PoC packaged by learner202649
CVE / AdvisoryCVE-2026-40217 (X41-2026-001, GHSA-3926-2jvf-fg29)
Categoryweb
SeverityCritical
CVSS Score8.8 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, per source advisory)
StatusPoC
Tagslitellm, llm-proxy, sandbox-escape, bytecode-manipulation, cwe-913, docker, root-rce, authenticated
RelatedN/A

Affected Target

FieldValue
Software / SystemLiteLLM (open-source LLM proxy/gateway), POST /guardrails/test_custom_code endpoint
Versions AffectedLiteLLM through the 2026-04-08 build (pre-v1.83.11); fixed in v1.83.11+
Language / PlatformPython 3 exploit against the LiteLLM proxy’s HTTP API, targeting its default Docker deployment (runs as root)
Authentication RequiredYes (a valid LiteLLM API/master key with access to the guardrail-testing endpoint)
Network Access RequiredYes

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

  1. Attacker authenticates to a LiteLLM proxy instance using a valid API/master key.
  2. Attacker submits a crafted apply_guardrail function to POST /guardrails/test_custom_code in which forbidden identifiers (__globals__, __builtins__, __import__) are built via string concatenation to dodge the regex filter.
  3. The payload obtains the base object class via str.mro()[1], creates a generator, and uses object.__setattr__ plus code.replace(co_names=...) to rewrite the generator’s bytecode so its co_names table references __globals__.
  4. Iterating the rewritten generator yields the real global namespace of an existing function, from which __builtins__["__import__"] is recovered.
  5. The recovered __import__ is used to import the os module and run an arbitrary shell command (e.g., os.popen("id").read()), with the result returned in the guardrail-test HTTP response.
  6. 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.py and exploit/payload.py in this folder, with docker-compose.yml to stand up a vulnerable target.

1
2
3
4
pip install -r requirements.txt
docker compose up -d
python3 exploit/exploit.py --target http://localhost:4000 --key "sk-litellm-master-key"
python3 exploit/exploit.py --target http://localhost:4000 --key "sk-litellm-master-key" --cmd "cat /etc/shadow"

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_code field 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

ActionDetail
Primary fixUpgrade to LiteLLM v1.83.11 or later, which replaces the hand-rolled regex sandbox with RestrictedPython
Interim mitigationBlock 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.