CVE-2016-10277是存在于摩托罗拉系列手机的bootloader高危漏洞,可以通过内核命令注入劫持手机的启动流程,加载攻击者控制的initramfs,从而达到root提权的目的。我们手上正好有一个摩托罗拉的MOTO X手机,于是参照[1]的漏洞利用过程,将CVE-2016-10277的漏洞利用过程实践了一把,复现过程还是十分曲折。

0×00 系统环境

1.手机: MOTO X(XT1581)

2.系统固件版本:XT1581_KINZIE_RETCN_DS_5.1.1_LPK23.229,未root

3.Android版本:5.1.1

在漏洞利用过程中需要用到手机boot.img中的aboot、initramfs,手机没有root的话是无法提取系统固件的,幸好我们在网上找到了对应的系统固件,可以直接提取使用。

0×01 漏洞原理

CVE-2016-10277的基本原理并不复杂,主要就是可以通过fastboot向bootloader注入内核命令参数。首先我们可以通过fastoot oem config 查看配置参数:

1.png

这些参数都未受保护,即使bootloader已锁,仍然可以通过fastboot oem config命令配置:

2.png

漏洞就在于bootloader未对配置的这些参数进行过滤,而这些参数会直接传递给内核的命令行。内核命令行参数的注入会影响bootloader的加载过程,攻击者如果精心构造某些参数,将达到控制手机启动,甚至root提权的目的。

0×02 漏洞验证

首先我们要先确定MOTO X是否受CVE-2016-10277漏洞的影响。

1) 注入参数设置property

执行命令:

fastboot oem config fsg-id "a androidboot.bar=1" 

3.png

该命令注入了androidboot.bar=1参数,该参数如果注入成功,将会设置系统的ro.boot.bar属性为1。当然,这里我们只是虚构了一个bar属性。

2) 启动系统查看property

执行命令:

 fastoot continue adb shell getprop ro.boot.bar 

4.png

可以看到成功设置系统属性,说明内核命令行参数注入成功,确定MOTO X受CVE-2016-10277漏洞的影响。

0×03 漏洞利用

通过该漏洞的命令行注入,我们可以向内核命令行注入众多参数,而这些参数又会在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 

5.png

执行命令后我们发现手机进入无限循环启动,无法进入系统,手机已崩溃,说明initrd参数起到了作用。为了验证能否顺利劫持initramfs加载,我们还需要找到可用的initramfs,并且找到向内存注入可控initramfs的方法。

由于不同的手机系统固件不一样,initramfs也不通用,我们只有通过网上下载的对应系统固件来提取initramfs。下载固件后解压缩找到boot.img,使用imgtool工具提取内核文件:

6.png

这里的ramdisk即是我们要找得initramfs。接下来我们想办法向内存中注入我们的ramdisk,可通过如下命令:

 fastboot flash thor ramdisk 

7.png

我们向bootloader flash一个不存在image,虽然不能刷入,但是ramdisk肯定已经存在于手机内存中某个位置,接下来我们需要的就是找到这个位置。ABOOT二进制文件中有一个target_get_scratch_address函数,该函数返回的地址就是downloaded image存在的地址,所以我们通过IDA查看ABOOT文件就能够找到该地址。

ABOOT文件存在于bootloader中,提取固件中的bootloader.img,使用imgtool提取却出错了:

8.png

用010editor查看,看到了aboot的存在,但是bootloader.img应该是做了定制,不是普通的image文件。

9.png

使用谷歌大法,找到了一个专门用于提取moto image文件的python脚本https://github.com/laginimaineb/unpack_motoboot

10.png

成功提取aboot文件后,放入IDA查看,由于没有删掉符号信息,可以快速定位到target_get_scratch_address函数:

11.JPG

我们从而得到flash image时image在内存中的地址为:0×11000000。综上,我们终于可以尝试劫持initramfs的加载:

12.png

不幸的是,手机依然无限循环重启。问题出在什么地方的?由于我们看不到任何启动时打印的信息,也不知从何入手。漏洞的发现者做出了一个猜测,有可能是flash image后,手机的启动过程中污染了我们发送的initramfs,导致initramfs被破坏。因此,我们需要将控制的initramfs填充,将真正的initramfs放到高地址的内存中:

.--------------------------------.----------------------. | Physical Address               | Data                 |
|--------------------------------|----------------------| | SCRATCH_ADDR                   | Corrupted PADDING    | 
| SCRATCH_ADDR + sizeof(PADDING) | Controlled initramfs |
`--------------------------------'----------------------' 

我们选择padding的数据长度为32MB,

13.png

这次终于成功启动了系统,说明我们成功劫持了initramfs:

13_1.png

3) 构造initramfs获取root权限

成功劫持了initramfs后,我们需要想办法替换或修改initramfs中的文件来进行root提权。我们首先解压缩initramfs,它是一个cpio打包、gzip压缩的文件:

14.png

我们在sbin目录下看到了adbd二进制文件,它是我们我们执行adb shell时的服务程序。

15.png

我们想要root提权,一种方法就是patch adbd程序,让adb shell直接以root权限执行,而不是降权执行。在AOSP的adbd源码中我们看到,只要我们patch掉should_drop_privileges的执行,那么就能让adb shell以root权限执行。

16.png

但是,我们将adbd放入IDA一看,发现strip掉了函数符号,IDA在ARM64下没法F5,而且厂商在AOSP基础上做了很多定制,导致使用strings找特征都不好使。

17.JPG

如果要直接patch adbd程序估计得花很多时间逆向分析,这里我们走一下捷径,直接分析https://github.com/alephsecurity/initrootpatch后的adbd,然后对比原来的adbd程序,分析patch点,最后根据32位程序来推测64位arm程序可能的patch点。事实证明这是一条快速通道,我们很快就对比找到了patch点:

23.png

24.png

一共两处patch点,根据patch点的结构,我们在64位adbd中找到相似的patch点:

25.png

于是我们patch掉这两个地方,用patch后的adbd替换原来initramfs中的程序,重新打包运行,adb shell直接就是root权限:

26.png

27.png

通过类似的方法,我们还可以patch init程序,关掉selinux。

0×04 总结

CVE-2016-10277是一个比较有意思的漏洞,原理不复杂,只是一个简单的命令行参数注入,但是它的攻击面却很广,危害也很大。我们在复现该漏洞的利用过程中,经历了非常曲折的一个过程。本来漏洞发现者已经给出了MOTO G4、G5手机可用的exploit,但是我们的手机是MOTO X,原来的exp都没法用,只有自己摸索漏洞利用过程。在研究过程学习了secure boot、aboot、selinux等知识,该漏洞的利用覆盖了很广的知识面,值得研究。

参考文献:

[1] https://alephsecurity.com/2017/06/07/initroot-moto/

*本文原创作者:thor@MS509Team