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

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