Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/pentesting-web/proxy-waf-protections-bypass.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,54 @@ location ~* ^/admin {
}
```

## Raw-vs-normalized URI authorization bypass

A reverse proxy can give one request **two security identities** when authorization evaluates the original URI while routing later uses a decoded, dot-segment-normalized URI. In nginx, policy built from `$request_uri` can therefore disagree with the location/upstream selected from the normalized `$uri`; this is more dangerous than a normal ACL bypass when the first path segment selects a tenant, application, or permission domain.<sup>[[15]](#references)</sup>

For example, an authorization subrequest may extract the target service from the raw path:<sup>[[15]](#references)</sup>

```nginx
map $request_uri $target_service {
default "";
~^/proxy/([a-z]+)/ $1;
}
```

A request such as `/proxy/allowed/..%2f..%2fproxy/forbidden/api` can be authorized for `allowed`, then decoded and normalized to `/proxy/forbidden/api` for upstream routing. Establish a denied baseline against the protected path first, then compare it with the traversal form; a stable `403` to `200` change is strong evidence that the policy and routing layers interpreted different destinations.<sup>[[15]](#references)</sup>

### Prefix exemptions become routing keys

Authentication allowlists based on lexical prefix checks are especially exploitable across this boundary. If the gate exempts every raw path satisfying `startsWith("/api/auth/public/")`, traversal appended after that prefix can retain the exemption while normalization selects an unrelated protected handler:<sup>[[15]](#references)</sup>

```text
raw: /api/auth/public/../../../proxy/internal/admin
policy: startsWith("/api/auth/public/") -> exempt
normalized: /proxy/internal/admin -> protected upstream
```

Preserve the path bytes during testing because HTTP clients may remove dot segments before sending the request:<sup>[[15]](#references)</sup>

```bash
curl -sk --path-as-is \
'https://target/api/auth/public/../../../proxy/internal/admin'
```

Also audit the service-extraction grammar itself. A regex such as `[a-z]+` does not recognize legitimate identifiers containing hyphens, so the authorization backend may receive an empty/default service while routing still reaches the hyphenated application.<sup>[[15]](#references)</sup>

### Trusted-host metadata as a second bypass

After reaching an internal-only upstream, inspect how it determines the caller. A loopback service always sees the reverse proxy as its TCP peer; if it then treats client-influenced `Host` or `X-Forwarded-Host` as proof of local origin, an external request with `Host: 127.0.0.1` may satisfy the trusted-host check. This applies when the proxy rebuilds the forwarded header from the incoming Host value rather than a fixed, authenticated service identity.<sup>[[15]](#references)</sup>

```bash
curl -sk --path-as-is \
'https://target/api/auth/public/../../../proxy/internal/admin' \
-H 'Host: 127.0.0.1'
```

A compact review workflow is to log and compare the original URI, normalized URI, extracted authorization target, selected upstream, socket peer, and every forwarded identity header for the same request; test direct and traversal variants with no-permission and unauthenticated sessions. Fixes should canonicalize once before both authorization and routing, use exact route/method allowlist entries, validate the complete service-name grammar, and authenticate proxy-to-service identity instead of trusting client-derived host metadata.<sup>[[15]](#references)</sup>

When this bypass exposes a dangerous internal handler, assess the reached primitive rather than repeating it here: [SQL injection](sql-injection/README.md), [command injection](command-injection.md), [JWT secret compromise](hacking-jwt-json-web-tokens.md), or [sudo command abuse](../linux-hardening/main-system-information/sudo-command-abuse.md).<sup>[[15]](#references)</sup>

## Bypass Mod Security Rules <a href="#heading-bypassing-aws-waf-acl" id="heading-bypassing-aws-waf-acl"></a>

### Path Confusion
Expand Down Expand Up @@ -323,5 +371,6 @@ data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+ #base64 encoding the javascri
- [12] [cheatsheetseries.owasp.org - OWASP](https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html)
- [13] [PortSwigger Bypass Bot Detection extension](https://github.com/PortSwigger/bypass-bot-detection)
- [14] [When a Web App Detects Burp Suite via TLS Fingerprinting](https://kecman.co/blog/burp-suite-tls-fingerprint-bot-detection-bypass.html)
- [15] [Pre-Auth RCE in UniFi OS β€” One Request to Root Behind Seven Products](https://catchify.sa/post/pre-auth-rce-unifi-os-one-request-to-root)

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