Skip to main content

vLLM CVE-2026-54236

MEDIUM
Insertion of Sensitive Information into Log File (CWE-532)
2026-06-17 https://github.com/vllm-project/vllm GHSA-hgg8-fqqc-vfmw
5.3
CVSS 3.1 · Vendor: https://github.com/vllm-project/vllm
Share

Severity by source

Vendor (https://github.com/vllm-project/vllm) PRIMARY
5.3 MEDIUM
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
vuln.today AI
5.3 MEDIUM

Network-reachable Anthropic API endpoint requires no authentication; only a single heap address is disclosed with no integrity or availability impact.

3.1 AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
4.0 AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N
Red Hat
5.3 MEDIUM
qualitative

Primary rating from Vendor (https://github.com/vllm-project/vllm).

CVSS VectorVendor: https://github.com/vllm-project/vllm

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
None
Availability
None

Lifecycle Timeline

3
CVSS changed
Jun 22, 2026 - 23:22 NVD
5.3 (MEDIUM)
Source Code Evidence Fetched
Jun 18, 2026 - 01:44 vuln.today
Analysis Generated
Jun 18, 2026 - 01:44 vuln.today

Blast Radius

ecosystem impact
† from your stack dependencies † transitive graph · vuln.today resolves 4-path depth
  • 2 pypi packages depend on vllm (2 direct, 0 indirect)

Ecosystem-wide dependent count for version 0.23.0.

DescriptionCVE.org

vLLM: incomplete CVE-2026-22778 fix leaks PIL repr addresses via the Anthropic API router

Researcher: Kai Aizen - SnailSploit (@SnailSploit), Adversarial & Offensive Security Research Severity: CVSS 3.1 5.3 (Medium) AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N Target: https://github.com/vllm-project/vllm

---

Summary

The fix for CVE-2026-22778 / GHSA-4r2x-xpjr-7cvv (PRs #31987 and #32319) introduced sanitize_message and applied it at four FastAPI exception-handling sites in the OpenAI router. The sanitizer strips object-repr memory addresses (<_io.BytesIO object at 0x7a95e299e750><_io.BytesIO object>) before error messages reach the client, defeating the ASLR-bypass primitive that CVE-2026-22778 chained with a libopenjp2 heap overflow for RCE.

The fix is incomplete: response paths added to vLLM at or after the same time as the fix continue to echo str(exc) directly to clients without sanitize_message. The original Stage 1 primitive - sending malformed image bytes so PIL raises UnidentifiedImageError whose message contains the BytesIO object repr - reaches all of them unmodified and leaks the heap address verbatim in the response body.

All five lines below are present in main HEAD (771e1e48b, 2026-05-26).

Affected sites

Current main HEAD (771e1e48b, 2026-05-26):

| File | Line | Code |

|

Why the global exception handler does not save these paths

|---|---|---|---| | 1 | vllm/entrypoints/anthropic/api_router.py | 78 | message=str(e), (inside POST /v1/messages exception handler) | | 2 | vllm/entrypoints/anthropic/api_router.py | 124 | message=str(e), (inside POST /v1/messages/count_tokens) | | 3 | vllm/entrypoints/anthropic/serving.py | 808 | error=AnthropicError(type="internal_error", message=str(e)), (SSE streaming converter) | | 4 | vllm/entrypoints/speech_to_text/realtime/connection.py | 75 | await self.send_error(str(e), "processing_error") (WebSocket event loop) | | 5 | vllm/entrypoints/speech_to_text/realtime/connection.py | 265 | await self.send_error(str(e), "processing_error") (WebSocket generation loop) |

api_server.py registers a catch-all app.exception_handler(Exception)(exception_handler) at line 262, and that handler calls create_error_response(exc) which DOES apply sanitize_message. However, FastAPI exception handlers fire only on unhandled exceptions that propagate out of a route function.

All affected HTTP paths catch Exception *inside* the route coroutine and construct the response themselves:

python
# vllm/entrypoints/anthropic/api_router.py:71-81 (POST /v1/messages)
try:
    generator = await handler.create_messages(request, raw_request)
except Exception as e:
    logger.exception("Error in create_messages: %s", e)
    return JSONResponse(
        status_code=HTTPStatus.INTERNAL_SERVER_ERROR.value,
        content=AnthropicErrorResponse(
            error=AnthropicError(
                type="internal_error",
                message=str(e),
# <-- unsanitized
            )
        ).model_dump(),
    )

Because the exception is caught and a JSONResponse is returned in-route, every registered FastAPI exception handler - including the sanitizing global one - is bypassed. The WebSocket path bypasses it for a different reason: WebSocket frames don't traverse FastAPI's HTTP exception handler chain at all.

Reachability - the same primitive as the parent CVE

The Anthropic Messages API accepts image content parts in the request body (type: "image" with base64 source.data or type: "image_url"). Image bytes are passed to the same multimodal loader used by the OpenAI router. Malformed bytes cause PIL.Image.open to raise:

UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x7a95e299e750>

The exception propagates up through handler.create_messages into the except Exception as e: at api_router.py:75. str(e) returns the exception message verbatim, including the address. The address ends up in the error.message field of the JSON response body returned to the attacker. ASLR entropy on the affected process drops from ~4 billion to ~8 candidates, identically to CVE-2026-22778 Stage 1.

The same primitive is reachable on POST /v1/messages/count_tokens (route #2), inside the SSE streaming converter when an exception is raised mid-stream (route #3), and over the realtime speech-to-text WebSocket when audio decoder or generation paths raise an exception containing any object repr (routes #4, #5).

Chronology - these are scope misses, not legacy code

  • 2026-01-09: PR #31987 (aa125ecf0) introduces sanitize_message and applies it to OpenAI router HTTP exception handlers.
  • 2026-01-15 (six days later): PR #32369 (4c1c501a7) adds vllm/entrypoints/anthropic/api_router.py containing line 78's message=str(e). The fix was not applied to the new router.
  • 2026-03-02 (~two months later): PR #35588 (9a87b0578) adds the Anthropic count_tokens endpoint, replicating the same message=str(e) pattern at line 124.
  • 2026-05-12 (~four months later): PR #42370 (d37e25ffb) consolidates speech-to-text entrypoints and the realtime WebSocket uses send_error(str(e), ...) for both error paths.
  • 2026-05-26: current main HEAD, all five lines still present.

Remediation

1. Apply sanitize_message symmetrically to the five sites

python
# vllm/entrypoints/anthropic/api_router.py - add at top:
from vllm.entrypoints.utils import sanitize_message
# Line 78 (POST /v1/messages) and Line 124 (count_tokens):
message=sanitize_message(str(e)),
python
# vllm/entrypoints/anthropic/serving.py - add at top:
from vllm.entrypoints.utils import sanitize_message
# Line 808:
error=AnthropicError(type="internal_error", message=sanitize_message(str(e))),
python
# vllm/entrypoints/speech_to_text/realtime/connection.py - add at top:
from vllm.entrypoints.utils import sanitize_message
# Lines 75 and 265:
await self.send_error(sanitize_message(str(e)), "processing_error")

2. Tighten the regex (defense in depth)

The current regex r" at 0x[0-9a-f]+>" is narrow - it only matches the exact CPython builtin object-repr suffix in lowercase hex with a trailing >. Future Python versions, C extensions, or custom __repr__ methods could produce non-matching formats that re-enable the leak:

python
# vllm/entrypoints/utils.py
def sanitize_message(message: str) -> str:
# Strip any standalone hex address; downstream observers don't need them.
    return re.sub(r"\b0x[0-9a-fA-F]{6,}\b", "0x?", message)

3. Future-proofing: consider a response middleware

Both the route-local exception handling pattern (Anthropic router) and the WebSocket path bypass FastAPI's exception handler chain. A response-level middleware that always invokes sanitize_message on outgoing error bodies would prevent this class of regression entirely.

Affected versions

  • All vLLM versions containing vllm/entrypoints/anthropic/api_router.py (introduced 2026-01-15 in PR #32369).
  • All vLLM versions containing vllm/entrypoints/speech_to_text/realtime/connection.py (introduced 2026-05-12 in PR #42370).
  • Confirmed present in main HEAD 771e1e48b (2026-05-26).

Steps to reproduce

  1. Clone the target: git clone --depth 1 https://github.com/vllm-project/vllm
  2. Run the proof of concept (PoC.py) against the cloned source.
  3. Observe the result shown under *Verified result* below.

Credit

Kai Aizen - SnailSploit (@SnailSploit). Adversarial & Offensive Security Research.

Fix

A fix for this vulnerability was added here: https://github.com/vllm-project/vllm/pull/45119

AnalysisAI

Heap memory address leakage in vLLM's Anthropic API router and speech-to-text WebSocket paths exposes the same ASLR-bypass primitive previously fixed in CVE-2026-22778. Five exception-handling sites across vllm/entrypoints/anthropic/api_router.py, vllm/entrypoints/anthropic/serving.py, and vllm/entrypoints/speech_to_text/realtime/connection.py echo raw str(e) output - including PIL BytesIO object repr strings containing heap addresses - directly to unauthenticated callers. A proof-of-concept is referenced in the advisory, and when chained with the parent CVE's libopenjp2 heap overflow, this leak reduces ASLR entropy from approximately 4 billion candidates to 8, enabling RCE; no public exploit identified at time of analysis for this CVE in isolation.

Technical ContextAI

vLLM's multimodal image pipeline passes user-supplied bytes to PIL.Image.open(); malformed input causes PIL to raise UnidentifiedImageError whose string representation includes the BytesIO heap address (e.g., <_io.BytesIO object at 0x7a95e299e750>). The CVE-2026-22778 fix introduced sanitize_message - a regex-based sanitizer stripping at 0x[0-9a-f]+> patterns - and applied it to four FastAPI exception-handling sites in the OpenAI router. However, three modules added after that fix (Anthropic router PR #32369 on 2026-01-15, count_tokens endpoint PR #35588 on 2026-03-02, speech-to-text WebSocket PR #42370 on 2026-05-12) use inline except Exception as e: blocks that return responses constructed with bare str(e), bypassing both sanitize_message and FastAPI's global exception handler (which fires only on unhandled exceptions escaping route coroutines). WebSocket paths additionally fall outside FastAPI's HTTP exception handler chain entirely. The root cause class is CWE-532 (Insertion of Sensitive Information into Log File / error output). The affected package is pkg:pip/vllm, confirmed present at main HEAD commit 771e1e48b (2026-05-26).

RemediationAI

Apply the upstream fix from PR #45119 (commit 94923629729381d7f7c9efde72071a2441f7fd82, https://github.com/vllm-project/vllm/pull/45119), which wraps all five str(e) calls with sanitize_message() in vllm/entrypoints/anthropic/api_router.py (lines 78 and 124), vllm/entrypoints/anthropic/serving.py (line 808), and vllm/entrypoints/speech_to_text/realtime/connection.py (lines 75 and 265). This is an upstream fix available via PR/commit; a released tagged version is not independently confirmed in available data. Until an official release is available, operators can apply the five-line patch manually by importing sanitize_message from vllm.entrypoints.serve.utils.api_utils and wrapping each str(e) call in the affected exception handlers. As a compensating control, operators who do not require the Anthropic-compatible API can disable that router entirely, eliminating routes 1-3; operators who do not require speech-to-text can disable the realtime WebSocket, eliminating routes 4-5. Disabling these endpoints breaks the corresponding API surface. The advisory also recommends broadening the sanitizer regex from the narrow r" at 0x[0-9a-f]+>" pattern to r"\b0x[0-9a-fA-F]{6,}\b" to prevent future bypass via custom __repr__ formats or uppercase hex.

More in Python

View all
CVE-2025-24016 CRITICAL POC
9.9 Feb 10

Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t

CVE-2025-27520 CRITICAL POC
9.8 Apr 04

BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser

CVE-2025-2945 CRITICAL POC
9.9 Apr 03

pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi

CVE-2013-5093 MEDIUM POC
6.8 Sep 27

The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python

CVE-2025-32375 CRITICAL POC
9.8 Apr 09

BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2024-21644 HIGH POC
7.5 Jan 08

pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.

CVE-2026-33017 CRITICAL POC
9.3 Mar 17

Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301

CVE-2017-9462 HIGH POC
8.8 Jun 06

In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse

CVE-2026-49869 CRITICAL POC
10.0 Jun 26

Unauthenticated remote code execution affects Kestra OSS (the open-source event-driven orchestration platform) prior to

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2024-21645 MEDIUM POC
5.3 Jan 08

pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne

Vendor StatusVendor

Share

CVE-2026-54236 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy