跳至主要内容

最強的 XSS:Universal XSS

我們前面提到的所有 XSS 的相關漏洞,問題幾乎都是出在網站本身,是因為網站的疏忽才導致了漏洞的發生,使得攻擊者可以在該網站上執行 JavaScript 程式碼。

但是有另外一種 XSS 就更厲害了,如同標題所說,是最強的 XSS。

原因很簡單,因為這種 XSS 攻擊的對象不是網站本身,而是瀏覽器或是內建的 plugin。

因為是瀏覽器的漏洞,所以網站本身不需要有任何問題,就算只是一個純靜態的網頁都可以被執行 XSS。藉由攻擊瀏覽器,可以達到的影響是:「無論在哪個網站都可以執行程式碼」,因此這種攻擊方式被稱為 Universal XSS,又簡稱 UXSS。

那 UXSS 的漏洞是怎麼產生的?我們來看幾個範例。

2006 年的 Firefox 的 Adobe Acrobat plugin

在一篇名為 Subverting Ajax 的 paper 中,描述了一個針對 Firefox 的 UXSS。

Firefox 上的 Adobe Acrobat plugin 含有漏洞,沒有做好參數的檢查。在載入 PDF 時,可以在網址列後面帶上 #FDF=javascript:alert(1) 的參數,就能在該 PDF 中產生 XSS。

舉例來說,https://example.com/test.pdf#FDF=javascript:alert(1),只要載入這個網址,就能在 https://example.com 這個 origin 上面執行 JavaScript 程式碼,這就是所謂的 UXSS。

雖然說原本的論文沒有提到細節,但我猜背後的原理大概就是這個 plugin 會用 FDF 這個參數帶的值去執行 window.open 之類的,所以就可以利用 javascript: 偽協議去執行程式碼。

2012 年的 Android Chrome 的 UXSS

Takeshi 在 2012 年回報了一個漏洞:Issue 144813: Security: UXSS via com.android.browser.application_id Intent extra

在 Android 的世界中,有一種東西叫做「intent」,它代表的是一種「意圖」,例如說想開啟一個新的畫面,就要傳送一個「我想開啟新畫面」的 intent。

而如果想要打開 Chrome 並且瀏覽特定頁面的話,就可以照著這個意圖寫出相對應的程式碼:

// 宣告新的 intent
Intent intent = new Intent("android.intent.action.VIEW");

// intent 要傳給的對象是 Chrome app
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 這個網址上執行程式碼,形成了 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 {
// Firstly, force chrome app to open a target Web page
Intent intent1 = getIntentForChrome("http://www.google.com/1");
startActivity(intent1);

// Wait a few seconds
Thread.sleep(5000);

// JS code to inject into the target (www.google.com)
String jsURL = "javascript:alert('domain='+document.domain)";

Intent intent2 = getIntentForChrome(jsURL);

// Need a trick to prevent Chrome from loading the new URL in a new tab
intent2.putExtra("com.android.browser.application_id", "com.android.chrome");

startActivity(intent2);
}
catch (Exception e) {}
}

// Get intent to invoke chrome app
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: Security: Same Origin Policy bypass and local file disclosure via portal element,是經由最新的功能 <portal> 執行的 UXSS。

這個漏洞的成因跟上面那個 Android 的一樣,範例程式碼如下(來自於原始報告):

const p = document.createElement('portal');
p.src = 'https://mail.google.com';
// after a while:
p.src = 'javascript:portalHost.postMessage(document.documentElement.outerHTML,"*")';
// the code above will get executed in the context of https://mail.google.com

當你在 portal 內載入一個網址以後,如果再載入 javascript:,會變成是在剛剛載入的網址的 origin 去執行 JavaScript,換句話說,你可以在任意的網址上執行 JavaScript,所以就是一個 UXSS,而這個漏洞拿到了 10000 美金的賞金。

2021 年 Chromium 透過下載圖片觸發的 UXSS

當你在 Chromium 對一張圖片按下右鍵並選擇下載圖片時,Chromium 背後在做的事情是動態執行一段 JavaScript 的程式碼,裡面會呼叫 internal 的 JavaScript 函式,像這樣:

__gCrWeb.imageFetch.getImageData(id, '%s')

其中 %s 就是圖片檔名,而這個檔名忘了做過濾,所以如果檔名是 '+alert(1)+' 的話,程式碼就會變成:

__gCrWeb.imageFetch.getImageData(id, ''+alert(1)+'')

就執行了 alert(1),當然這邊可以替換成任意程式碼,alert(1) 只是示範而已。

除此之外,如果現在有個 A 網域,裡面用 iframe 嵌入 B 網域,當你在 B 網域下載圖片時,這一段動態產生的 JavaScript 是在 top level window 也就是 A 網域的視窗執行的。

也就是說,利用這個漏洞,如果我能夠在別的網域裡面用 iframe 嵌入我的攻擊網址,就能在那個網域上面執行任意程式碼,構成了 UXSS。

原始的報告跟 PoC 可以參考 Muneaki Nishimura 回報的 Issue 1164846: Security: ImageFetchTabHelper::GetImageDataByJs allows child frames to inject scripts into parent (UXSS)

多個 Brave 瀏覽器 iOS app 的 UXSS

Brave 是一個基於 Chromium 且強調隱私的瀏覽器,背後的創辦人是 JavaScript 之父 Brendan Eich,而 Brave 的 iOS app 之前被日本的資安研究員 Muneaki Nishimura 發現了多個 UXSS 的漏洞(上面那個 Chromium 的 UXSS 也是他回報的)。

漏洞發生的原因跟上面講的很像,都是由於 app 本身動態執行了 JavaScript 程式碼,而這些 JavaScript 程式碼的輸入又沒有經過過濾,於是就導致了 UXSS 的產生。

例如說可能有段程式碼是這樣:

self.tab?.webview?.evaluateJavaScript("u2f.postLowLevelRegister(\(requestId), \(true), '\(version)')")

而 version 是我們可以控制的,與此同時這一段 script 又是執行在 top level,於是 sub frame 就可以對 parent 做攻擊,在其他 origin 上執行 XSS。

更多細節可以參考 Muneaki Nishimura 的投影片 Brave Browserの脆弱性を見つけた話(iOS編)

小結

像是這種 UXSS,通常網站本身是無能為力的,因為漏洞並不是出在網站,而是出在瀏覽器。

而對瀏覽器來說這其實也是一個影響力很大的漏洞,你仔細想想,如果攻擊者真的利用了 UXSS,他可以讀你的 gmail,讀你的 Facebook 訊息,把你的資料全部都拿走,是很可怕的一件事。

身為使用者,我們能做的就是時時刻刻把瀏覽器更新到最新版本,並且希望廠商趕快把漏洞修復。

UXSS 的嚴重程度比較高,案例也比較少,大多數都是年代久遠(例如說十年前)的漏洞,大家可以自己試著找找看以前發生過的 UXSS 漏洞(跟上面不重複),並貼在留言區跟其他人分享。