# [CRIT] [GHSA / CRITICAL] CVE-2026-48746: vLLM: OpenAI auth bypass

**Source:** GitHub Security Advisories
**Published:** 2026-06-16
**Article:** https://github.com/advisories/GHSA-94f4-hr76-p5j6

## Threat Profile

vLLM: OpenAI auth bypass

### Summary

A vulnerability in ASGI web servers and starlette's trust on those web servers enables an authentication bypass of the OpenAI API `AuthenticationMiddleware`, which was discovered during @x41sec's source code audit.
It allows to use the API without providing the configured `VLLM_API_KEY` or `--api-key`.

### Details

In https://github.com/vllm-project/vllm/blob/v0.14.0/vllm/entrypoints/openai/api_server.py#L689-L692 the `url_path` is taken from the `URL`, wh…

## Indicators of Compromise (high-fidelity only)

- **CVE:** `CVE-2026-48746`
- **CVE:** `CVE-2026-48710`

## MITRE ATT&CK Techniques

- **T1190** — Exploit Public-Facing Application
- **T1556** — Modify Authentication Process

## Kill chain phases observed

_(none detected from narrative keywords)_

## Recommended hunts

### vLLM CVE-2026-48746 auth bypass via malformed Host header on /v1/* (`/` or `?` in Host)

`UC_33_1` · phase: **exploit** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.url) as urls values(Web.http_method) as methods values(Web.status) as status values(Web.http_user_agent) as user_agents from datamodel=Web where Web.url="*/v1/*" AND (Web.dest="*/*" OR Web.dest="*?*" OR Web.dest="*..*") by Web.src, Web.dest | `drop_dm_object_name(Web)` | where like(dest, "%/%") OR like(dest, "%?%") | eval reason="Host header contains reserved URL chars - possible CVE-2026-48746 exploit" | convert ctime(firstTime), ctime(lastTime)
```

### vLLM /v1/* API access without Authorization header on auth-configured instance

`UC_33_2` · phase: **exploit** · confidence: **Medium** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.http_user_agent) as user_agents values(Web.status) as status_codes from datamodel=Web where Web.url IN ("*/v1/chat/completions","*/v1/completions","*/v1/models","*/v1/embeddings","*/v1/audio/*") by Web.src, Web.dest, Web.url | `drop_dm_object_name(Web)` | join type=outer src [ search index=proxy sourcetype=nginx_access uri_path IN ("/v1/chat/completions","/v1/completions","/v1/models") NOT "authorization" | stats values(src) as src by src ] | where mvfind(status_codes, "200")>=0 AND mvfind(status_codes, "401")<0 | eval reason="vLLM /v1/* served 200 with no Authorization header observed"
```

### vLLM 401/403 followed by 200 on /v1/* from same source IP within short window

`UC_33_3` · phase: **exploit** · confidence: **Medium** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.status) as statuses values(Web.url) as urls from datamodel=Web where Web.url="*/v1/*" by Web.src, Web.dest, _time span=10m | `drop_dm_object_name(Web)` | eval has_fail = if(mvfind(statuses,"401")>=0 OR mvfind(statuses,"403")>=0, 1, 0) | eval has_succ = if(mvfind(statuses,"200")>=0 OR mvfind(statuses,"201")>=0, 1, 0) | where has_fail=1 AND has_succ=1 | stats min(firstTime) as firstSeen max(lastTime) as lastSeen sum(count) as total values(urls) as urls values(statuses) as statuses by src, dest | eval window_seconds = lastSeen - firstSeen | where window_seconds <= 600 | convert ctime(firstSeen), ctime(lastSeen)
```

### IOC-driven hunts (use shared templates)

These are standard IOC-substitution hunts — the canonical SPL and KQL live once in [`_TEMPLATES.md`](../_TEMPLATES.md), so we don't repeat the same boilerplate on every CVE / hash / network-IOC briefing.

- **Asset exposure — vulnerability matches article CVE(s)** ([template](../_TEMPLATES.md#asset-exposure)) — phase: **recon**, confidence: **High**
  - CVE(s): `CVE-2026-48746`, `CVE-2026-48710`


## Why this matters

Severity classified as **CRIT** based on: CVE present, 4 use case(s) fired, 2 technique(s) inferred. Read the full article for actor attribution, tooling details, and any defanged IOCs in the body that aren't visible in the RSS summary.
