前言
- 現(xiàn)在很多App里都內(nèi)置了Web網(wǎng)頁(Hyprid App)橄仍,比如說很多電商平臺韧涨,淘寶、京東侮繁、聚劃算等等虑粥,如下圖
- 上述功能是由 Android的WebView 實現(xiàn)的,但是 WebView 使用過程中存在許多漏洞宪哩,容易造成用戶數(shù)據(jù)泄露等等危險娩贷,而很多人往往會忽視這個問題
- 今天我將全面介紹 Android WebView的使用漏洞 及其修復方式
目錄
1. 類型
WebView中,主要漏洞有三類:
- 任意代碼執(zhí)行漏洞
- 密碼明文存儲漏洞
- 域控制不嚴格漏洞
2. 具體分析
2.1 WebView 任意代碼執(zhí)行漏洞
出現(xiàn)該漏洞的原因有三個:
- WebView 中
addJavascriptInterface()
接口 - WebView 內(nèi)置導出的
searchBoxJavaBridge_
對象 - WebView 內(nèi)置導出的
accessibility
和accessibilityTraversal
Object 對象
2.1.1 addJavascriptInterface 接口引起遠程代碼執(zhí)行漏洞
A. 漏洞產(chǎn)生原因
JS調(diào)用Android的其中一個方式是通過addJavascriptInterface
接口進行對象映射:
webView.addJavascriptInterface(new JSObject(), "myObj");
// 參數(shù)1:Android的本地對象
// 參數(shù)2:JS的對象
// 通過對象映射將Android中的本地對象和JS中的對象進行關聯(lián)锁孟,從而實現(xiàn)JS調(diào)用Android的對象和方法
所以彬祖,漏洞產(chǎn)生原因是:當JS拿到Android這個對象后,就可以調(diào)用這個Android對象中所有的方法品抽,包括系統(tǒng)類(java.lang.Runtime 類)储笑,從而進行任意代碼執(zhí)行。
如可以執(zhí)行命令獲取本地設備的SD卡中的文件等信息從而造成信息泄露
具體獲取系統(tǒng)類的描述:(結(jié)合 Java 反射機制)
- Android中的對象有一公共的方法:getClass() 圆恤;
- 該方法可以獲取到當前類 類型Class
- 該類有一關鍵的方法: Class.forName突倍;
- 該方法可以加載一個類(可加載 java.lang.Runtime 類)
- 而該類是可以執(zhí)行本地命令的
以下是攻擊的Js核心代碼:
function execute(cmdArgs)
{
// 步驟1:遍歷 window 對象
// 目的是為了找到包含 getClass ()的對象
// 因為Android映射的JS對象也在window中,所以肯定會遍歷到
for (var obj in window) {
if ("getClass" in window[obj]) {
// 步驟2:利用反射調(diào)用forName()得到Runtime類對象
alert(obj);
return window[obj].getClass().forName("java.lang.Runtime")
// 步驟3:以后哑了,就可以調(diào)用靜態(tài)方法來執(zhí)行一些命令赘方,比如訪問文件的命令
getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
// 從執(zhí)行命令后返回的輸入流中得到字符串,有很嚴重暴露隱私的危險弱左。
// 如執(zhí)行完訪問文件的命令之后窄陡,就可以得到文件名的信息了。
}
}
}
- 當一些 APP 通過掃描二維碼打開一個外部網(wǎng)頁時拆火,攻擊者就可以執(zhí)行這段 js 代碼進行漏洞攻擊跳夭。
- 在微信盛行、掃一掃行為普及的情況下们镜,該漏洞的危險性非常大
B. 解決方案
B1. Android 4.2版本之后
Google 在Android 4.2 版本中規(guī)定對被調(diào)用的函數(shù)以 @JavascriptInterface
進行注解從而避免漏洞攻擊
B2. Android 4.2版本之前
在Android 4.2版本之前采用攔截prompt()進行漏洞修復币叹。
具體步驟如下:
-
繼承 WebView ,重寫
addJavascriptInterface
方法模狭,然后在內(nèi)部自己維護一個對象映射關系的 Map颈抚;將需要添加的 JS 接口放入該Map中
-
每次當 WebView 加載頁面前加載一段本地的 JS 代碼,原理是:
- 讓JS調(diào)用一Javascript方法:該方法是通過調(diào)用prompt()把JS中的信息(含特定標識嚼鹉,方法名稱等)傳遞到Android端贩汉;
- 在Android的onJsPrompt()中 驱富,解析傳遞過來的信息,再通過反射機制調(diào)用Java對象的方法匹舞,這樣實現(xiàn)安全的JS調(diào)用Android代碼褐鸥。
關于Android返回給JS的值:可通過prompt()把Java中方法的處理結(jié)果返回到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(參數(shù))
onButtonClick:function(arg0) {
// prompt()返回約定的字符串
// 該字符串可自己定義
// 包含特定的標識符MyApp和 JSON 字符串(方法名叫榕,參數(shù),對象名等)
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調(diào)用 onButtonClick() 或 onImageClick() 時姊舵,就會回調(diào)到Android中的 onJsPrompt ()
// 我們解析出方法名晰绎,參數(shù),對象名
// 再通過反射機制調(diào)用Java對象的方法
關于該方法的其他細節(jié)
細節(jié)1:加載上述JS代碼的時機
- 由于當 WebView 跳轉(zhuǎn)到下一個頁面時蠢莺,之前加載的 JS 可能已經(jīng)失效
- 所以寒匙,通常需要在以下方法中加載 JS:
onLoadResource();
doUpdateVisitedHistory()躏将;
onPageStarted()锄弱;
onPageFinished();
onReceivedTitle()祸憋;
onProgressChanged()会宪;
細節(jié)2:需要過濾掉 Object 類的方法
- 由于最終是通過反射得到Android指定對象的方法,所以同時也會得到基類的其他方法(最頂層的基類是 Object類)
- 為了不把 getClass()等方法注入到 JS 中蚯窥,我們需要把 Object 的共有方法過濾掉掸鹅,需要過濾的方法列表如下:
getClass()
hashCode()
notify()
notifyAl()
equals()
toString()
wait()
總結(jié)
對于Android 4.2以前,需要采用攔截prompt()的方式進行漏洞修復
對于Android 4.2以后拦赠,則只需要對被調(diào)用的函數(shù)以 @JavascriptInterface進行注解
-
關于 Android 系統(tǒng)占比巍沙,Google公布的數(shù)據(jù):截止 2017 .1 .8 ,Android4.4 之下占有約15%荷鼠,所以需要重視句携。
具體數(shù)據(jù)如下:
2.1.2 searchBoxJavaBridge_接口引起遠程代碼執(zhí)行漏洞
A. 漏洞產(chǎn)生原因
- 在Android 3.0以下,Android系統(tǒng)會默認通過
searchBoxJavaBridge_
的Js接口給 WebView 添加一個JS映射對象:searchBoxJavaBridge_
對象 - 該接口可能被利用允乐,實現(xiàn)遠程任意代碼矮嫉。
B. 解決方案
刪除searchBoxJavaBridge_
接口
// 通過調(diào)用該方法刪除接口
removeJavascriptInterface();
2.1.3 accessibility
和 accessibilityTraversal
接口引起遠程代碼執(zhí)行漏洞
問題分析與解決方案同上牍疏,這里不作過多闡述蠢笋。
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 協(xié)議的 url编矾,從而可以獲取 B 應用的內(nèi)部私有文件熟史,從而帶來數(shù)據(jù)泄露威脅
具體:當其他應用啟動此 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 協(xié)議
webView.getSettings().setAllowFileAccess(true);
// 默認設置為true碘菜,即允許在 File 域下執(zhí)行任意 JavaScript 代碼
使用 file 域加載的 js代碼能夠使用進行同源策略跨域訪問凹蜈,從而導致隱私信息泄露
- 同源策略跨域訪問:對私有目錄文件進行訪問
- 針對 IM 類產(chǎn)品,泄露的是聊天信息忍啸、聯(lián)系人等等
- 針對瀏覽器類軟件仰坦,泄露的是cookie 信息泄露。
如果不允許使用 file 協(xié)議计雌,則不會存在上述的威脅悄晃;
webView.getSettings().setAllowFileAccess(true);
但同時也限制了 WebView 的功能,使其不能加載本地的 html 文件凿滤,如下圖:
移動版的 Chrome 默認禁止加載 file 協(xié)議的文件
解決方案:
- 對于不需要使用 file 協(xié)議的應用妈橄,禁用 file 協(xié)議;
setAllowFileAccess(false);
- 對于需要使用 file 協(xié)議的應用翁脆,禁止 file 協(xié)議加載 JavaScript眷蚓。
setAllowFileAccess(true);
// 禁止 file 協(xié)議加載 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 的內(nèi)容數(shù)據(jù)
解決方案:設置setAllowFileAccessFromFileURLs(false);
當設置成為 false 時反番,上述JS的攻擊代碼執(zhí)行會導致錯誤沙热,表示瀏覽器禁止從 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 的內(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>
解決方案:設置setAllowUniversalAccessFromFileURLs(false);
4. setJavaScriptEnabled()
// 設置是否允許 WebView 使用 JavaScript(默認是不允許)
webView.getSettings().setJavaScriptEnabled(true);
// 但很多應用(包括移動瀏覽器)為了讓 WebView 執(zhí)行 http 協(xié)議中的 JavaScript,都會主動設置為true枫疆,不區(qū)別對待是非常危險的爵川。
即使把setAllowFileAccessFromFileURLs()
和setAllowUniversalAccessFromFileURLs()
都設置為 false,通過 file URL 加載的 javascript 仍然有方法訪問其他的本地文件:符號鏈接跨源攻擊
前提是允許 file URL 執(zhí)行 javascript养铸,即
webView.getSettings().setJavaScriptEnabled(true);
這一攻擊能奏效的原因是:通過 javascript 的延時執(zhí)行和將當前文件替換成指向其它文件的軟鏈接就可以讀取到被符號鏈接所指的文件雁芙。具體攻擊步驟:
1. 把惡意的 js 代碼輸出到攻擊應用的目錄下,隨機命名為 xx.html钞螟,修改該目錄的權限兔甘;
2. 修改后休眠 1s,讓文件操作完成鳞滨;
3. 完成后通過系統(tǒng)的 Chrome 應用去打開該 xx.html 文件
4. 等待 4s 讓 Chrome 加載完成該 html洞焙,最后將該 html 刪除,并且使用 ln -s 命令為 Chrome 的 Cookie 文件創(chuàng)建軟連接
注:在該命令執(zhí)行前 xx.html 是不存在的;執(zhí)行完這條命令之后澡匪,就生成了這個文件熔任,并且將 Cookie 文件鏈接到了 xx.html 上。
于是就可通過鏈接來訪問 Chrome 的 Cookie
- Google 沒有進行修復唁情,只是讓Chrome 最新版本默認禁用 file 協(xié)議疑苔,所以這一漏洞在最新版的 Chrome 中并不存在
- 但是,在日常大量使用 WebView 的App和瀏覽器甸鸟,都有可能受到此漏洞的影響惦费。通過利用此漏洞,容易出現(xiàn)數(shù)據(jù)泄露的危險
如果是 file 協(xié)議抢韭,禁用 javascript 可以很大程度上減小跨源漏洞對 WebView 的威脅薪贫。
- 但并不能完全杜絕跨源文件泄露。
- 例:應用實現(xiàn)了下載功能刻恭,對于無法加載的頁面瞧省,會自動下載到 sd 卡中;由于 sd 卡中的文件所有應用都可以訪問鳍贾,于是可以通過構(gòu)造一個 file URL 指向被攻擊應用的私有文件鞍匾,然后用此 URL 啟動被攻擊應用的 WebActivity,這樣由于該 WebActivity 無法加載該文件贾漏,就會將該文件下載到 sd 卡下面候学,然后就可以從 sd 卡上讀取這個文件了
最終解決方案
- 對于不需要使用 file 協(xié)議的應用,禁用 file 協(xié)議纵散;
// 禁用 file 協(xié)議梳码;
setAllowFileAccess(false);
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);
- 對于需要使用 file 協(xié)議的應用,禁止 file 協(xié)議加載 JavaScript伍掀。
// 需要使用 file 協(xié)議
setAllowFileAccess(true);
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);
// 禁止 file 協(xié)議加載 JavaScript
if (url.startsWith("file://") {
setJavaScriptEnabled(false);
} else {
setJavaScriptEnabled(true);
}