作者:ZSX

隨著前端技術的高速發展,越來越多的軟件正在使用瀏覽器相關技術作為其組成的一部分。和完全使用傳統客戶端技術開發的軟件相比,部分或完全使用網頁前端技術開發的軟件有著開發成本低、部署成本低的優勢。但自然而然地,我們也可以用常用的針對 Web 進行攻擊的技術來攻擊這一些客戶端。由于客戶端軟件擁有更大的權限,在 Web 上使用場景嚴重受限的攻擊技術,往往能造成更大的危害。

可以攻擊到哪些軟件呢?

這一種攻擊手法真正做到了“一視同仁”,無論是桌面端還是移動端,不管是什么操作系統,只要是使用了瀏覽器相關技術,且代碼存在缺陷的軟件,都有潛在被攻擊的可能。

本文在桌面端將圍繞著 MSHTML、Electron、nwjs、CEF、QtWebKit 等展開討論,移動端將圍繞著直接引用android.webkit.WebView、使用 Cordova 、 5+Runtime(DCloud)等庫展開討論。

攻擊方式有哪些呢?

常用的客戶端攻擊技術,包括緩沖區溢出漏洞等,不在本文的討論范圍中。于是我們還剩下在 Web 里負責最終用戶的 XSS / CSRF / MITM 等、負責服務器的 SQL Injection / SSRF / 命令注入 / 文件上傳等,同時還能把一些客戶端攻擊技術與 Web 結合,處理成新的攻擊手段。

用途較少的 Web 攻擊技術

負責服務器的 Web 攻擊技術對客戶端大部分都沒有什么用途,原因包括:

  1. 我們一般不直接和客戶端進行連接,往往都通過服務器中轉,那直接攻擊服務器會是更好的選擇;
  2. 客戶端的內網環境大多都沒有什么敏感內容能作為進一步攻擊的跳板(如攻擊Redis、攻擊MySQL、攻擊SQL Server)。

SQL Injection還是有一定程度上的利用價值的,但客戶端的 SQL 注入仍然比較雞肋。大部分客戶端都使用 SQLite 作為其本地關系型數據庫,一般只能注入獲得用戶信息;而使用 Electron 編寫的客戶端在本地開個 PouchDB 之類的輕量級非關系型數據庫也就是一兩行的事情。即使注入成功,還可能缺少把信息傳出的機制。需要配合其他漏洞使用。

CSRF和客戶端更沒有多大關系,它走的仍然是服務器的接口。

HTML Hijacking

在 Windows 下,有一種常見的攻擊手段叫 DLL Hijacking。絕大部分程序都有代碼重用的需求,不能把代碼靜態編譯到 exe 內。為了解決這種問題,就出現了動態鏈接庫(DLL)技術。程序使用 LoadLibrary 將DLL加載進內存,然后執行 DLL 內的代碼。自然而然地,如果我們把程序要加載的 DLL 替換為我們自己的 DLL,那我們就可以讓程序執行我們想要的任意代碼。需要注意的是,這并不是 Windows 的專屬問題,Linux 和 macOS 下也可以劫持 .so 文件。

而在 WebApp 中,我們可以注意到,幾乎所有的 .html 文件都是外部資源。對這些外部資源進行修改,就達到了和 DLL Hijacking 相同的效果。

Electron 中,資源通常以 asar 格式打包或者不打包,存放在 resources 目錄之下。asar 格式類似 tar,只做歸檔,不提供簽名和校驗功能。也就是說,我們只要把 resources 目錄進行替換,就可以讓 Electron App 執行任意代碼。

我們舉一個非常惡趣味的例子,讓 WordPress.com 和 Ghost 的 Native App 的代碼互換。

可以注意到,Ghost.exe的數字簽名還是一切正常的狀態。

——那邊用 CEF 的別笑,你們也一樣。只要你們的HTML文件存在本地,就擁有被劫持的可能。

這種攻擊對目標用戶有什么用呢?當然,它并不直接攻擊目標用戶。他的用途包括:

  1. 通過其他漏洞入侵用戶電腦后,可以通過這種方式把惡意代碼隱藏,達到就算用戶使用殺毒軟件清理病毒后,啟動你的程序便又“春風吹又生”的效果。早年的lpk.dll系列病毒便是基于這個原理。
  2. 如果你的程序有數字簽名,我就可以把惡意代碼藏在JS里。這并不破壞數字簽名,若用戶在非官方網站下載到帶料的你的程序,由于有數字簽名的存在,用戶會認為是你的程序有惡意代碼,或是根本查不到源頭。殺毒軟件一般對于有數字簽名的程序會更為信任,進一步可能達成“免殺”的效果。
  3. 方便黑客調試你的程序,找到其他的安全漏洞以便影響最終用戶。
如何防御

如果程序不帶數字簽名,那也不必花心思來防范這種攻擊了,投入產出不成正比。

使用 CEF 和 QtWebKit 的可以在加載每一個HTML之前通過已經簽名、且含有校驗信息的DLL對其進行校驗。

使用 nwjs 的話,因為它的HTML代碼是通過 zip 壓縮后直接通過copy /b附加到程序后部的,所以理論上直接打數字簽名即可解決。而 Electron 我并沒有找到特別好的方法,可以關注:https://github.com/electron/electron/pull/9648

RCE!

然后,就是 RCE(Remote Code Execution,任意代碼執行) 了。我們可以進行 MITM 攻擊,或者是利用 XSS 漏洞,還可以利用 Flash 的安全漏洞。針對 Web App 這一形式,如果其使用了<iframe><webview>我們甚至還可以進行所謂的“任意地址瀏覽”。

我們姑且不考慮利用瀏覽器內核本身的漏洞進行攻擊的手段。這類漏洞本身數量稀少,而且相當多還需要瀏覽器其它組件的支持,僅有一個內核也沒多大用途。況且這部分通常可以通過瀏覽器本身的安全通告發現,及時更新即可解決。在這之外,你可能沒想到的是,MITM 和 XSS 會是最后的主角。這兩種攻擊方式一種難度大 + 面對客戶端沒什么用,一種拿個 Cookie 之后就沒什么用了。但我們的戰場可是 Web 客戶端。如果我們的網頁是通過明文 HTTP 協議傳輸的,便可以通過 MITM 讓客戶端處理任意代碼。如果無法利用 MITM ,那么可以試圖尋找 XSS 漏洞來寫入任意代碼。而在瀏覽器之外的客戶端程序執行任意代碼,可能是致命的。

現在有不少客戶端的 Web 頁面是通過 HTTP 協議從互聯網上拉取的,不存儲在本地。比如說 Steam。還有一部分客戶端采取部分本地代碼、部分加載遠程網頁的形式。反正大家都是瀏覽器嘛,看看迅雷9,那就真的把自己當成瀏覽器來處理了。——但無論是 Electron 還是 Cordova,實際上都并不設計為瀏覽器,因此直接使用它們便會產生相應的安全隱患。使用 CEF、QtWebKit 等,也在一定情況下存在安全問題。

通過 XSS 等手段想辦法得到執行任意代碼的權限后,我們看看怎么逃離這個客戶端吧。

通用攻擊手段

首先是 Electron 和 Nwjs。這兩個框架的 JavaScript 代碼幾乎擁有和 C++ 代碼同等的權限,于是我們直接一行代碼彈個計算器。

require('child_process').execSync('c:\\windows\\system32\\calc.exe')

除去 XSS 之外,如果一個 Electron 同時負擔了瀏覽器訪問網頁的職能,在沒做安全配置的情況下照樣可以一行代碼彈個計算器。即使正確配置了nodeIntegration恐怕也無濟于事,今年的 BlackHat 大會發布了這么一篇文章,即是講述如何繞過該功能實現 RCE 的(CVE-2017-12581):https://www.blackhat.com/docs/us-17/thursday/us-17-Carettoni-Electronegativity-A-Study-Of-Electron-Security.pdf

其次是國內 DCloud 出的 5+Runtime(https://dcloud.io/runtime.html ),自帶Native.jshttps://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/88 ),對其進行攻擊效果也拔群。

而未正確配置的 CEF 和 QtWebKit,往往也可以造成任意代碼執行。一個典型的例子,目前在網絡上流傳的在 CEF 里啟用 Flash PPAPI 插件的代碼,幾乎無一例外關閉了web_security,同時打開了no_sandbox。很多開發者為了自己讀寫本地資源方便,也都關閉了它,如圖。

在低版本的 CEF 中,如果關閉了 web_security,只需要一行代碼即可打開計算器(在默認瀏覽器被設置成 IE 的情況下)

window.open('file://c:/windows/system32/calc.exe')

而在高版本的 CEF 中,無法打開計算器了。出于不知道什么原因,也無法讀寫文件(https://bugs.chromium.org/p/chromium/issues/detail?id=40787 ,可以確認的是 QtWebKit 也不行),就不能使用通用的方案處理了。

——但別忘了 Flash 的存在。我們可以想辦法看看這個程序有沒有加載 Flash。

針對使用多進程的 CEF,直接看啟動參數即可。

單進程的程序,就需要打開一個帶有 Flash 的頁面后,觀察其 DLL 加載情況。或者直接開 IDA 逆向一波。

拿到 Flash 版本后,查查該版本 Flash 有啥 Bug,一波帶走即可。

至于MSHTML?感謝 COM 組件。

(new ActiveXObject('WScript.Shell')).Run('calc.exe');
定制攻擊手段

通用的攻擊手段實在不多,如果多的話,像 QQ 和微信早就被日穿了。所以,我們需要針對每個應用尋找有效的攻擊代碼。

以 Cordova 為例。cordova-plugin-inappbrowser基本上是大家都必裝的插件。于是,我們調用系統瀏覽器,利用 URI Scheme 彈個B站客戶端先。

window.open('bilibili://bangumi/season/6463', '_system')

想讀通訊錄?看看有沒有navigator.contacts;讀寫文件?看看window.requestFileSystem

從上面的例子可以發現,我們的重點就是,

  1. 找到 Native 代碼與網頁的通訊方式。
  2. 確認 Native 暴露了哪一些 API 給網頁。

那具體應該怎么找呢?

首先先試試能不能打開瀏覽器開發者工具。針對 Android App,可以通過 Chrome 瀏覽器進行遠程調試。大部分使用了 WebView 的 App 發布后都沒有關閉遠程調試這一功能,這為我們的攻擊帶來了極大的方便。

隨手打開一個B站客戶端看看

如果能打開開發者工具,那要找特殊 API 就非常容易了;不能打開,也不是沒有辦法。

document.write(Object.keys(window).join('\n'))

通過這種方法可以把 window 下的鍵值全部輸出到屏幕上,下一步一步一步慢慢找。一般來說,瀏覽器可以調用的特殊 API 在windownavigatorexternal三個位置上都可以找到。找到可以利用的函數后,可以接著全文搜索函數名查找引用,模仿著直接調用即可。可以利用的功能包括“打開文件”(在 Windows 下一般使用ShellExecute實現,因.exe.scr的文件關聯為其自身,故可打開任意程序)、“打開程序”、“打開子窗口”等。

如果找不到可以利用的函數,或者在直接調用時做了一定程度上的過濾,則應當去找任意一個函數的源碼,研究其如何與 Native 交互。

可以用

document.write(someFunction.toString())

把函數源碼輸出到屏幕上(有開發者工具直接進 Source 看就好了)。

接著,我們需要配合逆向服用。

如果其顯示的是[native code],說明這是一個直接暴露給 JavaScript 的 API。對于 Android App 的 WebView,可以直接逆向為 .jar 后搜索addJavaScriptInterface函數,看到瀏覽器到底給 JavaScript 暴露了哪些東西。對于 CEF 等框架,可以從 Strings 搜索關鍵詞直接一步一步 X 上去找到最終執行的函數,或者直接附加進程調試。

如果其顯示的是 JavaScript 代碼,則可一步一步跟蹤上去直到頂層,看他的通信方式。目前比較流行的通信方式是自定義 URL Scheme 方式。其原理如下:

  1. JavaScript 通過 iframe 或者 location.href 嘗試打開地址my-custom-scheme://my-custom-data
  2. 當檢測到打開新地址時,Android WebView會觸發shouldOverrideUrlLoading函數,由其來決定該地址應當如何處理;CEF 如果之前使用CefRegisterSchemeHandlerFactory注冊過自定義 Scheme,則會交由其處理。
  3. 處理過程通常是異步的,并且地址切換后無法直接告知 JavaScript。所以,處理完成后,Native 代碼通過evaluateJavaScript或者直接讓框架訪問javascript:xxxxxxx調用某個頁面內的回調函數,完成通信。

所以對 Android 進行攻擊的話,尋找那幾個函數即可查到所有通訊方式。另外 Android 通常還會使用一些庫包裝 Scheme 處理,也不妨找找它們的特征,如:https://github.com/lzyzsd/jsbridge

能攻擊*Native嗎?

React Native如果使用了<webview>也會受到相應的影響。但如果沒有的話一般不用考慮,除非代碼中存在eval等可能任意執行 JavaScript 的地方。對 NativeScript 這種可任意調用 Java 庫的庫,還是存在一定危險性的。

舉一個比較有趣的知識+例子。在 IE8 之前,前端處理 JSON 通常使用以下代碼:

var json = eval('(' + data + ')')

當然現在的前端都已經用上了JSON.parse了,不過說不定有人堅持使用呢 :)

如何防御

1.使用 HTTPS,在有條件的情況下校驗證書,避免中間人攻擊。

2.寫入和輸出數據時做好過濾,不信任所有的用戶提交的數據。項目最初選型時就應該使用 React、Vuejs 等前端框架,避免自行拼接 HTML 出現問題。

3.在正式發布的軟件上,關閉 WebView 的調試模式,Android 上:

WebView.setWebContentsDebuggingEnabled(false);

4.對 Native 和 JavaScript 交互的代碼進行白名單處理,當檢測到域名非白名單域名時,拒絕 JavaScript 調用接口的請求。

5.在條件允許的情況下開啟瀏覽器沙箱,同時杜絕disable-web-security行為,使用單個功能的命令行參數解決。

6.必須配置好CSP,預防真的出現 XSS 后的進一步利用。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/465/