AndrewMohawk

DEF CON 34 badge research · August 2026

Two secrets, one tiny screen

How we recovered Flag1 and the badge-wide exchange key K0 from a sealed DEF CON 34 badge—without breaking AES, and without taking the irreversible developer-mode path.

Andrew MacPherson · @AndrewMohawk · 9 min read

The badge looks playful because it is: two badges can “mate” their light patterns by showing each other a pair of QR codes. Underneath that exchange is a small RISC-V computer, protected storage, authenticated encryption and a shared secret. The fun question was whether a sealed badge would give up either of its two prizes.

It did—but not over USB or serial. In the end, the most reliable output device was the badge’s own 128×128 OLED. We made it blink eight bytes at a time and filmed the screen with a phone.

First: what does badge “mating” do?

The first badge displays a request QR containing a fixed header and a fresh 12-byte nonce. The second badge creates a nine-byte gamete from its light genes, pads it to 16 bytes, and protects it with AES-256-GCM-SIV under the shared exchange key. The encrypted response QR goes back to the first badge, which authenticates it, mixes those incoming traits with its own, and lets the owner choose Keep or Revert.

The two targets

Flag1

A distinct 32-byte factory challenge value in keystore slot 260. Hardware access rules make it readable only from the trusted firmware identity. Its value is still intentionally withheld.

K0 / Ko

The shared 32-byte exchange-key record used by every badge to protect gametes. The design calls it Ko; the shipped database record is named k0. This key is now public.

K0 is why one browser can now act like a virtual mate: once it knows the badge’s request nonce, it can make a valid encrypted gamete response. K0 does not unlock the badge’s encrypted filesystem by itself, and recovering it did not mean AES had failed. We had to get the badge to do the protected work for us.

The foothold: an unsigned jump

The loader image has a signed body, but its first 132 bytes are outside the signature. That space includes the very first jump instruction executed after validation. Boot1’s update console can write those RRAM bytes before boot, so changing just that jump redirected execution into a small payload while the rest of the signed loader still validated.

That gave us machine-mode execution on our own badge, but machine mode alone was not the finish line. The hardware guard identifies callers from their virtual-memory context. The payload built the same user-mode, ASID-3 identity used by the trusted keystore service, read the protected data there, then returned to machine mode to display it.

We did not break AES or directly punch through the hardware ACL. We gained code execution, then recreated the identity the guard was designed to trust.

By this stage USB was gone and the usual debug paths were unavailable. The payload therefore drew each eight-byte page as an 8×8 bit grid: one byte per row, most-significant bit on the left. A page marker, a known UUID control, and CRC values let the decoder reject bad frames instead of trusting a visually plausible result.

Flag1 needed six tagged pages: one diagnostic page, one control page and four data pages. The K0 path used seven. It first reconstructed the badge’s PDDB master-key inputs on-chip, authenticated the wrapped .System key with AES-KWP, initialized the external flash in QPI mode, then swept decrypted pages for the exchange-key record. A published SHA-256 prefix narrowed candidates; a full captured AES-GCM-SIV tag authenticated the final key.

The captured proof

These are web-safe derivatives of the phone recordings: converted from HLG/HEVC to SDR/H.264 and stripped of capture metadata. The Flag1 screen is covered by an opaque mask; the prize value remains unpublished. K0 is shown because its release was cleared and it is the key used by the companion tool below.

FLAG1 // Live OLED extraction from the sealed badge. The bit grid is permanently masked in this public copy.
K0 // Tagged eight-byte pages cycling on the badge. Phone frames were decoded, voted and authenticated against the full exchange oracle.

What mattered

  1. Unsigned control flow is still control flow. Excluding a signature header is dangerous when executable bytes live inside it.
  2. Privilege is not identity. The successful read required the exact trusted user-mode context—not merely “more privilege.”
  3. A control must test the same claim. A readable UUID proved the display path, while authenticated AES-KWP and GCM-SIV checks proved the protected results.
  4. A screen can be a protocol. Page tags, repetition and CRCs turned shaky phone video into dependable bytes.

Make an impossible-looking gene

The recovered exchange key powers Gene Mate, a local browser tool. Choose a preset—including the deliberately extreme one—point your camera at the badge’s request QR, then scan the generated response on the badge. Nothing is uploaded.

Open Gene Mate

Source and disclosure

This research used my own badge and was privately disclosed to Andrew “bunnie” Huang before publication. The badge, firmware and game were created by bunnie and the Baochip team. Publication of the loader finding and K0 was cleared in August 2026; the raw Flag1 value is deliberately absent.