Skip to main content

Berkner Tech

Bypassing JTAG Lock with Hardware Access

A locked JTAG port is not always a closed one. Here is how physical access is used to re-open disabled debug interfaces, and why locking alone is not the end.

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, though, treats the lock as a question rather than an answer.

The lock is enforced by hardware reading a configuration bit at a specific moment: a fuse or option byte tells the chip to refuse debug connections, and early in boot the ROM or first-stage loader reads that bit and decides whether to enable the interface. The protection is real, but it is only as strong as that one read. The fuse itself is hard to change; the decision the chip makes based on it, 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 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 the lock did not cover, so 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, and 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, and What Goes Behind It

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, and most attackers never get that far. The mistake is treating the lock as absolute: it is a strong filter, not a guarantee, so a solid design assumes the port might be reopened and arranges things so that reopening it yields little.

Layer behind the lockWhat it does when the port is reopened
Encrypted firmwareA flash dump is an encrypted blob, not readable code
Keys in a secure elementThe debug port cannot reach the secret material
Verified / secure bootA modified image will not run even with the port open
Redundant, monitored fuse checkA single glitch is not enough to flip the lock decision

With those layers in place, a successful glitch buys the attacker an encrypted blob and a chip that still refuses to run their code, so the single clever attack that defeated the lock runs into three more problems. Treat the lock as one layer among several, stress-test the check itself for glitch resistance by reading the lock bit more than once in separated code, and put real protection behind it, and defeating the lock no longer hands over the device.

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.

Share:

More Posts