少し前に公開されたものだが、PortswiggerのDaily swig経由で見つけた、MSの小勝さんが報告したChromeのCSP bypassバグについて。
やられる側は例えば下記のようなページ。nonceのCSPを使っている。
<meta http-equiv="content-security-policy" content="script-src 'nonce-testrandom'"> <body> <?= $_GET['param'] ?> ← エスケープ無し </body>
攻撃者は以下のようにiframeを挿入してやる(ここでは簡単のためsrcdoc
を使っている)。
<meta http-equiv="content-security-policy" content="script-src 'nonce-testrandom'"> <body> <iframe srcdoc='<script>alert(window.origin)</script>'></iframe> ← 攻撃者が挿入したiframeタグ </body>
攻撃者はiframe内でalert
を動かそうとするが、iframeの親ページのCSPがiframeにも継承されるため、正しいnonceが無いJSの実行はブロックされる。
しかし古いChromeでは以下のような方法で回避できた、というのが小勝さんのレポート。
①attackerのページ[A]内のiframeに、victimのページ[V]を入れてやる([V]は上記のiframeが挿入されたCSP付きページ)。
┌─────────────────┐この時点で[I]には[V]のCSPが適用されている。
│attackerのページ[A] │
│ │
│┌───────────────┐│
││victimのページ[V] ││
││ ││
││┌─────────────┐││
│││XSSで挿入したiframe[I] │││
││└─────────────┘││
│└───────────────┘│
└─────────────────┘
②[A]は[I]のURLをdata:text/html,<script>history.back()</script>
に操作する。
この時点で[I]には[A]のCSP(空っぽ)が適用されるようになる。
③[I]のJSが動作してhistory backされる。[I]はXSSで挿入した内容(以下)に戻る。
<iframe srcdoc='<script>alert(window.origin)</script>'></iframe>
④backした後も、[I]には[A]のCSP(空っぽ)が適用される。
一方で[I]のオリジンは[V]を継承している。
こんな感じで、CSPのiframeへの継承というメンドクサイところを突いた攻撃です。
修正後は、④にて[I]には[V]のCSPが継承されるようになった模様。Daily swigによると修正にはHTML仕様の追加が必要だったらしい。
他のブラウザではどうかというと、Firefoxは昔から修正後のChromeと同じ挙動だったと思われる。
面白いことに、Safariは挙動が異なっていて③のJS(history.back)は実行されない。これは②で[A]が[I]のURLを変えてもCSPはそのまま[V]のものが適用されるから。それはそれで何となく気持ち悪くはある。