在IM中经常有嵌套网页的功能,主流都是用 Android的WebView 实现的,但是 WebView 使用过程中存在许多漏洞,容易造成用户数据泄露等等危险,而很多人往往会忽视这个问题。本文将介绍 Android WebView的使用漏洞 及其修复方式。
1. 类型
WebView中,主要漏洞有三类:
2. 具体分析
2.1 WebView 任意代码执行漏洞
出现该漏洞的原因有三个:
- WebView 中 addJavascriptInterface() 接口
- WebView 内置导出的 searchBoxJavaBridge_对象
- WebView 内置导出的 accessibility 和 accessibilityTraversalObject 对象
2.1.1 addJavascriptInterface 接口引起远程代码执行漏洞
A. 漏洞产生原因
JS调用Android的其中一个方式是通过addJavascriptInterface接口进行对象映射:
webView.addJavascriptInterface(new JSObject(), "myObj"); // 参数1:Android的本地对象 // 参数2:JS的对象 // 通过对象映射将Android中的本地对象和JS中的对象进行关联,从而实现JS调用Android的对象和方法
所以,漏洞产生原因是:当JS拿到Android这个对象后,就可以调用这个Android对象中所有的方法,包括系统类(java.lang.Runtime 类),从而进行任意代码执行。
如可以执行命令获取本地设备的SD卡中的文件等信息从而造成信息泄露
具体获取系统类的描述:(结合 Java 反射机制)
- Android中的对象有一公共的方法:getClass() ;
- 该方法可以获取到当前类 类型Class
- 该类有一关键的方法: Class.forName;
- 该方法可以加载一个类(可加载 java.lang.Runtime 类)
- 而该类是可以执行本地命令的
以下是攻击的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); // 从执行命令后返回的输入流中得到字符串,有很严重暴露隐私的危险。 // 如执行完访问文件的命令之后,就可以得到文件名的信息了。 } } }
当一些 APP 通过扫描二维码打开一个外部网页时,攻击者就可以执行这段 js 代码进行漏洞攻击。
在微信盛行、扫一扫行为普及的情况下,该漏洞的危险性非常大
B. 解决方案
B1. Android 4.2版本之后
Google 在Android 4.2 版本中规定对被调用的函数以 @JavascriptInterface进行注解从而避免漏洞攻击
B2. Android 4.2版本之前
在Android 4.2版本之前采用拦截prompt()进行漏洞修复。
具体步骤如下:
继承 WebView ,重写 addJavascriptInterface 方法,然后在内部自己维护一个对象映射关系的 Map;
将需要添加的 JS 接口放入该Map中
每次当 WebView 加载页面前加载一段本地的 JS 代码,原理是:
让JS调用一Javascript方法:该方法是通过调用prompt()把JS中的信息(含特定标识,方法名称等)传递到Android端;
在Android的onJsPrompt()中 ,解析传递过来的信息,再通过反射机制调用Java对象的方法,这样实现安全的JS调用Android代码。
关于Android返回给JS的值:可通过prompt()把Java中方法的处理结果返回到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(参数) onButtonClick:function(arg0) { // prompt()返回约定的字符串 // 该字符串可自己定义 // 包含特定的标识符MyApp和 JSON 字符串(方法名,参数,对象名等) 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调用 onButtonClick() 或 onImageClick() 时,就会回调到Android中的 onJsPrompt () // 我们解析出方法名,参数,对象名 // 再通过反射机制调用Java对象的方法
关于该方法的其他细节
细节1:加载上述JS代码的时机
由于当 WebView 跳转到下一个页面时,之前加载的 JS 可能已经失效
所以,通常需要在以下方法中加载 JS:
onLoadResource(); doUpdateVisitedHistory(); onPageStarted(); onPageFinished(); onReceivedTitle(); onProgressChanged();
细节2:需要过滤掉 Object 类的方法
由于最终是通过反射得到Android指定对象的方法,所以同时也会得到基类的其他方法(最顶层的基类是 Object类)
为了不把 getClass()等方法注入到 JS 中,我们需要把 Object 的共有方法过滤掉,需要过滤的方法列表如下:
getClass() hashCode() notify() notifyAl() equals() toString() wait()
总结
对于Android 4.2以前,需要采用拦截prompt()的方式进行漏洞修复
对于Android 4.2以后,则只需要对被调用的函数以 @JavascriptInterface进行注解
关于 Android 系统占比,Google公布的数据:截止 2017 .1 .8 ,Android4.4 之下占有约15%,所以需要重视。
2.1.2 searchBoxJavaBridge_接口引起远程代码执行漏洞
A. 漏洞产生原因
在Android 3.0以下,Android系统会默认通过searchBoxJavaBridge_的Js接口给 WebView 添加一个JS映射对象:searchBoxJavaBridge_对象
该接口可能被利用,实现远程任意代码。
B. 解决方案
删除searchBoxJavaBridge_接口
// 通过调用该方法删除接口 removeJavascriptInterface();
2.1.3 accessibility和 accessibilityTraversal接口引起远程代码执行漏洞
问题分析与解决方案同上,这里不作过多阐述。