GigaVulnerability: readout protection bypass on GigaDevice GD32 MCUs

Author

Security Researcher

Disclaimer. This article is for informational purposes only and is not intended to instruct or encourage any illegal activity. Our goal is to describe the existing vulnerabilities that can be exploited by attackers, to warn users, and to provide recommendations on how to protect their personal information online. The authors are not responsible for how the information is used. Remember to always prioritize the security of your personal data.

When developing hardware solutions based on microcontrollers (MCUs), manufacturers seek to protect their firmware from falling into the hands of attackers, because it may contain sensitive information, encryption keys, unique valuable algorithms, and so on. To achieve this, most microcontrollers implement flash memory readout protection technologies. But do they really provide sufficient protection?

Unfortunately, no. Not all readout protection technologies work as intended. Vulnerabilities in debug interfaces, non-trivial fault injection attacks, and even invasive tampering can be exploited to bypass the protection.

At Positive Labs, we conduct security research on a wide variety of hardware. So when a device based on GigaDevice’s GD32 microcontroller fell into our hands, we found ourselves investigating its protection technologies before we knew it. GD32 microcontrollers are quite popular and are used everywhere, including as replacements for STM32 microcontrollers, as they are often pin-compatible and even share the same memory address map. The research turned out to be quite fascinating, and the results were impressive!

Readout protection: what is it and how it works

Readout protection technologies go by different names. For example, STMicroelectronics calls it Readout Protection, or RDP, in nRF microcontrollers it’s known as APPROTECT; and for GigaDevice it’s called Security Protection. Further in this article, we will talk about Security Protection, but we will use the name Readout Protection (or RDP) because it is more widely used and best conveys the essence of the technology.

The operating principles of these technologies are very similar, despite their different names. All of these technologies implement multiple protection levels, each imposing its own restrictions:

  • RDP0 (level 0, no protection). No restrictions are imposed. In this state, microcontrollers are delivered to developers — they can be freely programmed and debugged.
  • RDP1 (level 1, low protection). At this level, access to flash memory is blocked when a debugger is connected. However, the contents of the SRAM generally remain accessible for reading and writing via the debugger. This level can be downgraded, but doing so will erase the flash memory contents. For some microcontrollers, this level of protection is the maximum available.
  • RDP2 (level 2, high protection). At this level, the debug interface is typically permanently disabled, and the possibility of booting via the built-in bootloader (used for programming) and accessing the SRAM is also disabled. As a result, the ability to interact with the microcontroller or influence its execution is significantly reduced. Moreover, this level cannot be downgraded, meaning the microcontroller cannot be repurposed for other uses.

Usually, the current protection level is determined by the value of a specific byte that is physically stored in the same flash memory cells (in the Option Bytes area) where the microcontroller firmware is located, but the method of programming this byte varies.

Before moving on, it’s worth talking about some known techniques for bypassing readout protection on various microcontrollers. This will help understanding the general approaches available. Moreover, some well-known techniques have proven useful and were applied in our research. In our opinion, to fully immerse oneself in the topic, one should review the two studies by Johannes Obermeier and his co-authors, along with related materials. Below is an overview based on them.

Shedding too much light on a microcontroller’s firmware protection

Here you can find a Johannes Obermeier’s STM32F0 research. In this research, three techniques are described: Cold-Boot Stepping (CBS), RDP2 downgrade and exploitation of debug interface vulnerabilities.

As we noted before, at the low level (RDP1), a SRAM content is typically accessible. During development, it’s necessary to take this into account and try to clear the SRAM contents of secrets as soon as possible. In some cases, however, even this will not suffice. The Cold-Boot Stepping (CBS) technique allows one to obtain a set of intermediate SRAM states with high precision—even down to the state after each instruction.

For example, the researchers describe a case where firmware integrity is verified using the CRC32 algorithm. By intercepting all intermediate CRC32 values with the CBS technique, it is possible to fully reconstruct the firmware content.

The second technique is an example of an invasive attack that requires access to the chip’s internals. The researchers used acid to gain access to the crystal inside the microcontroller. By exposing memory cells to ultraviolet rays of a specific wavelength, they were able to alter the byte responsible for the protection level. As a result, the protection level was reduced from RDP2 to RDP1. This attack is quite complex, as it requires the researcher to have knowledge of chemistry, memory cell physics, and crystal structure, and the process of etching the chip’s package should ideally be performed in a specialized laboratory.

The last technique demonstrated in the research exploits a vulnerability via the debug interface at RDP1. The researchers found that flash memory locking occurs upon the first access to the bus via SWD, and if that access is a flash memory read, there is a chance to obtain the read result. For the exploitation, the researchers used a different STM32 microcontroller as the attacking device. There are ported projects available for alternative microcontrollers (for example, RP2040). Additionally, note that the attack works on some other microcontrollers (not only STM32). If an attack board is not available to implement the attacking device, one can make do with a J-Link paired with OpenOCD and a software-controlled relay.

Thus, the researchers demonstrated that the protection of the STM32F0 can be completely bypassed regardless of the set RDP level. The attack on RDP1 is very easy to implement, which cannot be said for the invasive attack on RDP2.

One exploit to rule them all? On the security of drop-in replacement and counterfeit microcontrollers

The research itself and its materials can be found here.

The main focus of the research was on the STM32F1 microcontrollers and its clones, including those from GigaDevice. The research demonstrates various techniques for bypassing RDP1 via the debug interface, an invasive attack on RDP2, and a voltage glitch technique to bypass RDP1. More details on each method can be found in the original source. For now, we will only discuss those most critical to our research.

Access to the flash memory controller (FMC) can be gained through various means as shown in the block diagram above: the core has direct access to the FMC via the instruction and data buses. In addition, the FMC is connected to the AHB bus, and the bus master devices (the core, DMA engine) can initiate flash memory operations. Many well-known attacks are possible because not all of these methods of accessing the flash memory are accounted for when the lock is activated. In some microcontrollers, RDP1 does not block the core’s access via the instruction and data buses, which allows the flash memory content to be obtained using load instructions. Sometimes, only access via the instruction bus remains possible, in which case a security researcher can retrieve the flash memory contents by manipulating the vector table offset register (VTOR) and manually triggering various interrupts, as detailed in the “Exception(al) Failure — Breaking the STM32F1 Read-Out Protection” research. In the case of the GD32F103, the possibility of an attack by copying the flash memory content into SRAM via the DMA engine using a debugger is demonstrated. The idea of using the DMA engine proved useful in our research when exploiting one of the vulnerabilities.

Let’s take a look at the invasive attack on the GD32F103/GD32F130 chips. In this attack, the researchers exploit the microcontroller’s multi-chip structure.

As shown in the diagram, the microcontroller consists of a logic crystal and a flash memory crystal, connected by bonding wires, which can be accessed using a polishing sheet. In our opinion, this is simpler than the chemical decapsulation described earlier, but it still requires certain skills. The researchers discovered that the flash memory communicates using the QSPI protocol, through which, after a power-on reset, the following are read: factory configuration, built-in bootloader, Option Bytes, and part of the firmware. The read data is apparently cached for faster access later. This information about the primary initialization process also proved very useful. Based on this, we were able to make some assumptions about how the vulnerabilities we discovered operate at a lower level.

Finally, let’s talk briefly about fault injection attacks. It seems that, along with invasive attacks, these are the last resort for a potential attacker to bypass readout protection, especially in RDP2, when the debug interface is inaccessible and the attack surface is considerably reduced. In the discussed work, the researchers managed to use a voltage glitch to reset the flash memory lock state after connecting the debugger, while not losing the pre-prepared SRAM content needed for subsequent attack stages. This attack targets RDP1, but there are numerous successful examples of lowering RDP2 and enabling the debug interface using voltage glitches:

In summary, there are three main approaches for bypassing RDP technologies can be highlighted:

  • Attacks via the debug interface: relevant when the debug interface is accessible (for example, in RDP1) or when it has been already activated through other attacks.
  • Fault injection attacks in general, and voltage glitches in particular: effective attacks that can often be implemented without expensive tools, although they may require extensive pre-research to find the correct attack parameters.
  • Invasive attacks: complex attacks that require specialized equipment and skills, but in some cases can be carried out with improvised materials.

Finally, we move on to our research and the vulnerabilities we discovered. Two of them allow using the debug interface even if RDP2 is enabled and, depending on the microcontroller family, to obtain either a snapshot of the SRAM or the complete flash memory content.

How it all began

The research began with the study of an air quality sensor. The device was based on the GigaDevice GD32E230 ARM microcontroller. We were particularly interested in the firmware protection of the sensor and decided to investigate it. On the PCB, there was a SWD debug interface and NRST, VCC, and GND pins.

At first, we attempted to connect via SWD using J-Link—and we were surprised. Debugging didn’t work with the official J-Link utility, but we managed to read the Debug Port Identification Register (DPIDR). Even OpenOCD with a specific configuration for the microcontroller couldn’t handle this initially. Using a logic analyzer, we discovered that the J-Link utilities attempted various connection methods, including connecting during reset by pulling the NRST signal to ground. After realizing this, we modified the OpenOCD configuration and through some experiments achieved a similar result as with J-Link—the DPIDR was successfully obtained, although debugging still did not function properly. The observed behavior was unusual, and while we couldn’t successfully start debugging, we were not sure that the chip was locked in RDP2. It could just be a simple SWD pin misconfiguration.

We attempted to explore SWD capabilities in the microcontroller’s reset state. For this purpose, we made an OpenOCD configuration where NRST was pulled to GND during connection. Additionally, the NRST signal could be freely controlled:

We found that reading and writing to various addresses on the bus was possible, but the results of the operations did not always meet expectations. For example, writing a value to SRAM and reading it resulted in zeroes. Similarly, reading flash memory consistently returned zeroes:

When attempting to read different peripherals, reset values were obtained as described in the user manual, as expected. However, these values could not be changed:

Debug modules presented more interesting outcomes: they were not in reset state and could be configured. For example, setting the CDEBUG_EN bit in the Debug Halting Control and Status Register (DHCSR) required specifying the correct key in the register’s upper bits. Additionally, writing to the comparators of the Flash Patch and Breakpoint (FPB) module worked as intended.

Using the debug modules, we explored ways to halt the core upon startup, but SWD was still inaccessible. This was likely due to RDP2, but to confirm this conclusively, it was decided to procure an identical clean chip, flash it with our firmware, and see if RDP2 locking was possible.

Upon testing, the behavior of our RDP2-locked chip matched that of the target device. It can be definitively stated that RDP2 is active, which means that extracting the firmware via SWD is not possible. But the availability of the debug interface during reset was seemed very strange to us. Referring to the STM32 microcontroller documentation, it states that with RDP2 enabled, the debug interface is disabled even during reset. This is not the case with GD32, and we decided to investigate this microcontroller’s unique characteristics.

GigaVulnerability #1

The idea for the first potential vulnerability was quite obvious. As we discovered, the SWD interface is accessible in the reset state. It is possible that after exiting the reset state, some time is required to disable SWD, and this race window (the moment when SWD remains available after the reset signal state changes) could be exploited to access the bus. This can be illustrated schematically as shown in the figure:

To check if this vulnerability can be realized, we first need to understand a little bit about how SWD is structured. Its detailed description can be found in the Arm Debug Interface Architecture Specification. In this article, we will provide a concise overview of the key aspects necessary for vulnerability verification. Two signals are used in in the protocol:

  • SWCLK: clock signal
  • SWDIO: data input/output signal

Using these signals, it is possible to transmit specific packets for reading or writing registers in the debug port (DP) or access port (AP).

Physically, the debugger connects to the DP, through which access to various memory banks within one or multiple APs can be obtained.

In the DP, four registers can be accessed by their addresses, but depending on the type of operation, different registers may be available at the same address.

AddressReadWrite
0x00IDCODEABORT
0x04CTRL/STATCTRL/STAT
0x08
SELECT
0x0cRDBUFF

The IDCODE register is the DP identifier—the value we attempted to retrieve using J-Link and OpenOCD. As mentioned earlier, there can be multiple APs, and each AP can have more than four registers. The SELECT DP register is required to choose the appropriate AP and the register bank within it. Reading AP registers occurs with a one-operation delay: the first read returns an undefined value, the second read returns the result of the first operation, and so on. The RDBUFF DP register is used to retrieve the result of the last AP register read without initiating a new operation.

The ADI specification defines three power domains—groups of chip components whose power supply can be controlled independently:

  • Always-on power domain: consists of components that must always remain powered, such as the DP
  • System power domain: consists of system components
  • Debug power domain: consists of debugging components

Control over the system and debug power domains is managed using the four most significant bits of the CTRL/STAT register: CTRL bits request power domain activation, while ACK bits confirm the activation of the selected domain.

For our purposes, we will focus solely on MEM-AP, which is required for working with memory. While MEM-AP contains many registers, we only need three of them::

  • Control/Status Word Register (CSW, 0x00) configures memory access parameters (access width, address incrementing, etc.)
  • Transfer Address Register (TAR, 0x04) sets the target memory address.
  • Data Read/Write (DRW, 0x0c) is used for performing read or write operations.

The algorithm for reading memory via SWD is as follows:

  1. Reset the interface to bring it into a predictable state, followed by selecting the appropriate operating mode using special sequences on the data line. Some DPs can work with both SWD and JTAG protocols.
  2. Read the IDCODE DP register. In some cases, this step is required to enable DP and make it responsive, though this behavior may depend on the specific implementation.
  3. Enable the system and debug power domains via the CTRL/STAT register.
  4. Select the zero bank of the MEM-AP registers using the SELECT DP register.
  5. Configure the CSW AP register (e.g., for 32-bit memory access without address incrementing).
  6. Set the TAR AP register to the target memory address.
  7. Read the DRW AP register to perform the read operation.
  8. Read the RDBUFF DP register to retrieve the result of the read operation.

Let’s now return to the vulnerability test. The preparatory steps (1–6) can be performed while the system is in reset state, as SWD remains accessible and both DP and MEM-AP function normally. After that, the reset signal must be changed, and a read operation must be performed as quickly as possible. After a brief delay (allowing the read operation to complete), the reset signal can be toggled again, and the result can be obtained via the RDBUFF DP register.

Initially, J-Link was chosen for vulnerability testing because it operates quickly, and it seemed feasible to implement this algorithm using libjaylink. However, J-Link doesn’t allow synchronous control of the reset signal along with SWD lines. As a result, there was a significant delay between toggling the reset signal and the next SWD packet. By the time the DRW register was read, SWD was no longer operational, making it impossible to hit the race condition window. By sending multiple consecutive operations through J-Link and manually controlling the NRST signal state, it was observed that SWD continued to function for a short time after the reset signal changed, preserving the possibility of a successful exploit.

It became clear that instead of J-Link, a microcontroller had to be used. We chose the RP2040. This controller features two ARM Cortex‑M0+ cores and can operate at frequencies of up to 133 MHz. However, it can be overclocked to 250 MHz while still handling most tasks.

Yet the coolest feature of the RP2040 is the presence of PIO blocks — Programmable I/O. The microcontroller includes two such blocks, each containing four state machines that can execute a program consisting of I/O-oriented instructions simultaneously and independently from the main cores. With PIO, both common and uncommon protocols can be implemented. In some cases, PIO can even replace an FPGA.

Thanks to PIO, the RP2040 is widely used in hardware security, especially in various fault injection attacks. Here are just a few examples of its use:

  • PicoFly: a modchip for the Nintendo Switch that performs glitches on the eMMC lines to bypass secure boot. This is a prime example of the RP2040 replacing an FPGA, as its counterpart HWFLY employed a Lattice FPGA to execute the glitch
  • ChipSHOUTER-PicoEMP: a chip device for conducting electromagnetic fault injection attacks
  • Starlink User Terminal Modchip: a modchip for the Starlink terminal that enables a voltage glitch to bypass the terminal’s secure boot

A pleasant bonus of using the RP2040 is that a SWD debugger — Picoprobe — has already been implemented for it, so for executing our algorithm it was sufficient to use ready-made PIO programs and C functions to work with them. The resulting test bench is shown below, and its operation is controlled via UART over USB.

As a result of the test, we immediately obtained a positive outcome: the ability to read SRAM was confirmed! Below are screenshots demonstrating the success.

Similarly, memory writing can be achieved, but due to the reset following the write, its practical utility is limited. We examined the possibility of reading the entire SRAM and confirmed that the data is correct. In the dump, blocks of initialized data, stack contents with valid return addresses, and so on, can be identified. In addition to SRAM, we also confirmed the ability to read the peripheral registers. Furthermore, we verified that the Option Bytes can be read and once again confirmed that the chip is locked with RDP2.

Although accessing the flash memory contents via this vulnerability was not successful, the technique of Cold-Boot Stepping (CBS), which we mentioned earlier, could be adapted for it. In some cases, CBS is not even necessary—a timely SRAM dump, captured at the moment when the microcontroller’s SRAM contains the data of interest, is sufficient. This approach enabled us to confirm the possibility of decrypting the encrypted firmware update file of the target device. The file was obtained by sending encrypted fragments and extracting decrypted fragments from SRAM before they were written to flash memory.

Additionally, during this work it was discovered that the flash memory becomes locked when the debug power domain is enabled via SWD (specifically, the CDBGPWRUPREQ bit in the DP’s CTRL/STAT register), and that the lock is removed when it’s disabled. This feature will be described in more detail when describing the feasibility study of the next vulnerability. This significantly simplifies the attack, as it eliminates the need to power-cycle the microcontroller to unlock the flash memory for each iteration of block decryption. Thus, the initial goal was achieved, enabling us to study the firmware of the air sensor. However, our research on the GD32 microcontrollers did not end there.

We decided to investigate the presence of vulnerabilities in other families of GD32 microcontrollers. Given their vast number, we needed to optimize the task. We chose to focus solely on general-purpose microcontrollers and grouped certain families based on the availability of a common user manual. Additionally, the GD32F10x microcontrollers were excluded because they lack RDP2, and RDP1 was successfully bypassed in the study mentioned in the introduction. In the end, several identical microcontrollers were acquired from each group (both as spares and for testing lower levels of protection).

Summary table of the investigated microcontrollers and the results of testing the first vulnerability:

FamilyMCUReleaseRDP2GigaVulnerability #1
GD32F1x0GD32F130C8T6AJ2139YesNo
GD32F3x0GD32F330C8T6PJ2146YesNo
GD32F4xxGD32F405RGT6JJ2239YesNo
GD32L23xGD32L233RCT6MJ2306YesYes
GD32E23xGD32E230K8T6JJ2125YesYes
GD32E50xGD32E503VCT6MJ2119YesYes

The table also provides information regarding the chip release date. For example, “AJ2139” indicates that the chip was released in the 39th week of 2021. Newer chips might prove to be invulnerable. The table shows that the E and L families were vulnerable. Experiments with the F families revealed that SWD remains accessible in the reset state but becomes unavailable immediately after exiting it. Since the vulnerability applies only to RDP2, not all microcontrollers are represented in the table. The results were unsatisfactory, prompting further research, with several more interesting findings ahead.

GigaVulnerability #2

Previously, it was observed that on the investigated GD32E230 microcontroller, flash memory locking occurs when the debug power domain is enabled—that is, when the CDBGPWRUPREQ bit in the CTRL/STAT DP register is set — and that resetting this bit deactivates the lock after a full microcontroller reset. An idea arose to test how enabling and disabling the debug power domain affects flash memory locking during firmware execution (for example, from SRAM). It turned out that the lock can be removed in this way, representing yet another vulnerability that allows bypassing RDP1 restrictions. The algorithm consists of the following steps:

  1. Load the firmware into SRAM using a debugger and then launch it. The firmware will wait for a signal to trigger a flash memory dump. A communication channel such as UART can be used.
  2. Reset the CDBGPWRUPREQ bit in the CTRL/STAT DP register. This can be done using the command chip.dap dpreg 0x4 0x0 in OpenOCD.
  3. Send the signal to the loaded firmware via the chosen communication channel to initiate the flash memory dump.
  4. Read the contents of the flash memory.
FamilyMCUReleaseRDP2
GigaVulnerability #2
GD32F1x0GD32F130C8T6AJ2139YesYes
GD32F3x0GD32F330C8T6PJ2146YesNo
GD32F4xxGD32F405RGT6JJ2239YesYes
GD32L23xGD32L233RCT6MJ2306YesNo
GD32E23xGD32E230K8T6JJ2125YesYes
GD32E50xGD32E503VCT6MJ2119YesYes
GD32C10xGD32C103CBT6JJ2232NoYes
GD32E10xGD32E103CBT6JJ2153NoYes
GD32F20xGD32F205VCT6AJ2139NoYes
GD32F30xGD32F303CGT6JJ2121NoYes
GD32F403GD32F403RGT6JJ2117NoYes

As shown in the table, almost all of the acquired microcontrollers have proven vulnerable to this RDP1 attack.

GigaVulnerability #3

The previous vulnerabilities covered almost all of the microcontrollers examined, except for the F family with RDP2 locking. As noted earlier, in the case of the F families, access to SWD is available in the reset state but is blocked immediately after exiting it. However, by chance we discovered that the vulnerability does work after coming out of reset on power-on (POR, power on reset). That is, if you disconnect the microcontroller’s power, pull the NRST signal to ground, and then power it on again (and then try to repeat the algorithm from the first vulnerability) it will succeed. Moreover, the race window is significantly larger than in the E and L families: on the GD32F130 it is about 1600 µs compared to roughly 20 µs on the GD32E230. Furthermore, if the microcontroller remains in the reset state longer than this interval, SWD will not be blocked, allowing the attack to be repeated after each reset without power-cycling. However, this finding seemed rather useless because the read operation occurs after the power-on reset, at which point the SRAM and peripherals have not yet been initialized by the firmware.

It might be possible to obtain access to the initialized SRAM contents by applying the power-glitch technique described in this article. Recall that in that case, the researchers managed to reset the RDP1 lock state while preserving the preloaded SRAM contents. There is a possibility that in our case with the GD32F microcontroller families, one could also briefly disconnect the power so that the lock state is reset while the initialized SRAM, containing all the secrets and other valuable information, remains intact and can be read by exploiting the discovered vulnerability. We did not test this assumption and instead pursued a different approach in our research.

First, we investigated the possibility of manipulating the vector table offset register, but that did not help. In addition, we examined the possibility of launching the core while SWD was still available, but that also did not succeed. We then decided to move away from exploitation via the debug interface and explore voltage glitching as a means to lower the RDP2 level to RDP1.

Naturally, we began with voltage analysis, focusing on the moment after the power-on reset. The analysis was performed using a DSCope U3P100 oscilloscope.

The trace shows that immediately after the power-on reset, the voltage is highly noisy for approximately 18.5 ms, after which it stabilizes. It was experimentally verified that the microcontroller’s firmware begins executing after this observed noise period. Thus, it is likely that this noise corresponds to some initial power-on initialization. Among the noise, repeating voltage spikes are noticeable (marked by arrows) that form 32 intervals of roughly equal length.

A close look at the very start of the trace reveals that almost immediately after power-on, the first two spikes occur consecutively, and the next two spikes appear roughly 1.6 ms later, which is approximately the duration of the race window during which SWD is accessible. Moreover, we experimentally confirmed that SWD becomes disabled on subsequent resets if the initialization process reaches this point on the oscilloscope. Following that, 32 blocks separated by voltage spikes occur, as mentioned earlier. Let’s examine them more closely.

Within each block, smaller yet distinguishable voltage spikes can be observed, and if you count the intervals between them, there are 64. Similar interval patterns appear in other noisy blocks. We can interpret this data by referring to the studies mentioned in the introduction, where an invasive attack provided access to the QSPI lines and revealed that data is read from them during microcontroller startup. If we assume that the smallest block, which repeats 64 times, corresponds to reading 16 bytes from flash memory via QSPI, the following interpretation emerges:

The following data are read:

  1. Factory configuration (0x20 bytes).
  2. Bootloader (0xC00 bytes).
  3. Option Bytes (0x10 bytes).
  4. The first 32 flash memory pages (each 0x400 bytes), i.e., half of all pages, which corresponds to the data observed in another research.

After interpreting the data, we proceeded to test the possibility of voltage glitching. The target was chosen as the moment of reading the Option Bytes. The glitcher itself was built using the same RP2040 microcontroller mentioned earlier. Two MOSFETs were added — one for power control and one for the glitch pulse. The test bench is shown in the photo below. We also present oscilloscope screenshots taken during the glitch:

Unfortunately, the voltage glitch attempts were unsuccessful. The microcontroller proved to be extremely sensitive to power fluctuations: with a narrow glitch pulse, its behavior remained unchanged, while even a slight increase in pulse width caused it to stop responding. Restoring its operation required reconnecting the power. It was not possible to find the “golden middle” where the chip would remain functional and the debug interface would become accessible. Perhaps experimenting with the initial voltage or the glitcher component parameters might have helped. Nevertheless, we learned a great deal about the initial startup. Moreover, the glitcher we built later proved useful in bypassing APPROTECT on the nRF52.

Having failed with the power glitch approach, we decided to return to the possibility of conducting an attack through the debug interface. Up to that point, we had barely attempted to utilize the peripherals during the initial startup and decided to fill that gap. We started by simply testing the functionality of the GPIO by toggling the microcontroller’s pins. The following code snippet on the RP2040 is responsible for this:

And this worked: the signal on the microcontroller’s pin A9 changed in accordance with the output values we wrote to the GPIO registers:

Next, we tested the USART module:

As expected, following this initialization, the letter F was output via UART:

Then we decided to test the functionality of the DMA engine. As we know from other studies, this module can be useful for bypassing RDP.

During the SWD-accessible window, we initialized all the necessary peripherals, wrote a specific value to SRAM, and attempted to transfer it to UART via DMA — and it worked:

The next step was, of course, an experiment to see whether we could replace the SRAM address (0x20000000) with the flash memory address (0x08000000). Its success seemed doubtful, since at the time of configuration via SWD the flash memory had not yet been fully initialized, and besides, RDP2 is active, so it shouldn’t have worked. Yet we tried:

At first, it seemed that nothing had happened because no data was output immediately after our peripheral initialization via SWD. However, we found that some data eventually appears on UART after 18.5 ms:

It turned out that this is indeed the microcontroller’s firmware! In other words, we confirmed the possibility of bypassing RDP2 on the microcontroller by exploiting the vulnerability in the SWD debug interface.

FamilyMCUReleaseRDP2GigaVulnerability #3
GD32F1x0GD32F130C8T6AJ2139YesYes
GD32F3x0GD32F330C8T6PJ2146YesYes
GD32F4xxGD32F405RGT6JJ2239YesYes
GD32L23xGD32L233RCT6MJ2306YesNo
GD32E23xGD32E230K8T6JJ2125YesNo
GD32E50xGD32E503VCT6MJ2119YesNo


Based on the test results, all investigated F-family microcontrollers with RDP2 are vulnerable.
The final table with all the vulnerabilities:

As can be seen, all the acquired microcontrollers turned out to be vulnerable in one way or another.

Comparison of the E/L and F MCU families

By analyzing the results and referring to the user manuals of the respective microcontroller families, we arrived at several conclusions regarding the differences in the operation between the E/L and F families.

In the description of the flash memory controller for the E/L families, there is a section on the waiting time when executing instructions:

  • 0~2 waiting time within 64K bytes when CPU executes an instruction

This is probably due to the fact that the operating frequencies of these microcontrollers are so low (or, conversely, the flash memory speed is very high) that such waiting times for fetching instructions from flash are acceptable. Consequently, there is no need for cache memory. Moreover, the Option Bytes value (and in particular the byte that determines the protection level) is not cached. After each reset, it is re-read from flash, which provides us with a small window (20 µs) following each reset.

The description for the F families is bit different. Execution of instructions from the first portion of memory occurs without any waiting, while the remaining portion experiences a significant delay:

  • No waiting time within 32K bytes when CPU executes an instruction
  • A long delay when fetching 32K ~ 64K bytes data from flash

This behavior is achieved by using a cache that is initialized during the first startup after power is applied. We even observed this initialization process through voltage analysis. Furthermore, it was experimentally verified that when accessing beyond the “fast” part of flash memory, there is indeed a significant delay accompanied by noise in the voltage measurements. This noise is similar to what we observed when loading flash pages during initial startup. The entire initialization process takes a noticeable amount of time — 18.5 ms. If this occurred on every reset, it would be rather inconvenient; therefore, the cached data survives subsequent resets. As we found out, the Option Bytes value is also cached. This results in a large window — 1.6 ms — during which SWD remains accessible until the Option Bytes are read. However, once the Option Bytes have been read and cached at least once, the RDP level becomes cached as well, and on subsequent exits from the reset state, SWD is disabled immediately.

Most likely, this difference is related to the use of an additional flash memory chip connected via QSPI. Using X-ray imaging, we took pictures of the investigated microcontrollers. Although the additional chip is not directly visible, the presence of extra connection wires in the area of the main chip indicates that an extra flash memory chip is used in all F families.

In the other microcontroller families, this is not observed:

Results and conclusions

Based on the results obtained, it’s not difficult to conclude that some implementations of readout protection technology are far from perfect. At first glance, the fact that the debug interface is available for a few microseconds may not seem like a problem. However, our tests showed otherwise: as little as 20 microseconds is enough to extract the firmware and bootloader of an actual consumer device — and that is just one example. Such vulnerabilities are present in many GD32 microcontroller families, which means that the number of vulnerable end devices could be significantly higher.
As a result of our research, we have confirmed the feasibility of implementing new techniques to bypass the readout protection. All vulnerabilities were reported to GigaDevice under their responsible disclosure policy, and the vendor plans to fix them in new revisions of the microcontrollers.

Disclosure timeline:

30.03.2023 – A first email was sent to CN/CERT
06.04.2023 – A first email was sent to GigaDevice
25.04.2023 – Full disclosure report was sent to CN/CERT
12.06.2023 – Full disclosure report was sent to GigaDevice PSIRT
15.09.2023 – Recieved vendor feedback (plan to fix bugs in new silicon up to 04.2024) via CN/CERT
06.02.2024 – Recieved vendor feedback update (plan to fix bugs in new silicon up to 05.2024) via CN/CERT