This article should be pretty easy with what we learned last time but we could end up being surprised. I’ll try to exploit the same vulnerability, this time using ROP rather than ret-to-libc.
What is Return-oriented Programming (or ROP)
Return-oriented programming is an exploitation technique relying on the program’s own code to execute arbitrary assembly instructions. The general principle is that you choose memory addresses of a binary in the .text (or code) segment of a program ending with the instruction “ret”, meaning it will execute the instructions then move the stack and execute the next address present on the stack. These chosen memory addresses will be called gadgets.
Exploitation layout example:
[Buffer][gadget1][gadget2][gadget3]
Gadget example:
xor eax, eax; ret;
This technique have a lot of advantages and is still used in some recent exploits. First, it is relying on the program own code, meaning it can be used as a way to bypass NX, because this section even if it is not writable will be executable. It will also bypass ASLR, since ASLR won’t randomize the program code address. However it is defeated by PIE, because PIE will randomize the base address of the program, however we could still defeat PIE if we have an address leak of the program (for example with an arbitrary read or a format string or example).
Methodology change
There’s some change from the exploitation method of ret-to-libc obviously. This time we won’t rely on libc addresses, because those addresses are randomized. We could still use ret-to-plt/got if we have interesting functions being used in the program but it is not always the case.
What will be done rather, is that we are going to use system calls using the instruction “int 0x80;” (in x86) or “syscall” (in x64), to execute interesting calls, generally “execve” which will be used to execute system commands. To choose the system call, you’ll have to control the register eax, because each syscalls have a different “id” and when executing int 0x80, it will check the current value of eax. We can find a syscall table here for example.
To do an execve we’ll need to set eax to 11, ebx will be the program to execute, ecx is the arguments and edx the env. We are also going to need some way to write data and get its value.
What we are going to need
To exploit our ropchain we will need:
- One address of .data we will increment depending on our payload to mov data into (program name, args and env)
- int 0x80; as stated previously
- One mov to a pointer
- A way to control eax (first set it to zero, for example with xor, then a way to increment it)
- Some pop gadgets for ebx, ecx and edx at minimum, pop eax can be extremely useful as well
What did I learn today?
I knew most of what I was writing but there are still some elements I didn’t know since I never did a ropchain myself manually.
- To write to the stack and have its address we are going to use a static address of @.data to build our chain. Usually it is done by popping one address to an address we can “mov” information to.
- Differences between mov %eax, %ecx; and mov %eax, (%ecx); in the second case, it means we are going to put eax at the location of ecx. Using parenthesis or bracket means using a pointer.