2012/05/22

ドコモのSPモードでXSSっぽい文字列を含むURLへ接続できない

先日、ドコモが提供するスマートフォン用の回線であるSPモードを使用して普通にインターネットをしていたら接続できないページに出会いました。インターネットをしていて接続できないページがあるくらい珍しいことではないですが、接続できない状況が普通ではありませんでした。どうも特定の文字列がURLに含まれていると接続できなくなるようなのです。
例えば以下のようなURLに接続できません。

http://www.google.co.jp/search?q=%22%3E%3Cscript%3E

このようになります。
























いろいろな環境から接続を試みた結果は以下です。

× Android標準ブラウザからSPモードで接続
× Firefox MobileからSPモードで接続
× パソコンのFirefoxから携帯を使用してSPモードでテザリング接続
Android標準ブラウザで自宅の無線LANからWi-Fi接続

このようにアプリケーションに関係なく、SPモードを介した接続のみができない状況であることから、SPモードの通信自体になんらかの原因があると考えました。どのようなURLへ接続ができないのかを検証するために、あらゆる文字列を含んだURLへそれぞれ接続できるかを試してみました。

× http://example.com/?q1="><script>
× http://example.com/?q1="><script>aaa
http://example.com/?q1="></script> 
http://example.com/?q1="><body onload=alert(1)>
http://example.com/?q1="><script >
http://example.com/?q1="><scriptx>
http://example.com/?q1="><script
× http://example.com/?q1='><script>
http://example.com/?q1=><script>
× http://example.com/?q1='"><script>
× http://example.com/?q1='"''"''"'"><script>
http://example.com/?q1=aaa"><script>
http://example.com/?q1=">aaa<script>
× http://example.com/?q1="><SCRipt>
× http://example.com/?q1=1&q2="><script>
× http://example.com/?q1=1?q2="><script>
http://example.com/q1="><script>
× http://example.com/&q1="><script>
× http://example.com/%3Fq1="><script>
http://example.com/?_="><script>
× http://example.com/?1="><script>
http://example.com/?="><script>

これらの結果から推測できる遮断される条件を、JavaScriptの正規表現でまとめると大体以下のようになります。なお、httpsの場合はどのパターンも正常に接続できます。

location.pathname.match(/[?&][a-z0-9]+=("|')+><script>/i) || location.search.match(/[?&][a-z0-9]+=("|')+><script>/i)

また、何度か検証をしてしばらくしてから再度試してみたところ、今度は接続ができたことがありました。あれ、直った?と思いましたが、その後も何度か時間をあけて試していると、接続できたりできなかったりする場合があることに気付きました。
調査をしていくと、接続できる場合とできない場合に、規則があることがわかりました。
1.78.*.*といったIPアドレスをもらった場合には接続ができ、それ以外の1.66.*.*、183.74.*.*などといったIPアドレスでは接続ができなくなるようです。


現在この事象についてわかっているのはこれだけです。


なぜこのようなことが起きているのでしょうか。想像できるのは、SPモードというプロバイダ単位でXSS対策をしようとしているのではないか、ということです。

ただその割には、一番基本的な<script>タグですらきちんと止める気がないような遮断条件であるし、特定のIPアドレスでは遮断されなかったりで、一体何がしたいのかというかんじもあります。
まずは限定的に導入してみて、影響がどれほどのものなのかのテストでもしているのでしょうか?それとも単にバグ?

まだ理由ははっきりしませんが、攻撃への対策であるのなら、正当なリクエストにも攻撃に近い文字列は入りうるので、今のアクセスすらさせない方法はやりすぎであると思います。現段階ではまだ非常に限られた文字列だけですが、今後防御範囲を拡大しようとして、さらに副作用が広がるようなことがないことを願います。

 ひとまずは、特定の文字列を含むURLに接続ができない不具合としてドコモに問い合わせてみました。1日以内に返事を頂きましたが、期待した回答がもらえなかったため、まだやりとりが続いています。また何か進展があったらここに追記したいと思います。


 XSSっぽいリクエストをプロバイダが遮断するなんて前例がないなどとTwitterで言っていたら、徳丸さんが過去に発見したソフトバンクの事例に近いと、徳丸さんご本人からTwitterで教えて頂きました。ソフトバンクのガラケーでは、URL中のパーセントエンコードをしていない<>"が削除されるようです。

携帯電話事業者に学ぶ「XSS対策」 - ockeghem(徳丸浩)の日記
http://d.hatena.ne.jp/ockeghem/20100726/p1

なんか、携帯事業者はこういう方法をとりたがるんですかね。

追記 2012/6/1 19:00
いま試したところ、以前接続ができなかった1.66.*.*、183.74.*.*などといった、1.78.*.*以外のIPアドレスからも接続ができました。問題は解消されたとみてよさそう?

追記2 2012/6/12 17:00
ドコモから正式に、担当部署へ確認した結果、サーバーの設定を一部変更したことで修正したとの回答を貰いました。結局意図的だったのか不具合だったのかは不明ですが、少なくとも、修正したということは、このアプローチによるXSS対策をやめる側にたったか、単に不具合だったということを意味しており、危惧していた副作用などの問題が今後広がることはなさそうです。

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とする方法はいずれできなくなるかもしれません。