CVE-2026-42271: Verified Repro With Script Download
CVE-2026-42271: BerriAI LiteLLM SQL injection
CVE-2026-42271 is verified against BerriAI LiteLLM · pip. Affected versions: pip litellm >= 1.81.16, < 1.83.7 (also reported as v1.81.16 through v1.83.6). Vulnerability class: SQLi. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00261.
What Is CVE-2026-42271?
CVE-2026-42271 is a critical unauthenticated SQL injection vulnerability in BerriAI LiteLLM's proxy authentication path, listed in the CISA KEV catalog as actively exploited. A crafted Authorization bearer token reaches a raw SQL lookup, letting a remote attacker inject SQL into the LiteLLM proxy's database queries. Pruva reproduced it (reproduction REPRO-2026-00261).
CVE-2026-42271 Severity & CVSS Score
CVE-2026-42271 is rated high severity, with a CVSS base score of 8.8 out of 10.
High — serious impact or readily exploitable. Prioritize remediation.
Affected BerriAI LiteLLM Versions
BerriAI LiteLLM · pip versions pip litellm >= 1.81.16, < 1.83.7 (also reported as v1.81.16 through v1.83.6) are affected.
How to Reproduce CVE-2026-42271
pruva-verify REPRO-2026-00261 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00261/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-42271
- reached the target end-to-end
- full exploit chain demonstrated
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
Authorization Bearer token containing SQL syntax
- GET /model/info
- user_api_key_auth
- PrismaClient.get_data(combined_view)
Alternate API-Key and x-litellm-api-key authentication headers reach the same LiteLLM combined_view SQL-injection sink on vulnerable litellm==1.83.6, but litellm==1.83.7 parameterizes the sink and blocks both paths; no fixed-version bypass was confirmed.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-42271
BerriAI LiteLLM proxy versions before the upstream parameterization fix contain an API-key authentication SQL injection in the database lookup for virtual keys. During authentication, an attacker-controlled Authorization: Bearer ... value reaches the combined verification-token lookup and, in vulnerable LiteLLM 1.83.6, is interpolated directly into a raw SQL string. The reproduction starts real LiteLLM proxy processes with PostgreSQL and proves that a remote request to GET /model/info using the injected bearer token ' OR EXISTS(SELECT 1 FROM pg_sleep(3)) -- causes PostgreSQL to execute the injected pg_sleep(3) expression and the endpoint to return authenticated model information. The same request against LiteLLM 1.83.7 is rejected immediately because the token is bound as a SQL parameter.
- Package/component affected: BerriAI LiteLLM proxy authentication and database helper code, specifically the combined verification-token lookup in
litellm/proxy/utils.pyreached fromuser_api_key_authon protected proxy/API endpoints. - Affected versions: The reproduced vulnerable package is
litellm==1.83.6. The fixed negative control islitellm==1.83.7. Upstream fix commit identified in the repository is4dc416ee749122ca91e3bca095217478663419e7(fix(proxy): use parameterized query for combined_view token lookup). - Risk level and consequences: Critical. A remote unauthenticated attacker can place SQL syntax in the bearer token used by protected endpoints. In the reproduced path this bypasses virtual-key authentication and can execute database expressions. In a real deployment, the same primitive can be used for time/error/boolean SQL injection and can expose or manipulate LiteLLM gateway data such as virtual keys, configuration, and logs, depending on database privileges and SQL payload construction.
Impact Parity
- Disclosed/claimed maximum impact: Unauthenticated SQL injection through proxy/admin API requests, leading to extraction or modification of LiteLLM database contents and compromise of gateway configuration, keys, and logs.
- Reproduced impact from this run: A production-path remote API request to the real LiteLLM proxy executed attacker-supplied SQL (
pg_sleep(3)) through theAuthorizationbearer token. The vulnerable proxy returned HTTP 200 with model information, while an unknown-token baseline and the fixed build both returned HTTP 401. PostgreSQL logs captured both the interpolated vulnerable query and the fixed parameterized query. - Parity:
fullfor confirming the remote API SQL-injection vulnerability and fixed-version remediation;partialfor the broader post-exploitation consequence because this run did not dump or modify sensitive gateway database records beyond demonstrating SQL execution and authentication bypass. - Not demonstrated: Bulk extraction or modification of production secrets, provider credentials, or logs was intentionally not performed.
Root Cause
The vulnerable code constructs a raw SQL query for the combined verification-token view by formatting the token value into the query text:
WHERE v.token = '{token}'
The bearer token is attacker controlled. When Python optimization is enabled for product execution, the non-sk- assertion in the auth path is not enforced, allowing a token such as:
' OR EXISTS(SELECT 1 FROM pg_sleep(3)) --
to reach the database helper. In litellm==1.83.6, this becomes SQL equivalent to:
WHERE v.token = '' OR EXISTS(SELECT 1 FROM pg_sleep(3)) --'
PostgreSQL evaluates the injected EXISTS(SELECT 1 FROM pg_sleep(3)), delaying the request and making the WHERE condition true for at least one seeded virtual-key row. That row is then treated as the authenticated key object for the protected endpoint.
The fix changes the helper to accept query arguments and uses a positional parameter:
WHERE v.token = $1
response = await self._query_first_with_cached_plan_fallback(sql_query, hashed_token)
The upstream fix commit recorded in the reproduction evidence is:
4dc416ee749122ca91e3bca095217478663419e7—fix(proxy): use parameterized query for combined_view token lookup
Reproduction Steps
- Use
bundle/repro/reproduction_steps.sh. - The script:
- Reuses or creates the deterministic project cache described by
bundle/project_cache_context.json. - Installs/reuses
litellm==1.83.6andlitellm==1.83.7virtual environments. - Starts a local PostgreSQL instance and two real LiteLLM proxy processes, one vulnerable and one fixed.
- Creates a virtual key through each running proxy via
/key/generateso the verification-token table has a row. - Sends a baseline unknown-token request to
GET /model/info. - Sends the injected bearer-token request to vulnerable
GET /model/info. - Sends the identical injected bearer-token request to fixed
GET /model/info. - Writes
bundle/repro/runtime_manifest.jsonandbundle/repro/result.txtbefore exiting.
- Reuses or creates the deterministic project cache described by
- Expected evidence of reproduction:
- Vulnerable request takes approximately three seconds and returns HTTP 200.
- Fixed request returns HTTP 401 quickly.
- PostgreSQL logs show the vulnerable raw query with
WHERE v.token = '' OR EXISTS(SELECT 1 FROM pg_sleep(3)) --'and the fixed query withWHERE v.token = $1plus the payload only in bound parameters.
Evidence
Primary artifacts:
bundle/repro/reproduction_steps.sh— current-run reproducer.bundle/repro/runtime_manifest.json— runtime evidence manifest for the API endpoint proof.bundle/repro/result.txt— summarized timings/status codes.bundle/logs/reproduction_steps.log— full script output.bundle/logs/source_delta.txt— fix commit and patch hunk.bundle/logs/postgres_repro.log— PostgreSQL query evidence.bundle/logs/vulnerable_injection.txt— vulnerable endpoint response.bundle/logs/fixed_injection.txt— fixed endpoint response.bundle/logs/proxy_vulnerable.logandbundle/logs/proxy_fixed.log— LiteLLM proxy runtime logs.
Key current-run results from bundle/repro/result.txt:
result=confirmed
baseline_time=0.013450
baseline_code=401
vuln_injection_time=3.012161
vuln_injection_code=200
fixed_injection_time=0.015421
fixed_injection_code=401
vulnerable_version=litellm 1.83.6
fixed_version=litellm 1.83.7
fixed_commit=4dc416ee749122ca91e3bca095217478663419e7
Vulnerable response evidence from bundle/logs/vulnerable_injection.txt shows a successful protected endpoint response:
{"data":[{"model_name":"gpt-4o", ... }]}
Fixed response evidence from bundle/logs/fixed_injection.txt shows rejection of the exact same token:
{"error":{"message":"Authentication Error, Invalid proxy server token passed... Unable to find token in cache or `LiteLLM_VerificationTokenTable`","type":"token_not_found_in_db","param":"key","code":"401"}}
PostgreSQL evidence from bundle/logs/postgres_repro.log includes the vulnerable query text:
WHERE v.token = '' OR EXISTS(SELECT 1 FROM pg_sleep(3)) --'
The same log also includes the fixed query shape and parameter binding:
WHERE v.token = $1
DETAIL: Parameters: $1 = ''' OR EXISTS(SELECT 1 FROM pg_sleep(3)) --'
The runtime manifest records a real API endpoint path:
{
"entrypoint_kind": "endpoint",
"entrypoint_detail": "GET /model/info with attacker-controlled Authorization Bearer token",
"service_started": true,
"healthcheck_passed": true,
"target_path_reached": true,
"runtime_stack": ["postgresql", "litellm-proxy"]
}
Recommendations / Next Steps
- Upgrade LiteLLM proxy deployments to
1.83.7or later, or to a downstream package containing commit4dc416ee749122ca91e3bca095217478663419e7or equivalent parameterization. - Ensure all raw SQL helpers use parameterized arguments rather than f-string/interpolated query text.
- Add regression tests that send bearer tokens containing quotes, comments, and time-based SQL expressions through real protected proxy endpoints and verify they are rejected quickly.
- Rotate LiteLLM virtual keys, provider credentials, and any secrets stored in the LiteLLM database for deployments that exposed vulnerable versions to untrusted networks.
- Review PostgreSQL and proxy access logs for suspicious bearer-token values containing quotes, SQL comments,
UNION,SELECT,pg_sleep, or similar SQL syntax.
Additional Notes
- The reproduction script was executed successfully twice consecutively in the current run.
- The proof uses a real LiteLLM proxy process and a real PostgreSQL database; it does not mock the vulnerable SQL helper.
- The script bounds all HTTP requests with curl timeouts and writes all diagnostics under
bundle/logs/. - The proof avoids destructive SQL payloads. It uses
pg_sleep(3)and endpoint response divergence to demonstrate SQL execution and authentication bypass without dumping sensitive database contents.
Variant Analysis & Alternative Triggers for CVE-2026-42271
This variant stage tested whether the LiteLLM SQL-injection fix for the combined_view virtual-key lookup could be bypassed through alternate authentication header sources instead of the parent Authorization: Bearer source. Two materially distinct header entry paths, API-Key and x-litellm-api-key, both reached the same vulnerable sink in litellm==1.83.6 and produced remote SQL execution/authentication bypass on GET /model/info. The same payloads were rejected quickly by litellm==1.83.7, where the sink is parameterized, so this is an alternate trigger on vulnerable versions but not a confirmed fixed-version bypass.
Fix Coverage / Assumptions
The original fix relies on the invariant that all normal LiteLLM API-key authentication paths eventually call the same database sink: PrismaClient.get_data(table_name="combined_view", query_type="find_unique") in litellm/proxy/utils.py. Upstream commit 4dc416ee749122ca91e3bca095217478663419e7 changes that sink from raw interpolation:
WHERE v.token = '{token}'
to parameter binding:
WHERE v.token = $1
response = await self._query_first_with_cached_plan_fallback(sql_query, hashed_token)
It also changes _query_first_with_cached_plan_fallback() to accept *args and pass them through to self.db.query_first(...), including after cached-plan reconnect/retry.
The fix explicitly covers the combined-view token lookup, not just the Authorization header. It therefore covers the parent endpoint and alternate API-key headers that resolve to the same hashed_token lookup. It does not by itself parameterize unrelated raw SQL call sites elsewhere in the proxy. Source inspection found other raw SQL builders, but tested package litellm==1.83.7 includes additional allow-list validation for the most relevant user-facing raw ORDER BY candidate in /spend/logs/v2, and the other noted raw SQL paths use constants, environment/configuration values, or positional parameters rather than unauthenticated attacker input.
Variant / Alternate Trigger
Tested alternate triggers:
Candidate A:
API-Keyheader- Entry point:
GET /model/info - Input:
API-Key: ' OR EXISTS(SELECT 1 FROM pg_sleep(2)) -- api-key - Code path:
litellm/proxy/auth/user_api_key_auth.py::get_api_key()readsSpecialHeaders.azure_authorization(API-Key) and returns it as the submitted key;_fetch_key_object_from_db_with_reconnect()inlitellm/proxy/auth/auth_checks.pycallsprisma_client.get_data(..., table_name="combined_view");litellm/proxy/utils.py::PrismaClient.get_data()performs the combined verification-token SQL lookup.
- Entry point:
Candidate B:
x-litellm-api-keyheader- Entry point:
GET /model/info - Input:
x-litellm-api-key: ' OR EXISTS(SELECT 1 FROM pg_sleep(2)) -- x-litellm - Code path:
get_api_key()readsSpecialHeaders.custom_litellm_api_keyand strips/uses the received value; the same auth DB lookup then reaches the combined-view SQL sink.
- Entry point:
Both candidates are materially different header entry paths from the parent Authorization: Bearer path. Both are protected network API inputs and cross the same LiteLLM proxy trust boundary.
Additional bounded inspection:
security.mdwas read. LiteLLM treats unauthenticated protected proxy access as P1 and authenticated malicious actions as P2; this analysis does not rely on the excluded non-master_keymisconfiguration.A raw
/spend/logs/v2ordering candidate was inspected and briefly probed. Current testedlitellm==1.83.7rejects untrustedsort_byvalues withInvalid sort_by ... Must be one of: endTime, request_duration_ms, spend, startTime, total_tokens, so it is not a bypass of the auth-token fix.Package/component affected: BerriAI LiteLLM proxy authentication, specifically API-key extraction in
litellm/proxy/auth/user_api_key_auth.pyand the shared combined verification-token lookup inlitellm/proxy/utils.py.Affected version tested:
litellm==1.83.6.Fixed version tested:
litellm==1.83.7; repository release tag inspected:v1.83.7-stable(e0d5c28db02b3219dbd944666a55f49732197922); security fix commit:4dc416ee749122ca91e3bca095217478663419e7.Risk level and consequences: On vulnerable versions, alternate header sources can produce the same unauthenticated SQL injection/authentication bypass as the parent report. On the fixed version tested, the same alternate headers do not bypass the fix.
Impact Parity
- Disclosed/claimed maximum impact: Unauthenticated SQL injection through proxy/admin API requests, potentially enabling extraction or modification of LiteLLM database contents and compromise of gateway configuration, keys, and logs.
- Reproduced impact from this variant run: On
litellm==1.83.6, two alternate headers reached the vulnerable sink and caused PostgreSQL to executepg_sleep(2), withGET /model/inforeturning HTTP 200 protected model information. Onlitellm==1.83.7, identical requests returned HTTP 401 quickly. - Parity:
partialfor vulnerable-version alternate trigger coverage;noneas a fixed-version bypass because the patched version blocked both tested alternate header paths. - Not demonstrated: No destructive database reads/writes, secret dumping, or fixed-version compromise was performed.
Root Cause
The same underlying bug can be reached from multiple API-key header sources because LiteLLM accepts OpenAI/Azure/provider-compatible authentication headers and normalizes them into a key value. In vulnerable litellm==1.83.6, that value reaches PrismaClient.get_data(table_name="combined_view"), where the SQL predicate interpolates the token directly. A payload such as:
' OR EXISTS(SELECT 1 FROM pg_sleep(2)) -- api-key
is inserted into the SQL WHERE v.token = '<token>' clause, making PostgreSQL evaluate the injected expression. The fixed commit 4dc416ee749122ca91e3bca095217478663419e7 moves the same value into a bound $1 parameter, so alternate header sources no longer change SQL syntax.
Reproduction Steps
- Run
bundle/vuln_variant/reproduction_steps.sh. - The script:
- Starts a local PostgreSQL instance.
- Uses/reuses
litellm==1.83.6andlitellm==1.83.7virtual environments. - Starts real LiteLLM proxy processes with a configured
LITELLM_MASTER_KEY. - Seeds a valid virtual key row via
/key/generateso the combined-view lookup has a row to match after injection. - Sends candidate payloads to
GET /model/infousingAPI-Keyandx-litellm-api-keyheaders against both versions. - Writes
bundle/vuln_variant/result.jsonandbundle/vuln_variant/runtime_manifest.json.
- Expected evidence:
- Vulnerable API-Key request: approximately two seconds, HTTP 200.
- Vulnerable x-litellm-api-key request: approximately two seconds, HTTP 200.
- Fixed API-Key request: quick HTTP 401.
- Fixed x-litellm-api-key request: quick HTTP 401.
- PostgreSQL log contains vulnerable raw
WHERE v.token = '' OR EXISTS(...)and fixedWHERE v.token = $1parameterized queries.
The script exits 1 by design because no variant reproduced on the fixed version.
Evidence
Primary artifacts:
bundle/vuln_variant/reproduction_steps.sh— stage-specific reproducer, executed twice successfully.bundle/vuln_variant/result.json— structured timing/status checks.bundle/vuln_variant/runtime_manifest.json— runtime environment and proof artifact list.bundle/logs/vuln_variant/reproduction_steps.log— full script output from the idempotency run.bundle/logs/vuln_variant/postgres_variant.log— PostgreSQL statement evidence.bundle/logs/vuln_variant/vulnerable_api_key_header.txtandbundle/logs/vuln_variant/vulnerable_x_litellm_header.txt— vulnerable protected endpoint responses.bundle/logs/vuln_variant/fixed_api_key_header.txtandbundle/logs/vuln_variant/fixed_x_litellm_header.txt— fixed rejection responses.bundle/logs/vuln_variant/source_delta_variant.txtandbundle/logs/vuln_variant/fixed_version.txt— source/patch identity.
Key current-run result.json values:
{
"result": "alternate_trigger_blocked_by_fix",
"vulnerable_api_key_header": {"time_seconds": 2.014991, "http_code": "200"},
"vulnerable_x_litellm_header": {"time_seconds": 2.010438, "http_code": "200"},
"fixed_api_key_header": {"time_seconds": 0.019809, "http_code": "401"},
"fixed_x_litellm_header": {"time_seconds": 0.010958, "http_code": "401"}
}
The validation checks in result.json all evaluate true, including vulnerable delay/auth bypass, fixed rejection/no-delay, and PostgreSQL evidence of both vulnerable raw interpolation and fixed parameterization.
Recommendations / Next Steps
- Treat the patch as correctly addressing the shared combined-view token lookup sink, but add explicit regression tests for all accepted API-key header sources:
Authorization,API-Key,x-litellm-api-key,x-api-key, Google API key query/header compatibility, and Azure APIM subscription-key header where applicable. - Add a static or unit test guard that forbids direct interpolation of attacker-controlled strings into
query_raw,query_first, orexecute_rawSQL text. - Keep the
/spend/logs/v2sort_byandsort_orderallow-list tests because that path constructs a rawORDER BYclause even though it is blocked in the tested fixed package. - Rotate virtual keys and inspect logs for SQL-looking values in any supported API-key header on deployments that ran vulnerable versions.
Additional Notes
- The stage script was executed twice after fixes and completed deterministically with exit code
1, as expected for a negative fixed-version bypass result. - One earlier probe observed that a repeated identical payload through two alternate headers could hit cache and suppress the second timing delay. The final script uses unique SQL comment suffixes for the two header candidates so each runtime check independently reaches PostgreSQL.
- The repository checkout state was not changed; source identity was gathered using read-only
git rev-list,git show, andgit diffcommands.
CVE-2026-42271 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Unknown error
Artifacts and Evidence for CVE-2026-42271
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-42271
FAQ: CVE-2026-42271
How does the CVE-2026-42271 attack work?
Which versions of LiteLLM are affected by CVE-2026-42271, and where is it fixed?
How severe is CVE-2026-42271?
How can I reproduce CVE-2026-42271?
References for CVE-2026-42271
Authoritative sources for CVE-2026-42271 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.