# LMDeploy has an SSRF bypass

LMDeploy's _is_safe_url validates host with urlparse while requests parses backslash as path, letting `http://127.0.0.1:6666\@1.1.1.1` reach internal hosts.

- Payout: Research
- Severity: high
- Program: LMDeploy
- Found by: Fushuling, RacerZ-fighting, clzoom
- Disclosed: 2026-09-18
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-39wr-7q6h-cf68
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-39wr-7q6h-cf68
- Tags: area:web domain:web impact:internal-access kind:research platform:ghsa sev:high tech:python technique:parser-differential vuln:ssrf

**Target:** URL fetch/validation endpoint in lmdeploy
**Root cause:** _is_safe_url uses urlparse().hostname, which treats `\` as a normal char and `@` as userinfo delimiter, extracting 1.1.1.1; requests treats `\` as a path separator and connects to 127.0.0.1.
**Steps:**
1. Submit URL `http://127.0.0.1:6666\@1.1.1.1` to any lmdeploy feature that fetches URLs.
2. _is_safe_url parses hostname as 1.1.1.1, resolves it, sees a global IP, and returns safe.
3. requests.Session().get sends the request to 127.0.0.1:6666, hitting the internal service.
**How found:** Noticed validation uses urlparse while the actual fetch uses requests, hypothesized the two parsers disagree on ambiguous URL characters, and tested backslash-plus-@ to split the perceived host from the connected host.
**Exploitation:** Attacker supplies a crafted URL to an lmdeploy endpoint that validates then fetches it; the parser differential lets the request reach loopback/internal services, enabling SSRF against internal network resources.
**Insight:** When a validator and the HTTP client are different libraries, fuzz ambiguous delimiters (`\`, `@`, `#`, tabs) so the parsed hostname differs from the connected host; backslash before @ is a reliable splitter since urlparse keeps it in userinfo while requests treats it as path.

