ret2plt
Using the PLT and GOT tables to bypass ASLR.
Last updated
Using the PLT and GOT tables to bypass ASLR.
Last updated
The ret2plt
exploit is one of the most popular exploits for beating ASLR. This exploit involves calling puts@plt
and passing the address of puts@got
as an argument. This will cause puts
to leak its own address in libc
.
Once we have this, we can set the return address to main
and call system
with the address of "/bin/sh"
as an argument.
Be sure that you've read the introduction page for ASLR. This is important for discussing the PLT and GOT tables, and the theory behind this exploit.
If we perform checksec
on this binary:
For this challenge, we'll find that it's essential that PIE is turned off. This is because we will rely on knowing the absolute addresses of the PLT and GOT tables to jump to them.
We'll scan through gdb
to check for vulnerabilities. There are two functions: main
and read_in
.
main()
does nothing more than call read_in
and then prints You lose!.
read_in()
has a gets()
call into ebp-0x30
. There's nothing else useful in this function.
This isn't much to go off. We'll have to understand the ret2plt vulnerability better to exploit this binary.
The ret2plt exploit is a bit more complicated than some of our previous ones. Recall that the PLT table is a list of stubs that point to the GOT table. The GOT table is a list of addresses that point to the actual functions in libc
.
We said in the introduction paragraph that when we call entries in the PLT table, it's identical to calling the library function itself. Because the PLT is a compile-time construct, we know its address at compile-time. However, the GOT table is a run-time construct, so we don't know its address until the program is actually running.
When ASLR randomizes the address space, it randomizes the address of the GOT table. Therefore, if we can somehow leak one of the values inside the GOT table, we can leak the address of libc
in memory. This is the key to beating ASLR.
The most common way to do this is to leak the address of puts
. We do this by passing puts@got
as an argument to puts@plt
. This will cause puts
to print its own address in memory. We can then use this address to calculate the base address of libc
in memory.
Why does this work?
Recall that the GOT table connects a function call to the actual function in libc
. We know the address of the GOT table during runtime, but we can't directly access the value that it points to.
When we pass parameters to a function, function arguments actually take a pointer to the value. Therefore, when we pass the GOT entry for puts
(i.e., puts@got
), the puts
function will dereference that pointer and provide us with the value that that address. This value is the true address of puts
in libc
!
Now, we need to do this in our exploit. We'll need to pass puts@got
to puts@plt
. This will leak the true address of puts
in libc
. Since we know the offset, we'll compute the base address and beat ASLR!
The pwntools library helps us with this exploit. This exploit will come in two parts:
Performing the ret2plt
exploit to leak the address of puts
in libc
, hence beating ASLR.
Performing a ret2libc
exploit -- calling system
with the address of "/bin/sh"
as an argument to spawn a shell.
Part 1: Leak the address of puts
in libc
To do this, we need to overflow the buffer, then call puts@plt
using puts@got
as an argument. If you forgot the structure of the stack frame when passing arguments in 32-bit, refer to the args challenge.
This payload will do the trick:
Why is there a elf.sym.main
in there? When we've done this in the past, we've used 0x0
as a placeholder for the return address. This time, however, the return address is important. We don't want the program to crash after we've leaked the address. By returning to main
, we can run the program again, knowing the base libc
address, and perform a ret2libc
exploit.
Once we've done this, we'll send off the payload. We'll need to receive the leaked address and calculate the base address of libc
.
u32
performs the opposite of p32
. It converts a 4-byte string into an integer.
We've beaten ASLR! Now, we can perform a ret2libc
exploit.
Part 2: Spawn a shell
We'll need to call system
with the address of "/bin/sh"
as an argument. The return address doesn't matter this time, so we can use 0x0
as a placeholder.
Then, we send this off and ask for an interactive shell!
This will give a shell on the remote system! Here is the full exploit: