← Back to Writeups
CUSTOMEasyReversing

magic words

XESXOR9/2/20263 min read
#reversing#custom#easy#magic_byte_replacement#upx_magic_repair#upx_unpack

magic words

Platform: CUSTOM | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: magic_byte_replacement, upx_magic_repair, upx_unpack

Summary

Walkthrough of the CUSTOM Reversing magic words. Task: statically-linked ELF packed with UPX 3.96 that refuses to unpack (NotPackedException). Solution: the UPX! magic bytes were replaced with the ASCII 'magic word' 2021; restore all occurrences (l_

Description

Only one magic word and the flag is yours!

English summary: A single file upx1.upx is given. It is a statically-linked x86-64 ELF that is clearly UPX-packed, but the standard upx -d refuses to unpack it. The goal is to repair the packed file, unpack it, and read the flag.

Analysis

Recon:

bash
file upx1.upx
# ELF 64-bit LSB executable, x86-64, statically linked, no section header

strings upx1.upx | grep -i upx
# $Info: This file is packed with the UPX executable packer ...$
# $Id: UPX 3.96 Copyright ...$
strings upx1.upx | grep -i magic
# Feel the magic in

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

So the binary was packed with UPX 3.96, yet:

bash
upx -d upx1.upx
# NotPackedException: not packed by UPX

This is the classic UPX anti-unpacking trick: the UPX! magic bytes (hex 55 50 58 21) were overwritten so the loader no longer recognizes the file as UPX-packed.

The task title "magic words" and hint "Only one magic word and the flag is yours!" point directly at the UPX magic bytes. A healthy UPX ELF carries the UPX! magic in two places:

  • the l_info header near the ELF header, and
  • the tail PackHeader (repeated twice at the end). ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Scanning the file, the only literal UPX! present (offset 0x45481) is coincidental data inside the compressed stream (its version byte 0x75 is invalid). The real magics had been replaced with the ASCII string 2021 (hex 32 30 32 31) — a thematic "magic word" (the year replaced UPX!).

Effective magic locations to repair:

LocationOffset
l_info magic0xec
tail PackHeader magic #10x4be7a
tail PackHeader magic #20x4be84

After restoring the l_info magic, the following fields become valid, confirming the repair: at 0xf0 l_lsize=0x0968, l_version=0x0d (13), l_format=0x16 (22 = linux/amd64 elf64); p_info filesize/blocksize = 0xd5340. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Important: fixing only the l_info magic at 0xec is NOT enough. UPX still refuses until the two tail PackHeader magics at 0x4be7a / 0x4be84 are also restored.

Solution

Replace every occurrence of 2021 with UPX!, then unpack:

python
#!/usr/bin/env python3
# repair.py — restore corrupted UPX magic bytes
data = open('upx1.upx', 'rb').read().replace(b'2021', b'UPX!')
open('upx1_fixed2', 'wb').write(data)
bash
python3 repair.py
upx -d upx1_fixed2 -o upx1.unpacked
# 873280 <- 310952  35.61%  linux/amd64  Unpacked 1 file.

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The unpacked binary is not stripped, so the flag sits in .rodata right next to the teaser string:

bash
strings upx1.unpacked | grep -i -A1 'magic'
# Feel the magic in the air
# spbctf{REDACTED}
# flag1.c

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Takeaways / Lessons

  • Mastering magic_byte_replacement, upx_magic_repair, upx_unpack was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-07-05 • CUSTOM • magic words • Difficulty: Easy