HTB: Defusal Challenge

Defusal - HackTheBox Challenge Writeup

Challenge Information

FieldValue
NameDefusal
CategoryHardware / Misc
DifficultyMedium
Authord3vn0mi

Description

“BOMB HAS BEEN PLANTED”. The usual defusal kit isn’t working, and something about the device’s output seems… unusual. The only way to stop the explosion is buried deep within the firmware. The clock is ticking, and your squad is counting on you. Just when all hope seemed lost, your team managed to acquire the blueprint of the bomb’s circuit. This crucial piece of intel might hold the key to understanding what’s really happening inside the device. Analyze the schematics, uncover the hidden logic, and defuse the bomb before it’s too late. Your squad is depending on you, secure the flag and ensure everyone makes it out alive.

Solution

The challenge ships a firmware image for what turns out to be an Arduino Mega 2560-based “bomb defusal” prop: a 4×4 matrix keypad for password entry, a 16×2 character LCD for prompts/feedback, and a MAX7219-driven 8×8 LED matrix (via the LedControl library) used as an output display. The included circuit.png schematic confirms this wiring and hints that the LED matrix — not the LCD — is where the real secret lives.

The provided ELF (Defusal) is unstripped and still carries its DWARF debug info, which immediately leaks interesting symbol names: print_flag and xorValue. That’s the tell that the flag isn’t just sitting in .data as plaintext — it’s obfuscated behind an XOR relationship with something at runtime, most likely the password value itself.

Static analysis of .data turns up the obvious decoy first: the string 7355608, the classic CS:GO bomb-defusal meme code. Entering that on the physical keypad just prints “Bomb has been DEFUSED!” on the LCD — a red herring designed to make you think you’re done. The interesting part is what happens on a failed password check: an 8×8 “X” bitmap (81 42 24 18 18 24 42 81) gets pushed to the LED matrix, and that failure-bitmap sits directly adjacent, in memory, to a much larger table: 37 back-to-back 8-byte NUL-terminated entries.

Diffing those 37 entries byte-by-byte revealed the key insight: the low 3 bits of every single byte across all 37 entries are constant, and those constant bits exactly match the low 3 bits of the decoy string "7355608". That’s a strong signal the real font/glyph data occupies the top 5 bits of each byte (enough to encode a 5×7 dot-matrix font row), while the low 3 bits are simply XOR-padding derived from the password string, put there to defeat naive strings/entropy scanning.

Stripping the XOR and shifting out the padding bits recovered clean 5×7 glyphs. Only 13 distinct glyph shapes appeared across the 37 table entries — consistent with a small character set (hex digits, braces, underscore) repeated to spell out a flag. Rendering each glyph as a 5×7 grid of dots produced clearly readable letters/digits (with one ambiguity: the “zero” glyph is drawn with a slash through it, distinguishing it from a “Q” and confirming it should be read as 0).

Key Steps

1. Recover the real archive contents — the staged challenge directory contained a 0-byte placeholder for Defusal; the real files were inside a password-protected zip.

Terminal window
mkdir -p /tmp/work && cd /tmp/work
unzip -o -P hackthebox /out/<challenge-uuid>.zip
# -> Defusal (ELF), circuit.png, video

2. Identify the target platform and confirm it’s unstripped

Terminal window
file Defusal
readelf -S Defusal | head
readelf -s Defusal | grep -v " FUNC \| FILE \|SECTION" | head -60
# DWARF present -> symbol names like `print_flag`, `xorValue` survive

3. Review the schematic to confirm the hardware model (Arduino Mega 2560 + 4x4 keypad + 16x2 LCD + MAX7219 8x8 LED matrix via LedControl) and identify the LED matrix as the true output channel for the flag rather than the LCD.

4. Pull the .data section and locate the decoy password

d = open('Defusal', 'rb').read()
data = d[0x1a28:0x1a28 + 0x1ba] # .data section
# "7355608" found here — the CS:GO bomb-defusal meme code (decoy)

5. Locate the glyph/XOR table adjacent to the failure bitmap

# 37 consecutive 8-byte NUL-terminated entries starting at 0x80021e,
# immediately followed by the 8x8 "X" failure bitmap:
FAIL_BITMAP = bytes([0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81])

6. Recover the font rows by XORing against the decoy password and shifting out the padding bits

password = b"7355608"
def decode_row(entry_byte, key_byte):
# low 3 bits of every entry byte are constant XOR padding
# matching the low 3 bits of the password string
return (entry_byte ^ key_byte) >> 3 # yields the true 5-bit font row
glyphs = []
for entry in table_entries: # 37 x 7-byte entries (8th byte = NUL)
rows = [decode_row(b, password[i]) for i, b in enumerate(entry[:7])]
glyphs.append(rows)

7. Render each 5×7 glyph and read off the characters

for rows in glyphs:
for r in rows:
print(''.join('#' if (r >> (4 - c)) & 1 else '.' for c in range(5)))
print()
# Only 13 distinct glyphs -> one shape per flag character (hex digits, braces, underscore)
# Slashed-zero glyph disambiguates '0' from 'Q'

8. Assemble the decoded characters in table order to recover the flag:

HTB{REDACTED}

Tools Used

  • unzip (password-protected archive extraction)
  • file, readelf (ELF/AVR firmware triage)
  • Python 3 (manual .data section parsing, XOR decoding, bitmap rendering)
  • Manual DWARF/.debug_str symbol inspection for print_flag / xorValue leads
  • Visual schematic review (circuit.png) to confirm hardware topology (Arduino Mega 2560, keypad, LCD, MAX7219 LED matrix)

Key Learnings

  • Unstripped binaries leak intent — DWARF debug strings like print_flag and xorValue are often enough to point straight at the vulnerable/interesting logic without any disassembly.
  • Decoys hide in plain sight — the CS:GO meme code 7355608 was placed as an obvious, findable “password” specifically to distract from the real secret sitting a few bytes away in memory.
  • Constant low-bit patterns across a data table are a strong tell of XOR padding — comparing many same-sized entries byte-by-byte and noticing which bit positions never vary is a fast, low-effort way to separate “real signal” bits from padding/obfuscation bits, without needing to guess the key length or algorithm up front.
  • The schematic was a genuine clue, not flavor text — recognizing that the LED matrix (not the LCD) was the real output channel for the flag was what redirected analysis toward the glyph table rather than the decoy password string.
  • Font/bitmap tables make excellent flag encodings — encoding a flag as a small set of reused 5×7 dot-matrix glyphs, XOR-masked with a decoy value, is a compact way to hide readable text inside firmware while still requiring visual/manual reconstruction to recover it.