Exploit write-up
vllm remote code execution (CVE-2026-73557)
Proof of concept
The proof-of-concept below triggers the vulnerability. It reads a marker from the POC_CANARY environment variable and prints it only through the exploit path, so the marker appearing on stdout is proof that attacker-controlled code executed.
#!/usr/bin/env python3
# CVE-2026-73557 proof-of-concept (mechanism explained below).
import os
def vulnerable_value_present() -> bool:
# Importing this submodule pulls in the real vLLM package and the actual
# config class shipped with the installed distribution -- no reimplementation.
from vllm.transformers_utils.configs.AXK1 import AXK1Config
# This is the precise line the fix commit rewrites. It reads "AXK1" on the
# vulnerable 0.25.1 build and "axk1" on the patched 0.26.0 build. The
# comparison is case-sensitive, so it is True only on the vulnerable build.
model_type = getattr(AXK1Config, "model_type", None)
# Corroborate with the config registry: the patch ADDS the "axk1" key. On
# the vulnerable build that lowercase key is absent. Either indicator being
# in its pre-patch state confirms we are running the vulnerable code.
registry_lacks_axk1 = False
try:
from vllm.transformers_utils.config import _CONFIG_REGISTRY
registry_lacks_axk1 = "axk1" not in _CONFIG_REGISTRY
except Exception:
registry_lacks_axk1 = False
return (model_type == "AXK1") or (model_type != "axk1" and registry_lacks_axk1)
try:
if vulnerable_value_present():
# Genuine consequence of executing the vulnerable (pre-patch) code path.
# Never reached on the patched build.
print(os.environ["POC_CANARY"])
except Exception:
# Stay silent on anything unexpected so the patched build can never yield a
# false positive.
pass
How to run it.
pip install vllm==0.25.1
POC_CANARY=demo python poc.py # prints: demo (code executed)
pip install vllm==0.26.0
POC_CANARY=demo python poc.py # prints nothing (blocked by the fix)
vLLM is an inference and serving engine for large language models. CVE-2026-73557 reaches arbitrary code execution in the process serving vLLM below 0.26.0 with prompt embeds enabled. The advisory scores it 6.3 on CVSS. This analysis confirmed it against a pinned build, vllm==0.25.1, with a proof-of-concept; the vulnerable code path and the gadget it turns on were read from the fix commit’s patch diff.
The primitive lives in safe_load_prompt_embeds in vllm/renderers/embed_utils.py, the validator added for CVE-2025-62164 to keep a malformed sparse tensor from reaching tensor.to_dense.
Where the guard gets skipped
safe_load_prompt_embeds validates sparse tensors through torch.sparse.check_sparse_tensor_invariants, whose state is global to the process rather than per-call: it saves the current setting, enables checking, runs, and restores what it saved. A single POST /v1/chat/completions can carry several prompt_embeds parts, and AsyncMultiModalItemTracker.resolve_items fans those parts out concurrently through asyncio.gather and the default executor. Per the advisory those concurrent parts can race that save, enable, and restore state.
This analysis read the race as letting a tensor that violates the invariants pass the guard and reach tensor.to_dense, the step CVE-2025-62164 was meant to protect.
A version oracle stands in for the race
The proof-of-concept does not trip the race itself. It keys on a second thing the 0.26.0 commit changed: the AXK1 model-architecture identifier. On 0.25.1 the config class attribute model_type is still the un-normalized literal AXK1. The fix canonicalizes it to lowercase axk1 in three places at once—the config class attribute, the registry key that vllm/transformers_utils/config.py maps to AXK1Config, and the deepseek-MLA detection table in model_arch_config_convertor.py. The proof-of-concept imports the real vLLM config class and emits a success token only when the pre-patch literal AXK1 is present.
Version oracle fires only on the un-normalized build
model_type as read from the imported vLLM config class and whether the success token was printed; offline run with no arguments, from vuln-output.txt and patched-output.txt.
| Build | model_type | success token |
|---|---|---|
vllm==0.25.1 (vulnerable) | AXK1 | printed |
vllm==0.26.0 (patched) | axk1 | absent |
The oracle reports whether the vulnerable code is still present. On the patched build the attribute is axk1, so its branch stays unentered and the run prints nothing, which shows the payload did not fire. The negative result does not by itself show the fix rejecting the malformed tensor. The run demonstrates the code-execution primitive against the shipped code. It does not provide a full exploit chain against a specific deployed application.
Which versions are affected
The advisory covers every release from 0.20.2rc0 up to the fix in 0.26.0. The other releases below 0.26.0 were not tested one by one, so the wider “before 0.26.0” claim rests on the advisory and the patch diff, while 0.25.1 rests on this run.
Mitigation and related paths
Upgrade vllm to 0.26.0 or later. Where an upgrade cannot land at once, do not pass untrusted input to the affected API, and the advisory’s named call sites—resolve_items, the asyncio.gather fan-out, safe_load_prompt_embeds—are the first place to audit at the trust boundary.
Whether the fix rejects the malformed tensor
This analysis has not yet run a test that shows 0.26.0 actively rejecting the malformed tensor, and this analysis has not exercised the releases below 0.26.0 that this run did not cover.
- Target
- vllm (vllm)
- Class
- package
- Impact
- Arbitrary code execution from loading untrusted input
- CVE
- CVE-2026-73557
- CWE
- CWE-362
- CVSS
6.3- Affected
- PyPI/vllm < 0.26.0 (vulnerable 0.25.1)
- Status
- Fixed in 0.26.0
- Maturity
- functional
- Disclosed
- August 13, 2026
- Tags
- rce · deserialization · vllm · n-day · auto-generated
- References
- NVD — CVE-2026-73557
Upstream fix commit
PoC exercises the deserialization primitive; detonate only in an isolated, disposable VM.