thor123 FreeBuf
*本文原创作者:thor@MS509Team,本文属FreeBuf原创奖励计划,未经许可禁止转载
CVE-2016-10277是存在于摩托罗拉系列手机的bootloader高危漏洞,可以通过内核命令注入劫持手机的启动流程,加载***者控制的initramfs,从而达到root提权的目的。我们手上正好有一个摩托罗拉的MOTO X手机,于是参照[1]的漏洞利用过程,将CVE-2016-10277的漏洞利用过程实践了一把,复现过程还是十分曲折。
0x00 系统环境
1.手机: MOTO X(XT1581)
2.系统固件版本:
3.Android版本:5.1.1
在漏洞利用过程中需要用到手机boot.img中的aboot、initramfs,手机没有root的话是无法提取系统固件的,幸好我们在网上找到了对应的系统固件,可以直接提取使用。
0x01 漏洞原理
CVE-2016-10277的基本原理并不复杂,主要就是可以通过fastboot向bootloader注入内核命令参数。首先我们可以通过fastoot oem config 查看配置参数:
这些参数都未受保护,即使bootloader已锁,仍然可以通过fastboot oem config命令配置:
漏洞就在于bootloader未对配置的这些参数进行过滤,而这些参数会直接传递给内核的命令行。内核命令行参数的注入会影响bootloader的加载过程,***者如果精心构造某些参数,将达到控制手机启动,甚至root提权的目的。
0x02 漏洞验证
首先我们要先确定MOTO X是否受CVE-2016-10277漏洞的影响。
1) 注入参数设置property
执行命令:
fastboot oem config fsg-id "a androidboot.bar=1"
该命令注入了androidboot.bar=1参数,该参数如果注入成功,将会设置系统的ro.boot.bar属性为1。当然,这里我们只是虚构了一个bar属性。
2) 启动系统查看property
执行命令:
fastoot continue adb shell getprop ro.boot.bar
可以看到成功设置系统属性,说明内核命令行参数注入成功,确定MOTO X受CVE-2016-10277漏洞的影响。
0x03 漏洞利用
通过该漏洞的命令行注入,我们可以向内核命令行注入众多参数,而这些参数又会在OS启动阶段多个地方被引用,因此该漏洞的***面是很广的,这里我们主要尝试能否通过该漏洞进行root提权。我们首先简单介绍下手机的启动过程,找到其中的利用点。
1) Android手机的Secure Boot过程
MOTO系列的手机大部分使用高通芯片,而高通芯片的手机大致启动过程如下:
[Primary Bootloader (PBL)] `-. [Secondary Bootloader (SBL)] `-. [Applications Bootloader (ABOOT)] `-. [{boot,recovery}.img] |-- Linux Kernel `-- initramfs `-. [system.img]
手机开机后,首先启动的是bootloader,而bootloader又大致分为3个阶段,最先启动的是PBL,然后是SBL、ABOOT,最后通过ABOOT从boot.img或recovery.img中加载linux kernel和initramfs,进入系统加载阶段。initramfs是一个内存文件系统,bootloader一般会从固定的内存地址中加载,系统启动后会挂载到rootfs,即根目录/。initramfs包含很多重要文件,包括系统启动后第一个用户态进程init、服务启动脚本init.rc、selinux策略文件sepolicy、adbd程序等。如果我们能够让系统启动时加载我们构造的initramfs,那么我们就可以在这里面做很多劫持动作。而CVE-2016-10277的一个***面就是通过注入内核命令参数控制手机启动时的initramfs加载地址,加载我们指定的initramfs。
2) 通过参数注入劫持initramfs加载
通过CVE-2016-10277漏洞我们可以向内核注入initrd参数,该参数控制了initramfs的内存加载地址,参数形式如下:
initrd=<initramfs_address>,<initramfs_size>
首先我们测试是否可以劫持initramfs的加载地址,命令如下:
fastboot oem config fsg-id "a initrd=0x12341234,1024" fastboot continue
执行命令后我们发现手机进入无限循环启动,无法进入系统,手机已崩溃,说明initrd参数起到了作用。为了验证能否顺利劫持initramfs加载,我们还需要找到可用的initramfs,并且找到向内存注入可控initramfs的方法。
由于不同的手机系统固件不一样,initramfs也不通用,我们只有通过网上下载的对应系统固件来提取initramfs。下载固件后解压缩找到boot.img,使用imgtool工具提取内核文件:
这里的ramdisk即是我们要找得initramfs。接下来我们想办法向内存中注入我们的ramdisk,可通过如下命令:
fastboot flash thor ramdisk
我们向bootloader flash一个不存在image,虽然不能刷入,但是ramdisk肯定已经存在于手机内存中某个位置,接下来我们需要的就是找到这个位置。ABOOT二进制文件中有一个target_get_scratch_address函数,该函数返回的地址就是downloaded image存在的地址,所以我们通过IDA查看ABOOT文件就能够找到该地址。
ABOOT文件存在于bootloader中,提取固件中的bootloader.img,使用imgtool提取却出错了:
用010editor查看,看到了aboot的存在,但是bootloader.img应该是做了定制,不是普通的image文件。
使用谷歌大法,找到了一个专门用于提取moto image文件的python脚本https://github.com/laginimaineb/unpack_motoboot:
成功提取aboot文件后,放入IDA查看,由于没有删掉符号信息,可以快速定位到target_get_scratch_address函数:
我们从而得到flash image时image在内存中的地址为:0x11000000。综上,我们终于可以尝试劫持initramfs的加载:
不幸的是,手机依然无限循环重启。问题出在什么地方的?由于我们看不到任何启动时打印的信息,也不知从何入手。漏洞的发现者做出了一个猜测,有可能是flash image后,手机的启动过程中污染了我们发送的initramfs,导致initramfs被破坏。因此,我们需要将控制的initramfs填充,将真正的initramfs放到高地址的内存中:
.--------------------------------.----------------------. | Physical Address | Data | |--------------------------------|----------------------| | SCRATCH_ADDR | Corrupted PADDING | | SCRATCH_ADDR + sizeof(PADDING) | Controlled initramfs | `--------------------------------'----------------------'
我们选择padding的数据长度为32MB,
这次终于成功启动了系统,说明我们成功劫持了initramfs:
3) 构造initramfs获取root权限
成功劫持了initramfs后,我们需要想办法替换或修改initramfs中的文件来进行root提权。我们首先解压缩initramfs,它是一个cpio打包、gzip压缩的文件:
我们在sbin目录下看到了adbd二进制文件,它是我们我们执行adb shell时的服务程序。
我们想要root提权,一种方法就是patch adbd程序,让adb shell直接以root权限执行,而不是降权执行。在AOSP的adbd源码中我们看到,只要我们patch掉should_drop_privileges的执行,那么就能让adb shell以root权限执行。
但是,我们将adbd放入IDA一看,发现strip掉了函数符号,IDA在ARM64下没法F5,而且厂商在AOSP基础上做了很多定制,导致使用strings找特征都不好使。
如果要直接patch adbd程序估计得花很多时间逆向分析,这里我们走一下捷径,直接分析https://github.com/alephsecurity/initrootpatch后的adbd,然后对比原来的adbd程序,分析patch点,最后根据32位程序来推测64位arm程序可能的patch点。事实证明这是一条快速通道,我们很快就对比找到了patch点:
一共两处patch点,根据patch点的结构,我们在64位adbd中找到相似的patch点:
于是我们patch掉这两个地方,用patch后的adbd替换原来initramfs中的程序,重新打包运行,adb shell直接就是root权限:
通过类似的方法,我们还可以patch init程序,关掉selinux。
0x04 总结
CVE-2016-10277是一个比较有意思的漏洞,原理不复杂,只是一个简单的命令行参数注入,但是它的***面却很广,危害也很大。我们在复现该漏洞的利用过程中,经历了非常曲折的一个过程。本来漏洞发现者已经给出了MOTO G4、G5手机可用的exploit,但是我们的手机是MOTO X,原来的exp都没法用,只有自己摸索漏洞利用过程。在研究过程学习了secure boot、aboot、selinux等知识,该漏洞的利用覆盖了很广的知识面,值得研究。