Berkner Tech

Bypassing JTAG Lock With Hardware Access

Bypassing a locked JTAG debug port with a voltage glitch during the boot fuse check

Disabling JTAG is the right move, but disabled does not always mean unreachable. With physical access, several techniques aim to re-open a locked debug port, and understanding them shows why locking alone is not the end of the story.

Disabled Is Not Always Unreachable

A locked debug port feels like a solved problem. The chip refuses connections, a scan finds nothing, and the assessment seems to stop there. For most attackers it does, which is exactly why locking is worth doing.

A capable attacker treats the lock as a question rather than an answer. The lock is enforced by hardware reading a configuration bit at a specific moment, and anything that has a specific moment has a moment that can be attacked. That is the gap this article is about.

How the Lock Usually Works

A configuration fuse or an option byte tells the chip to refuse debug connections. Early in boot, ROM or the first-stage loader reads that bit and decides whether to enable the debug interface. The protection is real, but it is only as strong as that one read.

The important detail is that the lock state lives in non-volatile memory and is checked at startup. The fuse itself is hard to change. The decision the chip makes based on the fuse, in a single window during boot, is the softer target.

Confirm the Lock From Outside

Before attacking anything, confirm the port really is locked. A debugger that cannot initialize the chain, on a board where the SWD pins are clearly present, is the signature of an enabled lock rather than a wiring mistake.

openocd -f interface/stlink.cfg -f target/stm32f4x.cfg
Example output
Info : STLINK V2J37 (API v2) VID:PID 0483:374B
Error: init mode failed (unable to connect to the target)
Error: failed to read DP register: protected (RDP level 1)
# the part answers but refuses debug: readout protection is engaged

A chip that answers the adapter but refuses register reads with a protection error is locked, not absent. That distinction matters: a locked-but-present port is a candidate for a timing attack, while a truly dead port is not.

The Lock Is a Moment, Not a Wall

The mental shift that makes the attack possible is to stop thinking of the lock as a permanent wall and start thinking of it as a decision made once, very early, very fast. If you can disturb the chip at the instant it reads the fuse, you may be able to change the outcome of that decision without touching the fuse at all.

This is fault injection, and the debug-lock check is one of its classic targets, because the payoff, a fully open debug port, is so large relative to the effort once the technique is set up.

Glitching the Check

A voltage or clock glitch at the right microsecond can make the processor misread the protection bit or skip the branch that acts on it. The tooling for this is mature: a glitch platform like the ChipWhisperer drives a precise, brief disturbance into the power rail while the chip boots.

# chipwhisperer: insert a short power glitch a fixed offset after reset
scope.glitch.clk_src = 'clkgen'
scope.glitch.width   = 4
scope.glitch.offset  = -10
scope.glitch.ext_offset = 1180   # cycles after reset, tuned to the fuse read
target.reset(); scope.arm(); scope.capture()

The whole game is the ext_offset, the delay between reset and the glitch. It has to land on the handful of cycles where the chip reads and acts on the lock bit. Finding that offset is the real work, and it is mostly a search.

Finding the Glitch Window

You rarely know the exact moment up front, so you sweep. Step the offset across the plausible boot window, glitch on each attempt, and check whether the debug port opened. A power trace of the boot helps narrow the search to where the fuse read actually happens.

# sweep the glitch offset and test debug access after each attempt
for off in $(seq 1000 5 1400); do
  glitch_at "$off"
  openocd -c "init; halt" 2>&1 | grep -q "halted" && echo "WIN at offset $off"
done
Example output
... offset 1175: locked
WIN at offset 1180
... offset 1185: locked
# a single offset reliably opens the port -> the fuse-read window is ~1180 cycles in

When one offset starts returning a halted core, you have found the window. From there the glitch is repeatable, and the locked port opens on demand. The search can take hours, but it only has to succeed once per chip family.

Partial Protection Gaps

Glitching is not the only route. Some chips lock JTAG but leave another debug path partly open, a boot ROM command interface, a UART bootloader, or a vendor test mode that the lock did not cover. Attackers look for the path the designers forgot to close.

Reading the reference manual for every documented way into the chip, then checking which ones the lock actually disables, often turns up an alternate door. A lock that covers JTAG but not the ROM serial bootloader is a common and useful oversight.

What a Successful Bypass Yields

Once debug is back, the chip is wide open in the usual way: halt the core, read flash and RAM, single-step the firmware, and dump whatever the encryption or the application was protecting. The lock was the only thing standing in the way, and now it is gone.

# after the glitch lands, the previously locked flash reads out normally
openocd -c "init; halt; dump_image fw.bin 0x08000000 0x100000; shutdown"
Example output
target halted due to debug-request
dumped 1048576 bytes in 12.4s
# full firmware recovered from a port that was "locked"

That dump is the proof that a lock alone is not enough. Everything the device kept behind the debug port is now in a file, recovered from a chip the vendor believed was sealed.

Why a Lock Still Matters

None of this means locking JTAG is pointless. It raises the bar substantially, turning a two-minute dump into a setup that needs a glitch rig, a tuned offset, and sometimes hours of searching. Most attackers never get that far, and the lock filters them out.

The mistake is treating the lock as absolute. It is a strong filter, not a guarantee, and a design that bets everything on it has bet on an attacker not bothering with fault injection, which is an increasingly poor bet as the tools get cheaper.

Defense in Depth Behind the Lock

A solid design assumes the debug port might be reopened and arranges things so that reopening it yields little. Encrypt the firmware so a dump is not immediately readable, keep the important keys in a secure element the debug port cannot reach, and verify boot so a modified image will not run even with the port open.

With those layers in place, a successful glitch buys the attacker an encrypted blob and a chip that still refuses to run their code. The single clever attack that defeated the lock now runs into three more problems, which is exactly what defense in depth is for.

Designing Firmware to Survive a Glitch

The check itself can be made harder to skip. Reading the lock bit more than once, in separated code with the results compared, means a single glitch is not enough. A brownout detector and clock supervision make the disturbances that glitches rely on more likely to reset the chip than to slip through.

None of these are absolute either, but each one multiplies the attacker’s effort. A boot check that is one branch instruction is fragile by design. A check that is redundant, monitored, and time-randomized is a genuinely hard target.

What This Means for Your Design

The takeaway is not that locking JTAG is futile, it is that the lock should be one layer among several. Treat it as a strong filter that stops the casual attacker, and assume a determined one might defeat it with a well-timed glitch.

Behind the lock, the firmware should be encrypted, the valuable keys should live in hardware the port cannot reach, and secure boot should refuse a modified image. Arranged that way, defeating the lock does not hand over the device, which keeps one clever attack from ending the whole engagement.

Where This Fits

Assessing debug-port protections, and whether they survive fault injection, is part of a thorough hardware penetration test. If you want your debug lock and the layers behind it stress-tested the way a capable attacker would, that is the kind of work we do at Berkner Tech.


References and Further Reading