$ cat writeup.md…
$ cat writeup.md…
broncoctf2026
Task: two academic Booth-multiplier paper figures (PNG) hide a multi-stage puzzle; the paper identity is misdirection. Solution: extract URLs from bit-plane 7 (MSB) of the red channel, follow tinyurl -> Google Drive to a .b input sequence, and since inputs are only 0000/1110 the radix-4 Booth PP decoder collapses to 1110->1 / 0000->0, giving 18 ASCII bytes = the flag.
I'm supposed to find the flag somewhere but this guy just sent me two images to try to find it! What the flip, I don't know anything about Electrical Engineering!!
Two files are provided:
ChallengeCircuit.png (301x250 RGBA) — a CMOS transistor-level schematic of a radix-4 Booth partial-product decoder.Challenge.png (715x396 RGBA) — "TABLE VI — Comparisons of The Radix-4 Booth Designs for Generating One Partial Product Row".Goal: recover the bronco{...} flag hidden somewhere in/behind these images.
Reverse-image searching both figures identifies their common source:
Yen-Jen Chang, Yu-Cheng Cheng, Shao-Chi Liao, Chun-Huo Hsiao, "A Low Power Radix-4 Booth Multiplier With Pre-Encoded Mechanism," IEEE Access vol. 8, pp. 114842–114853, 2020, DOI 10.1109/ACCESS.2020.3003684.
ChallengeCircuit.png = Figure 9(b), the proposed low-cost decoder.Challenge.png = Table VI.Lesson (misdirection): identifying the paper is NOT the solution. Guessing the
normalized paper title as the flag (e.g.
bronco{a_low_power_radix-4_booth_multiplier_with_pre-encoded_mechanism}) is
wrong. The paper is only context — it teaches the Booth decoder needed in the
final stage.
tEXt/zTXt/trailing data.255.binwalk, strings, exiftool — nothing.The hidden data is in bit-plane 7 (the most significant bit) of the red channel, read row-major (top-left → bottom-right), packed 8 bits per byte, MSB-first. The key insight is to check HIGH bit-planes and single-channel extraction, not just LSB.
Extraction yields a printable URL in each image:
ChallengeCircuit.png → https://tinyurl.com/3pya79weChallenge.png → https://tinyurl.com/hnexnehb#!/usr/bin/env python3 # Extract bit-plane 7 (MSB) of the RED channel, row-major, MSB-first packing. from PIL import Image import numpy as np def extract_msb_red(path): img = np.array(Image.open(path).convert("RGBA")) red = img[:, :, 0].flatten() # row-major bits = (red >> 7) & 1 # bit-plane 7 (MSB) n = (len(bits) // 8) * 8 bytes_out = np.packbits(bits[:n]) # MSB-first by default # keep the leading printable ASCII run s = bytes(bytes_out).split(b"\x00")[0] printable = bytes(c for c in s if 32 <= c < 127) return printable.decode(errors="ignore") print(extract_msb_red("ChallengeCircuit.png")) # https://tinyurl.com/3pya79we print(extract_msb_red("Challenge.png")) # https://tinyurl.com/hnexnehb
tinyurl.com/3pya79we → Google Drive folder "BroncoCTFChallengeCircuit" containing hintscircuit.txt.tinyurl.com/hnexnehb → Google Drive folder "BroncoCtfChallengeNonCircuit" containing hintstable.txt and inputsequence.b.Files download via https://drive.google.com/uc?export=download&id=FILE_ID.
hintscircuit.txt (circuit folder):
Here the link to where you can find the actual gate level diagram (ResearchGate publication 342326148, page 6). Use
readmembinstead of writing out all the binary yourself. On the gate-level diagram you don't need to worry aboutzero_ias it will be zero whatever you do. Use the same method (MSB stego) to grab the input sequence. Once you feed the input binary to the circuit, convert it to ASCII and you get the flag.
hintstable.txt (non-circuit folder):
The binary file is your input sequence. When feeding it into the Verilog, the order should be: MSB =
neg_i, thenx_j, thennx_{j-1}, LSB =ot_i.zero_iis always zero. Free Verilog: edaplayground; use SystemVerilog/Verilog for testbench + design, Icarus Verilog 12.
inputsequence.b: 18 rows, each row = 8 space-separated 4-bit groups. Each 4-bit
group is {neg_i (MSB), x_j, nx_{j-1}, ot_i (LSB)}. Every group is either 0000
or 1110:
0000 1110 1110 0000 0000 0000 1110 0000
0000 1110 1110 1110 0000 0000 1110 0000
0000 1110 1110 0000 1110 1110 1110 1110
0000 1110 1110 0000 1110 1110 1110 0000
0000 1110 1110 0000 0000 0000 1110 1110
0000 1110 1110 0000 1110 1110 1110 1110
0000 1110 1110 1110 1110 0000 1110 1110
0000 1110 1110 0000 1110 1110 1110 1110
0000 1110 1110 1110 0000 1110 1110 0000
0000 0000 1110 1110 0000 0000 1110 0000
0000 1110 1110 0000 1110 0000 0000 0000
0000 1110 1110 0000 1110 0000 0000 0000
0000 1110 0000 1110 0000 1110 0000 1110
0000 0000 1110 1110 0000 1110 1110 0000
0000 1110 1110 0000 1110 1110 0000 1110
0000 1110 0000 0000 0000 0000 1110 0000
0000 1110 0000 1110 1110 0000 0000 1110
0000 1110 1110 1110 1110 1110 0000 1110
The intended path builds the gate-level radix-4 Booth partial-product (PP) decoder
in Verilog (readmemb the .b file, drive the decoder, dump PP bits). But because
only two input patterns ever appear, feeding them through the proposed PP
decoder gives:
PP(0000) = 0PP(1110) = 1So each 4-bit group collapses to a single output bit. Eight groups per row → one byte → one ASCII character; 18 rows → 18 characters. No simulator required.
#!/usr/bin/env python3 # Radix-4 Booth PP decoder collapses to a 1-bit map on the only two inputs seen. rows = open("inputsequence.b").read().strip().splitlines() m = {"1110": "1", "0000": "0"} # PP(1110)=1, PP(0000)=0 flag = "" for r in rows: bits = "".join(m[g] for g in r.split()) # 8 groups -> 8 bits, MSB-first flag += chr(int(bits, 2)) # 1 byte -> 1 ASCII char print(flag) # bronco{REDACTED}
Confirmation: submitted to CTFd /api/v1/challenges/attempt (challenge_id 38) →
{"status":"correct"}.
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar