Most modern microcontrollers take measures to protect the contained software. As we have shown previously these protections are not perfect and can be bypassed. In this blog post we take a look at the (Nordic Semiconductor nRF52 series devices). These devices are the evolution of the nRF51 series and are also used to implement Bluetooth functionality with low power consumption. A prominent example of a device with an nRF52 based MCU is the Apple AirTag.
As previously we are interested in extracting the firmware from a locked device and gaining full debug access. For this series we are no longer able to achieve this with pure software attacks and need to move to hardware attacks, namely voltage glitching. In the following we will first describe the device and its protection. We then outline our attack plan including a background on voltage glitching and present our attack setup. We also cover how to identify the correct timings needed for the attack and how to re-enable full debug access. All software is available in our Github repository.
This post is part of a series on this topic and serves as an overview and introduction. Following posts will cover parts of the attack in more detail.
Our target is the development board of the Nordic Semiconductor nRF52 series. This series is the successor to the nRF51 series (development board) and provides many of the same features. The SoC combines a flexible ARM core with wireless features such as Bluetooth and NFC. The updated series features an ARM Cortex-M4 CPU with built-in flash and RAM. The focus of this MCU is applications of the Internet of Things (IoT) including low power use cases. As most chips today, this MCU also has a ReadOut Protection (ROP). ROP is an umbrella term describing different technologies preventing the unauthorized access to the software stored on microcontrollers. The nRF52 allows disabling the ROP by erasing the entire storage, returning the device to factory state. For a more detailed introduction to the concepts of ROP see our previous blog post.
The ROP for the nRF52 series no longer distinguishes between vendor and customer ROM, the entire chip is either protected or unlocked. This is achieved by disabling the debug interface entirely. In this series there are two externally visible states of this interface: 1) full debug access, 2) interface disabled. The state is controlled via the APPROTECT register, details can be found in the manual. While this is not a dedicated Readout Protection (ROP) it prevents any access to the flash from outside sources and thus achieves the same result. To access the ROM contents we need to force enable the debug port and then use debugging commands to extract the information. However in contrast to the previous series, a locked device allows for no debug access at all.
As our goal is to enable the debug interface, we need to find a way to influence the internal state of the MCU, including the state of this peripheral. While in normal operation environments the MCU is secure against attacks, we can force it into abnormal circumstances and try to corrupt the internal state this way. The general field of these attacks is called fault attacks. In our case we use voltage glitching.
Voltage glitching refers to either over- or undervolting either the complete SoC or parts of it for very short time periods. The goal is to disrupt normal operation without triggering recovery mechanisms like watchdog timers or brown out detectors. Usually a brown out detector resets the chip to a known good state once the supply voltage dropped below a certain threshold. However if the voltage drop is short enough, this component might not trigger a reset and allow the MCU to continue operating. But, as the chip was used outside of its normal operation conditions, the internal state is unpredictable. Common faults due to voltage drops in the CPU core are corrupted CPU register values or skipped instructions. Our goal is to abuse these effects to keep the debug interface active.
We need to time the voltage drop to the right moment, so that the chip either skips over the code that disables the debug interface or corrupts the register values in a way that enables the interface. We have no insight into this code, as it is part of the bootstrap code that runs before the first instruction is fetched from user supplied ROM. Without any further information, we are limited to guessing the correct moment to launch our attack. Another problem is that we need to drop the voltage very quickly and let it recover to its normal level just as quick. Lastly it is usually not enough to just cut power to the entire MCU due to additional power supply filtering.
To summarize we have the following problems to solve:
As our first step, we want to determine when the voltage glitch should occur. As we have no insight into the bootstrap code, we need to guess at the correct time. In the most general terms, our attack window starts once we supply power to the chip and ends once the first user instruction is fetched from ROM. We can easily control the start of the interval, but have no way to accurately determine the start of user code execution. It is of course possible to set a rough end point, but brute forcing the correct time point would still take too long. However we can reason about the internal state by checking external visible behavior like power consumption and voltage levels.
To help inferring the internal state of the MCU, we use the following as a rough approximation of the process after power is supplied:
Steps 1 and 2 are required and must progress as normal to allow the SoC to power up. Once the process reaches step 5, the peripheral is already configured and we can no longer try to corrupt its state. This means our attack window opens at some point during steps 3 and 4.
As we know from the manual that the settings that control the debug interface state are located in user programmer ROM, it stands to reason that this memory region is distinct from the region used to store the bootstrap code. The bootstrap code is often not traditional ROM, but is instead represented directly in the physical layout of the chip. This means the SoC needs to access another memory region, implemented using a different technology. Commonly this requires enabling the memory controller for this area, which leads to an increased power consumption. We can observe this spike in power consumption by measuring the supply voltage. The supply voltage will show a slight drop when the current consumption increases. This drop in supply voltage marks the power up of the user programmable ROM and thus the opening of our attack window to influence steps 3 and 4. For details on how to determine the start of this window refer to the addendum We still do not know the end point of our attack window, but we can simply set a conservative limit for this parameter by waiting for the chip to attempt finishing its bootup.
Another parameter for the attacks is the duration of the glitch. Too short and we might not corrupt any state, too long and the protection mechanisms might reset the MCU or the SoC simply stops working due to the extensive state corruption. As we have no insight into the bootstrap code, we are again limited to brute force. However the search space is fairly small as long glitches will lead to a SoC reset as the brownout detector will trigger.
In summary, we now have two parameters with at least rough timing bounds we need to test for each glitching attempt.
As we are performing a hardware attack, we of course need to develop a setup to carry out the voltage glitching. While we could simply glitch the entire SoC supply, an approach that directly targets the CPU core will likely yield better results. This directed attack will lessen the impact on other components, potentially preventing them from entering an unrecoverable error states. Additionally this attack reduces the chance of the protection circuitry resetting the chip.
By analyzing the recommended power supply layout of the chip, we can see that the core voltage is accessible at an external pin. This is used to connect a stabilizing capacitor that reduces voltage spikes of this supply voltage. We use exactly this protection mechanism to our advantage by glitching at this pin. We soldered a wire onto the chip-side connector of this capacitor and connected it to the collector of an NPN-transistor. Once activated this transistor connects the core voltage to ground, causing a sudden drop in voltage.
We now have a setup to drop the core voltage and need a way to accurately time the glitch. For this we use another embedded dev board to allow for cycle accurate timing control. For our purposes a clone of the “bluepill” board based on the STM32F103 SoC is sufficient. We connect the GPIO pin B6 of this board to the base of the transistor connected between core voltage and ground. Whenever this GPIO pin goes high, the transistor starts conducting and drops the core supply voltage to near zero. We use a firmware intended to glitch the Apple AirTag. The bluepill board also serves as the power supply for the nRF52 dev board via another GPIO pin. By controlling the power supply we can perform a clean reset of the target after an attack attempt.
The firmware is intended to be used with an AirTag and provides an additional input for sensing that the chip supply voltage is present. We do not use this functionality and simply connected the sense pin to the power supply. While reducing the timing accuracy, we were still able to successfully attack the target. We also connect the ground pins of both boards.
Lastly we need a debug probe to access the debug interface. The Nordic dev board has a built in Segger J-Link, but our tests showed that the debug probe is also reset every time we power down the board. This lead to firmware corruption on the J-Link and slowed the attack down significantly. Additionally the dev board is designed to mostly use the USB power supply connected via the J-Link when the probe is active, instead of the externally connect power supply we require for our controlled resets. We therefore soldered two additional wires to the board to connect the Serial Wire Debug (SWD) interface to an external Segger J-Link. The same effect can be achieved by using the Debug In connector on the dev board, but we did not have the proper cable at hand. The J-Link is also connected to ground and supply voltage to ensure the debug interface uses the common ground and the correct voltage level.
Our attack setup thus consists out of 1) a transistor connected to the CPU core supply voltage and ground, 2) a dev board to control the glitch timing, and 3) a debug probe to try to access the debug interface.
As described earlier, we do not have perfect timing information and instead rely on brute forcing two parameters, the glitch start delay and the glitch duration. To automatically run through many combinations, we use a set of Python scripts to control both the bluepill board and the J-Link. The bluepill board exposes a simple serial interface which is accessed via the pySerial library. The J-Link is controlled via the PyLink library.
The glitching script brute forces multiple combinations of glitch delay and glitch duration inside specified limits. The glitch delay is the time from the power up of the chip to the glitch start. The glitch duration defines how long the supply voltage is pulled low. A single glitch attempt runs through these steps:
If in step 4 the J-Link can attach successfully, a series of memory reads is issued to download the firmware from the device. The results are then written to a file and the process ends. It is also possible to issue additional debugging commands as the debug interface is in full access mode. For example it is possible to set break points and single step execution. However as we describe later, it is more convenient to simply dump the firmware at this stage.
In addition to the glitching script we also created utility scripts to lock, unlock and flash the Nordic dev board. All our scripts can be found in the repository.
By combining all parts – the timing information, the glitch setup and the controlling software – we can successfully dump the firmware of the protected device. Like in the previous attack, we can also restore full debug access by erasing the chip and flashing the dumped firmware. One crucial difference to the normal flashing process is that we do not enable APPROTECT and thus the debug interface remains active. We can then use dynamic analysis to further explore the binary, e.g. via GDB.
We will also cover parts of this attack in more detail in a later post.
While the improved Readout Protection of the nRF52 series prevents software-only attacks, it is not enough to protect against hardware attacks. This again shows that relying on standard defense mechanisms is not sufficient to protect valuable software components.
If you have any questions either regarding the topic of this post or security for embedded devices in general, please reach out to us.
We skipped over how we determined the glitch delay to simplify the section above. As described we use the voltage drop indicating the activation of the ROM memory controller as the start of our attack window. We can observe this drop with an oscilloscope connected to the supply voltage. Another channel is connected to the core voltage pin allowing us to observe our glitch in action. We simply line up the supply voltage drop with the glitch start by modifying the glitch delay of the glitch script. After finding the correct delay to trigger the glitch right at the voltage drop, we can use this is the start of our brute-force search. Below we show the a snapshot of the glitch in action. The core voltage is displayed in yellow, the supply voltage in blue. The drop is very pronounced on the core voltage, but the additional capacitors smooth the voltage drop visible at the supply voltage. This highlights the importance to attack as close to the target component as possible.. Thanks to rigol-grab for making it easy to grab screenshots from our oscilloscope.
While it is possible calculate the glitch delay in cycles based on the time difference of our target time point and the point when the supply voltage rises as well as the clock rate of the bluepill SoC, we chose to simply use binary search to determine the value. We also account for noise by choosing a value slightly lower than an exact match, meaning the glitch script runs through more attempts than strictly required until it arrives at the correct value.