CVE-2026-41523

GHSA-q8gq-377p-jq3r HIGH
Published June 16, 2026

### 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`). ###...

Full CISO analysis pending enrichment.

What systems are affected?

Package Ecosystem Vulnerable Range Patched
vLLM pip < 0.22.0 0.22.0
82.8K 130 dependents Pushed 2d ago 43% patched ~30d to patch Full package profile →

Do you use vLLM? You're affected.

How severe is it?

CVSS 3.1
7.5 / 10
EPSS
0.1%
chance of exploitation in 30 days
Higher than 17% of all CVEs
Exploitation Status
No known exploitation
Sophistication
N/A

What is the attack surface?

AV AC PR UI S C I A
AV Network
AC High
PR None
UI Required
S Unchanged
C High
I High
A High

What should I do?

Patch available

Update vLLM to version 0.22.0

Which compliance frameworks are affected?

Compliance analysis pending. Sign in for full compliance mapping when available.

Frequently Asked Questions

What is CVE-2026-41523?

### 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`](https://github.com/vllm-project/vllm/blob/main/vllm/model_executor/layers/pooler/activations.py#L48) as its sole security control to restrict which activation functions can be loaded from a HuggingFace model's `config.json`: ```python # 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()`](https://github.com/vllm-project/vllm/blob/main/vllm/utils/import_utils.py#L106) — an unrestricted import gadget: ```python 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` — SequencePooler - `vllm/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: ```python 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: 1. Victim loads a malicious model from HuggingFace (user interaction) 2. vLLM runs under `python -O` or `PYTHONOPTIMIZE=1` (documented in production use) 3. 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

Is CVE-2026-41523 actively exploited?

No confirmed active exploitation of CVE-2026-41523 has been reported, but organizations should still patch proactively.

How to fix CVE-2026-41523?

Update to patched version: vLLM 0.22.0.

What is the CVSS score for CVE-2026-41523?

CVE-2026-41523 has a CVSS v3.1 base score of 7.5 (HIGH). The EPSS exploitation probability is 0.05%.

What are the technical details?

Original Advisory

### 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`](https://github.com/vllm-project/vllm/blob/main/vllm/model_executor/layers/pooler/activations.py#L48) as its sole security control to restrict which activation functions can be loaded from a HuggingFace model's `config.json`: ```python # 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()`](https://github.com/vllm-project/vllm/blob/main/vllm/utils/import_utils.py#L106) — an unrestricted import gadget: ```python 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` — SequencePooler - `vllm/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: ```python 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: 1. Victim loads a malicious model from HuggingFace (user interaction) 2. vLLM runs under `python -O` or `PYTHONOPTIMIZE=1` (documented in production use) 3. 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

Weaknesses (CWE)

CWE-617 — Reachable Assertion: The product contains an assert() or similar statement that can be triggered by an attacker, which leads to an application exit or other behavior that is more severe than necessary.

  • [Implementation] Make sensitive open/close operation non reachable by directly user-controlled data (e.g. open/close resources)
  • [Implementation] Perform input validation on user data.

Source: MITRE CWE corpus.

CVSS Vector

CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H

Timeline

Published
June 16, 2026
Last Modified
June 16, 2026
First Seen
June 16, 2026

Related Vulnerabilities