In this section, we will take a closer look at the Global Offset Table. In the previous section, we learnt how to use jumping to the PLT stubs as a technique to reuse functions in libc. When jumping to PLT, the GOT entry for that corresponding function acted as a malleable space where the dynamic address would be held. We shall exploit that malleability.
Now, let's depart from the standard paradigm of stack overflows for the moment. We shall begin looking at vulnerable programs that allow for write-what-where primitives, albeit in a limited fashion.
Our first simple example is the following:
The program is vulnerable in two ways:
It provides an information leak opportunity when the now_playing.album
pointer is overwritten and the album name is printed.
It provides a write what where primitive when the now_playing.album
pointer is overwritten and input is provided to the second prompt.
Running the binary:
It's a little screwy but nothing that fancy yet. Let's begin by trying to achieve the first vulnerable condition (arbitrary read). First, we can take an easy to spot target to leak. We can use the "This is a Jukebox" string. First, we need to figure out its address.
Now, here's a skeleton exploit that will demonstrate the leaking of that string.
Testing it out:
See the "(This is a Jukebox)"? That means it worked. Now, what we are most interested in are mostly pointers. So let's make a small addition that would parse the leak and transform it into a nice number for us.
Running it:
Awesome, now we can begin thinking about our exploit.
At the moment, we do not have a target to leak and to overwrite. We must be careful to pick a suitable one because the information leak and arbitrary write has to be performed on the same address. Additionally, the write has to result in EIP control at some point of the program's execution since we do not have that yet.
If we take a look at the source code again, the following function is called last:
Interestingly, this is perfect for our uses. If we leak the address of puts in libc, we can calculate the address of the libc base and subsequently, the address of the system function. Also, once we have that, we can write the address of the system function into the puts@got entry so that when this final line executes, it will actually execute:
Which means that system will be called with a parameter that we control! How convenient!
First, let's see if we can leak the address of puts@got. First, we need the address.
Now, we can modify our earlier iterations of the exploit.
Running the script gives us a sanity check that we are reading the right thing.
Now, let's try and get EIP control. This should be as simple as sending four bytes.
It works, the program crashed at 0xdeadc0de
.
Let's gather our offsets and we can write our final exploit script.
Final exploit script:
Getting our shell:
After the mistakes of the previous Event, Kaizen has decided to secure his system. Can you find a way to exploit the new binary?
It was compiled with a stack canary.
gcc -m32 -znoexecstack -o ./build/2_event1 ./src/2_event1.c
The binary can be found here and the source can be found here. The remote target is nc localhost 1902
.
The solution script may be found here.