For this challenge we will use a global offset table (GOT) overwrite. If you are unfamiliar with the mechanics of the GOT check out this site.
The exploitation phase will be very similar to format3. We will use the direct parameter access method to overwrite address space with our addresses of interest. We will use a shortcut to simplify exploitation. The details of this are explained below.
GOT Overwrite Concepts
You may have noticed the call to the exit() function, which was added in this challenge. You also may have noticed that it is called directly after our format string vulnerability. You may have also noted the hello function indicating successful code redirection. Now if you understand the GOT, where I’m going with this is probably quite straightforward. If you don’t I strongly suggest you read this. I found it very helpful during my research. We will be overwriting the GOT entry for exit with that of hello.
Since this is the first call to exit in this executable normally the GOT would point back into the procedure linkage table (PLT), which would then call the init function which would in turn call the runtime link editor (RTLD). The RTLD then finds the address of the function and places it in the GOT. If this is confusing, you may also check out the section on GOT overwrites starting on page 157 of the Bug Hunter’s Diary.
However, in our case, we will overwrite the GOT entry pointing back into the PLT with an address of our choosing. In this way, once execution is transferred to the GOT we actually gain control.
Checking out the GOT
The first thing I did was to trace the GOT manually out of curiosity. I began by disassembling the vuln function and placing a breakpoint at 0x0804850f – directly before the call to exit.
I then ran the program and stepped into the call to exit. This landed me in the procedure linkage table.
From here we can examine the GOT.
Notice that the GOT points back into the PLT at the address of the push. In case you were wondering, the push 0x30 is an argument to the RTLD to help it determine which symbol needs to be loaded.
With this, we now know the entry in the GOT for exit is at 0x08049724. Because I wanted to explore the code I went about finding the entry the long way. You could have simply used objdump – R format4 to get:
You can see the address of exit on the left hand side at the bottom. Now we just need to know the starting address of the hello function. We can find this with objdump -t format4 | grep hello. Alternatively we could just disassemble the hello function and look at the first instruction.
The hello function starts at address 0x080484b4.
With this knowledge in hand we are ready to start our exploit.
Before I show the exploit, I would like to explain short writes. In our previous exploits we have always written things byte by byte. This has been fine, but as you no doubt discovered in format3 it is labor intensive. In our scenario here we can make things a bit easier.
A short is a two-byte word. We can use the h format string parameter in front of an operator such as %n to write two bytes instead of one. For example, we could do something like:
./fmt_vuln $(printf “\x42\x42\x41\x41”)PAD_NUMBERu%4$hn
Except we would use a real address instead of As and Bs. In this example we would overwrite the last two bytes of whatever is at address 0x41414242. Now you may have noticed after the PAD_NUMBER I have the letter ‘u’. This is to mark it as unsigned.
We can take a further shortcut for our exploitation phase. Note that the address of hello is 0x080484b4 and the address of exit is 0x08049724. Because they both start with the same 2 bytes, we don’t need to overwrite that. Therefore, we only need to do one write of value 0x84b4, which comes out to 33972 decimal. We are using short writes which could support a value up to 65536 so we can fit this all into one write. When you factor all this in you come up with:
The one thing I didn’t mention was the offset of 4. I used the same methods from the previous format string tutorials to determine the offset was 4. This does indeed work:
You get a lot of white space and at the bottom it should tell you that it worked.
To break things down for you, the “\x24\x97\x04\x08” is the address of exit in the GOT. The %33968u is 0x84b4-4, which is the second half of the address of the hello function. We subtracted four because of the four byte address that came before that also counts towards our %n. The u notes that the value is unsigned. The %4$hn is a direct parameter access at location four which writes a 2 byte value into the lower half of the four byte value stored at 0x08049724.