groundzero
Beating ASLR with a provided leak.
Last updated
Beating ASLR with a provided leak.
Last updated
This binary, we will be beating ASLR using a provided leak. This will be very similar to gimme, but in this case, we are beating ASLR instead of PIE.
To bypass ASLR, we need the address of a function inside the library. This means the function must be a PLT function rather than one defined inside the binary. Common examples of functions to leak are system()
, printf()
, or puts()
.
Let's start with running checksec
:
We notice that ASLR is not part of this description! This is because the binary does not know that ASLR is turned on because the binary is not affected by ASLR. Only the libraries are affected by ASLR.
The direct consequence of this is that we will never know if ASLR is turned on for a remote server. If we write an exploit that ignores ASLR and it doesn't work, we should assume that ASLR is probably enabled, and we will need to leak the libc
base address to fix our exploit.
We also notice no canary, meaning stack overflow exploits are possible. PIE is enabled, but this won't be problematic (be sure you understand why!).
Let's step through gdb
and see what we can find.
We first notice there is no win()
function. However, system@plt
is in the binary, meaning we'll most likely need to call that ourselves. We did something like this in the ROP challenges. We will need an argument to pass to system()
, which is usually either /bin/sh
or cat flag.txt
. If we check for either of those already being in the binary:
/bin/sh
is in memory! This means we just need to pass its address into system()
and we'll achieve a shell.
Now that we have the attack vector inspiration, we must execute it. We need a way to leak the libc
base address so that we know the address of system()
and "/bin/sh"
. Then, we need to overflow the buffer and call our system()
function.
If we check read_in
, there is a call to printf
. Before this call, there are two items pushed on the stack:
If we check the data that is in the top two entries of the stack:
We see that this print statement is printing the address of system()
! Since this function resides in the libc
binary, this means that we can use it to leak libc
.
After this, there is a gets()
call that has us writing to ebp-0x34
:
This gives us everything we need to write our exploit. We have a way to leak libc
, then a way to overflow the buffer.
There are two ways to write the exploit.
Get all the offsets from gdb
and use those to get the base address, then find the offset of /bin/sh
and use that as the argument.
Get pwntools to do it all for you and save the hassle of collecting all the information yourself.
We're going to go with the second option. The first one is a good challenge to the reader to test their mastery of gdb
and writing exploits. The second one will mimic the way we wrote PIE exploits.
We'll start by defining our binary and process. We must define a variable called libc
(or something similar) that's valued at elf.libc
. This will let us set our base address once we leak it.
This is the same way that we use elf.address
to define the base address before we called any functions.
From here, we'll collect the leak from the output and set the base address. When we do this, the functions and addresses will be based on libc
rather than elf
, because that is the binary being randomized.
I recommend getting in the habit of printing leaks when they happen so you can verify that your code is working. Then, you can verify the accuracy of the leak.
From here, we'll write the exploit.
We can get the address of system
using libc.sym.system
.
We can get the address of /bin/sh
using next(libc.search('/bin/sh'))
. Why does this work? libc.search()
returns a generator object of all the addresses of the string /bin/sh
. This is Python's equivalent of an iterator. To get the first address, we can use next()
to get the first object.
Finally, we'll send the payload and drop into an interactive shell.
If we run this, we'll get a shell, and we can call cat flag.txt
to get the flag.
This process is also known as a ret2libc
. The ret2libc
exploit involves getting the address of a function inside the libc
binary (we almost always pick system
), and then jump to that function. The most common form of the ret2libc
exploit is to jump to system()
and pass /bin/sh
as the argument. This is what we did in this exploit.
Since ASLR was enabled, we had to beat ASLR and then perform the ret2libc
. ASLR is not always enabled for it to be a ret2libc
exploit. If ASLR is not enabled, we can just jump to system()
and pass /bin/sh
as the argument.
Later on, we'll introduce another technique called the ret2plt
, which beats ASLR but jumps to a location on the PLT table instead of the function itself. This is useful when we don't have a leak, but we know the address of a function in the PLT table. We'll cover this at the end of the ASLR section.