Photo by Harrison Broadbent / Unsplash

Writeup: Hello-ESP - EspilonCTF (ESP)

Feri Harjulianto

Challenge Overview

FieldDetails
NameHello ESP
CategoryReverse Engineering / Firmware
PlatformESP32 (Xtensa LX6)
Flag FormatESPILON{...}

Flag

ESPILON{st4rt_th3_w1r3}

Files Provided

FileSizeDescription
bootloader.bin26,752 BESP32 bootloader
partition-table.bin3,072 BPartition table
hello-espilon.bin178,848 BMain application firmware

Solution Walkthrough

Step 1: Initial Recon

Extracted HELLO-ESP.zip to obtain three .bin files and a README with flashing instructions. Running strings on hello-espilon.bin revealed key output messages:

Welcome to ESPILON ctf new category ESP have fun!
=== Hello ESP ===
System ready.
Encrypted flag:
XOR Key:

This indicated the firmware prints an XOR-encrypted flag at runtime.

Step 2: Binary Structure Analysis

Parsed the ESP32 application image header (magic byte 0xE9):

SegmentLoad AddressSizeContent
00x3F40002037,932 BDROM (read-only data)
10x3FFB00008,988 BDRAM (initialized data)
20x4008000018,592 BIRAM (code)
30x400D002079,920 BIROM (flash code)
40x400848A033,304 BIRAM (code)

Step 3: Locating the Encrypted Data

Found the format strings in DROM (Segment 0) at file offset 0x30E0. Searched for 32-bit cross-references to these string addresses in the code literal pool:

Literal Pool OffsetPoints ToContent
0x106DC0x3F40307CWelcome banner
0x106E80x3F4030D0"System ready."
0x106EC0x3F4030E0"Encrypted flag: "
0x106F00x3F407570Encrypted flag bytes
0x106F40x3F4030F4"%02X " (format string)
0x106F80x3F4030FC"XOR Key: "
0x106FC0x3F40756CXOR key string

Step 4: Extracting Key and Ciphertext

XOR Key at file offset 0x756C:

4C 41 49 4E  ->  "LAIN"

Encrypted flag at file offset 0x7570 (23 bytes):

09 12 19 07 00 0E 07 35 3F 35 7D 3C 38 1E 3D 26 7F 1E 3E 7F 3E 72 34

Step 5: Decryption

Applied repeating-key XOR with key "LAIN":

enc = bytes([0x09,0x12,0x19,0x07,0x00,0x0e,0x07,0x35,
             0x3f,0x35,0x7d,0x3c,0x38,0x1e,0x3d,0x26,
             0x7f,0x1e,0x3e,0x7f,0x3e,0x72,0x34])
key = b"LAIN"
flag = bytes([enc[i] ^ key[i % 4] for i in range(len(enc))])
# Result: ESPILON{st4rt_th3_w1r3}
IndexEnc ByteKey ByteXOR Result
00x09L (0x4C)E
10x12A (0x41)S
20x19I (0x49)P
30x07N (0x4E)I
............

Tools Used

  • Python 3 (struct, byte manipulation)
  • stringsxxd (initial recon)
  • Manual ESP32 image header parsing

Key Takeaways

  • ESP32 firmware images use magic byte 0xE9 and contain multiple segments (DROM, DRAM, IRAM, IROM)
  • Xtensa architecture uses L32R literal pools for loading constants, making cross-reference tracing straightforward
  • No physical hardware or flashing was needed; static analysis of the binary was sufficient to recover the flag
CTFWriteupReverse