ARCHIVE / 01 · FIELD NOTES
Breaking the DEF CON 34 Badge
15 Hours of Hyperfocus
A technical account of fifteen hours investigating the DEF CON 34 badge — hardware, my own hypotheses, and agentic AI as a multiplier, under human direction and responsibility.
Coming back, looking, deciding
I came back to DEF CON after more than ten years. JMA Integra made the trip possible, and I arrived with the reasonable intention of attending talks, reconnecting with the community and learning. That intention lasted until I had the badge in my hands. It didn’t look like a souvenir: it was a complete, expressive system, deliberately designed to invite you to take its assumptions apart.
I don’t normally write like this: my reports tend to be serious, corporate and far less personal. This time I wanted to keep the human context without letting it crowd out the technical analysis.
Clifford Stoll’s talk set the tone. Stoll still has a contagious way of turning curiosity into serious work without draining the joy out of it. I left remembering why I got interested in security in the first place: not to accumulate answers, but to chase a question until the system could no longer hide how it worked.
My reconnaissance started with the visible protocol. The badge exchanged authenticated QR codes, so I tried to capture them, compare states and understand which parts of the flow were actually protected. The screen introduced a mundane difficulty right away: its flicker warped whole frames, and a photograph could look like evidence of a different value when it was only a different exposure.
Before touching anything I accepted what was at stake. The badge held secrets that only existed inside it, and a miscalculated write could lock or erase them irreversibly. There was no undo button and no guaranteed second chance: losing a secret to haste would have ended the investigation with no remedy. So from the start I imposed a strict discipline: minimal changes, one hypothesis per experiment, controls kept, and verified restoration of the device between stages. The fifteen hours in the title run from 18:18 to 09:18: laptop, badge connected, and the decision never to accept a coincidence as proof.
The first obstacle: getting into load mode
Before any elegant idea, I had to solve something purely physical: how to put the badge into the mode that lets you load code. It wasn’t obvious how to force that state, and powering it off in software simply didn’t work: the device wouldn’t land in the condition I needed in order to update.
The sequence that finally worked was manual and unintuitive: remove both batteries, press a button to discharge the capacitors and drain the circuit to zero, insert one battery, hold a button down, and only then insert the second battery. That choreography — not a software power-off — was what left the badge ready to enumerate and accept a load.
It sounds like a minor detail and it isn’t. Without that state there wasn’t even a starting point, and every failed attempt burned time from a window that wasn’t infinite. Solving load mode was the precondition for everything else.
Attack surface: authentication isn’t full coverage
I started from the path I had already observed in the QR codes. The real exchanges were authenticated, and it made no sense to assume the cryptography would give way to persistence. The initial goal was more modest: map the attack surface, separate data, transport and control flow, and ask what exactly the loader verified before running an update.
The loader image contained signature, hash and additional authenticated-data checks. That protected a wide region and made an arbitrary modification useless. However, the first instruction was a JAL, and the coverage left out a very narrow window corresponding to its immediate. There wasn’t a large unauthenticated space; there were a few bytes with direct influence over the jump destination. By keeping the opcode and altering only that part of the immediate, I could redirect execution to an added stage in free space of the partition.
Eureka 1 — The useful boundary was in control flow. Authentication protected the expected content, but its coverage left a minimal window in the first
JAL. The right question wasn’t how to forge a signature, but which execution decision could change without touching what was authenticated.
Before taking it to hardware I compared the original and modified packages. The diff had to show only the intended change in the JAL and the new stage in its separate location. Then I confirmed the loader accepted the package and transferred control. Even here, unglamorous problems showed up: a USB cable carrying no data, and the exact moment to release the Update button. Solving them was part of turning a static observation into a repeatable physical procedure.
K0: let the badge read and the host think
The first payload to look for K0 tried to do too much. A complex BIO scanner trapped cleanly; that proved it had executed, but not that it was a reliable base. So I reduced the task to its minimal unit. BIO, the small programmable I/O engine, didn’t need to recognize keys or run cryptography: it only had to read SRAM predictably and hand over the data.
The final reader was 160 bytes of program. That size wasn’t the volume acquired: each operation returned 1 KiB blocks. The important primitive was a BDMA window that boot had configured and that persisted, letting BIO reach SRAM through the cross-domain transfer engine. I reused that window instead of adding complexity inside the badge.
I moved to the host everything the host did better. The collector received the serial stream, framed each block explicitly, checked sequences and errors, saved the result in a resumable way, and only retried safe operations. I also had to correct operational assumptions: a FIFO that looked independent was shared, some errors arrived asynchronously, and the default serial buffer couldn’t handle the throughput. Robustness didn’t come from a cleverer payload but from a cleaner separation between acquisition and analysis.
The capture was partial and sparse. The file represented a logical window larger than the blocks that had actually arrived; I didn’t present it as a full dump. That didn’t invalidate what was acquired, but it forced me to record which fragments existed and to avoid inferences about the gaps. The host searched for candidates only in materialized blocks and preserved the provenance of every match.
A 32-byte sequence appeared as a plausible candidate. Plausible wasn’t enough. A string can have the right length, sit near expected structures and even match a known fingerprint by accident. First I independently checked its SHA-256 against the permitted reference. Then I used it with two real, distinct transcripts of the badge protocol. In both, AES-256-GCM-SIV produced an authenticated decryption and the tag was valid.
Those two verifications were independent of each other: they didn’t repeat the same packet nor depend on a photograph. Only at that point did I mark the result as demonstrated. The badge had performed a minimal read; the host had searched, reconstructed and validated.
Eureka 2 — The device didn’t need to know what we were looking for. A 160-byte BIO reader, 1 KiB blocks and the persistent BDMA window were enough. By moving search, retries and validation to the host, every failure had a cleaner interpretation and two real tags could act as independent proof.
I don’t publish the key, the transcripts or the dump. The verifiable chain is enough to explain the reasoning: narrow coverage, minimal jump, reduced reader, declared partial acquisition and two correct authentications.
Flag 1: when more privilege yields less access
K0 didn’t contain Flag 1. The second piece was associated with slot 260 of Fw0, under ASID3, and the problem was no longer transporting SRAM but running the read from the correct access context. My first hypothesis was conventional: if a region was protected, S-mode would offer more capability than U-mode.
I built a bounded reader and ran it physically in S-mode. I got exactly 32 zeros and a reproducible CRC. The result was negative, but not ambiguous. Noise, an incomplete read or an unstable address would have produced variation; the same size, the same content and the same CRC indicated a deterministic denial. They also didn’t prove the region was empty. They proved that combination of mode and domain did not reveal the content.
I went back to the RTL. CONTROL_INVERT_PRIV and the internal vex_mm signal showed that the effective privilege for that check didn’t follow the “higher opens more” intuition. With the inversion active, S-mode and M-mode led to the path that returned zero; U-mode under ASID3 allowed the right experiment to be framed.
I prepared a minimal Sv32 map so I could audit all of it. The stub page was executable and readable in U-mode; the target page, readable only in U-mode; the scratch, readable and writable; the return stack sat outside U-mode. The code copied the region byte by byte with a simple load/store pair and returned through the expected exception. It included no scanner, no search and no writes to the target.
Running physically in U-mode produced the expected 32-byte result. I validated the authorized digest and the CRC, repeated the run and restored the official loader. The evidence wasn’t a favorable simulation: it was the reproducible difference between the S-mode control at zero and a verified U-mode read on the badge.
Eureka 3 — More privilege was not more access. The stable zeros falsified my initial hypothesis and pointed at the real model. The breakthrough came from reducing the context to U-mode,
ASID3and a minimal Sv32 map, not from raising privilege or widening the payload.
Flag 2: the stepping first, the row second
The third stage pointed at IFR, but that sentence still hid the decisive question: which silicon revision did I physically have? A PCB revision, a photo or an expectation about fabrication couldn’t answer it. Before preparing an IFR read I ran a probe whose only purpose was to identify the stepping. The badge on hand answered A0.
Eureka 4 — The silicon revision was a condition of the attack. Confirming A0 before touching
RRCCRturned an idea about the design into an experiment applicable to the real badge. The stepping came first; any conclusion about A1 had to stay separate from what I had tested.
On A0 there was a narrow path via RRCCR. I designed the operation to change a single bit temporarily and reversibly. The payload read and saved the base value, applied the minimal change, performed two reads of the target row and two of the control, restored the register and verified the restoration before showing anything. If the screen, the cable or my interpretation failed, the critical state was already back in place. Here the risk of losing the secret was higher than ever: any slip on a sensitive register could put the piece out of reach.
My working hypothesis was that Flag 2 might be split, and that combining four extracts would give a complete key. Reality was simpler: they weren’t four pieces that added up, but a single candidate row. R17 was the only compatible, non-zero candidate. The two reads matched, its CRC was stable, and the observed structure fit the expected length and locks. On top of that, the RTL reserved that row and normal firmware didn’t consume it.
The underlying problem was that, unlike K0, I had no way to check for myself whether Flag 2 was good. There was no cryptographic oracle and no public digest to validate the 16 bytes against. I could accumulate evidence — structure, locks, RTL reservation, negative controls — but I couldn’t close the proof on my own. That distinction shaped everything that followed: treat it as a high-confidence hypothesis, never as fact, until external confirmation.
R19, read with the same temporary change, returned zero and acted as the primary negative control. Then I repeated the pattern on R7–R11 to rule out alternative explanations related to other protected material. Those rows produced no compatible candidate either. That kept me from picking R17 just because it was “the only thing that looked interesting.”
Restoration had its own evidence. It wasn’t enough to run a write-back: I compared the restored value against the base copy kept before the change, and I arranged the presentation so that verification happened before I depended on the display. Order mattered, because a power loss or an unreadable capture must not leave the temporary access open while I sorted out a secondary problem.
The screen flicker was again a source of false disagreements. Two exposures of the same screen could highlight different halves and look like different values. That’s why the evidence relied on repeated reads, CRC, structure and controls, not on picking the most legible photograph.
A1: the hypothesis bunnie confirmed
The RRCCR path I used existed on A0. My badge was exactly that: a Boot1 audit identified it as A0, OEM, Boot1 v0.10.1, so that unit couldn’t validate an A1 path. Even so, reviewing the A1 tapeout RTL I formed a strong theory about how Flag 2 could be extracted on A1 despite the patch.
The observation was this: info_access_error_pre is qualified by axi_info & data_op, while the Vex iBus uses ARPROT=3'b110, which makes an instruction fetch inst_op=1 and data_op=0. Since IFR also sits outside codesel, it looked like a read-locked IFR row could still reach the CPU through an instruction fetch, even when a normal lw (a data load) is masked.
I proposed two tests in simulation. The first: a non-sensitive canary in a read-locked IFR row, fetched as code, recording mcause, mepc, mtval and the controlled register deltas; since RVC complicates exact recovery, I’d model the decoder and trap behavior first. The second: interleaving a denied dBus read with iBus traffic and backpressure, because the RRC appears to load the raw 256-bit row into ahb_rd_buf before masking hrdata, so it was worth checking that the request metadata and the buffered data could never become mis-associated.
I couldn’t prove it, because I didn’t have an A1 badge in front of me, and I refused to present it as anything more than a hypothesis. When I put it to Andrew “bunnie” Huang, he confirmed it: on A1, instruction fetches can indeed read from a locked IFR row. The fix is to mark the IFR region as no-execute from the virtual memory subsystem; someone even PoC’d that fetch-based read, using the illegal exception vector to pull out the faulting instruction. You can’t recover 100% of the locations, but you can get roughly half — which is already enough to be a problem.
It was one of the parts I enjoyed most in the whole exercise: not just closing the challenge on the silicon I had, but understanding the system well enough to anticipate its behavior on a version I never touched.
Many eyes, one responsibility
It’s worth being precise about the role of AI. The hypotheses were mine: attacking control flow instead of the signature, reducing the reader to its minimum, dropping to U-mode when high privilege returned zeros, fixing the stepping before touching RRCCR, the theory about A1. What AI gave me was parallelism—many eyes at my disposal—that made the investigation faster: reviewing code, generating variants, comparing diffs, hardening the serial collector and independently checking hashes, tags, CRC and structure. It was wrong plenty of times too, and my job was to steer it, demand traceability and decide what counted as proven.
The physical limit and the responsibility were mine. Nobody else connected the cable, worked out the battery sequence, authorized each temporary write, restored the device or reported the result. AI amplified my ability to read, produce and push back; I brought the context, the judgment and the accountability.
During the awards and the Discord chatter afterward, I noticed a real resistance in the community to giving credit to any work that AI had a hand in. I understand the wariness, but we are a community that literally feeds on new technology: we learn it, adapt it, understand it, break it and study it. Rejecting a new technology outright—and one of this caliber—doesn’t sit well with the spirit of what we do.
Everyone had access to the same badge, the same challenge, the same AI and the same tools I did. Only two of us solved the puzzle. bunnie mentioned the challenge and the people who cracked it during the ceremony, but I felt that part of the community and the organization saw the achievement as minor, despite how few people managed it. I’m genuinely happy with what I did: getting to talk with bunnie about his own work—and even help him improve it—is already an enormous reward. But I didn’t want to leave that last part unsaid.
Confirmation, limits and public context
bunnie’s direct confirmation closed R17’s identity that same morning without changing the order of the evidence: first there was a reproducible A0 read and a high-confidence hypothesis; then it was reported; finally the confirmation arrived. Beyond bunnie’s confirmation about A1, which I do include, I don’t reproduce other private conversations or sensitive material from those exchanges. I only summarize the conclusions needed to bound what was demonstrated and the clarification about A1.
Later I could thank him for the challenge in person. The badge had done exactly the thing I value most in this kind of artifact: turning curiosity, documentation and physical measurement into a technical conversation with its designers.
The CHEESO Hall of Fame offers public corroboration that K0, Flag 1 and Flag 2 ended up associated with my entry. It’s context, not proof of the timestamp or the order of arrival: the capture doesn’t contain a chronology on its own, and a public table can change. The timeline described above rests on my contemporaneous records and the direct confirmation, not on counting rows in an image.
This text is limited to the work done during DEF CON and the direct clarification received that morning; it doesn’t incorporate later research to improve the story in hindsight. The photographs show the dummy payloads of Flag 1 and Flag 2 and moments from the process; I don’t publish K0, the dumps, the protocol transcripts or other private conversations. I lay out the method and the checks only as far as needed to understand the chain, without turning the write-up into a release of restricted material.
Thanks to DEF CON for making a challenge of this quality possible and for reminding me, after more than a decade, of the joy of researching within a community. Thanks to bunnie for designing an extraordinary badge and for confirming the scope of the result precisely. And thanks to JMA Integra for the trust and for the chance to come back. AI provided speed and many eyes; the community provided context; and the responsibility of telling a candidate from a proof stayed human.