模糊测试是一种自动向程序传递输入数据并监控其输出的自动化测试技术。通过这种技术,安全人员可以测试程序的可靠性以及识别潜在的安全漏洞。
我们(360成都安全响应中心)将对Stagefright Media Framework进行模糊测试。它是Android系统上用于解析多媒体文件的逻辑算法库,其中包含了大量的安全漏洞,攻击者通过构造特殊的多媒体文件导致拒绝服务或特权升级甚至远程执行代码。
我们将要使用的模糊测试工具为Michał Zalewski开发的一款最为流行的基于代码覆盖率的开源测试工具:AFL(American Fuzzy Lop)。 借助于其高效的策略,AFL已经在真实产品中发现了大量的漏洞。
在本文中,我们将指导你如何在Linux上使用AFL对stagefright进行模糊测试,从而更高效的复现已知漏洞或发掘新漏洞。另外,本文不仅适用于stagefright,其中的一些经验同样适用于其它由C/C++编写的Android本地程序。
要求
在本节中,我们将首先向你介绍本文接下来使用的环境要求以及软件版本。为了避免出现其他未遇见的错误,我们建议你和我们保持完全一致。
操作系统:Ubuntu 16.10 64bit, 不建议使用32位系统;
AFL版本:2.39b,最新的总是更好的;
AOSP版本:7.1.1_r25,如果你使用的是其他版本,你可能无法直接使用补丁文件;
llvm和clang版本:3.8。
我们假设你已经完成了下载并且编译AOSP的工作。另外,如果你想要使用ASAN,我们建议你编译AOSP为x86版本。
概述
官方AFL只支持在Linux上进行模糊测试,而stagefright是在Android多媒体框架下工作的,因此我们无法直接使用AFL对stagefright进行模糊测试。为了解决这个问题,我们提出了下面两种方案。
方案A:将AFL移植到Android上,从而在Android模拟器或者真实的Android设备上进行模糊测试;
方案B:将stagefright移植到Linux上,从而直接使用官方AFL对其进行模糊测试。
其中,我们已经实现了方案A–android-afl,并且公布了源代码,你可以从仓库得到更多信息。
方案A的主要流程图如下图所示。
方案B的主要流程图如下图所示。
显而易见,相比于方案A,方案B更加简洁,其效率也更好(通常来说,PC的性能远高于任何Android手机或者Android模拟器);另一方面,由于stagefright本身的复杂性,其实现也更加困难。
在后续的文章中,我们将一步一步向你介绍如何实现方案B。通过它,我们已经发现了两个漏洞。
CVE-2016-6764:mediaserver的拒绝服务漏洞;
CVE-2016-6766:mediaserver的拒绝服务漏洞。
细节
移植stagefright到Linux
显而易见,我们首先要让stagefright在Linux上正常工作。
移植binder和ashmem
stagefright需要通过ashmem驱动来共享内存,然而默认情况下Linux内核并不包含ashmem驱动。幸运的是,我们可以通过修改Linux内核配置,并重新编译安装新内核,从而激活ashmem驱动。
我们也许还有更好的解决方案,例如:使用shm代替ashmem或者完全去掉ashmem相关的代码;
注意,本文并未使用binder驱动,这里移植binder只是顺便而已。
使用以下命令下载内核源码。
$ sudo apt install linux-source
转到你要保存内核源码的目录并提取压缩文件。
$ cd kernel
$ tar jxvf /usr/src/linux/linux-source-4.8.0.tar.bz2
$ cd linux-source-4.8.0
拷贝旧的.config文件到源码根目录并开始配置。
$ cp -vi /boot/config-`uname -r` .config
$ make oldconfig
接着使用下面的命令来激活ashmem驱动。
$ make menuconfig
转到Device Drivers->Android,选中Andoid Drivers和Android Binder IPC Driver。
转到Device Drivers->Staging drivers->Android,选中Enable the Anonymous Shared Memory Subsystem。
现在你可以开始编译安装内核了,执行下面的命令。
$ make -j16
$ sudo make modules_install
$ sudo make install
你还需要配置udev规则,从而使得任何用户均可访问binder和ashmem。
$ echo -e "KERNEL==\"binder\", MODE=\"0666\"\nKERNEL==\"ashmem\", MODE=\"0666\"" | sudo tee /etc/udev/rules.d/android.rules
最后,重启你的电脑以启用新内核。
修改Stagefright源码
注意,变量ANDROID_BUILD_TOP为AOSP的根目录,ANDROID_PRODUCT_OUT为AOSP的输出目录。
在这一节,你需要对stagefright源码(包括libstagefright和 stagefright命令行工具)进行改动,原因主要为以下两点。
平台性:stagefright使用了binder驱动进行进程间通信,然而默认情况下Linux内核并不包含binder驱动(实际上,我们可以通过修改Linux内核配置,并重新编译安装新内核,从而激活binder驱动);
依赖性:stagefright命令行工具无法独立的对多媒体文件进行解析,它依赖于其他服务进程(如:servicemanager,mediaserver等)。
我们将不会阐述解决上诉两个问题的具体细节,你可以直接使用我们提供的适用于7.1.1_r25版本的补丁文件。如果你使用的版本和我们不同,你可能需要参照补丁文件,手动修改代码。
下载补丁文件stagefright.diff,转到$ANDROID_BUILD_TOP/aosp/master/frameworks/av目录并应用补丁。
$ cd $ANDROID_BUILD_TOP/frameworks/av
$ git apply stagefright.diff
编译
编译好x86版本的AOSP后,转到stagefright源码目录,并编译。
$ cd $ANDROID_BUILD_TOP/frameworks/av/cmds/stagefright
$ mm -j16
编译结束后,你可以在$ANDROID_PRODUCT_OUT/system/bin目录找到stagefright可执行程序。
配置运行环境
为了让系统能正确找到加载器以及依赖库的位置,你需要做以下软连接。
$ sudo ln -s $ANDROID_PRODUCT_OUT/system /system
拷贝解码器配置文件到/etc目录。
$ sudo cp $ANDROID_PRODUCT_OUT/system/etc/media_codecs_google_audio.xml /etc
$ sudo cp $ANDROID_PRODUCT_OUT/system/etc/media_codecs_google_telephony.xml /etc
$ sudo cp $ANDROID_PRODUCT_OUT/system/etc/media_codecs_google_video.xml /etc
$ sudo cp $ANDROID_PRODUCT_OUT/system/etc/media_codecs.xml /etc
另外,如果需要在后续使用ASAN,你还需要做以下软连接。
$ ln -s $ANDROID_PRODUCT_OUT/system/bin/linker $ANDROID_PRODUCT_OUT/system/bin/linker_asan
$ ln -s $ANDROID_PRODUCT_OUT/obj/lib/libclang_rt.asan-i686-android.so $ANDROID_PRODUCT_OUT/system/lib/libclang_rt.asan-i686-android.so
测试运行
现在,你可以尝试在Linux上运行stagefright了。例如,解析一个MP4文件,运行结果如下。
$ /system/bin/stagefright Disco.240p.mp4
thumbnailTime: 0 us (0.00 secs)
AVC video profile 66 and level 13
format changed.
...................$
avg. 180.73 fps
avg. time to decode one buffer 5485.86 usecs
decoded a total of 304 frame(s).
很好,你已经完成了最为困难也最为重要的工作!
移植AFL
首先,你需要从官网下载最新的AFL源码并解压。
$ wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
$ tar zxf afl-latest.tgz
接着,对AFL源码进行修改以修复下面几个错误。
error: undefined reference to '__fprintf_chk'
error: undefined reference to 'shmat'
error: undefined reference 'afl-area_prev'
同样,我们直接给出适用于2.39b版本(此时的最新版本)的AFL的补丁文件。如果你使用的版本和我们不同,你可能需要参照补丁文件,手动修改代码。
下载补丁文件afl-2.39b.diff到你的电脑,转到AFL源码根目录并安装补丁。
$ cd afl-2.39b
$ patch -p2 < afl-2.39b.diff
使用以下命令编译安装AFL。
$ make clean all
$ cd llvm_mode
$ EXTRA_CFLAGS="-target i686--linux-android -U_FORTIFY_SOURCE" make clean all
$ cd ../
$ sudo make install
使用AFL和ASAN进行重编译
首先,进入你想要进行模糊测试的模块目录。
$ cd MODULE_PATH
其次,在其Android.mk文件中添加以下代码。
LOCAL_CLANG := true
export AFL_CC := /usr/bin/clang
LOCAL_CC := afl-clang-fast
export AFL_CXX := /usr/bin/clang++
LOCAL_CXX := afl-clang-fast++
注意,由于unsupported reloc这个错误,我们不推荐使用afl-gcc/afl-g++。另一方面,根据AFL官方的资料,afl-clang-fast/afl-clang-fast++也是更高效的。
接着,如果你想使用ASAN,你需要添加下面一行代码
LOCAL_SANITIZE := address
或者
LOCAL_SANITIZE := integer
最后,重新编译stagefright。
$ mm -j16
对于复杂的模块来说,你需要重复上面步骤数次,以对多个感兴趣的模块进行插桩。例如,你也许想要对以下模块进行插桩。
$ANDROID_BUILD_TOP/frameworks/av/media/libstagefright
$ANDROID_BUILD_TOP/frameworks/av/media/libstagefright/omx
$ANDROID_BUILD_TOP/frameworks/av/media/libstagefright/yuv
$ANDROID_BUILD_TOP/frameworks/av/media/libstagefright/colorconversion
$ANDROID_BUILD_TOP/frameworks/av/media/libstagefright/codecs/aacenc
$ANDROID_BUILD_TOP/frameworks/av/media/libstagefright/matroska
$ANDROID_BUILD_TOP/frameworks/av/media/libstagefright/filters
$ANDROID_BUILD_TOP/frameworks/av/media/libstagefright/webm
$ANDROID_BUILD_TOP/frameworks/av/media/libstagefright/mpeg2ts
$ANDROID_BUILD_TOP/frameworks/av/media/libstagefright/id3
…more…
恭喜你,所有准备工作都已经完成了,让我们开始模糊测试吧!
模糊测试
首先,你需要为AFL创建两个目录,一个为in,用于存放预先准备的输入样本;另一个为out,用于存放AFL模糊测试过程中生产的一些有用信息以及自动生成的会让程序挂起或者崩溃的样本。
$ mkdir in
$ mkdir out
$ cp -r testcase/* in
其次,你需要以root用户修改/proc/sys/kernel/core_pattern,以修复Pipe at the beginning of ‘core_pattern’这个错误。
$ sudo -s
$ echo core >/proc/sys/kernel/core_pattern
接着,你还需要设置CPU的工作模式为performance,以此来提高AFL的效率。
$ sudo -s
$ cd /sys/devices/system/cpu
$ echo performance | tee cpu*/cpufreq/scaling_governor
如果使用了ASAN,你可能需要执行以下命令。
$ export ASAN_OPTIONS=abort_on_error=1:detect_leaks=0:symbolize=0:allocator_may_return_null=1
最后,执行以下命令开始模糊测试。
$ afl-fuzz -m 4096 -t 10000 -i in -o out -- /system/bin/stagefright @@
如果一切顺利,你将看到类似的AFL的工作屏幕。
建议
在这一节,我们将给你一些额外的建议,以帮助你更快的发现程序中的漏洞。
使用尽可能小但覆盖全面的测试样本集;
对于你想要进行模糊测试的模块,尽可能的编译为静态模块而不是动态模块;
不要对你不敢兴趣的模块进行插桩;
使用并行模糊测试(-M选项和-S选项),更多介绍请参考AFL源码目录中的 docs/parallel_fuzzing.txt;
ASAN需要大量的内存,因此你应该提高-m选项的值。
待办事项
本文还有许多可以改进的地方,但是我们不会在stagefright花费过多的精力了。