AndroidNative层文件解析漏洞挖掘指南

本文以手Q的一次文件解析类漏洞挖掘为例,叙述了Android Native层文件解析类型漏洞挖掘的过程

手Q这个应用从功能来说十分的庞大,如果使用类似MFFA的框架去挖掘文件解析类漏洞,效率低,而且文件入口在哪儿、如何利用脚本进行自动化都是非常大的问题。本文在一次手Q的文件解析类漏洞挖掘的过程中,提出了一种可能的解决问题的方案,妄称指南不吝赐教。

0x01 问题分析

目前面临的问题是,不知道手Q的文件入口在哪儿(指代码层面),而且依据手Q的用户分析,手Q很有可能根本不提供代码入口,没有代码入口就没有自动化,靠着UI触发进行测试那是脑子秀逗了。所以首要解决的问题得自己提供文件入口了,目前看来得自己抠出so,自己写测试程序,自己载入测试用例,自己收集crash了,挖漏洞真是一条孤独的路。

0x02 流程图

首先要先分析手Q的lib文件,找出待测的so,然后编写能够调用该so的测试程序,将测试用例放进去进行自动化测试,最后收集native crash,人工分析之后拿到漏洞。流程非常简单,将大问题拆分成以下几个问题,分别克服之后就能大成总目标了:

l 拿什么so?这个so用来做什么的?什么时候会被调用?

l 测试程序怎么在没有源码的情况下调用so?

l 测试用例用什么生成?

0x03 so筛选

筛选的方案其实大致有两类:猜测某个so是做什么的,然后动态调试验证;先动态调试调用某个功能之后看手Q调用了哪个so。两个方案使用的技术都差不多,主要都是利用动态调试最后确定so。

首先要先拿到so,把apk当作zip解压就行,里面有个lib库,拿到就好。然后我按照关键字去筛选这些so,比如我有个预先的目标,想测的文件格式有如下:GIF、WAV、JPG、PNG等等,所以我找到了这个文件libGIFEngine.so,看起来就像一个解析GIF的so,加入备选。

验证方案:如何验证呢?动态调试手Q,断点下在调用so的相关函数,如果在发送GIF时,能够中断,就说明该so选对了。至于如何建立一套动态调试手Q环境,则不是本篇覆盖的内容了。

在调试的过程中,我发现了有个函数叫NativeOpenFile的函数,毫不犹豫下断点,果然在发送GIF之后,程序中断在这里。本阶段工作完成。选出so:libGIFEngine.so

0x04 测试程序的编写

首先稍微提一下JNI调用规范,so都要符合JNI调用规范才能被调用,至于该规范细节是什么不用理解,只用理解一点:如何调用so的函数?

所有能够直接被Java层代码调用的so函数,函数名都有固定格式,以下是libEngine.so的七个能被调用的函数:

同时,在Java层代码中也要这么声明这些Native函数才能够被调用。但是这里有个问题,测试程序要满足这个命名路径,即下图所示。所以需要把自己的测试程序的包名写成com.tencent.image,把测试的类名写成nativegifimage即可。

Java_com_tencent_image_nativegifimage.函数名

之前要声明调用的so,用System.loadLibrary或者System.load调用某个so就可以了。调用过程不再赘述,然后就可以开始编写测试函数了。

鉴于我走过的坑,这里我要插几句话:Android Studio1.5之后有了个新功能,可以在build.gradle中指定jniLibs路径,记得把测试类和主Activity分开。

开始测试之前,需要冷静一下思考这里要做什么:要测试这七个函数,调用GIF文件产生Native Crash,那么代表测试的进程会挂掉,如何能够让测试程序自动的继续进行下去?这是写测试程序需要考虑的问题。

基于此,我给出了测试程序的模型,仅供参考。

由于测试Task比较符合Service的特性,所以最后选了Service注册一个新进程进行测试。然后在主Activity进程中新建一个线程监控Service的运行状态,一旦检测到没有运行,自动重启Service,且记录下crash的文件名,好进行记录、复现、存储等等。所以我对Test Case进行编号,然后Service每测试通过一个就发送广播包,而中断之后重启Service时,发送个Intent同步一下测试进度即可。

然后按照上述逻辑编写程序就行。这里还有个问题,就是手Q定义了自己的异常,所以我们的测试程序中也需要相应的异常定义,由于异常处理的根源就是发送一个Error Code,所以只要能找到手Q中Error Code的定义即可。注意将手Q中各种常量转成相应的Error Code如下图所示。

原来的Error Code定义。

修改之后的Error Code定义。

然后程序大概就能跑起来了,当然这中间还要经过对各个Native函数的调用参数、调用逻辑的解析等等,就慢慢看代码吧,如果有自动化代码分析工具的话,这里的过程也可以自动化。

0x05 Test Case生成

其实File Fuzz中,生成测试用例是核心的一环。我在工作中选取了Peach3作为生成Test Case的工具。Peach3作为Smart Fuzz的工具,可以依据文件模版生成测试用例,但是需要人工编写PIT文件驱动Peach运行,而编写PIT文件又是一个非常麻烦的工作,所以我不在本文中叙述这些了,我在近期会更新相应的Peach3的PIT文件编写教程,敬请期待。

关于Peach的变异策略,我采用了有序+随机策略,这样可以保证每次生成Test Case中测试序号一致的文件变异内容保持一致。有利于重放、收集Test Case,也有利于后期在分布式系统上大规模的测试。

0x06 测试得出crash

我在测试的过程中大概测试了40w左右的Test Case,发现了一些crash,但是由于对手Q调用Native层函数的逻辑不够清晰,所以部分crash不能在手Q上直接触发。但是也有一些crash可以直接触发,而是否有利用空间留待后续的人工分析。

0x07 未来的工作

目标:自动化、规模化

作为一个程序猿,把工作和生活尽可能的自动化作为梦想是很自然的事情,漏洞挖掘过程中有以下几处没有自动化的:

l 筛选so

l 理出手Q调用逻辑

l 编写测试程序

其中筛选so需要依靠经验和人工分析才能得到,编写测试程序则可以通过代码重用提高效率,而手Q关于目标Native函数的调用逻辑则可以通过工具进行分析得出,这方面可以再优化一下。将来能够自动化分析Native函数的调用逻辑,甚至根据调用逻辑得出一条手Q的执行路径,辅助编写测试程序就是非常愉快的事情了。

另外File Fuzz也是Fuzz的一种,需要大量的运行时间才行,所以分布式进行测试结合腾讯云进行负载均衡的测试和存储是一条非常有用的路。

顺便介绍下腾讯的SO加固保护方案:是指通过加壳、混淆、VM等技术,保护Native层代码,防止被逆向、篡改、二次打包等行为。腾讯御安全平台有相关介绍和试用体验。