high · 7.5Sep 18, 2026

LMDeploy SSRF Bypass via urlparse and requests Parser Disagreement

Rohit Hatagale
AI Security Researcher, SecureLayer7

A backslash in a crafted URL tricks LMDeploy's safety check into seeing a public IP while the actual HTTP request goes to an internal address, letting an attacker reach services inside the server's…

Packagelmdeploy
Ecosystempip
Affected>= 0.12.3, < 0.15.0
Fixed in0.15.0

The problem

LMDeploy uses _is_safe_url() to block requests to private/loopback addresses before fetching user-supplied URLs with requests.Session().get().

The flaw is that urlparse and the requests library disagree on what host a URL like http://127.0.0.1:6666\@1.1.1.1 points to. urlparse treats the backslash as a literal character and reads @ as the userinfo-host separator, so it extracts 1.1.1.1 as the hostname and passes the check. requests treats the backslash as a path delimiter, so it actually connects to 127.0.0.1:6666.

Any endpoint that accepts a URL and passes it through _is_safe_url before fetching is affected, including multimodal image loaders.

Proof of concept

A working proof-of-concept for this issue in lmdeploy, with the exact payload below.

text
http://127.0.0.1:6666\@1.1.1.1

The root cause is a CWE-115 / CWE-918 interpretation conflict: two different parsers receive the same string and disagree on the host component. urlparse (RFC 3986 strict) keeps \ as part of the userinfo field and uses @ to split authority, yielding hostname 1.1.1.1.

The requests library (and the underlying urllib3 / http.client stack) treats \ as a path separator, so the authority is 127.0.0.1:6666 and the rest becomes the path.

The patch in v0.15.0 closes this gap by normalizing or rejecting URLs that contain backslashes before the hostname is extracted, so both parsers agree on the host before any IP-range check is applied.

The fix

Upgrade to lmdeploy >= 0.15.0. The release rewrites the _is_safe_url validation logic to reject URLs containing backslashes in the authority component, eliminating the parser disagreement. As an interim measure, add egress firewall rules that block outbound connections to RFC1918 ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), loopback (127.0.0.0/8), and link-local (169.254.0.0/16) from the LMDeploy process.

Reporter not attributed.

References: [1][2][3]

Related research