Carson帶你學Android:盤點那些你不知道的WebView漏洞


前言

  • 現在很多App里都內置了Web網頁(Hyprid App),比如說很多電商平臺,淘寶、京東、聚劃算等等,如下圖
京東首頁
  • 上述功能是由 Android的WebView 實現的,但是 WebView 使用過程中存在許多漏洞,容易造成用戶數據泄露等等危險,而很多人往往會忽視這個問題
  • 今天我將全面介紹 Android WebView的使用漏洞 及其修復方式

Carson帶你學WebView系列文章
Carson帶你學Android:這是一份全面&詳細的WebView學習攻略
Carson帶你學Android:最全面、易懂的Webview使用教程
Carson帶你學Android:全面總結WebView與 JS 的交互方式
Carson帶你學Android:手把手構建WebView緩存機制及資源預加載方案
Carson帶你學Android:盤點那些你不知道的WebView漏洞


目錄

目錄

1. 類型

WebView中,主要漏洞有三類:

  • 任意代碼執行漏洞
  • 密碼明文存儲漏洞
  • 域控制不嚴格漏洞

2. 具體分析

2.1 WebView 任意代碼執行漏洞

出現該漏洞的原因有三個:

  • WebView 中 addJavascriptInterface() 接口
  • WebView 內置導出的 searchBoxJavaBridge_對象
  • WebView 內置導出的 accessibilityaccessibilityTraversalObject 對象

2.1.1 addJavascriptInterface 接口引起遠程代碼執行漏洞

A. 漏洞產生原因

JS調用Android的其中一個方式是通過addJavascriptInterface接口進行對象映射:

 webView.addJavascriptInterface(new JSObject(), "myObj");
// 參數1:Android的本地對象
// 參數2:JS的對象
// 通過對象映射將Android中的本地對象和JS中的對象進行關聯,從而實現JS調用Android的對象和方法

所以,漏洞產生原因是:當JS拿到Android這個對象后,就可以調用這個Android對象中所有的方法,包括系統類(java.lang.Runtime 類),從而進行任意代碼執行。

如可以執行命令獲取本地設備的SD卡中的文件等信息從而造成信息泄露

具體獲取系統類的描述:(結合 Java 反射機制)

  • Android中的對象有一公共的方法:getClass() ;
  • 該方法可以獲取到當前類 類型Class
  • 該類有一關鍵的方法: Class.forName;
  • 該方法可以加載一個類(可加載 java.lang.Runtime 類)
  • 而該類是可以執行本地命令的

以下是攻擊的Js核心代碼:

function execute(cmdArgs)  
{  
    // 步驟1:遍歷 window 對象
    // 目的是為了找到包含 getClass ()的對象
    // 因為Android映射的JS對象也在window中,所以肯定會遍歷到
    for (var obj in window) {  
        if ("getClass" in window[obj]) {  

      // 步驟2:利用反射調用forName()得到Runtime類對象
            alert(obj);          
            return  window[obj].getClass().forName("java.lang.Runtime")  

      // 步驟3:以后,就可以調用靜態方法來執行一些命令,比如訪問文件的命令
getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);  

// 從執行命令后返回的輸入流中得到字符串,有很嚴重暴露隱私的危險。
// 如執行完訪問文件的命令之后,就可以得到文件名的信息了。
        }  
    }  
}   
  • 當一些 APP 通過掃描二維碼打開一個外部網頁時,攻擊者就可以執行這段 js 代碼進行漏洞攻擊。
  • 在微信盛行、掃一掃行為普及的情況下,該漏洞的危險性非常大

B. 解決方案

B1. Android 4.2版本之后

Google 在Android 4.2 版本中規定對被調用的函數以 @JavascriptInterface進行注解從而避免漏洞攻擊

B2. Android 4.2版本之前

在Android 4.2版本之前采用攔截prompt()進行漏洞修復。
具體步驟如下:

  • 繼承 WebView ,重寫 addJavascriptInterface 方法,然后在內部自己維護一個對象映射關系的 Map;

將需要添加的 JS 接口放入該Map中

  • 每次當 WebView 加載頁面前加載一段本地的 JS 代碼,原理是:
    • 讓JS調用一Javascript方法:該方法是通過調用prompt()把JS中的信息(含特定標識,方法名稱等)傳遞到Android端;
    • 在Android的onJsPrompt()中 ,解析傳遞過來的信息,再通過反射機制調用Java對象的方法,這樣實現安全的JS調用Android代碼。

關于Android返回給JS的值:可通過prompt()把Java中方法的處理結果返回到Js中

具體需要加載的JS代碼如下:

javascript:(function JsAddJavascriptInterface_(){  
// window.jsInterface 表示在window上聲明了一個Js對象

//   jsInterface = 注冊的對象名
// 它注冊了兩個方法,onButtonClick(arg0)和onImageClick(arg0, arg1, arg2)
// 如果有返回值,就添加上return
    if (typeof(window.jsInterface)!='undefined') {      
        console.log('window.jsInterface_js_interface_name is exist!!');}   
    else {  
        window.jsInterface = {     

     // 聲明方法形式:方法名: function(參數)
            onButtonClick:function(arg0) {   
// prompt()返回約定的字符串
// 該字符串可自己定義
// 包含特定的標識符MyApp和 JSON 字符串(方法名,參數,對象名等)    
                return prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onButtonClick',args:[arg0]}));  
            },  
              

            onImageClick:function(arg0,arg1,arg2) {   
         return
prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onImageClick',args:[arg0,arg1,arg2]}));  
            },  
        };  
    }  
}  
)()

// 當JS調用 onButtonClick() 或 onImageClick() 時,就會回調到Android中的 onJsPrompt ()
// 我們解析出方法名,參數,對象名
// 再通過反射機制調用Java對象的方法

關于該方法的其他細節

細節1:加載上述JS代碼的時機

  • 由于當 WebView 跳轉到下一個頁面時,之前加載的 JS 可能已經失效
  • 所以,通常需要在以下方法中加載 JS:
onLoadResource();
doUpdateVisitedHistory();
onPageStarted();
onPageFinished();
onReceivedTitle();
onProgressChanged();

細節2:需要過濾掉 Object 類的方法

  • 由于最終是通過反射得到Android指定對象的方法,所以同時也會得到基類的其他方法(最頂層的基類是 Object類)
  • 為了不把 getClass()等方法注入到 JS 中,我們需要把 Object 的共有方法過濾掉,需要過濾的方法列表如下:
getClass()
hashCode()
notify()
notifyAl()
equals()
toString()
wait()

總結

  • 對于Android 4.2以前,需要采用攔截prompt()的方式進行漏洞修復
  • 對于Android 4.2以后,則只需要對被調用的函數以 @JavascriptInterface進行注解
  • 關于 Android 系統占比,Google公布的數據:截止 2017 .1 .8 ,Android4.4 之下占有約15%,所以需要重視。

具體數據如下:

Paste_Image.png

2.1.2 searchBoxJavaBridge_接口引起遠程代碼執行漏洞

A. 漏洞產生原因

  • 在Android 3.0以下,Android系統會默認通過searchBoxJavaBridge_的Js接口給 WebView 添加一個JS映射對象:searchBoxJavaBridge_對象
  • 該接口可能被利用,實現遠程任意代碼。

B. 解決方案

刪除searchBoxJavaBridge_接口

// 通過調用該方法刪除接口
removeJavascriptInterface();

2.1.3 accessibilityaccessibilityTraversal接口引起遠程代碼執行漏洞

問題分析與解決方案同上,這里不作過多闡述。

2.2 密碼明文存儲漏洞

2.2.1 問題分析

WebView默認開啟密碼保存功能 :

mWebView.setSavePassword(true)`
  • 開啟后,在用戶輸入密碼時,會彈出提示框:詢問用戶是否保存密碼;
  • 如果選擇”是”,密碼會被明文保到 /data/data/com.package.name/databases/webview.db 中,這樣就有被盜取密碼的危險

2.2.2 解決方案

關閉密碼保存提醒

WebSettings.setSavePassword(false) 

2.3 域控制不嚴格漏洞

2.3.1 問題分析

先看Android里的* WebViewActivity.java*:

public class WebViewActivity extends Activity {
    private WebView webView;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview);
        webView = (WebView) findViewById(R.id.webView);

        //webView.getSettings().setAllowFileAccess(false);                    (1)
        //webView.getSettings().setAllowFileAccessFromFileURLs(true);         (2)
        //webView.getSettings().setAllowUniversalAccessFromFileURLs(true);    (3)
        Intent i = getIntent();
        String url = i.getData().toString(); //url = file:///data/local/tmp/attack.html 
        webView.loadUrl(url);
    }
 }

/**Mainifest.xml**/
// 將該 WebViewActivity 在Mainifest.xml設置exported屬性
// 表示:當前Activity是否可以被另一個Application的組件啟動
android:exported="true"

即 A 應用可以通過 B 應用導出的 Activity 讓 B 應用加載一個惡意的 file 協議的 url,從而可以獲取 B 應用的內部私有文件,從而帶來數據泄露威脅

具體:當其他應用啟動此 Activity 時, intent 中的 data 直接被當作 url 來加載(假定傳進來的 url 為 file:///data/local/tmp/attack.html ),其他 APP 通過使用顯式 ComponentName 或者其他類似方式就可以很輕松的啟動該 WebViewActivity 并加載惡意url。

下面我們著重分析WebView中getSettings類的方法對 WebView 安全性的影響:

  • setAllowFileAccess()
  • setAllowFileAccessFromFileURLs()
  • setAllowUniversalAccessFromFileURLs()

1. setAllowFileAccess()

// 設置是否允許 WebView 使用 File 協議
webView.getSettings().setAllowFileAccess(true);     
// 默認設置為true,即允許在 File 域下執行任意 JavaScript 代碼

使用 file 域加載的 js代碼能夠使用進行同源策略跨域訪問,從而導致隱私信息泄露

  1. 同源策略跨域訪問:對私有目錄文件進行訪問
  2. 針對 IM 類產品,泄露的是聊天信息、聯系人等等
  3. 針對瀏覽器類軟件,泄露的是cookie 信息泄露。

如果不允許使用 file 協議,則不會存在上述的威脅;

webView.getSettings().setAllowFileAccess(true);     

但同時也限制了 WebView 的功能,使其不能加載本地的 html 文件,如下圖:

移動版的 Chrome 默認禁止加載 file 協議的文件

Paste_Image.png

解決方案:

  • 對于不需要使用 file 協議的應用,禁用 file 協議;
setAllowFileAccess(false); 
  • 對于需要使用 file 協議的應用,禁止 file 協議加載 JavaScript。
setAllowFileAccess(true); 

// 禁止 file 協議加載 JavaScript
if (url.startsWith("file://") {
    setJavaScriptEnabled(false);
} else {
    setJavaScriptEnabled(true);
}

2. setAllowFileAccessFromFileURLs()

// 設置是否允許通過 file url 加載的 Js代碼讀取其他的本地文件
webView.getSettings().setAllowFileAccessFromFileURLs(true);
// 在Android 4.1前默認允許
// 在Android 4.1后默認禁止

AllowFileAccessFromFileURLs()設置為 true 時,攻擊者的JS代碼為:


<script>
function loadXMLDoc()
{
    var arm = "file:///etc/hosts";
    var xmlhttp;
    if (window.XMLHttpRequest)
    {
        xmlhttp=new XMLHttpRequest();
    }
    xmlhttp.onreadystatechange=function()
    {
        //alert("status is"+xmlhttp.status);
        if (xmlhttp.readyState==4)
        {
              console.log(xmlhttp.responseText);
        }
    }
    xmlhttp.open("GET",arm);
    xmlhttp.send(null);
}
loadXMLDoc();
</script>

// 通過該代碼可成功讀取 /etc/hosts 的內容數據

解決方案:設置setAllowFileAccessFromFileURLs(false);

當設置成為 false 時,上述JS的攻擊代碼執行會導致錯誤,表示瀏覽器禁止從 file url 中的 javascript 讀取其它本地文件。

3. setAllowUniversalAccessFromFileURLs()

// 設置是否允許通過 file url 加載的 Javascript 可以訪問其他的源(包括http、https等源)
webView.getSettings().setAllowUniversalAccessFromFileURLs(true);

// 在Android 4.1前默認允許(setAllowFileAccessFromFileURLs()不起作用)
// 在Android 4.1后默認禁止

AllowFileAccessFromFileURLs()被設置成true時,攻擊者的JS代碼是:

// 通過該代碼可成功讀取 http://www.so.com 的內容
<script>
function loadXMLDoc()
{
    var arm = "http://www.so.com";
    var xmlhttp;
    if (window.XMLHttpRequest)
    {
        xmlhttp=new XMLHttpRequest();
    }
    xmlhttp.onreadystatechange=function()
    {
        //alert("status is"+xmlhttp.status);
        if (xmlhttp.readyState==4)
        {
             console.log(xmlhttp.responseText);
        }
    }
    xmlhttp.open("GET",arm);
    xmlhttp.send(null);
}
loadXMLDoc();
</script>

解決方案:設置setAllowUniversalAccessFromFileURLs(false);

4. setJavaScriptEnabled()

// 設置是否允許 WebView 使用 JavaScript(默認是不允許)
webView.getSettings().setJavaScriptEnabled(true);  

// 但很多應用(包括移動瀏覽器)為了讓 WebView 執行 http 協議中的 JavaScript,都會主動設置為true,不區別對待是非常危險的。

即使把setAllowFileAccessFromFileURLs()setAllowUniversalAccessFromFileURLs()都設置為 false,通過 file URL 加載的 javascript 仍然有方法訪問其他的本地文件:符號鏈接跨源攻擊

前提是允許 file URL 執行 javascript,即webView.getSettings().setJavaScriptEnabled(true);

這一攻擊能奏效的原因是:通過 javascript 的延時執行和將當前文件替換成指向其它文件的軟鏈接就可以讀取到被符號鏈接所指的文件。具體攻擊步驟:

  1. 把惡意的 js 代碼輸出到攻擊應用的目錄下,隨機命名為 xx.html,修改該目錄的權限;
  2. 修改后休眠 1s,讓文件操作完成;
  3. 完成后通過系統的 Chrome 應用去打開該 xx.html 文件
  4. 等待 4s 讓 Chrome 加載完成該 html,最后將該 html 刪除,并且使用 ln -s 命令為 Chrome 的 Cookie 文件創建軟連接

注:在該命令執行前 xx.html 是不存在的;執行完這條命令之后,就生成了這個文件,并且將 Cookie 文件鏈接到了 xx.html 上。

于是就可通過鏈接來訪問 Chrome 的 Cookie

  1. Google 沒有進行修復,只是讓Chrome 最新版本默認禁用 file 協議,所以這一漏洞在最新版的 Chrome 中并不存在
  2. 但是,在日常大量使用 WebView 的App和瀏覽器,都有可能受到此漏洞的影響。通過利用此漏洞,容易出現數據泄露的危險

如果是 file 協議,禁用 javascript 可以很大程度上減小跨源漏洞對 WebView 的威脅。

  1. 但并不能完全杜絕跨源文件泄露。
  2. 例:應用實現了下載功能,對于無法加載的頁面,會自動下載到 sd 卡中;由于 sd 卡中的文件所有應用都可以訪問,于是可以通過構造一個 file URL 指向被攻擊應用的私有文件,然后用此 URL 啟動被攻擊應用的 WebActivity,這樣由于該 WebActivity 無法加載該文件,就會將該文件下載到 sd 卡下面,然后就可以從 sd 卡上讀取這個文件了

最終解決方案

  • 對于不需要使用 file 協議的應用,禁用 file 協議;
// 禁用 file 協議;
setAllowFileAccess(false); 
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);
  • 對于需要使用 file 協議的應用,禁止 file 協議加載 JavaScript。
// 需要使用 file 協議
setAllowFileAccess(true); 
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);

// 禁止 file 協議加載 JavaScript
if (url.startsWith("file://") {
    setJavaScriptEnabled(false);
} else {
    setJavaScriptEnabled(true);
}



3. 總結


歡迎關注Carson_Ho的簡書

不定期分享關于安卓開發的干貨,追求短、平、快,但卻不缺深度


請點贊!因為你的鼓勵是我寫作的最大動力!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容