One Key Bug in OneKey Mini
"Not your keys, not your crypto." That's the lesson a friend of mine learned after losing a significant amount due to the collapse of FTX. He vowed that he would never put his life savings to a centralized exchange again. But is a hardware wallet the ultimate solution?
During Black Friday sales, he purchased several hardware wallets at a discount and sent me a OneKey Mini for my security expertise. As a new Web3 hacker with extensive experience in Web2, I don't trust the security of anything without thorough investigation.
Together with my colleagues at Offside Labs, we delved into hardware wallet vulnerability research and discovered significant security issues. In this blog post, we will share the first journey and findings of our research.
What We Learn
So what is a hardware wallet? A Hardware Wallet is a specialized device designed to secure digital currencies. Hardware wallets store sensitive information, like recovery mnemonics, within the secure computational compents of the device. Private keys are never exposed to the internet, thus reducing the risk of online threats and keeping your assets secure.
Trezor is a famous hardware wallet manufacturer that I knew about even before the commence of my web3 research. It is an actively maintained open-source project and it has been forked by many other competitor wallet manufacturers. OneKey also forked Trezor's repository for their own products.
As a hardware wallet project which was launched 12 years ago, I am pretty confident that I can easily find some previously discovered vulnerabilities. I took some time to make an investigation on its topic and summarized the following attack surfaces of hardware wallets.
Trezor Side-channels. Prior to firmware version 1.3.1 (around 2015), Trezor was vulnerable to side-channel attacks leveraging information obtained from power fluctuations during the public key computation process. I read abount this attack surface from Jochen-Hoenicke's blog
Trezor Secrets from SRAM. Some old version of Trezor failed to wipe out the content of SRAM when the hardware wallet is rebooted, so hackers are capable to recover some sensitive information. Saleem Rashid detailed a process of leaking private secrets from SRAM by doing hardware reset.
KeepKey Glitching. STM32 has a security mechanism known as Read Protection (RDP). Wallet.Fail and Chip.Fail demonstrated that downgrading RDP can reliably be performed at boot with voltage glitching. Once a device is at RDP1, its SRAM can be read out. You can ead more about this attack surface on Kraken's blog post.
KeepKey Buffer Overflow. Christian Reitter has an excellent series of blog post on a serious buffer overflow vulnerability in the KeepKey hardware wallet. In specific versions of arm-none-eabi-gcc, the stack canary code is incorrectly generated, resulting in a fixed canary value that can be easily bypassed.
OneKey Man-in-the-Middle. Unciphered demonstrated a critical security vulnerability for the OneKey wallet. This security vulnerability allowed anyone with physical access to the hardware wallet to swipe its mnemonics by inserting a particular piece of hardware between the CPU and the secure unit of the wallet.
The discussion of these attack surfaces highlights that while hardware wallets are generally secure for storing cryptocurrency, there can be vulnerabilities that could potentially expose your private keys. Generally speaking, remote code execution vulnerabilities + physical access to your hardware wallet = takeover of your crypto assets there.
What We Find
I'm now well aware of what can go wrong with a hardware wallet. It's time to dive in and play with my little OneKey Mini.
OneKey Mini, priced $1 less than its Trezor competitor Trezor Model One, is one of the cheapest hardware wallets available in the market. Popular in Chinese-speaking regions, it offers additional features like a Secure Element (SE) and Chinese language support. However, it is just the addition of these relevent code and extra features that has expanded its attack surface and introduced risks due to their unsatisfying code quality. We can dive into its firmware code here.
One Pitfall
Due to the fact that OneKey is a fork of the Trezor project, much of their code are the same and we can easily figure out which part of the code is added by OneKey developers. The first thought occured to me was that I can figure out how OneKey implement their additional Chinese language support functionality, compared to the corresponding Trezor code. I quickly came across this font_data_read()
function, which reads in the font files from the additional flash storage by calling inner flash_read_enc()
.
I analyzed all the functions that are related to flash reading and writing. Then I stumbled on these two additional "(facotry) = true" constraints on MessageType_SpiFlashWrite
and MessageType_SpiFlashRead
message types. I mean this is a factory, not facotry right? I guess it is a typo. 🤔
I searched for both factory
and facotry
in the project to prove my guessing. This (facotry) = true
constraint suggests that the developers intended to limit certain SpiFlash read/write operations to Factory Mode. This mode is active before the products are distributed to customers. While the getattr
in messages_map.py
correctly spell factory, there's a typo in the messages.proto
file. As a result, MessageType_SpiFlashWrite
and MessageType_SpiFlashRead
are mistakenly accessible in every OneKey Mini product.
We accidentally gained access to the fsm_msgSpiFlashRead
and fsm_msgSpiFlashWrite
functions. These should only be accessible in factory mode.
One Overflow
I carefully inspected these two functions. Within the fsm_msgSpiFlashRead
function, I found that there's an absence of validation for the msg
parameter regarding its length. This oversight implies that when storing user input data into the variable resp
with function msg_write
, those data can be written past the boundaries of the allocated buffer.
Furthermore, the presence of the fsm_msgSpiFlashWrite
function opens the possibility for a hacker to manipulate the overflowed content. This function allows us to write arbitrary data into any address in the flash memory, at our complete disposal.
Things were looking promising here, yet the exploit's effectiveness hinged on the ability to overwrite meaningful data with the overflow. I promptly shared my findings with my teammates and collaborated on addressing a legitimate exploit.
The Exploit
The stack protection mechanism is active but the overflowed object resp
didn't lie on the stack, so there was no concern to circumvent the stack protection mechanism in the exploit. Upon examining the compiled and symbolized binary file, we notice that the resp
object was located at memory address 0x20009d58
. Interestingly, not far from this location was the timer_array
at 0x2001659c
. This proximity suggests a potential target for the overflow to corrupt or manipulate. The timer_func
is defined as:
typedef void (*timer_func)(void);
#define TIMER_NUM 4
typedef struct {
char name[32];
uint32_t current;
uint32_t cycle;
timer_func fp;
} TimerDsec;
TimerDsec timer_array[TIMER_NUM] = {0};
In this particular scenario, the function pointer fp
is executed by the system's sys_tick_handler
. This means that by strategically overwriting the contents of fp
, it becomes possible to manipulate the Program Counter to execute arbitrary code. This capability is crucial for gaining control over the system's operations. However, we encountered an initial obstacle. At system startup, the firmware security function, mpu_config_firmware
, configures the memory segment that includes resp
(where we intended to place our payload) to be read-write but explicitly sets it to execute never. This security measure prevents execution of any code that resides in this segment, effectively safeguarding the system against unauthorized code execution.
Despite this security configuration, having control over the PC provided a significant advantage. It allowed us to circumvent the Memory Protection Unit (MPU) settings that were initially blocking our attempts to execute code from the resp
segment. By executing the mpu_config_off
function, we could disable the MPU restrictions entirely. This action removed the execution barriers on the resp
segment. With these restrictions lifted, we were able to redirect the PC to execute shellcode specifically placed within resp
. This maneuver effectively bypassed the system's initial security mechanisms, allowing us to execute our custom shellcode and manipulate the system as needed.
The Demo
Here is a demo of how we used this vulnerability to access the mnemonic before unlocking the OneKey Mini.
During my research and development of this exploit, guidance from my Offside Labs colleagues proved invaluable. Their top-tier expertise and remarkable experience significantly enhanced this enjoyable research journey. Immersed in the vibrant atmosphere of Offside Labs, I've found an invigorating desire to dedicate myself to the enthralling world of web3.
Timeline
Here is the complete timeline of our research and the vulnerability disclosure process:
2023-11-09: Purchased OneKey Mini.
2023-11-15: Began reviewing the source code of the OneKey Mini and - discovered the bug.
2023-11-16: Confirmed the severity of the bug by controlling the PC - register.
2023-11-22: Completed the first version of the demo video.
2023-12-05: Reported the vulnerability to OneKey.
2023-12-20: Patch was merged.
2023-12-25: Received bounty ($5k, top tier) for the vulnerability report.
2024-01: Mandatory updates were rolled out.
OneKey has generously set their top-tier bounty for this vulnerability, offering a reward of $5,000.
Following this discovery, my confidence in using hardware wallets to protect my cryptocurrencies was somewhat shaken. I still harbor doubts about whether these devices are truly the best option for safeguarding my investments, especially if I were to lose the devices one day.
References
wallet.fail - Hacking the most popular cryptocurrency hardware wallets
Inside Kraken Security Labs: Flaw Found in Keepkey Crypto Hardware Wallet
Inside Kraken Security Labs: Flaw Found in Keepkey Crypto Hardware Wallet (Part 2)
KeepKey Stack Buffer Overflow Vulnerability (CVE-2021-31616)
Unciphered Reveals Now-Patched Vulnerability in OneKey Wallet