SecureKeyManager will let you create/edit/remove keys. The show method doesn’t really help much, since it only shows the count of created keys, but not their content.
So, let’s take a look at the remaining functions before getting into the exploit.
The key itself will contain 32 characters for a Title and the rest of the allocated chunk will be used for the Key. For readabilitys sake, we’ll use this pseudo struct.
The add method will allocate the space needed to store the key, and ask for a title and the key itself.
It fails on validating our input for key length. We can enter a negative length, forcing malloc into allocating a chunk with size 0, which will mean doom for the binary later on. Also the allocated space doesn’t gets initialized, so we might reuse the containing data or overwrite existing data with the Title or Key.
Nothing special here. Since it checks for malloc_usable_size and subtracts the title length here, we won’t be able to write a key, if the chunk doesn’t provides enough space for it. But we could use this, if we’re able to create a chunk in a somehow more interesting place to overwrite data.
The remove function frees the allocated space, but doesn’t clear the key_list entry for it. It does indeed zero out the corresponding byte in key_map, which is used to validate, if a key is in use or not.
The main vulnerability in this binary for me was the fact, that the entered length for the key doesn’t get validated, thus allowing us to pass a size of 0 to malloc by specifying a key length of -32. This will force malloc into allocating a chunk with the minimum size of 0x21.
So, let’s see, what could possibly go wrong in the add_key method by doing this.
Since add_key now adds 32 to it, for reserving space for the Title, this will result in malloc (0). The reserved chunk will look like this
There are only 24 bytes available for our chunk, so what happens when we now enter the allowed 30 bytes (getnline decreases the input size, to make sure we don’t enter too much here ;))
So, we’re able to overwrite the chunk size of the following chunk. This is good enough to make this binary stall badly :)
Our attack plan for this will be
Corrupt the chunks on the heap to create an overlapping freed chunk
Force malloc into giving us a chunk overlapping the key table in the bss
Specify a fake chunk in the got table, letting us overwrite the got entries
Overwrite atoi with printf
Leak everything
Overwrite atoi with system
Enjoy shell
Quick setup for communicating with the service:
This will do the login for the binary and it will also prepare a fake chunk size in the login credentials 0x71, which we’ll be using later to overwrite all the stuff in the bss.
After login, the bss part will look like this:
Now, let’s forge the heap for our needs.
We’ll create a chunk with size 0x0 first. Then two fastbin chunks and a blocker chunk to avoid consolidation on free (the blocker chunk isn’t really needed, but makes it easier to understand and explain, what’s happening on the heap. It would also work without the blocker chunk, but we would be taking chunks from the top chunk then instead of unsorted bin list).
This produces the following heap structure
Now, we’ll just remove our first chunk, which will place it into the fastbin list and recreate it. malloc will see the chunk in the fastbin list and serves it again to reuse it.
With this, we’ll overwrite the size of Chunk 2 with 0xe1, tricking malloc into thinking, this chunk would contain all the data between 0x603030 and 0x603100. The size must be forged this way, that it creates a valid chunk (this is fulfilled here, since it takes up the space between chunk 2 start and Chunk4 start)
When we now free chunk 2, it will see the chunk size of 0xe1 and create an overlapping freed chunk in unsorted bin list, just waiting to get served again.
Now, we’ll free chunk 3 also. Since it has size 0x71, it will be put into fastbin list, letting bin list untouched.
Now we can create another key with a size matching the one of chunk 2. malloc will then serve us chunk 2 again, since it’s on top of the bin list, which is overlapping chunk 3, sitting in the fastbin list.
The chunk, that’s created now, overlaps chunk 3 and overwrites its metadata (and its FD pointer)
By this, we now have a fastbin chunk, whose FD pointer points to the fake chunk, we created in the beginning in our account object. Adding another key with size 0x71 will put this FD pointer into the fastbin list, and the next key, which will be added, will be created on top of the key list array.
Adding this key will overwrite the top portion of the bss structure with the Title in payload1 and the bottom part with Key in payload2.
The bss will now look like this
Our first key now points to 0x602030 which is inside the got table
If we now try to edit Key 0, edit_key will call malloc_usable_size on 0x602030 resulting in an allowed size of 0x4006b0, which should be enough for us to overwrite everything we need :)
Since we have no leaks by now, we’ll use this to replace atoi with printf. So everytime the menu handler tries to convert our input to an integer, it will instead call printf with our input, creating a format string vulnerability.
For calling something in the menu from now on, we’ll have to consider the fact, that after overwriting atoi with printf, the menu handler will no longer be able to convert our input into an integer. Though, printf will return the count of characters printed out, so we can still select 3 for examply by just printing 3 characters instead.
So, we’re on the finish line, let’s leak libc address
With libc on hand, it’s just another overwrite, replacing atoi with system
atoi now points to system. So everything, we now enter in the menu handler, will be passed to system instead of printf.