This post is about a new tool I’ve been working on, rop_tools. This is a tool for working with rop gadgets in ELF binaries (currently only 32-bit). But first I’ll talk about the reasons why I wrote the tool.
I was recently working on exploiting a binary in linux. This binary was small, and I needed all the gadgetry I could find. I turned first to msfrop, but it wasn’t hacking it. I needed to look 2, 3, 4, any arbitrary number of instructions back. I was interested in not just ret gadgets, but gadgets that jump to registers and call registers.
So I wrote my own tool, rop_tools. I’m not going to run against my target binary for this post, but we’ll run against the ravm assembler which comes in at 28k. Here are some examples:
rop_tools arguments
./rop_tools rop_tools brought to you by rainbowsandpwnies ./rop_tools [-cjr] [-d depth] (-e| -l depth, in instructions, to search backwards -e filename of elf to analyze -j search for jmp reg gadgets -k search for conditional jmp reg gadgets (for when your day is really going that bad, and probably won't return anything) -l runs lua script -r search for ret gadgets
Simple ret gadgets
./rop_tools -e ~/sigsac/ravm/assembler -r | tail section: .fini _fini + b 0804a177: 00 5b 81 -- -- -- -- -- add [ebx-0x7f], bl 0804a17a: c3 -- -- -- -- -- -- -- ret _fini + 1a 0804a186: c9 -- -- -- -- -- -- -- leave 0804a187: c3 -- -- -- -- -- -- -- ret 65 gadgets
ret gadgets with a depth of 3
./rop_tools -e ~/sigsac/ravm/assembler -r -d 3 | tail -n 16 0804a169: c3 -- -- -- -- -- -- -- ret section: .fini _fini + 18 0804a184: 59 -- -- -- -- -- -- -- pop ecx 0804a185: 5b -- -- -- -- -- -- -- pop ebx 0804a186: c9 -- -- -- -- -- -- -- leave 0804a187: c3 -- -- -- -- -- -- -- ret _fini + 15 0804a181: e5 ff -- -- -- -- -- -- in eax, 0xff 0804a183: ff 59 5b -- -- -- -- -- call dword far [ecx+0x5b] 0804a186: c9 -- -- -- -- -- -- -- leave 0804a187: c3 -- -- -- -- -- -- -- ret 67 gadgets
Why 67 gadgets for depth 3 when depth 1 only returned 65? If you look at these last two rop gadgets, you’ll notice ret is in the same place, but we can get gadgets of depth 3 (we don’t count the ret in depth) by going 3 bytes back from the ret or 6 bytes back from the ret. We can’t afford to miss gadgets when all our libraries start out ASLRed!
Showing the jmp reg and call reg gadgets
./rop_tools -e ~/sigsac/ravm/assembler -j -c | tail -n 20 08049460: ff e9 -- -- -- -- -- -- jmp ecx lexer + 3b5 08049495: ff 89 95 3c ff ff -- -- dec dword [ecx+0xffff3c95] 0804949b: ff e9 -- -- -- -- -- -- jmp ecx frame_dummy + 1d 0804874d: 04 08 -- -- -- -- -- -- add al, 0x8 0804874f: ff d0 -- -- -- -- -- -- call eax __do_global_ctors_aux + 19 0804a159: eb 04 -- -- -- -- -- -- jmp 0x6 0804a15b: ff d0 -- -- -- -- -- -- call eax __do_global_ctors_aux + 18 0804a158: 83 eb 04 -- -- -- -- -- sub ebx, 0x4 0804a15b: ff d0 -- -- -- -- -- -- call eax section: .fini 6 gadgets
A note on performance (performance is important). All ret, jmp reg and call reg gadgets from libc
time ./rop_tools -e /lib/i386-linux-gnu/libc-2.13.so -r -j -c | tail __libc_thread_freeres_fn + 1222b7 001222b7: fd -- -- -- -- -- -- -- std 001222b8: ff e8 -- -- -- -- -- -- jmp eax __libc_thread_freeres_fn + 1222bc 001222bc: fd -- -- -- -- -- -- -- std 001222bd: ff e8 -- -- -- -- -- -- jmp eax 9598 gadgets real 0m4.472s user 0m3.920s sys 0m0.232s
That’s enough of that, let’s talk about the GOT. In this binary I was exploiting, I needed to make a call to setreuid(). If I could add/subtract to the GOT, I would be golden. However, I could only add/subtract 8-bit values. This… this was an issue. I didn’t have infinite size for my rop chain, and I needed to add some pretty big offsets.
The next step was find a rop gadget with the following criteria:
- add [some_register], some_other_register
- Was not far from the address of a function that would be loaded into the GOT of my target binary
I could then modify the GOT to give me access to the ROP gadget I needed, ret to an address in the PLT to execute my target gadget, and then use that target gadget to add a 32-bit value to the GOT. rop_tools didn’t support the finding of these gadgets in libc… yet.
So I added lua scripting to rop_tools, wrote a script, and found exactly what I needed:
sprintf -00000095 0004785b add [ecx-0x1], ebx 0004785e dec ecx 00047860 ret
Now all I need to do is subtract 0×95 bytes from the sprintf GOT entry and I’m golden. How automated is this process? Here are the variable you set to fire off the script:
ROP_OFFSET_SIZE = 256
DEPTH = 2
-- set TARGET_MNEMONICS to nil if you want to show all possibilities
TARGET_MNEMONICS = {"add", "sub"}
LIBC_FILENAME = "/lib/i386-linux-gnu/libc-2.13.so"
TARGET_FILENAME = "/path/to/target"
I would call that pretty automated. If you find this helpful, let me know. I’ll continue to add scripts to rop_tools as I find a need and write them.