CVE-2026-54236
vLLM: incomplete CVE-2026-22778 fix leaks PIL repr addresses via Anthropic router
vLLM is an inference and serving engine for large language models (LLMs). Prior to 0.23.1rc0, the fix for CVE-2026-22778, which introduced a sanitize_message helper that strips object-repr memory addresses from error messages before they reach the client, is incomplete: several response paths echo str(exc) directly to clients without calling sanitize_message. The unsanitized sites include the Anthropic API router in vllm/entrypoints/anthropic/api_router.py (the POST /v1/messages and POST /v1/messages/count_tokens handlers), the Server-Sent Events streaming converter in vllm/entrypoints/anthropic/serving.py, and the realtime speech-to-text WebSocket in vllm/entrypoints/speech_to_text/realtime/connection.py. These paths catch the exception inside the route coroutine and construct the JSONResponse themselves, bypassing the sanitizing global FastAPI exception handler, and WebSocket frames do not traverse that handler chain at all. Using the same primitive as the parent issue, an unauthenticated attacker can send malformed image bytes through the Anthropic Messages API image content parts so that PIL.Image.open raises an UnidentifiedImageError whose message contains the BytesIO object repr, leaking the heap memory address verbatim in the error.message field of the response body. This vulnerability is fixed in 0.23.1rc0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- Low
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
5.3
vLLM: incomplete CVE-2026-22778 fix leaks PIL repr addresses via Anthropic router
vLLM is an inference and serving engine for large language models (LLMs). Prior to 0.23.1rc0, the fix for CVE-2026-22778, which introduced a sanitize_message helper that strips object-repr memory addresses from error messages before they reach the client, is incomplete: several response paths echo str(exc) directly to clients without calling sanitize_message. The unsanitized sites include the Anthropic API router in vllm/entrypoints/anthropic/api_router.py (the POST /v1/messages and POST /v1/messages/count_tokens handlers), the Server-Sent Events streaming converter in vllm/entrypoints/anthropic/serving.py, and the realtime speech-to-text WebSocket in vllm/entrypoints/speech_to_text/realtime/connection.py. These paths catch the exception inside the route coroutine and construct the JSONResponse themselves, bypassing the sanitizing global FastAPI exception handler, and WebSocket frames do not traverse that handler chain at all. Using the same primitive as the parent issue, an unauthenticated attacker can send malformed image bytes through the Anthropic Messages API image content parts so that PIL.Image.open raises an UnidentifiedImageError whose message contains the BytesIO object repr, leaking the heap memory address verbatim in the error.message field of the response body. This vulnerability is fixed in 0.23.1rc0.
- Record
CVE-2026-54236- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-532
- Updated
- 2026-11-02
EUVDEUVD-2026-38403
5.3
vLLM is an inference and serving engine for large language models (LLMs). Prior to 0.23.1rc0, the fix for CVE-2026-22778, which introduced a sanitize_message helper that strips object-repr memory addresses from error messages before they reach the client, is incomplete: several response paths echo str(exc) directly to clients without calling sanitize_message. The unsanitized sites include the Anthropic API router in vllm/entrypoints/anthropic/api_router.py (the POST /v1/messages and POST /v1/messages/count_tokens handlers), the Server-Sent Events streaming converter in vllm/entrypoints/anthropic/serving.py, and the realtime speech-to-text WebSocket in vllm/entrypoints/speech_to_text/realtime/connection.py. These paths catch the exception inside the route coroutine and construct the JSONResponse themselves, bypassing the sanitizing global FastAPI exception handler, and WebSocket frames do not traverse that handler chain at all. Using the same primitive as the parent issue, an unauthenticated attacker can send malformed image bytes through the Anthropic Messages API image content parts so that PIL.Image.open raises an UnidentifiedImageError whose message contains the BytesIO object repr, leaking the heap memory address verbatim in the error.message field of the response body. This vulnerability is fixed in 0.23.1rc0.
- Record
EUVD-2026-38403- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-hgg8-fqqc-vfmw
5.3
vLLM: incomplete CVE-2026-22778 fix leaks PIL repr addresses via Anthropic router
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 |
|---|---|---|---|
| 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) |
Why the global exception handler does not save these paths
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:
# 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,…
- Record
GHSA-hgg8-fqqc-vfmw- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-532
- Updated
- 2026-11-02
NVD
5.3
vLLM is an inference and serving engine for large language models (LLMs). Prior to 0.23.1rc0, the fix for CVE-2026-22778, which introduced a sanitize_message helper that strips object-repr memory addresses from error messages before they reach the client, is incomplete: several response paths echo str(exc) directly to clients without calling sanitize_message. The unsanitized sites include the Anthropic API router in vllm/entrypoints/anthropic/api_router.py (the POST /v1/messages and POST /v1/messages/count_tokens handlers), the Server-Sent Events streaming converter in vllm/entrypoints/anthropic/serving.py, and the realtime speech-to-text WebSocket in vllm/entrypoints/speech_to_text/realtime/connection.py. These paths catch the exception inside the route coroutine and construct the JSONResponse themselves, bypassing the sanitizing global FastAPI exception handler, and WebSocket frames do not traverse that handler chain at all. Using the same primitive as the parent issue, an unauthenticated attacker can send malformed image bytes through the Anthropic Messages API image content parts so that PIL.Image.open raises an UnidentifiedImageError whose message contains the BytesIO object repr, leaking the heap memory address verbatim in the error.message field of the response body. This vulnerability is fixed in 0.23.1rc0.
- Record
CVE-2026-54236- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-532
- Updated
- 2026-11-02
OSV
5.3
vLLM: incomplete CVE-2026-22778 fix leaks PIL repr addresses via Anthropic router
vLLM is an inference and serving engine for large language models (LLMs). Prior to 0.23.1rc0, the fix for CVE-2026-22778, which introduced a sanitize_message helper that strips object-repr memory addresses from error messages before they reach the client, is incomplete: several response paths echo str(exc) directly to clients without calling sanitize_message. The unsanitized sites include the Anthropic API router in vllm/entrypoints/anthropic/api_router.py (the POST /v1/messages and POST /v1/messages/count_tokens handlers), the Server-Sent Events streaming converter in vllm/entrypoints/anthropic/serving.py, and the realtime speech-to-text WebSocket in vllm/entrypoints/speech_to_text/realtime/connection.py. These paths catch the exception inside the route coroutine and construct the JSONResponse themselves, bypassing the sanitizing global FastAPI exception handler, and WebSocket frames do not traverse that handler chain at all. Using the same primitive as the parent issue, an unauthenticated attacker can send malformed image bytes through the Anthropic Messages API image content parts so that PIL.Image.open raises an UnidentifiedImageError whose message contains the BytesIO object repr, leaking the heap memory address verbatim in the error.message field of the response body. This vulnerability is fixed in 0.23.1rc0.
- Record
CVE-2026-54236- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-532
- Updated
- 2026-11-02
OSVGHSA-hgg8-fqqc-vfmw
5.3
vLLM: incomplete CVE-2026-22778 fix leaks PIL repr addresses via Anthropic router
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 |
|---|---|---|---|
| 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) |
Why the global exception handler does not save these paths
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:
# 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,…
- Record
GHSA-hgg8-fqqc-vfmw- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-532
- Updated
- 2026-11-02
OSVPYSEC-2026-3408 No score
vLLM: incomplete CVE-2026-22778 fix leaks PIL repr addresses via Anthropic router
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 |
|---|---|---|---|
| 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) |
Why the global exception handler does not save these paths
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:
# 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,…
- Record
PYSEC-2026-3408- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
vllmPyPI |
through 0.23.0 | 0.24.0 |
vllmPyPI |
before 0.24.0, plus listed versions 0.0.10.1.00.1.10.1.20.1.3 Show 45 moreShow less0.1.40.1.50.1.60.1.70.10.00.10.10.10.1.10.10.20.11.00.11.10.11.20.12.00.13.00.14.00.14.10.15.00.15.10.16.00.17.00.17.10.18.00.18.10.19.00.19.10.2.00.2.10.2.1.post10.2.20.2.30.2.40.2.50.2.60.2.70.20.00.20.10.20.20.21.00.22.00.22.10.23.00.3.00.3.10.3.20.3.30.4.0 |
0.24.0 |
vllmPyPI |
through 0.23.0, plus listed versions 0.0.10.1.00.1.10.1.20.1.3 Show 45 moreShow less0.1.40.1.50.1.60.1.70.10.00.10.10.10.1.10.10.20.11.00.11.10.11.20.12.00.13.00.14.00.14.10.15.00.15.10.16.00.17.00.17.10.18.00.18.10.19.00.19.10.2.00.2.10.2.1.post10.2.20.2.30.2.40.2.50.2.60.2.70.20.00.20.10.20.20.21.00.22.00.22.10.3.00.3.10.3.20.3.30.4.00.4.0.post1 |
None known |
Public exploits
Links only. Every CVE stores no exploit code.
| Source | Link | Date |
|---|---|---|
| Nuclei | vLLM <= 0.23.0 - Anthropic Router Heap Address Information Leak |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, Nuclei, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-54236 is a medium vulnerability (CVSS 5.3) in Vllm, published June 17, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.93%, the 60th percentile. Fixed in vllm 0.24.0.
References
github.com/vllm-project/vllm/security/advisories/GHSA-hgg8-fqqc-vfmw
github.com/vllm-project/vllm/pull/45119
github.com/vllm-project/vllm/commit/94923629729381d7f7c9efde72071a2441f7fd82
nvd.nist.gov/vuln/detail/CVE-2026-54236
github.com/advisories/GHSA-hgg8-fqqc-vfmw
github.com/pypa/advisory-database/tree/main/vulns/vllm/PYSEC-2026-3408.yaml
github.com/vllm-project/vllm
pypi.org/project/vllm
github.com/CVEProject/cvelistV5/tree/main/cves/2026/54xxx/CVE-2026-54236.json
Left out here: 77 versions and the rest of 3 source descriptions. The JSON record has them.