2012/05/09

UTF-16によるContent Security Policyの迂回

UTF-16をContent Security Policy(以下CSP)の迂回に使ってみようという記事です。ほとんどの場合で問題になりませんが場合によってはこうもできるかもしれないよね、ということを示すために書きます。真面目に読まないでください。

シンプルな迂回方法

やろうとしていることを理解しやすくするために、先にシンプルな迂回方法を示します。

CSPで選択的に外部ドメインのJavaScriptの読み込みを許可しているような場合、読み込みを許可したドメインにscriptタグのsrcとしてエラーのないように悪意のあるスクリプトを書けてしまう箇所があれば、当然XSSがあった時にCSPを迂回して攻撃することができてしまいます。
僕が攻撃しようと思ったら真っ先に狙いに行くのはJSONPのcallback関数名です。具体的に例を示すと、

 CSPを導入したサイトで、外部サイトのcsp.example.comに読み込みたいJavaScriptがあるため、csp.example.comからのJavaScriptの読み込みを許可したとします。そのとき、csp.example.com上に以下のようなページがあったとします。
http://csp.example.com/jsonp?callback=callback
callback({"city":"Tokyo"})

この時以下のようにできてしまったら、まずいということです。

http://csp.example.com/jsonp?callback=alert%281%29//
alert(1)//({"city":"Tokyo"})
このようにJSONPのcallback関数名に使用できる文字があまり制限されていなければ、XSSがあった場合にすんなり期待したCSPの制限を超えてJavaScriptの実行を許してしまいますね。Michal Zalewski氏もこの問題を含めたCSPの記事を書いています。

The subtle / deadly problem with CSP
http://lcamtuf.blogspot.com/2011/08/subtle-deadly-problem-with-csp.html

とはいえ、なかなかページの先頭から何か書けるところというのはこの例くらいしかないですよね。そんな時にひねり出したのがscript要素のcharset属性にUTF-16を指定して迂回する方法です。

UTF-16による迂回

この方法が使える場合というのは、以下の条件を満たした時です:

JavaScriptのsrcとして読み込むことを許可させたサイト上で、

・HTTPレスポンスヘッダにcharset指定がない。
・入力値が出力される箇所より手前の文字をUTF-16(BE/LE)化した時、その文字列がJavaScriptの変数として使用できる文字で構成されている。
・入力値の0x00が直に埋め込まれる。


きびしいですね。こういう条件を満たした都合がよいサイトを実際に用意しましたので、JavaScriptが実行される様子をCSPに対応したブラウザ(FirefoxかChrome推奨)でご覧ください。
このページにはStored XSSの脆弱性があり、scriptが挿入されたと考えてください。サイトオーナーは、「l0.cm」から読み込みたいJavaScriptがあったのでCSPで許可しているという具合です。


http://vulnerabledoma.in/csp_utf16

(※記事を書いた時点では、Firefox 12とSafariは僕が期待した動作をしていません。Firefox 12だとonclickでアラートが出るし、Safariだと全くJavaScriptが動きません。たぶんまだ完全にブラウザがサポートしていないからだと思いますが、CSPのルールの記述の仕方に問題があるようだったら教えて下さい…。)

追記 2012/6/6 --------------------------------
Firefox 12でonclickが動いちゃうの、脆弱性だったらしい…。Firefox 13で修正された模様。

MFSA 2012-36: インラインスクリプトによる Content Security Policy の回避
http://www.mozilla-japan.org/security/announce/2012/mfsa2012-36.html

段階的に導入しやすいようにあえて動くようにしてるとか、そういうんだと思ってた…。 まぁそんな訳でFirefox 13では期待した動作(onclickが動かない)をしています。
------------------------------------------------

(※一応CSPをつけない場合の動きもここから見られるようにしてあります。)

FirefoxかChromeではアラート「1」が出ると思います。これは、l0.cmドメイン上のHTMLページをUTF-16BEのscriptのsrcとして読み込んでいることによります。srcとして実際に解釈されるのは以下です。

 㱨瑭氾਼桥慤㸊㱴楴汥㹸=1;alert(1)//㰯瑩瑬放਼浥瑡⁣桡牳整㴢畴昭㠢㸊㱢潤社਼栱㹃卐⁳牣㰯栱㸊砀㴀㄀㬀愀氀攀爀琀⠀㄀⤀⼀⼊㰯扯摹㸊㰯桴浬
どう見ても立派なエラーのないJavaScriptですね。
ページの頭から自分の入力値の手前までにあるUTF-16の文字列「㱨瑭氾਼桥慤㸊㱴楴汥㹸」(もともとは「<html>[0x0A]<head>[0x0A]<title>x」)を強引に変数とみなすために「=1」で1を代入します。後は続けてJavaScriptを好きに書いて終わりに「//」でコメントアウトしているだけです。
まぁ先頭がうまいこと変数に使用可能な文字になってくれることが極めて珍しいので、最初に書いたように、問題になることはほとんどないと思いますが、こういうのもあり得なくはないよね、ということでしたとさ!
 
また、CSPとは別の問題ですが、1を代入した変数部分に機密情報が入る場合、定義済みの変数名を外部から取り出すことで、情報を盗るという技もあり得ると思います。さらに「while(1)」という文字列をUTF-16とみなして、読み出し防止のループをスルーさせることができる場合もあるかもしれません。これも凄くみみっちい問題なので、以下に都合がいいかんじの例を置くだけにします。
Opera以外のモダンなブラウザなら動くと思います。

http://l0.cm/while16/

ちなみに

Firefox Hacks RebootedのCSPの項を見ていたら、p407の脚注に「Firefox 4ではまだ制限されていませんが、仕様によるとスクリプトファイルのContent-Typeはapplication/javascriptやapplication/jsonに制限されます」とあるので、この例のようなtext/htmlのページをscriptのsrcとする方法はいずれできなくなるかもしれません。 

0 件のコメント:

コメントを投稿