Breaking STM32 readout protection: From UV light to CPU state tracing

It has been a while I wanted to summarize some attacks on the STM32 somewhere, and looking at a presentation at Hardwear.io 2025 in USA, I was hopping to have time doing it, then got busy but → better writing it now than never!

STM32 microcontrollers from STMicroelectronics are everywhere: industrial controllers, medical devices, IoT gateways, automotive ECUs, consumer electronics. Their popularity also makes them a prime target for security researchers and attackers alike. At the heart of STM32 firmware confidentiality lies the Readout Protection (RDP) mechanism, a hardware feature designed to prevent unauthorized access to the internal flash memory via debug interfaces (SWD/JTAG).

But over the past years, the research community has systematically tackled this protection, layer by layer. This post traces that journey from the first UV-C optical attacks to the latest CPU state tracing technique presented at Hardwear.io USA 2025, so we get some references in here.

(source: https://hardwear.io/usa-2025/presentation/TraceRip-Hardwear.pdf)

So let’s talk about RDP first, and what it means to us to refresh our mind.

The RDP Model: the 3 Levels

On STM32 platforms, we can find mainly 3 readout level:

  • RDP Level 0: no protection. flash fully accessible via debug interface,
  • RDP Level 1: debug connection allowed, but flash reads via debugger trigger a HardFault and lock the flash interface. SRAM and peripherals remain accessible. Most commonly deployed level,
  • RDP Level 2: full lockdown. Debug interface permanently disabled. ST warns this is irreversible (with caveats, as we’ll see)

The critical assumption underpinning RDP Level 1: if you can’t read flash through the debugger, the firmware is safe. As research has shown, that assumption is fundamentally flawed.

Evolution of attacks

2017: Shedding too much Light on a Microcontroller’s Firmware Protection(Obermaier & Tatschner)

The foundational work came from Johannes Obermaier, and Stefan Tatschner at Fraunhofer AISEC, published at USENIX WOOT 2017. Their paper “Shedding too much Light on a Microcontroller’s Firmware Protection” targeted the STM32F0 and revealed three weaknesses:

1. UV-C Optical Fault Injection (Level 2 → Level 1 downgrade) RDP level is stored in option bytes in flash. Exposing a decapped chip to UV-C light selectively discharges protection bits, downgrading Level 2 to Level 1. Invasive (requires decapsulation), but the first demonstrated path from “permanently locked” to “attackable.”

(source: https://www.usenix.org/system/files/conference/woot17/woot17-paper-obermaier.pdf)

2. Debug Interface Race Condition (Level 1 bypass on STM32F0) On the STM32F0, the very first bus access after debugger connection succeeds before protection kicks in. Firmware extractable one word at a time: power cycle, connect, read one word, repeat.

(source: https://www.usenix.org/system/files/conference/woot17/woot17-paper-obermaier.pdf)

3. Cold-Boot Stepping (Level 1 data leakage via SRAM) If firmware performs a CRC integrity check at boot, intermediate CRC values pass through SRAM. Since SRAM remains readable under RDP Level 1, an attacker can power-cycle with precise timing, read the CRC state, and back-calculate flash contents byte by byte.

(source: https://www.usenix.org/system/files/conference/woot17/woot17-paper-obermaier.pdf)

This last technique doesn’t require any silicon bugs → it exploits a design-level assumption: that leaking processor state and SRAM is not equivalent to leaking flash.

The paper can be read there: https://www.usenix.org/system/files/conference/woot17/woot17-paper-obermaier.pdf

2020: Exception(al) Failure - Breaking the STM32F1 Read-Out Protection (Obermaier, Schink)

Targeting the STM32F1 series, vulnerability CVE-2020-8004 exploited how the Cortex-M3 fetches exception vectors. While data reads via the DCode bus are blocked by RDP Level 1, the ICode bus used for instruction and vector fetches is not blocked. By manipulating the vector table and triggering exceptions, roughly 90% of flash could be extracted without glitching.

In parallel, the Flash Patch/Breakpoint (FPB) unit was abused with a voltage glitch to redirect the reset vector for full flash extraction on STM32F1.

Blog post: Exception(al) Failure - Breaking the STM32F1 Read-Out Protection | blog.zapb.de
WOOT’20 Slides: https://www.usenix.org/system/files/woot20-paper-obermaier.pdf

2021–2024: The glitching era

As earlier logic-level exploits got patched, researchers turned to voltage fault injection (VFI).

(source: Glitching STM32 Read Out Protection - Anvil Secure)

In 2025, STM32-TraceRip: tracing the untraceable

At Hardwear.io USA 2025, Mark Omo and James Rowley from Marcus Engineering presented a technique called STM32-TraceRip: 100% flash recovery on the STM32G0 without any glitching, without UV light, without silicon bugs, but purely by observing the CPU state during normal execution which is smart.

First they tried recovering bytes from a CRC stored in SRAM → the Cold-Boot Stepping approach from Obermaier’s 2017 work.

The idea: if the bootloader computes a CRC over flash at startup, the intermediate CRC value sits in SRAM, which is still readable under RDP Level 1. Read it at different points in time, back-calculate the flash byte by byte.

But… it didn’t work. The reason? Obermaier’s reference CRC code passed the accumulator by pointer (uint32_t * const crc), which forces the compiler to store it in SRAM. Real-world CRC implementations don’t do that → the accumulator stays in a CPU register, and flash data is fetched directly into the processor. Nothing interesting ever hits SRAM.

(source: https://hardwear.io/usa-2025/presentation/TraceRip-Hardwear.pdf)

So they redirected the attack by looking at the core behaviors instead.

The core insight

RDP Level 1 blocks debugger access to flash but still allows reading CPU core registers via the debug interface. Any bus access outside the debug block triggers a HardFault and locks flash, so no breakpoints, no single-stepping, but you can connect, cause the fault, and read the resulting CPU state.

If flash is being used, its contents are reflected in the processor state. The flash is only useful because it’s used. And if it’s used, it leaks.

So they went deeper and instead of reading SRAM, they read the CPU core registers themselves.

Probing the registers

By debuging the core, they found that the target runs a standard boot-time CRC integrity check (CRC-16/XMODEM, polynomial 0x1021) in commercial firmware.

To look for potential leaks, they used the following setup:

  1. Custom tooling on a Raspberry Pi Pico with modified OpenOCD firmware, and a custom DAP vendor command (DAP_ProcessVendorCommand),
  2. For each capture: power off target → power on in reset → configure debug block → release reset → wait precise delay → trigger bus read causing HardFault,
  3. Read all CPU registers (R0–R12, SP, LR, PC, xPSR…) and a small SRAM window.
  4. Increment delay, repeat millions of times…

The process is non-deterministic at the end, the target’s internal RC oscillator creates jitter.

But from the register data they identified:

Register Role
R0 CRC accumulator (16-bit)
R2 Current byte address
R3 End address
R4 Bits remaining to shift (0–8)
R5 Polynomial (0x1021)
R7 Start address

For each CPU state snapshot produces one or more guesses of the CRC at a given address. Then:

  1. For each guess, compute 256 possible next-CRC values (one per possible input byte),
  2. Check which match guesses on adjacent bytes: creating “chains.”,
  3. Iteratively prune inconsistent chains forward and backward,
  4. Find anchor points (addresses with only one guess) and propagate certainty,
  5. The byte values connecting each CRC state are the flash contents.

With a 16-bit CRC, there not a lot of chance of collisio, creating ambiguous parallel chains of 20–100 bytes. These get resolved by anchors and by PC-aware refinement.

And to recover PC, they have shown that PC always reads as 0xFFFFFFFE (ARM EXC_RETURN for HardFault). But the real PC gets pushed onto the stack — in SRAM, which is readable. By grabbing the exception stack frame, they recover the actual instruction address for most frames.

Critical example: if PC points just past ldrb r1, [r2, #0x0], then R1 is the flash byte value, no CRC math needed. A single frame with a known PC can resolve an entire chain of ambiguous guesses.

Results

After collecting data across 3–5 capture runs (~2.5 days each due to the 600ms boot CRC × 50ms per capture cycle), they achieved 100% recovery of the application flash from approximately 0x08000650 to 0x0801FFFF. The first ~1000 bytes are missed due to probe startup delay, which is low-hanging fruit for improvement.

They tested on STM32F0, F1, G0, and C0 → they are all vulnerable. The RDP scheme works the same way across these families.

Key takeaways

  1. RDP Level 1 is not a security boundary: it’s a speed bump at best,
  2. RDP Level 2 can be downgraded via UV-C or voltage glitching, making Level 1 attacks applicable,
  3. Boot CRCs are a double-edged sword: essential for reliability, but they create a side channel. Consider using hardware CRC peripherals or running checks from RAM after decryption,
  4. Defense in depth: encrypt firmware at rest, use authenticated secure boot (not just CRC), implement anti-tamper, choose certified parts (SESIP/PSA Level 3+),
  5. For pentesters and red teamers: a Raspberry Pi Pico, some Python, and patience can now extract firmware from a wide range of deployed STM32 devices.

Lesson from TraceRip applies beyond STM32: any security model that assumes leaking processor state is safe while the processor is executing protected code is built on sand.

Resources

Slides: https://hardwear.io/usa-2025/presentation/TraceRip-Hardwear.pdf
Video: https://www.youtube.com/watch?v=eSN824_PUa0

What’s your experience attacking or defending STM32 devices? Encountered interesting protection schemes or bypasses in the wild? Drop your thoughts below!

Following up on the evolution of STM32 RDP attacks covered above → here’s a concrete illustration of where things stand in practice (150 yuans).

(source: An Interesting Find: STM32 RDP1 "Decryptor" - Karolis Stasaitis)

Karolis Stasaitis (@carlossless) recently documented something interesting: while browsing Xianyu (闲鱼, China’s second-hand marketplace), he found a whole market of turnkey STM32 RDP1 “decryptors”: search for “STM32解密” and you’ll see dozens of listings.

For about 150 yuan (~19 EUR), you get a USB dongle, adapter PCBs for F0/F1/F2/F4 packages, and a Windows utility. Solder the chip on the adapter, plug it in, click a button → full flash dump. No glitching rig, no tuning parameters, no understanding the underlying attack. It just works.

He tested it on an STM32F205RBT6 with RDP Level 1 enabled and got a clean 128KB flash readout at room temperature: the seller’s recommendation to use freeze spray wasn’t even necessary. The only quirk was the tool overshooting past the flash boundary and padding with 0xFF, but the actual contents were correct.

The dongle has an SOP-16 IC with its markings scraped off, and the Windows app triggers Defender and requires Chinese Simplified encoding to run, so the UX isn’t exactly polished. But the point stands: what took research labs, custom rigs, and careful timing a few years ago is now a commodity product on a Chinese flea market.

This is exactly the trajectory we described above:

  • 2017: UV-C + decapping + custom tooling (Obermaier & Tatschner)
  • 2020: Debug interface logic bugs (CVE-2020-8004)
  • 2021–2024: Voltage glitching with ChipWhisperer setups
  • 2025: Pure CPU state tracing with a Raspberry Pi Pico (TraceRip)
  • Now: solder, plug, click, done :smiley:

What is missing there?

Full writeup from Karolis: An Interesting Find: STM32 RDP1 "Decryptor" - Karolis Stasaitis

Has anyone here bought one of these or similar devices? Would be curious to know what’s actually inside that dongle, and whether it’s doing a known attack (voltage glitch? race condition?) or something else entirely.

You’re missing CVE-2020-13466 which is most likely what that Chinese packaged dongle is. The bar hasn’t been lowered that much, it’s been known for 6 years and was already a fully documented basic attack, for instance: GitHub - CTXz/stm32f1-picopwner: Dump read-out protected STM32F1's with a Pi Pico - A Pi Pico implementation of @JohannesObermaier's, Marc Schink's and Kosma Moczek's Glitch and FPB attack to bypass RDP (read-out protection) level 1 on STM32F1 chips · GitHub

Even back in 2020 is was relatively easy to conduct if you could write the two shellcodes since iirc they weren’t public back then.

Also TraceRip (as cool as it is) is not a STM32-specific vuln, more like a symptom of that SRAM problem that’s been plaguing them. It’s dependent on the vendor implementation, I’ve seen plenty of systems that booted without CRC’ing the internal flash.

1 Like

Thank you for the comment @gquere, that completes perfectly the thread! I will get the Chinese dongle to try and also someone pointed me to other resources: Looks like STM32 RDP1 bypass is now a ~19€ product on Chinese marketplaces. Solder chip on adapter -> plug USB dongle -> click button -> full flash dump. No glitching, no custom rig. Wrote up the… | 📡 Sebastien Dudek

So I guess I will read that and see what it also missing in addition to CVE-2020-13466 :slight_smile:

In the meantime I’m trying to acquire this dongle, let’s also try talking about the other resources that were posted lately.

Indeed, a Hacker News thread gives some clues to identify the likely technique.

Most probable techniques: from WOOT’s 17 race condition to WOOT’20 “Glitch & FPB” attack

@gquere, who also completed the post here, seems to have identified a similarity on HN:

“This dongle is very likely to be the Obermaier h3 attack but now packaged. […] The fact that they mention freezing the chip heavily weighs in that direction since it’s needed for higher clock chips.”

User Graziano_M then discussed a possible attack chain used by this device. This is the “Shellcode Exec. via Glitch and FPB” attack from Obermaier, Schink & Moczek’s paper “One Exploit to Rule them All?” (PDF), which we briefly mentioned in our main post alongside the Exception(al) Failure work.

Note: for F0 targets (Cortex-M0), the FPB trick doesn’t apply since M0 only has a simpler BPU without flash patching. The dongle likely falls back to the debug interface race condition from WOOT’17 (Obermaier & Tatschner) for these chips, which is also what stm32f0-pico-dump implements.

Assumptions about the attack chain

Looking at the different comments, it probably exploits three properties of Cortex-M3/M4 chips under RDP1:

  1. SWD debug remains accessible: you can’t read flash, but SRAM is fully readable/writable,
  2. The Flash Patch and Breakpoint unit (FPB) persists across reset: FPB comparators survive a chip reset without being cleared,
  3. SRAM content survives brief power loss: especially when cold (freeze spray).

And so this sequence might be used for the exploit:

  • Step 1: Load payload via SWD: attach as debugger, write a two-stage exploit firmware into SRAM. Stage 1’s job is to configure the FPB. Stage 2 is the flash dumper,
  • Step 2: Boot from SRAM, run Stage 1: detach the debugger, strap BOOT0/BOOT1 pins for SRAM boot, reset. Stage 1 executes and programs the FPB to patch the reset vector fetch at 0x00000004, redirecting it to Stage 2’s entry point in SRAM,
  • Step 3: Quick power glitch: cut power just long enough for a hardware reset, but short enough that SRAM retains its contents. Even an ESP8266 doing digitalWrite(LOW); digitalWrite(HIGH); with zero delay between them would be sufficient. The FPB comparators also survive this reset,
  • Step 4: Boot “from flash” with hijacked execution: the chip powers up normally, no debugger attached, so RDP doesn’t block flash access. It reads the reset vector from flash at 0x00000004, but the FPB intercepts this fetch and redirects execution to Stage 2 in SRAM,
  • Step 5: Dump everything: Stage 2 now runs with full flash access (the chip thinks it booted normally). It reads flash linearly from 0x08000000 and sends it out over UART.

Already reproduced on Pi Pico?

This dongle seems to package what already exists in some open-source implementations:

  • STM32F1 on Pi Pico: stm32f1-picopwner by CTXz: full implementation of the Glitch & FPB attack, tested on Blue Pill boards. Includes the two-stage SRAM payload, SWD bit-bang via the Pico, and a Python dump script,
  • STM32F4 adaptation: stm32f4-rdp-workaround by lolwheel: confirms freeze spray is needed for F4. Author used an ESP8266 for the power glitch with just consecutive digital writes, no delay,
  • STM32F0 debug race: stm32f0-pico-dump by racerxdl: exploits the F0-specific debug interface race condition (first bus access succeeds before RDP kicks in), one DWORD per power cycle,
  • Original attack code: f103-analysis/h3 by Obermaier: the reference implementation that the dongle likely packages.

Block Harbor also has a nice write-up using picopwner on a real-world target (odometer manipulation investigation on an STM32F105).

(source: blog of Block Harbor)

Feel free to share your experience, or experiments with these technques! And maybe if I get this dongle, I will be able to tell more about it :slight_smile: but for now I’m just indexing stuff here.

While digging further, someone relayed me o X/Twitter raised an interesting question: could the same attack work on GD32F103, the popular Chinese clone of the STM32F103 found in countless cheap “Blue Pill” boards, and quite possibly inside the dongle itself?

The thread is illuminating precisely because it highlights where the technique breaks down:

  • FPB seems absent, or non-functional on GD32F103: multiple people confirmed that the Flash Patch and Breakpoint unit doesn’t behave the same way as on genuine STM32s, making the core Glitch & FPB attack inapplicable directly,
  • Obermaier’s D2 method (which doesn’t rely on FPB) was attempted but also failed for several users: with symptoms suggesting the memory bus gets locked before any meaningful SRAM execution occurs,
  • Voltage glitching, however, seems to be a viable path. A YouTube demo claims success, and PT Security published a dedicated write-up, GigaVulnerability: Readout Protection Bypass on GigaDevice GD32 MCUs which goes deep into GD32-specific RDP weaknesses.

This raises an interesting commercial question about the dongle: does it actually support GD32 clones? If so, it almost certainly uses also voltage glitching? In addition to the FPB trick used for genuine STM32s. If it doesn’t, then it’s quietly relying on the fact that most buyers won’t notice, or care about the distinction.

And another thing in my stack I’d say… :smiley: