GHSA-V359-JJ2V-J536

Vulnerability from github – Published: 2026-03-09 19:55 – Updated: 2026-03-10 18:39
VLAI?
Summary
vLLM has SSRF Protection Bypass
Details

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

# 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
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "vllm"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.15.1"
            },
            {
              "fixed": "0.17.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-25960"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-03-09T19:55:32Z",
    "nvd_published_at": "2026-03-09T21:16:15Z",
    "severity": "MODERATE"
  },
  "details": "## Summary\n\nThe 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.\n\n## Affected Component\n\n- **File**: `vllm/connections.py`\n- **Function**: `load_from_url_async`\n\n## Vulnerability Details\n\n### Root Cause\n\nThe SSRF [fix](https://github.com/vllm-project/vllm/pull/32746) 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.\n\nThese two URL parsers handle backslash characters (`\\`) differently:\n\n| Parser | Input URL | Parsed Host | Parsed Path | Behavior |\n|--------|-----------|-------------|-------------|----------|\n| `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 |\n| `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 |\n\n### Attack Scenario\n\n```python\n# Attacker provides this URL\nmalicious_url = \"https://httpbin.org\\\\@evil.com/\"\n\n# 1. Validation layer (urllib3.parse_url)\nparsed = urllib3.util.parse_url(malicious_url)\n# parsed.host == \"httpbin.org\"  \u2705 Passes validation\n\n# 2. Actual request (aiohttp with yarl)\nasync with aiohttp.ClientSession() as session:\n    async with session.get(malicious_url) as response:\n        # Request actually goes to evil.com!  \u274c Bypass!\n```\n\n### Why This Happens\n\n1. **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=/`\n2. **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`\n\nThis inconsistency allows an attacker to:\n- Bypass the hostname allowlist check\n- Access arbitrary internal/external services\n- Perform full SSRF attacks\n\n## Fixes\n\n- https://github.com/vllm-project/vllm/pull/34743",
  "id": "GHSA-v359-jj2v-j536",
  "modified": "2026-03-10T18:39:20Z",
  "published": "2026-03-09T19:55:32Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/vllm-project/vllm/security/advisories/GHSA-qh4c-xf7m-gxfc"
    },
    {
      "type": "WEB",
      "url": "https://github.com/vllm-project/vllm/security/advisories/GHSA-v359-jj2v-j536"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-25960"
    },
    {
      "type": "WEB",
      "url": "https://github.com/vllm-project/vllm/pull/34743"
    },
    {
      "type": "WEB",
      "url": "https://github.com/vllm-project/vllm/commit/6f3b2047abd4a748e3db4a68543f8221358002c0"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/vllm-project/vllm"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "vLLM has SSRF Protection Bypass"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…