ANZEN Research: How We Compromised Keystone via a USB Vulnerability

Updated Aug 25, 2026
ANZEN Research: How We Compromised Keystone via a USB Vulnerability

Key Takeaways

• The USB SDK lacks validation for host-controlled lengths, resulting in out-of-bounds read and write operations on a fixed buffer

• Attackers can hijack the USB class callback table to escalate out-of-bounds writes into MCU arbitrary code execution

• Erroneous MPU configuration allows shellcode residing in SRAM to be executed directly

• By acquiring the Passcode, OTP, and cryptographic material from the secure element chips, the AES Key can be reconstructed to extract BIP39 mnemonic phrases

• Third-party SDKs also constitute part of the core security boundary of hardware wallets and must be included in comprehensive audits

Security Update (July 2026): The issue discussed in this article was discovered by OneKey Anzen during a joint security audit with Keystone in March 2026, affecting Keystone 3 Pro firmware that integrates the MCU vendor's USB SDK. Keystone released firmware v2.4.0 on April 1, 2026, fully fixing this issue; as of the release of the joint announcement, neither party has found evidence of this vulnerability being used to attack Keystone users. Successful exploitation requires an attacker to simultaneously have physical possession of the device, know the correct PIN, unlock the device, obtain user approval for the USB connection, and connect the device to an attacker-controlled computer. Users running firmware below v2.4.0 should immediately upgrade to the latest version; using air-gapped QR signing can avoid this specific USB attack path, but completing the upgrade is still recommended. For details, see Keystone and OneKey Anzen Joint Security Update.

In early August, Black Hat USA 2026 took place in Las Vegas. Due to visa issues, our presentation was transitioned to an on-demand format. This talk originated from our audit of the Keystone wallet, where we discovered a severe issue in the chip vendor's USB SDK and ultimately extracted the mnemonic seed phrase.

In fact, this is hardly the first time issues have surfaced in USB protocol stacks. Over the past decade and more, countless critical device attacks have been tied to USB protocol stacks—from USB driver vulnerabilities and remote redirection in 2014, to Fusée Gelée, checkm8, and Kamakiri, and onward to usbliter8 disclosed in 2026.

The Frequent Emergence of USB Protocol Vulnerabilities

2014: Do USB Attacks Truly Require Physical Access?

In 2014, NCC Group presented "USB Attacks Need Physical Access Right? Not Any More..." at Black Hat Asia.

At the time, people generally categorized USB vulnerabilities as "local physical attacks": an attacker had to plug a malicious device into the target machine to trigger driver vulnerabilities via malformed descriptors or class-specific data.

In that presentation, researchers leveraged RemoteFX USB Redirection to redirect low-level USB devices to a remote Windows Server. The malicious USB device was physically attached to the attacker's own client, yet the driver that actually parsed the malicious USB data ran on the remote server.

2018, Fusée Gelée: Treating Request Length as Object Length

Disclosed in 2018, Fusée Gelée is one of the most classic cases among USB BootROM vulnerabilities. The vulnerability resides in NVIDIA Tegra's Recovery Mode, or RCM. The device starts its USB stack in an extremely early BootROM environment, waiting for the host to upload a recovery image. The flaw exists in a seemingly ordinary GET_STATUS control request.

According to USB semantics, a status object is only 2 bytes. The host indicates how much data it is willing to receive at most via wLength, and the device should return min(host-requested length, actual available data length).

However, when handling an endpoint recipient, BootROM incorrectly executed logic similar to the following:

status = get_usb_endpoint_status(index);

/* Error: Using the request length provided by the host */
size_to_tx = setup_packet.length;

memcpy(dma_buffer, &status, size_to_tx);

In other words, the actual length of the 2-byte object was not used; instead, the attacker-controlled wLength directly became the length parameter for memcpy, theoretically reaching up to 65,535 bytes.

Furthermore, before verifying signatures, RCM already permitted attackers to place a relatively large payload into memory; this payload area happened to be located near the active BootROM stack. By selecting the DMA buffer and crafting the memory layout, the out-of-bounds copy could ultimately overwrite attacker-controlled data onto the execution stack.

At the time, the BootROM environment lacked common mitigations such as stack canaries, ASLR, and effective memory execution protection. The end result: before any security lockdowns or privilege demotions occurred, the attacker had already achieved arbitrary code execution within the BootROM context.

This bears very direct similarities to the hardware wallet vulnerability discussed later in this article.

2019, checkm8: What the Attacker Controls Is Not Just Data, but Also "Incompletion"

Disclosed in 2019, checkm8 is one of the most far-reaching Apple SecureROM vulnerability cases. Spanning multiple generations of iOS devices, it enabled attackers to achieve code execution in the BootROM context under DFU mode, thereby dumping SecureROM, decrypting firmware keybags, or switching the device into a demoted state that allows JTAG. Because the flaw exists within the BootROM permanently baked into the silicon during chip manufacturing, shipped devices cannot remediate this vulnerability via iOS updates.

Unlike Fusée Gelée, checkm8 is not a simple length-checking mistake. Its core is a use-after-free occurring in the Apple DFU USB state machine: a USB data phase does not terminate normally, leaving the global variables storing data-phase state uncleared; meanwhile, the underlying DFU buffer is freed when the USB stack exits.

Under normal circumstances, when BootROM receives a DFU_DNLOAD request with a DATA phase, it stores the receive buffer and wLength. Once data reception completes, the completion callback is responsible for clearing these states; upon exiting DFU, the corresponding I/O buffer is freed.

DFU_DNLOAD(setup)
{
    ep0_buffer    = dfu_io_buffer;
    ep0_remaining = setup.wLength;
}

DATA_complete()
{
    ep0_buffer    = NULL;
    ep0_remaining = 0;
}

DFU_exit()
{
    free(dfu_io_buffer);
}

checkm8 exploits the gaps between these steps: the attacker first initiates a request with a DATA phase, prompting BootROM to record the buffer pointer and remaining length, and then intentionally fails to complete the data transfer. By canceling the transfer or inducing a timeout, the DATA completion callback never executes; subsequently, triggering a DFU state transition or USB reset causes the USB stack to exit and free the original buffer.

At this point, the memory has been freed, yet the old pointer remains in the receive state:

free(dfu_io_buffer);
ep0_buffer = old_dfu_io_buffer;  // Dangling pointer

Next, heap grooming is required. Public exploits leak a series of USB request objects to alter subsequent allocation layouts, causing the new DFU buffer to land elsewhere while allowing the old address to be reoccupied by objects such as usb_device_io_request.

Sending USB data once more causes BootROM to write along the lingering ep0_buffer, effectively overwriting the new object. The exploit modifies fields such as callback and next to point the callback address to a pre-placed payload. When the next USB request completes, SecureROM jumps directly into the attacker's code.

2020, Kamakiri: How a USB Request Became an Indirect Jump

Kamakiri targeted the BootROM download mode in a range of MediaTek SoCs. It allowed attackers to bypass the authentication path that originally restricted unauthorized Download Agents, running an unsigned stage 1 payload in the BootROM context. With this execution capability in hand, subsequent payloads could continue manipulating flash memory, dumping memory, altering boot states, or unbricking devices that could no longer boot normally.

The exploit chain for Kamakiri is very short because the MediaTek BootROM inherently provided the first capability the attacker needed: uploading data to a known SRAM region via the download protocol. The attacker places the stage 1 payload there and triggers the vulnerability using a specific USB control request.

The vulnerability lies precisely in the dispatch handling of USB control requests. Independent reverse engineering summarized the relevant logic roughly as follows:

handler = handler_array[value * 13];
handler();

The critical issue is that the selector value from the USB request is used to index a function pointer table without adequate bounds checking. As long as the attacker finds a suitable control value such that the table entry lookup resolves to the previously uploaded SRAM payload address, a USB request can be turned into an indirect call to the payload. Because BootROM layouts differ across SoCs, specific index values and payload addresses may require adaptation or brute-forcing.

The public PoC sent a control transfer like this after uploading stage 1:

ctrl_transfer(
    0xA1,   # class request, device-to-host, interface recipient
    0,
    0,
    10,     # wIndex
    0
)

2026, usbliter8: The USB Controller Shifted the DMA Write Pointer Ahead of the Buffer

Disclosed in June 2026, usbliter8 once again extended unpatchable BootROM exploits to Apple A12, A13, as well as S4 and S5 SoCs.

Upon successful exploitation, attackers can execute code in SecureROM's EL1 context, modify DFU behavior, temporarily demote the chip's production mode, or bypass signature checks to boot raw, unsigned iBoot. This also means the application processor's boot chain of trust is broken and cannot be remediated via subsequent iOS updates.

usbliter8 differs somewhat from the previous cases; it primarily exploits the DMA pointer semantics of the Synopsys DWC2 USB controller itself when handling SETUP transactions.

According to the USB protocol, the data payload of a SETUP transaction is fixed at 8 bytes. The DWC2 controller allows receiving three consecutive SETUP packets and writes them sequentially into memory via DMA. Three normal SETUP packets occupy a total of 24 bytes. When the fourth SETUP transaction arrives, the controller unconditionally subtracts 24 bytes from the DOEPDMA write pointer, thereby rewinding the address back to the start of this SETUP buffer set. For normal 8-byte SETUP packets, this logic completes the cycle perfectly.

However, the controller also accepts abnormal SETUP data shorter than 8 bytes and advances the DMA pointer according to the actual written length, with an internal write granularity of 4 bytes. When an attacker repeatedly sends 4-byte short packets, the first three packets only advance the address by 12 bytes in total, yet when the fourth packet arrives, the controller still unconditionally subtracts 24 bytes. Consequently, the next round of SETUP data is no longer written to the original buffer, but rather 12 bytes prior to the start of the buffer. By continuously repeating this process, the DMA write position shifts toward lower addresses with a predictable step size, ultimately forming a stable buffer underflow.

Generally speaking, out-of-bounds DMA from a USB controller does not necessarily compromise arbitrary system memory. Modern SoCs typically employ an IOMMU to restrict the address ranges accessible by the USB controller. However, in the SecureROM environment of the A12 and A13, USB DART is in a bypass state; hence the controller's DMA lacks effective address isolation. This allows the underflow to escape the narrow SETUP buffer and continue overwriting other SRAM objects.

On the A12, the USB DMA buffer is adjacent to the USB task stack, allowing the exploit to overwrite the saved link register and gain program counter control during task switching. The A13 introduced Pointer Authentication, making direct overwriting of return addresses unfeasible; hence, the public exploit employed a more complex memory corruption chain, ultimately overwriting the USB interrupt handler pointer in BSS to enter attacker code on the next USB interrupt. Both paths ultimately achieve SecureROM EL1 execution.

From USB Out-of-Bounds Read/Write to Arbitrary Execution

The root cause of the vulnerability shared at Black Hat USA this time also lies within a USB SDK.

A line-coding request in CDC requires only a small, fixed structure, but the integrated USB SDK allocates a fixed 8-byte CmdBuff for it and subsequently passes the host-supplied req->wLength directly to the underlying transmit or receive functions. The code performs no length checks, nor does it mandate that the length match the protocol-specified fixed length based on the specific request. This results in out-of-bounds read and write operations.

USB SDK out-of-bounds read/write vulnerability codeUSB SDK out-of-bounds read/write vulnerability code

Of course, possessing an out-of-bounds read/write is merely the first step—it only provides a memory corruption primitive. Ultimately, our goal is to achieve arbitrary execution capabilities.

Since the vulnerability occurs in the USB CDC path, I first inspected the USB protocol stack along the dispatch flow of the current request. While analyzing g_usbDev, I noticed dev.class_cb. Rather than a regular data pointer, it is the runtime callback table of the USB class driver; when processing requests, the USB core invokes functions such as Setup, DataIn, and DataOut through this table. As long as class_cb can be overwritten, a subsequent normal USB request will pass through function pointers under our control, making it an ideal hijacking target.

The entire exploit can be divided into two USB requests.

The first request uses SET_LINE_CODING to trigger an out-of-bounds write. In this write, I needed to accomplish three things simultaneously: place the shellcode into CmdBuff, lay out a fake callback table in nearby SRAM, and finally overwrite class_cb with the address of this fake table.

Subsequently, I send an ordinary GET_LINE_CODING request. The USB core processes this request as usual, invoking the class driver's Setup callback via class_cb->Setup(...). The difference is that class_cb no longer points to the original callback table, but rather to our forged table; the fake Setup in turn points to the shellcode in CmdBuff. Thus, the second USB request directly steers the PC into the code we wrote to SRAM.

Since the shellcode is located in SRAM, data execution prevention (non-executable data regions) normally needs to be addressed. However, the firmware configured the MPU using an incorrect size format, resulting in a protection range of 0; the MPU region originally intended to restrict BSS execution permissions was effectively inactive. Consequently, this code located within CmdBuff could be executed directly.

Hijacking the USB callback table to execute shellcodeHijacking the USB callback table to execute shellcode

Once theoretically viable, practical implementation began. First, I tested the actual range of the out-of-bounds read and write. I crafted control transfers of 64 bytes in size; the out-of-bounds read could continuously leak approximately 24 KB of SRAM, but writing out-of-bounds up to 4,352 bytes caused a consistent crash.

Subsequent inspection revealed that at this offset, the out-of-bounds data overwrote a pointer at CmdBuff + 0x1130 that was still in active use, causing EP0 to immediately lose the ability to receive further data.

Location where out-of-bounds write leads to device crashLocation where out-of-bounds write leads to device crash

Resolving this issue was relatively straightforward: since we already had out-of-bounds read capability, we could directly read the genuine state out-of-bounds and load the dumped state data into the shellcode. This constituted a complete arbitrary code execution logic.

Although we were now able to overwrite class_cb, I did not want this hijack to break the USB channel. The long write first places the shellcode and forged callback table into SRAM and temporarily directs class_cb to the fake table; sending GET_LINE_CODING subsequently leads the USB core to jump into the shellcode via the fake Setup. Once the task completes, the shellcode writes the result behind CmdBuff, restores the original class_cb, and invokes the original Setup to let this EP0 transfer complete normally. In this manner, every hijack is merely a brief detour: USB remains functional and ready to inject subsequent tasks.

The firmware originally attempted to mark BSS as non-executable via the MPU, but the configuration code used an incorrect size format, resulting in a calculated protection range of zero.

From MCU Arbitrary Execution to Mnemonic Extraction

Obtaining MCU arbitrary execution does not immediately mean mnemonic phrases can be extracted. The specific architecture of the hardware wallet is as follows. In summary, the encrypted mnemonic information is stored in the DS28S60 SE, while the AES Key used to decrypt this information involves OTP as well as two SEs: ATECC608 and DS28S60.

Hardware wallet security architectureHardware wallet security architecture

The generation process of this AES Key is shown below:

AES Key generation flowAES Key generation flow

The credentials involved in SE interactions and AES generation are primarily derived from the wallet's Passcode (unlock PIN) and OTP, with the workflow shown below:

Passcode and OTP credential generation flowPasscode and OTP credential generation flow

Since the Passcode can be obtained by cracking the acquired password hash, all that remains is obtaining the OTP salt to complete the AES Key generation. For OTP extraction, since the shellcode runs at the same privilege level as the normal firmware, it can write to the MPU registers via the USB vulnerability to disable the MPU, thereby reading the OTP.

The final exploitation method is as follows; once the AES Key is acquired, the BIP39 entropy can be decrypted to reconstruct the mnemonic phrase.

Complete attack chain from USB vulnerability to mnemonic extractionComplete attack chain from USB vulnerability to mnemonic extraction

Summary

Throughout this research, AI was extensively utilized in vulnerability analysis and the exploit development process. The advent of AI has significantly enhanced the exploit development efficiency of security researchers. AI is also altering the research costs associated with such vulnerabilities. In the past, discovering and exploiting vulnerabilities often demanded substantial manual hours to achieve stable exploitation; today, code comprehension, structure matching, script generation, and failure root-cause troubleshooting can all be accelerated substantially.

Furthermore, whether it is a USB SDK or other third-party components, these are areas easily overlooked during product security reviews. Yet all too often, it is precisely such weak links that lead to the complete compromise of product security.

Secure Your Crypto Journey with OneKey

View details for Shop OneKeyShop OneKey

Shop OneKey

The world's most advanced hardware wallet.

View details for Download AppDownload App

Download App

Trade global assets. Start with your email in minutes.

View details for OneKey SifuOneKey Sifu

OneKey Sifu

Crypto Clarity—One Call Away.

Keep Reading