vLLM CVE-2026-41523
HIGHSeverity by source
AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Attacker is a remote unauthenticated model publisher (AV:N/PR:N) but victim must load the model and run vLLM under -O (UI:R, AC:H); successful exploit yields full process compromise (C:H/I:H/A:H).
Primary rating from Vendor (https://github.com/vllm-project/vllm).
CVSS VectorVendor: https://github.com/vllm-project/vllm
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Lifecycle Timeline
3Blast Radius
ecosystem impact- 1 pypi packages depend on vllm (1 direct, 0 indirect)
Ecosystem-wide dependent count for version 0.22.0.
DescriptionCVE.org
Summary
An assert-based security check in vLLM's activation function loading allows any unauthenticated attacker to achieve arbitrary code execution on the server by publishing a malicious HuggingFace model, when vLLM runs in Python optimized mode (python -O or PYTHONOPTIMIZE=1).
Details
vLLM uses an assert statement at vllm/model_executor/layers/pooler/activations.py:48 as its sole security control to restrict which activation functions can be loaded from a HuggingFace model's config.json:
# vllm/model_executor/layers/pooler/activations.py:35-53
function_name: str | None = None
if (
hasattr(config, "sentence_transformers")
and "activation_fn" in config.sentence_transformers
):
function_name = config.sentence_transformers["activation_fn"]
elif (
hasattr(config, "sbert_ce_default_activation_function")
and config.sbert_ce_default_activation_function is not None
):
function_name = config.sbert_ce_default_activation_function
if function_name is not None:
assert function_name.startswith("torch.nn.modules."), (
"Loading of activation functions is restricted to "
"torch.nn.modules for security reasons"
)
fn = resolve_obj_by_qualname(function_name)()Python's assert statements are stripped at compile time when running in optimized mode (python -O or PYTHONOPTIMIZE=1). When the assert is absent, the attacker-controlled function_name from the model's config.json is passed directly to resolve_obj_by_qualname() - an unrestricted import gadget:
def resolve_obj_by_qualname(qualname: str) -> Any:
module_name, obj_name = qualname.rsplit(".", 1)
module = importlib.import_module(module_name)
return getattr(module, obj_name)This is the same vulnerability class as CVE-2017-1000433 (pysaml2 assert-based auth bypass), flagged by Bandit B101 and Ruff S101, and the reason Django proactively replaced all assert-based security checks (ticket #32508).
Attacker-controlled input sources:
config.sentence_transformers["activation_fn"](line 40)config.sbert_ce_default_activation_function(line 45)
Affected call sites - get_act_fn() is called via resolve_classifier_act_fn() from:
vllm/model_executor/layers/pooler/seqwise/poolers.py:122- SequencePoolervllm/model_executor/layers/pooler/tokwise/poolers.py:130- TokenPooler
Broader systemic risk: resolve_obj_by_qualname is called from ~20 locations across the codebase with no validation of its own. Any future caller feeding user-controlled input to it without validation creates the same vulnerability class.
Suggested fix: Replace the assert with an explicit conditional raise:
if not function_name.startswith("torch.nn.modules."):
raise ValueError(
"Loading of activation functions is restricted to "
"torch.nn.modules for security reasons"
)Impact
Arbitrary code execution. A malicious model author publishes a HuggingFace model with a crafted config.json. When a victim loads this model with vLLM running under python -O or PYTHONOPTIMIZE=1, arbitrary code executes during model initialization with the privileges of the vLLM process.
The attack requires:
- Victim loads a malicious model from HuggingFace (user interaction)
- vLLM runs under
python -OorPYTHONOPTIMIZE=1(documented in production use) - Model uses a cross-encoder architecture (e.g. BERT or RoBERTa with sequence classification)
Coordinated disclosure note: This vulnerability was also reported via huntr.com on April 2, 2026 (https://huntr.com/bounties/dcb05b04-e625-41e7-adbc-bbae0cc2d64c). A GitHub Security Advisory was also filed because it is vLLM's stated preferred disclosure channel per SECURITY.md.
Fix
A fix for this was introduced in this commit: https://github.com/vllm-project/vllm/commit/b3c7ffcab82c2439726f8cb213800f6f38c023d3
Articles & Coverage 1
AnalysisAI
Arbitrary code execution in vLLM versions prior to 0.22.0 allows remote unauthenticated attackers to run code on the inference server by publishing a malicious HuggingFace model, when vLLM is launched in Python optimized mode (python -O or PYTHONOPTIMIZE=1). The sole guardrail restricting which activation function classes can be loaded from a model's config.json is implemented with a Python assert, which is stripped at compile time under -O, leaving an unrestricted import gadget directly fed by attacker-controlled data. No public exploit identified at time of analysis, but the vendor advisory (GHSA-q8gq-377p-jq3r) and a coordinated huntr.com submission document the issue in detail.
Technical ContextAI
vLLM is a high-throughput inference and serving engine for large language models written in Python (pkg:pip/vllm). In vllm/model_executor/layers/pooler/activations.py, the get_act_fn() routine reads an activation_fn / sbert_ce_default_activation_function string from the loaded HuggingFace model config and uses 'assert function_name.startswith("torch.nn.modules.")' as its only allowlist before passing the value to resolve_obj_by_qualname(), which performs importlib.import_module() followed by getattr() with no further validation. Python documents that assert is removed when the interpreter runs with -O or PYTHONOPTIMIZE=1, so the allowlist disappears in exactly the production-tuned configurations vLLM operators are likely to use. The root cause is CWE-94 (Improper Control of Generation of Code) realized through an assert-based security check, the same class CISA and Bandit (B101) / Ruff (S101) flag and the reason Django removed assert-based checks in ticket #32508; CVE-2017-1000433 in pysaml2 is a direct prior analogue. The reachable call sites are SequencePooler (vllm/model_executor/layers/pooler/seqwise/poolers.py:122) and TokenPooler (tokwise/poolers.py:130), invoked when loading cross-encoder / sentence-transformer models (e.g. BERT or RoBERTa for sequence classification).
RemediationAI
Vendor-released patch: upgrade vLLM to 0.22.0 or later, which replaces the assert with an explicit 'if not function_name.startswith("torch.nn.modules."): raise ValueError(...)' in vllm/model_executor/layers/pooler/activations.py (commit b3c7ffcab82c2439726f8cb213800f6f38c023d3, advisory https://github.com/vllm-project/vllm/security/advisories/GHSA-q8gq-377p-jq3r). If you cannot upgrade immediately, the simplest compensating control is to stop running vLLM under python -O or with PYTHONOPTIMIZE=1 - this restores the assert and closes the vulnerable code path, at the cost of slightly larger memory footprint and losing the marginal CPU savings from stripped asserts and __debug__ branches. As a model-supply-chain control, restrict which HuggingFace repositories vLLM is allowed to load (pin to specific vetted revisions or mirror them to an internal registry) and forbid auto-pulling models from arbitrary external authors; this is high-value but introduces operational overhead in model curation. Defenders who maintain a fork can also locally backport the conditional-raise change shown in the upstream commit to older vLLM versions.
Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t
BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser
pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi
The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python
BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica
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
pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.
In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse
Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/
pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne
Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301
Cross-user flow execution in Langflow (< 1.9.1) lets any authenticated API-key holder run another user's flow by passing
Same weakness CWE-94 – Code Injection
View allSame technique Authentication Bypass
View allVendor StatusVendor
Share
External POC / Exploit Code
Leaving vuln.today
GHSA-q8gq-377p-jq3r