Owning a Locked OnePlus 3/3T: Bootloader Vulnerabilities

Feb 08, 2017 • Roee Hay

In this blog post I disclose two vulnerabilities in the OnePlus 3/3T bootloader. The first one, CVE-2017-5626, is a critical severity vulnerability affecting OxygenOS 3.2-4.0.1 (4.0.2 is patched). The vulnerability allows for a physical adversary (or one with ADB/fastboot access) to bypass the bootloader’s lock state, even when Allow OEM Unlocking is disabled, without user confirmation and without triggering a factory reset. This vulnerability allows for kernel code execution (albeit with a 5 seconds warning upon boot). The second vulnerability, CVE-2017-5624, affecting all versions of OxygenOS to date (Feb 10 UPDATE: OxygenOS 4.0.3, released Feb 09, seems to be patched), allows the attacker to disable dm-verity. The combination of the vulnerabilities enables a powerful attack – persistent highly privileged code execution without any warning to the user and with access to the original user’s data (after the victim enters his credentials).

Both issues were responsibly disclosed to and acknowledged by OnePlus Security. The first vulnerability, CVE-2017-5626, was reported on January 23rd. It was also found independently by a OnePlus engineer. CVE-2017-5624, reported on January 16th, should be fixed in a future OxygenOS release – the reason for its today’s public disclosure is because someone already published it on January 24th.

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:

// 'erase' handler
int__fastcallsub_91847118(char*partitionName,inta2,inta3){...v3=partitionName;v4=returnTRUE1(partitionName,a2);if(!v4){LABEL_7:...if(v4){if(dword_9198D804==dword_9198D804)returneraseParition(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";returnFAIL2((int)v7,v5);}gotoLABEL_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";returnFAIL2((int)v7,v5);}LABEL_23:assert(v4,v5,v6);}v4=sub_918428F0(v3,v5);if(!v4||magicFlag_dword_91989C10)gotoLABEL_7;v6=dword_9198D804;...v7="Critical partition erase is not allowed";returnFAIL2((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. The boot and recovery partitions are verified by the bootloader – flashing a modified boot partition, for instance, will prompt the following warning upon boot:

.

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.

Disabling dm-verity (CVE-2017-5624)

The verification of the system partition, as opposed to boot & recovery, is driven by dm-verity. What we discovered, is that one can instruct the locked bootloader to bring up the platform with dm-verity disabled by another fastboot command: fastboot oem disable_dm_verity.

So again, this sets some flag at 91960740 (which we named dmVerity). It is then used by the bootloader when it constructs the kernel cmdline:

The androidboot.enable_dm_verity kernel command line argument propagates to the ro.boot.enable_dm_verity which later instructs OnePlus’s init whether or not to disable dm-verity:

Combining the 2 Vulnerabilities

The couple of vulnerabilities can be combined together for code execution with privileged SELinux domains, without any warning to the user and with access to the original user data. In order to demonstrate this (and there are probably thousands of better ways with a higher severity), 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.