miauのブログ

はてなダイアリー「miauの避難所」をはてなブログに移行しました。 https://zenn.dev/miau に移行しようと考え中

currentStyle を利用した CSSXSS も IE 側で対策済みらしい

今やってる案件で、セキュリティ対策方針みたいなのを決めているところなんですが。いろいろ調べてると

というページが出てきて。これは本質的には IE脆弱性についての言及なんですが、トラックバックやコメントを見ても「MS に報告した」とか「修正された」とかいう情報が見当たらなくてずいぶん振り回されてしまったのでそのお話。

結論としては、MS10-071 で対策済みなので、ちゃんとパッチを全部あてている IE では気にしなくていい話のようです。

CSSXSS について

2006 年くらいに流行った IE6 の脆弱性を突いた攻撃手法です。概要としては、

こちらが図も載っていてわかりやすいです。

この脆弱性

2006年6月度のInternet Explorer 用の累積的なセキュリティ更新プログラム (916281) (MS06-021)を適用することで回避可能になった。ただし、CSSXSS脆弱性を悪用した罠ページを信頼済みサイトゾーンに登録してしまうと防御できないが、これは他の脆弱性と同様である。また、ローカルにダウンロードしたHTMLを開いてしまった時にもCSSXSS脆弱性が発動するケースがある。

とあるように、2006 年 6 月時点で対策済み、というのが一般に言われていることです。(もう少し詳細な制限事項が Re: IE 6.0のCSSインポート機能に脆弱性。攻撃サイトにより情報漏えいが発生する危険 : 投稿 : HotFix Report BBS に載っています。)

ちなみに、CSSXSS というのは俗称のようですが、面倒なのでここでは CSSXSS と記載します。(CSSXSS と呼ばれる由縁は 2008-11-17 - hoshikuzu | star_dust の書斎 に載っていました。)

冒頭のサイト

再度リンクを張っておくと。

私は当初これを読み違えて「MS06-021 が不完全であり、UTF-7エンコードされている場合は別ドメインのコンテンツを取得できる」のかと思ってしまったのですが、そういうことではなくて。

  • IE には MS06-021 で別ドメインの cssText を参照できなくする対策が入ったが、currentStyle を利用すれば別ドメインのコンテンツを取得できる(IE脆弱性
  • mixi では "{" が "{" に置き換えられるので、UTF-7エンコードした "+AHs-" を利用することで mixi での実証に成功した
  • mixi はすぐに "+" をエスケープする対応を入れた。この記事は mixi脆弱性という視点で書かれているので、筆者は「修正済み」としている。
    • 筆者も IE の問題であることは認識しており、次の記事で脆弱性報告について言及はされている。

ということですね。トラックバック等を辿っても、この IE脆弱性の対策状況はわかりませんでした。

currentStyle を使った情報取得は、cssText を使った方法に比べると制限が多い(取得したい値がユーザ入力値の後に、CSS としてうまく解釈できる形で存在していないといけない)ですが、もし IE脆弱性が残っているとなると、Web 開発をする立場としては

防御するには (Webページ作成者)

  • IE6 が勘違いしないページを作る
    • 出力が HTML の場合、{ を、{に書き換えておけば OK!
    • 念のため、個人情報などの秘密のデータが、「CSSのように見える部分」に含まれないよう、HTMLの出力順序をうまく調整する。
    • JavaScript の場合、"{"の置換は現実的ではありません。というか不可能です。JavaScript には、ユーザの個人情報を含めないよう設計しましょう。
    • CGIに詳しい方だとお解りになると思いますが、この攻撃では、GETメソッドで取得したページしか読むことができません。なので、セッション情報はPOSTで送るようにして、個人情報を含んだページを出す場合、セッション情報が、POSTで送られてきてることを必ず確認するようにしましょう。 (PerlCGIモジュール等、全メソッドで送られてきたフォーム情報をマージして、呼び出し元の関数に返すようなプログラムも存在します。)

というような CSSXSS 用の対策を行う必要がありますし、その上 UTF-7 と解釈されるケースを考えると、"+" も "+" に置換する必要があります。

また、個人情報だけでなく CSRF 対策用のトークンも閲覧できてしまうことになりますので、このトークンを外部漏らさないためには登録処理の手前で POST の画面を挟む必要がある→確認画面のようなページを挟む必要がある?という感じで、開発標準だけでなく外部設計にも関わる面倒な話になってきてしまいます。

この脆弱性はてなブックマークのコメントでも

HiromitsuTakagi セキュリティ, 脆弱性, ブラウザ, sameorigin, IE
ブラウザの脆弱性Microsoftに通報するべき。Webサイト側に対策する義務はない。(対策する自由はあるが。) 2008/11/12

と指摘があるように、本来 Web サイト開発者側で対策する部分ではありません。ですが、実際に利用者のセキュリティが脅かされる以上、プロダクトオーナーにリスクを説明し、ユーザの利便性やシステムの開発・保守コストとのバランスでどこまで対策するか決定すべきでしょう。

検証してみた

一番嫌なのは「がんばって対策を入れたけど、実はすでにパッチが出ていた」(後に残ったのは無駄に保守性が低いシステム)というケースですので、まずは再現するか確認してみました。もし再現したら

から報告しよう、と思いつつ。

検証環境の準備(XP Mode)

元 URL は IE6 で確認したとのことなので、念のため IE6 を用意します。今回は Windows 7 Professional を使っているので、XP Mode を使いました。

基本的に手順どおりにいくんですが、2 点ほど注意点を。

  1. Virtual PC 2007 が入っていると Windows Virtual PC は起動できない。(起動時に警告されます。インストール時に警告してくれればいいのに・・・)
  2. 手順に従うと Windows XP Mode update を入れてしまいそうですが、これは注意書きにあるように「ハードウェアが仮想化テクノロジに対応していない PC で Windows XP Mode を実行」するためのものです。こちら に各メーカ用の BIOS 設定が載っているのでまずこちらを試しましょう。

XP Mode が使えない場合は、期限つきですが MS 配布のイメージが使えます

ちなみにこの環境は Windows XP SP3 で、

916281 [MS06-021] Internet Explorer 用の累積的なセキュリティ更新プログラム

とあるので MS06-021 は適用済みです。

検証の実施

ローカルホストにバーチャルホストを 2 つ立てて検証を行ってみました。検証に使ったファイルの一式はこちら。

XP Mode 上の IE6 でこのページを参照してみると・・・「書き込みできません。」という JavaScript エラーになって currentStyle が参照できませんでした。被攻撃サイトを UTF-7 で解釈しない場合は currentStyle 参照時に JS エラーになる代わりに、JS が動作を停止しました。もしかすると currentStyle から取得するところだけがポイントなのではなく、UTF-7 で取得していることも条件に含まれているのかもしれません。

どちらにせよ CSSXSS は再現せず。実はもうパッチが出ているというオチ・・・?

再度情報収集

ということで、再度情報収集してみると・・・

この資料の 52 ページ目。

infomation leakage via CSS - CSSを通じた情報の漏えい

  • Published: Nov 2008 in Japan
    • 2008年11月に日本で公開
  • Republished: Sep 2010, SA41271
    • 2010年9月、Secuniaよりアドバイザリ
  • Fixed: MS10-071 – Oct 2010
  • Affected : IE6 / IE7 / IE8

対策済みとのことでしたorz
事前調査をもう少し深くやっておけば何もやらなくてよかったのになぁ。

いちおう URL を記載しておくと、Securina のアドバイザリが

で、MS10-071

こちら。たぶん currentStyle を使った CSSXSS

クロス ドメインの情報漏えいの脆弱性 - CVE-2010-3330

のことなのかなと。

このパッチが適用されていれば IE のバージョンが 6.0.2900.6036 以上になっているらしいんだけど、XP Mode の IE は 6.0.2900.5512 だから未適用のような気もする。適用済みのパッチ一覧にも Windows Update にも 2360131 の文字はないし、もしかしたら検証のやり方がまずかったのかも・・・。

(追記)
「このパッチが適用されていれば IE のバージョンが 6.0.2900.6036 以上になっているらしい」というのは CVE 経由で

AND Mshtml.dll version is less than 6.0.2900.6036

と書いてあったのを誤読したようです。IE のバージョンは 6.0.2900.5512 のままでしたが、すべての更新プログラムを適用した状態では mshtml.dll のバージョンは 6.0.2900.6104 に上がっていました。検証を行った XP Mode の初期状態はどうだったのか、後でまた確認しようかと。

また、MS10-071 は「累積的なセキュリティ更新プログラム」なので、次の「累積的なセキュリティ更新プログラム」で上書きされる=適用済みのパッチ一覧に表示されないのが正常動作、ということのようですね。
(追記その2)

XP Mode を入れなおして mshtml.dll のバージョンを確認すると 6.0.2900.5848 でした。
これは MS09-034 で導入されるバージョン(たぶん SP3 中に含まれる)のようで、やっぱり MS10-071 は適用されていない気がする。なんで再現しなかったんだろう・・・。

(追記ここまで)

ちょっとすっきりしないところではあるんですけど、ひとまず CSSXSS は気にしない方向で進めようかと思います。

(蛇足その1)UTF-7 でのアンダースコアの扱い

currentStyle を使った CSSXSS の検証サイト

mixi.php
(中略)UTF-7だとアンダーバーが記述されたファイルが読み込めないため。

と書いてあって、その動作を知らなかったので検証してみた。

確かに IE6 で UTF-7 でページ表示した場合、

<a href="/send_message.pl">send message</a>

のようなリンクをクリックすると、リクエストは

GET /send+AF8-message.pl HTTP/1.1

というように UTF-7エンコードされてしまってうまく機能しない。(link タグでも同様。)

不思議な挙動だと思ったけど、考えてみると基本的なところはそれほど不思議ではなくて。

<a href="/?q=あ">search</a>

のようなリンクが UTF-8 のページにあった場合、一度 UTF-8エンコードされ(/?q=\xE3\x81\x82)、それが URL エンコードされた形(/?q=%E3%81%82)でリクエストが投げられる。であれば UTF-7 ページ中のリンクも一度 UTF-7エンコードされて解釈されるのは正しい挙動に思える。

でもなんで "+" が "%2B" に URL エンコードされないで送出されているんだろう?これだと仮にサーバがパスを UTF-7 としてデコードする機能を持っていたとしても、" " として解釈されるからパスが解決できず、筋が通っていない気がする。

ちなみに "%" も UTF-7エンコードされた際に "+ACU-" になってしまうので、"%5F" と書いて "_" を表現することもできない。「UTF-7だとアンダーバーが記述されたファイルが読み込めない」というのは正しいようだ。

一方この検証サイトでは「アンダースコアが含まれるため」として .js も別ファイルに切り出しているけど、アンダースコアが妙な挙動をするのは上記のとおり URL の部分だけなので、実はこの説明は正しくない。UTF-7 のせいでうまく動作していないのは

if (/name=post_key\s+type=hidden\s+value="([^"]+)"/.test(str))

この "+" の部分が UTF-7エンコード開始とみなされているから。UTF-7 表現の "+-" に置き換えてやるか、"{1,}" のように "+" を使わない形に正規表現を書き換えれば、UTF-7 で書かれた HTML ページの script タグに直接埋め込んでも OK。

UTF-7 に .js を埋め込む場合はエンコード開始文字の + に気をつける必要があって、簡単なところでは

alert(2+-1);

は ASCII で解釈すると 1 になるけれど、UTF-7 で解釈すると 3 になる。(UTF-7 で "+" を表す文字列が "+-" のため。)

(蛇足その2)UTF-7 の判定条件

当初「UTF-7 だと MS06-021 が有効にならない」という話だと思っていたので、どういう文字列が含まれていたら UTF-7 とみなされるかも調べようとしていました。結局調査していませんが、途中まで書いておきます。

数少ない情報。Chrome だとページ送りできないので、Enter か「'」で次ページ、「%」で前ページに移動しましょう。

  • charsetの指定がなくUTF-7っぽい文字列が含まれると、自動選択でUTF-7と判断される
  • 日本語が含まれる HTML は UTF-7 とみなされないが、UTF-7 の iframe を経由すると UTF-7 とみなされる

ということのようです。

文字コードの判定には内部的に mlang.dll の DetectInputCodepage が使われているという話だったので、API Monitor で Headers\mlang.h.xml とか作ってモニタリングすれば「UTF-7 の iframe を経由すると」の部分も API の引数がどうなっているか調べられそうだなとか考えたり。

もし引数がわかったら

みたいな感じでいろんなケースで呼び出せば、どういう文字が UTF-7 とみなされるか詳細を調べられそう。

Perl には mlang.dll をラップする Win32::MultiLanguage - search.cpan.org というモジュールがあるけど、戻り値が数字で返ってきてわかりにくいので、python の ctypes を使ったりしたほうが検証は楽かもしれない。