Sage 1.4.5 に今も残る深刻な脆弱性について

■ 自作ソフト Posted by ひぐま (Higmmer) on 2010-02-09 at 01:46:47

◆共有テーマ: Firefox [コンピュータ]

以下で言及している脆弱性は Sage 1.4.6 において修正されましたが
引き続き 適切な対策 を講じることを推奨します (2010年3月10日追記)

本家よりFirefox 3.6対応版の Sage 1.4.5 がリリースされました。開発者ブログによると「セキュリティを改善した」とのことなので早速テストしてみました。

その結果、このバージョンにおいても依然として幾つかの脆弱性が残っていることを確認しました。

Roberto Suggi Liverani氏が報告した脆弱性のうち、link要素内に特殊なURIを埋め込む攻撃に対して依然として脆弱ですし、それどころか、後述のようにエントリ本文に対して従来よりも簡単にスクリプトを埋め込むことができるようにさえなっています。一体何を修正して何を確認したのか、大いに疑問を感じざるを得ません。

公平のために付言すれば、一応セキュリティ面において若干の改善が行われたのは事実のようですが、残念ながらその改善はこれらの脆弱性に対しては効果を発揮していません。一体どこに問題があるのか、ポイントは3つあります。

問題1) nsIScriptableUnescapeHTML#parseFragment は安全か?

Sage 1.4.4 以降では、それまで実装していたオレオレ構文解析器 正規表現によるHTMLフィルタを捨て、Firefox本体に備わっているAPIを使ってエントリ本文のフィルタリング処理を行うように変更されています。コードで示すと以下の通りです。

var sanitizer = Cc["@mozilla.org/feed-unescapehtml;1"].getService(Ci.nsIScriptableUnescapeHTML); var fragment = sanitizer.parseFragment(item.getContent(), false, null, document.documentElement); ds = new XMLSerializer().serializeToString(fragment);

これはAdBlock Plusの作者も推奨している方法で、過去には「JavaScript の除去には nsIScriptableUnescapeHTML を使え(?)」とも言われており、一見何の問題もないように見えます。しかしFirefox 3以降での仕様変更なのかバグなのかは定かではありませんが、現行の nsIScriptableUnescapeHTML#parseFragment は一部の属性(値)内にスクリプトが記述されていてもそれを除去しないようです(*1)。つまりわざわざフィルタをすり抜けるようにフィードに小細工をしなくても正々堂々と(?)スクリプトを注入することが可能となります。はっきり言ってこれでは元のオレオレ構文解析器より部分的には弱くなってしまっています。

ちなみにSage-Too/Sage++では nsIScriptableUnescapeHTML#parseFragment ではなく nsIDOMNSRange#createContextualFragment を使ってDOMツリーを構築していますが、XHTMLの出力前にツリー上の全てのノードを自前で走査してホワイトリストで許可されていない要素・属性を除去する処理を行っています。

(*1) このメソッドが必ずしも安全性を担保しないことはWizz RSS News Readerの作者も指摘しています
[2011年9月4日追記]

この問題はFirefox 3.5.17 / 3.6.14 及びそれ以降のバージョンで修正されました。現在では nsIScriptableUnescapeHTML#parseFragment を使ってスクリプトを正しく除去することができるようになっています。

問題2) そもそもフィルタ処理されない要素がある

また、そもそもの問題としてフィード内の全ての要素が前記のフィルタで処理されているわけではないという問題があります(*2)。その代わり不正な文字などを確実にエスケープすれば問題はないのですが、Sage 1.4.4以前ではそれも不十分でした(Sage 1.4.5で対策が施されました)。

Sage-Too/Sage++でもそれらの部分に対してはDOMフィルタによる処理は行っていませんが、代わりに問題を起こす可能性のある文字をXHTML出力の直前段階で徹底的にエスケープしているのに加え、不正なリンクURIを検出した場合はそれを除去する処理などを加えることで安全性を確保しています。

しかしSage 1.4.5ではリンクURIの検査が不十分なため、依然として特殊なURIを用いた攻撃が可能なままです。

(*2) 処理されない要素 … title、link、author 要素など。
[2010年3月10日追記]

Sage 1.4.6では危険なリンクURIを除去する処理が追加されました。Firefox本体のフィードプレビュー機能が行っているのと同じ方法が用いられています。

問題3) 侵入したスクリプトは chrome 特権モードで動作する

そしてこれが最悪なのですが、以前にも書いた通り本家Sageではchromeスキームで開いたページにフィードを直接書き出しているため、ひとたびスクリプトの侵入を許すと攻撃者はFirefoxの動作を完全に乗っ取り、ありとあらゆる攻撃を行うことが可能となるという重大な危険性があります。

ではどうすればいいのか。 AdBlock Plusの作者は type="content" 属性を指定した browser 又は iframe 要素を使う方法を提唱しています。そのような要素を作成してその中にフィードから生成したHTMLを流し込むようにすれば、たとえそこにスクリプトが混入したとしても、それらがchromeとコンテンツの境界を越えてより上位のウィンドウ、つまりFirefox本体にアクセスすることはできなくなります(*3)。実はこれはFirefox自身が行っているのと同じ方法でもあります。

具体的には "feedsummary.html" を以下のように変更します。

<body> <!-- これを追加 --> <iframe id="feed-frame" type="content" style="border:none;width:100%;height:100%;"></iframe> </body>

そして "feedsummary.js" の "displayFeed" 関数を以下のように変更します。

displayFeed: function (feed) { document.title = feed.getTitle() + " - Sage"; //document.body.innerHTML = CreateHTML.createHTMLSource(feed); document.getElementById("feed-frame").contentDocument.body.innerHTML = CreateHTML.createHTMLSource(feed); },

たったこれだけで悪意あるスクリプトがchrome特権モードで動くことを防止することができます(実験で確認済み)。

但し実際はこの変更を行うとデフォルトCSSが適用されなくなるという副作用がある(コンテンツからchromeパッケージ内のファイルにアクセスできなくなるため)ので更に若干の変更が必要になります。

参考までに、本家 Sage 1.4.5 にそれらの変更を施して一応動作する状態にしたXPIパッケージを以下に置いておきます。但しこれはあくまでもスクリプトの権限昇格を防止するだけであり、スクリプト侵入の脆弱性そのものを修正するものではないので注意して下さい。

注:本バージョンにはユーザーCSSが使用できないという不具合があります
(chromeとコンテンツの境界を越えてローカルファイルにアクセスできなくなったため)

[2010年3月10日追記]

Sage 1.4.6では既知の脆弱性は修正されましたが、上述の type="content" 属性を使用する対策は採用されませんでした。従って、もし未知の脆弱性が存在した場合、攻撃者がそれを利用して悪意あるスクリプトをchrome特権モードで動作させることができる可能性を完全には否定できません

尚、くどいようですがSage-Too/Sage++ではフィードの表示にchromeスキームではなくfileスキームを使用しています(*4)。そのため仮にスクリプトの侵入を許したとしてもそれが特権モードで動作することはありませんが、ローカルファイルの読み取りなど一部の攻撃は可能となる恐れがありますので、心配ならばこのページの情報を見て適切な対策を取ることを推奨します。

(*4) Sage-Too/Sage++は本家Sageの古いバージョン(1.3.x)からの派生アドオンであるため。

The vulnerabilities described below have been fixed in Sage 1.4.6,
but I recommend taking proper countermeasures continuously. (Edited on Mar 10th, 2010)

Firefox 3.6 compatible version of the original Sage 1.4.5 has been released. According to the developer's blog, security improvements are added to this version, so I have tested it immediately.

As a result, I've confirmed that some vulnerabilities still remain in this version.

It's still vulnerable to the URI exploit attack, one of reported by Roberto Suggi Liverani. Far from it, attackers can inject malicious script to the entry bodies with more simple method than before as described below. I really wonder what the author has fixed and confirmed.

I say for fairness, it's true that some security improvements have added, but they are not effective for these vulnerabilities. What's problem? - there are three points.

Point 1) Is nsIScriptableUnescapeHTML#parseFragment safe?

In Sage 1.4.4 and later, the entry bodies are filtered by Firefox built-in API instead of Sage original HTML filter. The code is below:

var sanitizer = Cc["@mozilla.org/feed-unescapehtml;1"].getService(Ci.nsIScriptableUnescapeHTML); var fragment = sanitizer.parseFragment(item.getContent(), false, null, document.documentElement); ds = new XMLSerializer().serializeToString(fragment);

This is a method being recommended by the author of AdBlock Plus, there seem to be apparent no problem. However, it's not obvious whether that is a specification change or bug, the current nsIScriptableUnescapeHTML#parseFragment no longer removes a part of the attributes (or values) even if it includes script (*1). Therefor, attackers can inject malicious script with "fair play" instead of using some tricks to bypass the filter. To be honest, that is partially more vulnerable than the previous Sage's filter.

FYI, Sage-Too and Sage++ create DOM tree with nsIDOMNSRange#createContextualFragment instead of nsIScriptableUnescapeHTML#parseFragment, in addition, the own process has been implemented that is scanning all nodes on the tree before XHTML output and removing elements and attributes not allowed by the whitelist.

(*1) The author of Wizz RSS News Reader also has pointed out that this method doesn't guarantee the absolute security.
[Edited on Sep 4th, 2011]

This problem was fixed on Firefox 3.5.17 / 3.6.14 and later.
Now you can remove scripts properly by using nsIScriptableUnescapeHTML#parseFragment.

Point 2) There are some elements not being filtered out

The next problem is, not all elements in a feed are processed by the above filter (*2). Instead of that, there is no problem if doing escape for invalid characters etc, but Sage 1.4.4 or less didn't do it enough. (The countermeasure has been added to Sage 1.4.5).

In Sage-Too and Sage++, the DOM filter also doesn't process those parts of feeds, instead, the characters that have potential to cause problems are escaped thoroughly just before XHTML output, furthermore, the security is ensured with an additional process of detecting and removing invalid URIs.

However, Sage 1.4.5 is still exploitable for particular URIs due to lack of sufficient URI validation.

(*2) Elements not processed: title, link, or author element etc.
[Edited on Mar 10th, 2010]

The process of removing dangerous URI links has been added into Sage 1.4.6. It's using the same way as Firefox's Feed Preview does.

Point 3) A injected script runs under the chrome privileged mode

And this is the worst, as previously discussed, there is a serious risk that attackers can hijack Firefox completely and do any type of attacks if once you allow intrusion of some scripts, because the original Sage writes feeds directly into chrome scheme zone.

Then what should we do? The author of AdBlock Plus is suggesting the method of using browser or iframe element with type="content" attribute. If you create such element and pour generated HTML into it, any injected scripts cannot access to the outer window, i.e. Firefox itself, beyond the chrome-contents boundary (*3). In fact this is the same way as Firefox does.

Concretely, modify "feedsummary.html" as below.

<body> <!-- Add this --> <iframe id="feed-frame" type="content" style="border:none;width:100%;height:100%;"></iframe> </body>

And modify the "displayFeed" function in "feedsummary.js" as below.

displayFeed: function (feed) { document.title = feed.getTitle() + " - Sage"; //document.body.innerHTML = CreateHTML.createHTMLSource(feed); document.getElementById("feed-frame").contentDocument.body.innerHTML = CreateHTML.createHTMLSource(feed); },

Only with this change, you can prevent malicious scripts from running under the chrome privileged mode. (I've confirmed it by the test).

Actually this change causes a side-effect that the default CSS cannot be applied because accessing from contents to chrome becomes impossible, so we need some more modifications.

FYI, I've added those modifications to the original Sage 1.4.5 and packed it to an installable XPI file. Note that this is only to prevent privilege escalation attacks, not to fix the actual vulnerabilities to the possibility of script injection.

* This version has a defects that the user CSS is not available.
This is caused because it gets impossible to access local file beyond the chrome-contents boundary.

[Edited on Mar 10th, 2010]

The known vulnerabilities have been fixed in Sage 1.4.6, however, the above countermeasure of introducing type="content" attribute has not been adopted. Therefore, if there are unknown vulnerabilities, the possibility cannot be completely denied that attackers can exploit them to run malicious scripts under chrome privileged mode.

***

In addition, I know you know, Sage-Too and Sage++ is using file scheme to render feeds instead of chrome scheme (*4). Therefore, even if some script is injected at the worst case, it won't run under privileged mode, but a certain type of attacks may be possible e.g. local file stealing. I recommend reading this information and taking proper measures.

(*4) Because Sage-Too and Sage++ are derived add-ons of the old version of original Sage 1.3.x.

トラックバック

この記事について書く(FC2ブログユーザー)
※言及リンクの無いトラックバックは無効です

PageTop▲

コメント

PageTop▲

コメントの投稿

 
 
 
 
 
 (後で編集・削除したいなら必須)
 
  

PageTop▲