Exploit Protection Bypassing
Detect Enabled Protections
You can use the checksec script from pwntools for ELF executables:
Return Oriented Programming
Finding ROP Gadgets
msfelfscan
Benefits:
Is more abstract, searches for things that get you the desired result instead of specific instructions
Drawbacks:
Has no option to only search executable memory regions afaik.
Doesn't work if you're on a 64-bit machine and trying to find ROP gadgets for a 32-bit binary
Installation
You need metasploit-framework:
Then you need to install the necessary ruby gem(s):
And after that, you should be able to use msfelfscan:
Usage
Useful flags:
-j
to look for jumps to specific places/things
For example, the following command looks for jumps to esp:
Warning: It will also display gadgets that are in non-executable memory segments.
You can use readelf
to see which memory segments are executable. Look for LOAD types with the E (execute) flag. For example:
In this case, a memory segment starting at 0x08049000 with a size of 0x6d444 is executable (ends at 0x80B6444).
Here's how you can filter out gadgets in a specific memory segment using awk:
ROPgadget
Benefits:
Knows to search only executable memory regions.
Drawbacks:
You need to use grep to filter out what you want. Therefore it only allows you to search by specific instructions, not by general goals.
You can use ROPgadget to print out all the ROP gadgets of a binary:
Pwntools
You could try to find gadgets like this (for example a push esp;ret
gadget):
But when I tried it, it didn't find the gadgets that msfelfscan and ROPgadget had found. So perhaps it's less reliable.
ROP Gadget Techniques
Write-What-Where
These are gadgets that allow you to arbitrarily write anything anywhere
ASLR without PIE
If the program is NOT a position-independent executable, then ROP gadgets will always be at the same location. Meaning you can construct a ROP chain to do what you want.
In other words, if the code of the application is always in the same place. Then you can return to some place in the .text
section which contains code you want to execute.
Return to stack
ASLR bypass where you return to the stack.
Prerequisites:
DEP/NX is not enabled (aka the stack is executable)
Stack canaries and other protections that interfere with what you're doing need to be turned off
You need to control data on the stack
This is true with stack overflows
You need a register to point to data on the stack that you control
This is true by default with stack overflows, because the stack pointer points to a place you control
But you might need to chain ROP gadgets to change the register to a suitable value
With the stack pointer, be wary of shellcode corruption issues. Shellcode corruption did not impact my exploit when I did this (though it did occur, just not in an important place)
The idea
Here's the idea (in this example, the register you use is the stack pointer):
You don't know what the address of your payload is on the stack, but the relative location of the stack pointer to your payload should always stay the same.
Therefore, you try to find a
jmp esp
(or similar, likepush esp;ret
) ROP gadget and jump to your payload on the stack.
Exploitation
I used this in Exploit Exercises Fusion Level01.
Just to test, I fuzzed the application with A's until I got a segfault. I stepped until the ret
just before the segfault and looked at the stack:
This confirms that I control data on the stack around the stack pointer. So if I can find a jmp esp
or push esp;ret
gadget, then I can jump to a place in memory that I control. After that, I'll be able to execute any code I want.
To find a jmp esp
, you can use msfelfscan:
After finding the address of the JMP ESP gadget, all I had to do was overwrite the saved return pointer with the address of the gadget, and I was able to jump to code that I controlled.
Note: You will probably land just after the address of the ROP gadget on the stack.
Overflown stack layout:
Filler space
Saved return pointer
Overwrite this with the address of the ROP gadget
Gadget return location
This is where the
jmp esp
ROP gadget will jump toAdd whatever code you want to execute here
Here's how to get the shellcode for a relative jump 0x20 bytes forward using command-line pwntools:
Ret2libc
ASLR + NX/DEP bypass. Also known as "return to libc", "return to text" (ret2text).
This is a return-oriented programming technique where you redirect code execution to a loaded standard library (usually libc). Usually this is done from a stack overflow.
Prerequisites:
Assuming ASLR is enabled, you need an information leak, so you know the addresses of libc functions.
Bruteforcing might also be viable, especially for 32-bit executables.
You might be able to create an information leak from the initial stack overflow.
Assuming you're using a stack overflow for this, stack canaries need to be disabled, as well as any other relevant mitigations.
Caveat: You don't need to have an information leak if you know the libc version, as demonstrated here.
I used this in Fusion level 2 and Fusion level 3. My solutions are available here.
For now, let's assume you're doing it from a stack overflow. Therefore, start by getting control of the stack pointer as normal.
Creating the Information Leak
Once you have control of the stack pointer, you'll need to leak a pointer to something in libc, like an address of a function. All the offsets in libc stay the same, even with ASLR, so if you can find one address in libc, you'll know them all.
Assumptions for creating the information leak:
The application's output (from puts, printf, etc) is sent back to you over the network.
A function that outputs user-controlled text (printf, puts, etc) is in PLT.GOT. This is true if that function is used anywhere in the application.
PIE is not enabled
Note: If assumption #1 is not true, then you'll have to figure out some other way of leaking an address. For example, if the application displays some text to you (like a chat message or whatever), then you could see if to use that functionality to write out an address. In Fusion level 3, you were able to use SSRF to leak the info.
The idea here is to create a ROP chain that calls puts (or a similar function) with the address of a PLT.GOT entry of a libc function (such as puts) as the argument. As a result, the address of the function will be leaked to you over the network (due to assumption 1).
Let's assume that the application uses puts. Since PIE is disabled (assumption 3), you can get the static addresses of puts@plt and puts@plt.got from the binary.
Here's how the overflow with the ROP chain might look like (taken from fusion2 solution):
As a result:
The value at puts@plt.got will be printed (along with a bunch of other junk probably).
Code execution will be redirected to return_address
In case the ASLR addresses don't change between executions (true for forked processes), you can have return_address be random junk. The process will crash, but the leaked address will be the same when a new process is forked, so you can move on to the exploitation part.
Otherwise, it makes sense to set return_address to the part of code where the vulnerability occurs (or just the main()
function). Assuming you can give input again, you'll be able to exploit the vulnerability again, this time with the knowledge of the leaked address.
Or, if you want to continue your ROP chain, just set return_address to a ret instruction.
Calculating libc addresses
Now that you've leaked an address to a function, you'll be able to use that to calculate the address of any libc component in memory (since the offsets in memory are the same as they are in the libc shared object file).
But, which version of libc is even used? Depending on the version, the offsets will be different.
Luckily, people have created databases of libc versions and their offsets that you can search. Since ASLR only adds an offset to any addresses, you can determine the version of libc used just by knowing one or two leaked offsets (this means you might have to leak more than one address to be sure of which version of libc is used).
More precisely, you only need the last 3 digits of the leaked offsets. And I think the last 3 digits stay the same, so you don't have to leak everything all at once if you don't want to.
Here's a good database with an example search. You can also download the libc files here, which is useful. In fusion2, the correct version was 2.13.
With this knowledge, you can calculate the addresses to any libc components, like this:
As you can see, the above code calculates the addresses for system()
, exit()
and the string /bin/sh
. You'll use these for exploitation.
Exploitation
Now that you have the leaked address, you can use it to pop a shell.
For simplicity's sake. this part assumes that:
The application's output (from puts, printf, etc) is sent back to you over the network.
Your input is read into the application.
This makes it so that when you do system("/bin/sh")
, then you can actually interact with that shell. But obviously, even if it's not that simple, you can execute arbitrary system commands. You'll just have to create a ROP chain to construct a suitable argument for the system call (like a reverse shell command).
Since you have all the addresses you need, all you need to do is to create a ROP chain that goes into system()
and calls /bin/sh
. To exit nicely, you can set the return address to be the address of the exit()
function.
Last updated