Author: 4rbit3r
Hello, this was a nice challenge from InCTF 2017, let’s see how to get the exploit working.
As usual, running checksec on the binary gives:
Nothing much here, let us look at the program itself. It is a menu driven program with some basic functionalities:
- Add note
- Edit note
- Remove note
- View note
These notes are stored as pointers in a global array named table
and the size for each corresponding note is stored in another global array, (stored above table in memory) named size
.
If we take a look at the code, we’ll see that a verify function is being called to check if the index requested by the user is less than 9 or not. If we look closely there, we see that it only checks for the upper limit. So we can enter negative numbers and see if that gets us anywhere. 😉
Now, first, we need the libc leak. We will try and leak the GOT. But how to do it? The view
function prints out the value at any address in the table array (by taking the index value), we can use it to print out the value at an address before the table array through the indexing vulnerability. To get a leak, we need to print the value at a GOT address. For that, we need to write the GOT address somewhere in our memory first and then call the view function with the corresponding index value. To write the GOT address, we can simply create a note of size, say, 0x602020 (the GOT of puts). This puts a GOT address in the array size
which is placed above array table
in memory. I used the GOT address of atoll (0x602050) for convenience. So, to sum up, to get a leak we can do the following:
- Make a note of size 0x602050 (the got of atoll). This value is now written to the address size[0] (if you chose the index as 0).
- Call the view function at index -12 (if the index that you chose earlier was 0), this makes a reference to table[-12] – which is essentially size[0], where the GOT address is written!
- Read the bytes printed out, that gives us the leak. 🙂
Now that we have the leak, let us work on executing the system function. What we can do for that is simply a GOT overwrite through, again, the indexing vulnerability. What we are going to do is this: call the edit function with the index as -12 again – which makes a reference to table[-12] ~ size[0], where the GOT address is written. Here, I had chosen to write the GOT of the atoll
function in order to be able to overwrite it with the address of the system function.
There is a problem here though. In the edit function, there is a call to get_inp with the arguments as table[index] and size[index] as given here.
In the get_inp (address, size)
function, the input is read into address till size bytes. This means that even though our table[-12] points to the desired GOT address, we also need size[-12] to point to some positive value. As it turns out, size[-12] points to a negative value (a libc address in the GOT table), but on looking around a bit we can see that size[-10] points to a PLT address which is read as a positive integer. Hence, we should make another note at index 2 with size as a GOT address like 0x602050 (atoll) and then call the edit function with index -10. This lets us edit the value at our GOT address. I overwrote the GOT address of the function atoll
with the address of the system function. Hence, for the GOT overwrite, the method goes:
- Make a chunk of a size of a GOT address at index 2.
- Edit the chunk at index -10. This calls the get_inp function with arguments as table[-10] (~ size[2], the GOT address entered by you) and size[-10] (~ a PLT address, which is a positive integer).
- Write the address of the system function here.
Now, calling the system function. If we look at the code, can see that the atoll
function is called within the add
function with an argument that is a pointer to a string that we give as input, so it seems to be a good choice to overwrite this function with the address of the system function. It is actually called within the function which asks the user to input a size for the new note, so we can just call the add
function and give /bin/sh
when asked for input size and voila! We have a shell. 🙂
Summing it up:
- Make a note of size 0x602050 (the got of atoll). This value is now written to the address size[0] (if you chose the index as 0).
- Call the view function at index -12 (if the index that you chose earlier was 0), this makes a reference to table[-12] – which is essentially size[0], where the GOT address is written!
- Read the bytes printed out, that gives us the leak.
- Make a chunk of a size of a GOT address at index 2.
- Edit the chunk at index -10. This calls the get_inp function with arguments as table[-10] (~ size[2], the GOT address entered by you) and size[-10] (~ a PLT address, which is a positive integer).
- Write the address of the system function here.
- Supposing you overwrote the atoll function, call the
add
function. - Enter
/bin/sh\x00
when asked for input size.
That gives the shell. Here is the script:
from pwn import * p = remote('127.0.0.1', 5656) p.recvuntil('>> ') p.sendline('1') p.recvuntil('index: ') p.sendline('0') p.recvuntil('size: ') p.sendline(str(0x602050)) # the atoi of atoll p.recvuntil('input: ') p.sendline('/bin/sh\x00') p.recvuntil('>> ') p.sendline('1') p.recvuntil('index: ') p.sendline('2') p.recvuntil('size: ') p.sendline(str(e.got['atoll'])) p.recvuntil('input: ') p.sendline('/bin/sh\x00') p.recvuntil('>> ') p.sendline('4') p.recvuntil('index: ') p.sendline('-12') leak = p.recv(6) leak = leak.ljust(8, '\x00') leak = u64(leak) system = leak + 58592 print hex(system) p.recvuntil('>> ') p.sendline('2') p.recvuntil('index: ') p.sendline('-10') p.recvuntil('input: ') p.sendline(p64(system)) p.recvuntil('>> ') p.sendline('1') p.recvuntil('index: ') p.sendline('1') p.recvuntil('size: ') p.sendline('/bin/sh\x00') p.interactive()
Let us know if you have any questions in the comments section below. Happy pwning! 🙂
Leave a Reply