# Caching the Un-cacheables - Abusing URL Parser Confusions (Web Cache Poisoning Technique)

Glassdoor's CDN normalized dot-segments while the backend didn't, letting a self-XSS on /Job/* be cached under /Award/* as stored XSS.

- Payout: unconfirmed
- Severity: high
- Program: Glassdoor
- Credited: Harel
- Disclosed: 2022-09-02
- Write-up quality: 9/10
- Original: https://nokline.github.io/bugbounty/2022/09/02/Glassdoor-Cache-Poisoning.html
- Library page: https://bugbountylibrary.com/r/blog%3Anokline-github-io-bugbounty-2022-09-02-glassdoor-cache-poisoning-html
- Tags: domain:web impact:integrity kind:bounty platform:blog sev:high tech:cdn technique:chained technique:parser-differential vuln:cache-deception vuln:cache-poisoning vuln:xss

**Target:** CDN cache + backend URL parser
**Root cause:** The CDN frontend normalizes dot-segments per RFC 3986 before matching cache rules, but the backend web server does not, so /Job/../Award/x is cached as /Award/x while served as /Job content.
**Steps:**
1. Identify self-XSS on /Job/*: parameter name/value reflected unsanitized in a script tag; `?attack=VULN%3C/script%20` closes the tag but WAF blocks `>` and trailing chars.
2. Bypass WAF by splitting payload: cookie `optimizelyEndUserId=BRUH><svg/onload=a=self['aler'%2B't']%3Ba(document.domain)>` supplies the closing `>` and JS.
3. Confirm cache rule: everything under `/Award/*` is cached (HIT/MISS headers even on 404s).
4. Test parser disagreement: `/Award/../this_should_not_cache` is not cached (frontend normalizes), `/Job/../this_should_give_a_404` returns 200 (backend doesn't normalize).
5. Send `GET /Job/../Award/RANDOMPATHTATDOESNOTEXIST?cachebuster=046&attack=VULN%3C/script%20` with the malicious cookie; backend serves /Job content with XSS, CDN caches it under /Award path.
6. Victim visits `https://glassdoor.com/Award/RANDOMPATHTATDOESNOTEXIST?cachebuster=046&attack=VULN%3C/script%20` and XSS fires from cache.
7. For stored XSS, use `/mz-survey/interview/collectQuestions_input.htm/../../../Award/RANDOMPATHTATDOESNOTEXIST123?cachebuster=050` with payload split across `Cookie: gdId=VULN%22</script%20` and two `X-Forwarded-For` headers (`VULN` then `VULN><svg/onload=self[`alert`](document.domain)>`); WAF only checks first XFF, backend reflects both.
**How found:** Noticed parameter reflection in a script tag on /Job pages, then probed WAF by removing characters from a blocked payload until it passed; separately mapped cache behavior by mutating extensions and paths to infer the /Award/* wildcard rule; hypothesized the CDN and backend might disagree on dot-segment normalization and tested with /Award/../ and /Job/../ probes.
**Exploitation:** Attacker crafts a single request to a dot-segment path under /Job (or /mz-survey) carrying the XSS split across cookie and duplicate X-Forwarded-For headers; the CDN caches the response under the normalized /Award URL, so any victim visiting that /Award URL receives the attacker's JavaScript executing in glassdoor.com origin.
**Escalation:** Chained a non-exploitable self-XSS (cookie-controlled) with a relaxed /Award/* cache rule and a frontend/backend dot-segment parsing disagreement to convert it into reflected and then stored XSS affecting cached pages.
**Insight:** When a CDN caches by path prefix, test whether its URL parser normalizes dot-segments, encoded slashes, or case differently than the origin—send `/cached-prefix/../uncached-vuln-path` and compare cache headers vs. response body to detect the disagreement.
