$ cat writeup.md…
$ cat writeup.md…
hackthebox
Task: 4MB ESP32 flash dump (firmware.bin) with a runtime flag gated behind an eFuse factory-MAC 'real hardware' check. Solution: parse the ESP partition table and app image, reconstruct an ELF from the 6 segments, reverse the Xtensa LX6 code in Ghidra (Xtensa:LE:32), find the flag generator that single-byte XORs (0x42) a 31-byte DROM blob — the MAC gate only gates printing, so the flag is fully recoverable offline.
Someone leaked the new Espresso firmware, can you try to figure out what it does?
Provided: a password-protected zip (password hackthebox) containing firmware.bin,
a 4 MB ESP32 flash dump. "Espresso" is a wordplay on Espressif (the ESP32 vendor).
The goal is to recover a flag the firmware generates at runtime — but only when it
believes it is running on genuine hardware.
unzip -P hackthebox espresso.zip # -> hw_espresso/firmware.bin file firmware.bin # -> data wc -c firmware.bin # -> 4194304 (4 MB)
A hexdump shows the image is mostly 0xFF padding (erased NOR flash). The real
content sits at the standard ESP32 flash offsets.
| Offset | Magic | Meaning |
|---|---|---|
0x1000 | 0xE9 | second-stage bootloader (ESP image) |
0x8000 | 0xAA50 | partition table |
0x10000 | 0xE9 | application (ESP image) |
@0x8000)Each entry is 32 bytes: magic(0xAA50) type subtype <I addr <I size label[16].
import struct data = open("firmware.bin","rb").read() off = 0x8000 while True: e = data[off:off+32] if e[:2] != b"\xAA\x50": break _, t, st, addr, size = struct.unpack("<HBBII", e[:12]) label = e[12:28].rstrip(b"\x00").decode() print(f"{label:10} type={t} sub={st} addr={addr:#08x} size={size:#08x}") off += 32
nvs type=1 sub=2 addr=0x009000 size=0x006000 (empty)
phy_init type=1 sub=1 addr=0x00f000 size=0x001000
factory type=0 sub=0 addr=0x010000 size=0x100000 (the 1 MB application)
@0x10000)ESP image magic 0xE9, 6 segments, chip_id=0 ⇒ ESP32 (original Xtensa LX6).
Build info from strings: ESP-IDF v6.1-dev, project name espresso,
entry point 0x400814ac.
Segment map (file offset is relative to the app image at firmware.bin+0x10000):
| Seg | load_addr | length | file off | region | notes |
|---|---|---|---|---|---|
| 0 | 0x3f400020 | 0x008b68 | 0x000020 | DROM | rodata / strings |
| 1 | 0x3ffb0000 | 0x002a6c | 0x008b90 | DRAM | |
| 2 | 0x40080000 | 0x004a14 | 0x00b604 | IRAM | |
| 3 | 0x400d0020 | 0x00c7b8 | 0x010020 | IROM | main flash-cached app code |
| 4 | 0x40084a14 | 0x0060f4 | 0x01c7e0 | IRAM | |
| 5 | 0x50000000 | 0x000028 | 0x0228dc | RTC |
flag did not generate correctly.
It seems you are running the firmware on cloned hadware.
Buy the real hardware, or perhaps try to emulate it. ;)
Plus symbols get_efuse_factory_mac, esp_efuse_utility_read_reg, efuse_init.
This screams: the firmware reads the factory MAC from eFuse and compares it to a
hardcoded value before printing the flag. The hint "try to emulate it ;)" tells
us the intended path is to bypass that check (emulate the chip), or — better —
recover the flag statically from the dump.
Pitfall (note for other agents): radare2's Xtensa plugin mis-sizes instructions
and desyncs the disassembly (spurious excw), making it unreliable here. capstone
5.0.3 ships no Xtensa module.
Working path: reconstruct an ELF from the 6 segments and analyze it with
Ghidra 12.0 headless, processor Xtensa:LE:32.
0x400d0020) must be force-disassembled at each
entry a1 function prologue (opcode byte 0x36), then mass-decompiled via
pyghidra.FUN_400d5cb0// enc = 31 bytes at DROM 0x3f407688 // (pointer literal _DAT_400d07d8 in IROM; app.bin file offset 0x7688) for (i = 0; i < 0x1f; i++) out[i] = enc[i] ^ 0x42; out[0x1f] = 0;
enc blob (31 bytes):
0a160039712f372e76362b2c251d2a351d2b311d31721d2172722e6363633f
FUN_400d5c04 / FUN_400d5c34FUN_400d5c04 computes the factory MAC (get_efuse_factory_mac) and memcmps
6 bytes against a hardcoded expected MAC, returning a bool. FUN_400d5c34
consumes it:
Key insight: the MAC gate only gates printing. It does not feed the
flag bytes — those come solely from the static enc blob and the XOR key 0x42.
So the flag is fully recoverable from the dump with no device and no emulation.
The decrypted plaintext (3mul4ting_hw_is_s0_c00l) confirms the intended path was
to emulate the chip past the MAC check.
0x42)python3 -c "enc=bytes.fromhex('0a160039712f372e76362b2c251d2a351d2b311d31721d2172722e6363633f'); print(''.join(chr(b^0x42) for b in enc))"
HTB{3mul4ting_hw_is_s0_c00l!!!}
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar