Android:你不知道的 WebView 使用漏洞
前言
- 現(xiàn)在很多App里都內(nèi)置了Web網(wǎng)頁(yè)(Hyprid App),比如說(shuō)很多電商平臺(tái),淘寶、京東、聚劃算等等,如下圖
- 上述功能是由?Android的WebView?實(shí)現(xiàn)的,但是 WebView 使用過(guò)程中存在許多漏洞,容易造成用戶(hù)數(shù)據(jù)泄露等等危險(xiǎn),而很多人往往會(huì)忽視這個(gè)問(wèn)題
- 今天我將全面介紹?Android WebView的使用漏洞?及其修復(fù)方式
閱讀本文前請(qǐng)先閱讀:?
Android開(kāi)發(fā):最全面、最易懂的Webview詳解?
最全面 & 最詳細(xì)的 Android WebView與JS的交互方式 匯總
目錄
1. 類(lèi)型
WebView中,主要漏洞有三類(lèi):
- 任意代碼執(zhí)行漏洞
- 密碼明文存儲(chǔ)漏洞
- 域控制不嚴(yán)格漏洞
2. 具體分析
2.1 WebView 任意代碼執(zhí)行漏洞
出現(xiàn)該漏洞的原因有三個(gè):
- WebView 中?addJavascriptInterface()?接口
- WebView 內(nèi)置導(dǎo)出的?searchBoxJavaBridge_對(duì)象
- WebView 內(nèi)置導(dǎo)出的?accessibility?和?accessibilityTraversalObject 對(duì)象
2.1.1 addJavascriptInterface 接口引起遠(yuǎn)程代碼執(zhí)行漏洞
A. 漏洞產(chǎn)生原因
JS調(diào)用Android的其中一個(gè)方式是通過(guò)addJavascriptInterface接口進(jìn)行對(duì)象映射:
webView.addJavascriptInterface(new JSObject(), "myObj"); // 參數(shù)1:Android的本地對(duì)象 // 參數(shù)2:JS的對(duì)象 // 通過(guò)對(duì)象映射將Android中的本地對(duì)象和JS中的對(duì)象進(jìn)行關(guān)聯(lián),從而實(shí)現(xiàn)JS調(diào)用Android的對(duì)象和方法- 1
- 2
- 3
- 4
所以,漏洞產(chǎn)生原因是:當(dāng)JS拿到Android這個(gè)對(duì)象后,就可以調(diào)用這個(gè)Android對(duì)象中所有的方法,包括系統(tǒng)類(lèi)(java.lang.Runtime 類(lèi)),從而進(jìn)行任意代碼執(zhí)行。
如可以執(zhí)行命令獲取本地設(shè)備的SD卡中的文件等信息從而造成信息泄露
具體獲取系統(tǒng)類(lèi)的描述:(結(jié)合 Java 反射機(jī)制)
- Android中的對(duì)象有一公共的方法:getClass() ;
- 該方法可以獲取到當(dāng)前類(lèi) 類(lèi)型Class
- 該類(lèi)有一關(guān)鍵的方法: Class.forName;
- 該方法可以加載一個(gè)類(lèi)(可加載 java.lang.Runtime 類(lèi))
- 而該類(lèi)是可以執(zhí)行本地命令的
以下是攻擊的Js核心代碼:
function execute(cmdArgs) { // 步驟1:遍歷 window 對(duì)象// 目的是為了找到包含 getClass ()的對(duì)象// 因?yàn)锳ndroid映射的JS對(duì)象也在window中,所以肯定會(huì)遍歷到for (var obj in window) { if ("getClass" in window[obj]) { // 步驟2:利用反射調(diào)用forName()得到Runtime類(lèi)對(duì)象alert(obj); return window[obj].getClass().forName("java.lang.Runtime") // 步驟3:以后,就可以調(diào)用靜態(tài)方法來(lái)執(zhí)行一些命令,比如訪(fǎng)問(wèn)文件的命令 getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs); // 從執(zhí)行命令后返回的輸入流中得到字符串,有很?chē)?yán)重暴露隱私的危險(xiǎn)。 // 如執(zhí)行完訪(fǎng)問(wèn)文件的命令之后,就可以得到文件名的信息了。} } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 當(dāng)一些 APP 通過(guò)掃描二維碼打開(kāi)一個(gè)外部網(wǎng)頁(yè)時(shí),攻擊者就可以執(zhí)行這段 js 代碼進(jìn)行漏洞攻擊。
- 在微信盛行、掃一掃行為普及的情況下,該漏洞的危險(xiǎn)性非常大
B. 解決方案
B1. Android 4.2版本之后
Google 在Android 4.2 版本中規(guī)定對(duì)被調(diào)用的函數(shù)以?@JavascriptInterface進(jìn)行注解從而避免漏洞攻擊
B2. Android 4.2版本之前
在Android 4.2版本之前采用攔截prompt()進(jìn)行漏洞修復(fù)。?
具體步驟如下:
繼承 WebView ,重寫(xiě)?addJavascriptInterface?方法,然后在內(nèi)部自己維護(hù)一個(gè)對(duì)象映射關(guān)系的 Map;
將需要添加的 JS 接口放入該Map中
每次當(dāng) WebView 加載頁(yè)面前加載一段本地的 JS 代碼,原理是:
- 讓JS調(diào)用一Javascript方法:該方法是通過(guò)調(diào)用prompt()把JS中的信息(含特定標(biāo)識(shí),方法名稱(chēng)等)傳遞到Android端;
- 在Android的onJsPrompt()中 ,解析傳遞過(guò)來(lái)的信息,再通過(guò)反射機(jī)制調(diào)用Java對(duì)象的方法,這樣實(shí)現(xiàn)安全的JS調(diào)用Android代碼。
關(guān)于Android返回給JS的值:可通過(guò)prompt()把Java中方法的處理結(jié)果返回到Js中
具體需要加載的JS代碼如下:
javascript:(function JsAddJavascriptInterface_(){ // window.jsInterface 表示在window上聲明了一個(gè)Js對(duì)象// jsInterface = 注冊(cè)的對(duì)象名 // 它注冊(cè)了兩個(gè)方法,onButtonClick(arg0)和onImageClick(arg0, arg1, arg2) // 如果有返回值,就添加上returnif (typeof(window.jsInterface)!='undefined') { console.log('window.jsInterface_js_interface_name is exist!!');} else { window.jsInterface = { // 聲明方法形式:方法名: function(參數(shù))onButtonClick:function(arg0) { // prompt()返回約定的字符串 // 該字符串可自己定義 // 包含特定的標(biāo)識(shí)符MyApp和 JSON 字符串(方法名,參數(shù),對(duì)象名等) 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]})); }, }; } } )()// 當(dāng)JS調(diào)用 onButtonClick() 或 onImageClick() 時(shí),就會(huì)回調(diào)到Android中的 onJsPrompt () // 我們解析出方法名,參數(shù),對(duì)象名 // 再通過(guò)反射機(jī)制調(diào)用Java對(duì)象的方法- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
關(guān)于該方法的其他細(xì)節(jié)
細(xì)節(jié)1:加載上述JS代碼的時(shí)機(jī)
- 由于當(dāng) WebView 跳轉(zhuǎn)到下一個(gè)頁(yè)面時(shí),之前加載的 JS 可能已經(jīng)失效
- 所以,通常需要在以下方法中加載 JS:
- 1
- 2
- 3
- 4
- 5
- 6
細(xì)節(jié)2:需要過(guò)濾掉 Object 類(lèi)的方法
- 由于最終是通過(guò)反射得到Android指定對(duì)象的方法,所以同時(shí)也會(huì)得到基類(lèi)的其他方法(最頂層的基類(lèi)是 Object類(lèi))
- 為了不把 getClass()等方法注入到 JS 中,我們需要把 Object 的共有方法過(guò)濾掉,需要過(guò)濾的方法列表如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
總結(jié)
- 對(duì)于Android 4.2以前,需要采用攔截prompt()的方式進(jìn)行漏洞修復(fù)
- 對(duì)于Android 4.2以后,則只需要對(duì)被調(diào)用的函數(shù)以 @JavascriptInterface進(jìn)行注解
- 關(guān)于 Android 系統(tǒng)占比,Google公布的數(shù)據(jù):截止 2017 .1 .8 ,Android4.4 之下占有約15%,所以需要重視。?
具體數(shù)據(jù)如下:
2.1.2 searchBoxJavaBridge_接口引起遠(yuǎn)程代碼執(zhí)行漏洞
A. 漏洞產(chǎn)生原因
- 在Android 3.0以下,Android系統(tǒng)會(huì)默認(rèn)通過(guò)searchBoxJavaBridge_的Js接口給 WebView 添加一個(gè)JS映射對(duì)象:searchBoxJavaBridge_對(duì)象
- 該接口可能被利用,實(shí)現(xiàn)遠(yuǎn)程任意代碼。
B. 解決方案
刪除searchBoxJavaBridge_接口
// 通過(guò)調(diào)用該方法刪除接口 removeJavascriptInterface();- 1
- 2
2.1.3?accessibility和?accessibilityTraversal接口引起遠(yuǎn)程代碼執(zhí)行漏洞
問(wèn)題分析與解決方案同上,這里不作過(guò)多闡述。
2.2 密碼明文存儲(chǔ)漏洞
2.2.1 問(wèn)題分析
WebView默認(rèn)開(kāi)啟密碼保存功能 :
mWebView.setSavePassword(true)`- 1
- 開(kāi)啟后,在用戶(hù)輸入密碼時(shí),會(huì)彈出提示框:詢(xún)問(wèn)用戶(hù)是否保存密碼;
- 如果選擇”是”,密碼會(huì)被明文保到 /data/data/com.package.name/databases/webview.db 中,這樣就有被盜取密碼的危險(xiǎn)
2.2.2 解決方案
關(guān)閉密碼保存提醒
WebSettings.setSavePassword(false)- 1
2.3 域控制不嚴(yán)格漏洞
2.3.1 問(wèn)題分析
先看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設(shè)置exported屬性 // 表示:當(dāng)前Activity是否可以被另一個(gè)Application的組件啟動(dòng) android:exported="true"- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
即 A 應(yīng)用可以通過(guò) B 應(yīng)用導(dǎo)出的 Activity 讓 B 應(yīng)用加載一個(gè)惡意的 file 協(xié)議的 url,從而可以獲取 B 應(yīng)用的內(nèi)部私有文件,從而帶來(lái)數(shù)據(jù)泄露威脅
具體:當(dāng)其他應(yīng)用啟動(dòng)此 Activity 時(shí), intent 中的 data 直接被當(dāng)作 url 來(lái)加載(假定傳進(jìn)來(lái)的 url 為 file:///data/local/tmp/attack.html ),其他 APP 通過(guò)使用顯式 ComponentName 或者其他類(lèi)似方式就可以很輕松的啟動(dòng)該 WebViewActivity 并加載惡意url。
下面我們著重分析WebView中g(shù)etSettings類(lèi)的方法對(duì) WebView 安全性的影響:
- setAllowFileAccess()
- setAllowFileAccessFromFileURLs()
- setAllowUniversalAccessFromFileURLs()
1. setAllowFileAccess()
// 設(shè)置是否允許 WebView 使用 File 協(xié)議 webView.getSettings().setAllowFileAccess(true); // 默認(rèn)設(shè)置為true,即允許在 File 域下執(zhí)行任意 JavaScript 代碼- 1
- 2
- 3
使用 file 域加載的 js代碼能夠使用進(jìn)行同源策略跨域訪(fǎng)問(wèn),從而導(dǎo)致隱私信息泄露
如果不允許使用 file 協(xié)議,則不會(huì)存在上述的威脅;
webView.getSettings().setAllowFileAccess(true);- 1
但同時(shí)也限制了 WebView 的功能,使其不能加載本地的 html 文件,如下圖:
移動(dòng)版的 Chrome 默認(rèn)禁止加載 file 協(xié)議的文件
解決方案:
- 對(duì)于不需要使用 file 協(xié)議的應(yīng)用,禁用 file 協(xié)議;
- 1
- 對(duì)于需要使用 file 協(xié)議的應(yīng)用,禁止 file 協(xié)議加載 JavaScript。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
2. setAllowFileAccessFromFileURLs()
// 設(shè)置是否允許通過(guò) file url 加載的 Js代碼讀取其他的本地文件 webView.getSettings().setAllowFileAccessFromFileURLs(true); // 在Android 4.1前默認(rèn)允許 // 在Android 4.1后默認(rèn)禁止- 1
- 2
- 3
- 4
當(dāng)AllowFileAccessFromFileURLs()設(shè)置為 true 時(shí),攻擊者的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>// 通過(guò)該代碼可成功讀取 /etc/hosts 的內(nèi)容數(shù)據(jù)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
解決方案:設(shè)置setAllowFileAccessFromFileURLs(false);
當(dāng)設(shè)置成為 false 時(shí),上述JS的攻擊代碼執(zhí)行會(huì)導(dǎo)致錯(cuò)誤,表示瀏覽器禁止從 file url 中的 javascript 讀取其它本地文件。
3. setAllowUniversalAccessFromFileURLs()
// 設(shè)置是否允許通過(guò) file url 加載的 Javascript 可以訪(fǎng)問(wèn)其他的源(包括http、https等源) webView.getSettings().setAllowUniversalAccessFromFileURLs(true);// 在Android 4.1前默認(rèn)允許(setAllowFileAccessFromFileURLs()不起作用) // 在Android 4.1后默認(rèn)禁止- 1
- 2
- 3
- 4
- 5
當(dāng)AllowFileAccessFromFileURLs()被設(shè)置成true時(shí),攻擊者的JS代碼是:
// 通過(guò)該代碼可成功讀取 http://www.so.com 的內(nèi)容 <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>- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
解決方案:設(shè)置setAllowUniversalAccessFromFileURLs(false);
4. setJavaScriptEnabled()
// 設(shè)置是否允許 WebView 使用 JavaScript(默認(rèn)是不允許) webView.getSettings().setJavaScriptEnabled(true); // 但很多應(yīng)用(包括移動(dòng)瀏覽器)為了讓 WebView 執(zhí)行 http 協(xié)議中的 JavaScript,都會(huì)主動(dòng)設(shè)置為true,不區(qū)別對(duì)待是非常危險(xiǎn)的。- 1
- 2
- 3
- 4
即使把setAllowFileAccessFromFileURLs()和setAllowUniversalAccessFromFileURLs()都設(shè)置為 false,通過(guò) file URL 加載的 javascript 仍然有方法訪(fǎng)問(wèn)其他的本地文件:符號(hào)鏈接跨源攻擊
前提是允許 file URL 執(zhí)行 javascript,即webView.getSettings().setJavaScriptEnabled(true);
這一攻擊能奏效的原因是:通過(guò) javascript 的延時(shí)執(zhí)行和將當(dāng)前文件替換成指向其它文件的軟鏈接就可以讀取到被符號(hào)鏈接所指的文件。具體攻擊步驟:?
1. 把惡意的 js 代碼輸出到攻擊應(yīng)用的目錄下,隨機(jī)命名為 xx.html,修改該目錄的權(quán)限;?
2. 修改后休眠 1s,讓文件操作完成;?
3. 完成后通過(guò)系統(tǒng)的 Chrome 應(yīng)用去打開(kāi)該 xx.html 文件?
4. 等待 4s 讓 Chrome 加載完成該 html,最后將該 html 刪除,并且使用 ln -s 命令為 Chrome 的 Cookie 文件創(chuàng)建軟連接
注:在該命令執(zhí)行前 xx.html 是不存在的;執(zhí)行完這條命令之后,就生成了這個(gè)文件,并且將 Cookie 文件鏈接到了 xx.html 上。
于是就可通過(guò)鏈接來(lái)訪(fǎng)問(wèn) Chrome 的 Cookie
如果是 file 協(xié)議,禁用 javascript 可以很大程度上減小跨源漏洞對(duì) WebView 的威脅。
最終解決方案
- 對(duì)于不需要使用 file 協(xié)議的應(yīng)用,禁用 file 協(xié)議;
- 1
- 2
- 3
- 4
- 對(duì)于需要使用 file 協(xié)議的應(yīng)用,禁止 file 協(xié)議加載 JavaScript。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
3. 總結(jié)
- 本文主要對(duì)Android WebView的使用漏洞及其修復(fù)方式進(jìn)行了全面介紹
- 關(guān)于WebView的系列文章希望對(duì)你有所幫助?
Android開(kāi)發(fā):最全面、最易懂的Webview詳解?
最全面總結(jié) Android WebView與 JS 的交互方式?
手把手教你構(gòu)建 Android WebView 的緩存機(jī)制 & 資源預(yù)加載方案 - 接下來(lái)我會(huì)繼續(xù)講解其他安卓開(kāi)發(fā)的知識(shí),有興趣可以繼續(xù)關(guān)注Carson_Ho的安卓開(kāi)發(fā)筆記!!!!
請(qǐng)幫頂或評(píng)論點(diǎn)贊!因?yàn)槟銈兊墓膭?lì)是我寫(xiě)作的最大動(dòng)力!
版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載,更多文章請(qǐng)繼續(xù)關(guān)注Carson_Ho的博客! http://blog.csdn.net/carson_ho/article/details/64904635總結(jié)
以上是生活随笔為你收集整理的Android:你不知道的 WebView 使用漏洞的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: android WebView详解,常见
- 下一篇: Android 8.0 adb分析