strvec implemented a vector (array), which can store and return entries. On start, it will ask for a name (which will be put on the stack), and for the size of the array to use.
I joined a bit late and n0psledbyte was working on it and pointed out, that passing a size of 0x20000000 will lead to an integer overflow in
since size is defined as an int, the multiplication will overflow size and create a smaller chunk than expected. But the specified size nmem will be sotred in vec->size.
Definitely something to work with. With this we can create a vector chunk and access data behind that chunk on the heap.
So, let’s create a corrupted vector and one entry
The vector chunk is only created with a size of 0x7a0 but vec->size shows, that we can use indices up to 0x00000000200000f2, so we can now also create and read entries outside of this vector.
To get a heap leak, we’ll create another entry and choose the index so, that the entry pointer will be put into our first chunk.
The next entry will be allocated after the first entry and the pointer to it will be stored inside the first entry, so we can just read it from there.
Next, we need a libc leak, and we already got a chunk on the heap, which won’t fit into tcache…
The vector itself… So, we can just create a new entry, in which we store a pointer to the vector and then free it via the oob index access.
After freeing the vector:
Now, we can create a new entry, which will be put into the just freed vector chunk (overwriting vector->size), and moving the libc addresses further down the heap, so we can access them again via a valid index
Similar to the heap leak, we can now write a pointer to 0x5555555592d0 on the heap and read it via oob access.
We just have to take into consideration, that this will create a new chunk (0x31) in the current freed bin, so the address we want to leak will then be at 0x555555559300.
Since we were asked to create a name on the stack in the beginning, I was quite sure, that we were meant to create a fake chunk on the stack and use that to get rip control.
At the beginning, I had put a 0x31 fake chunk size in name for preparing this. But to be able to free a chunk on the stack, we first need a stack address. Well, since we now have heap and libc leak, we can use the same to read environ from libc.
Though we cannot directly use this to create a chunk on the stack, since the binary is using a canary. But, same as before, we can just leak it (just have to “misalign” it by 1 byte, since the LSB of the canary will always be 0x0).
I have already put the address to our “fake chunk” on the stack on the heap, so now we can just free it.
Memory before freeing the stack note
and after
With all the leaks at hand, we can now allocate the stack chunk, overwrite canary, rbp and return address of main, but since our chunks can only be 0x20 big, we cannot do a real ropchain here (can only put one gadget there).
But well, we can put a heap address into rbp and overwrite the return address just with leave, which will pivot the stack into the heap, where we can have a prepared (slightly bigger) ropchain :)
Exiting the challenge now will crash though, since system will add a bigger stack frame and rsp will access unmapped memory outside of heap. So, let’s just allocate “some” more notes to get our ropchain a little bit further down the heap.
With this, we’ll have enough space above our ropchain, so this should no longer be a problem.
Only one thing missing…
Exiting the challenge will try to cleanup the vector (by calling vector_delete, which will iterate through the array and try to free every entry)… which will most likely fail completely and crash…
So, you could either try to cleanup your vector manually, so all the frees will succeed or you take the easy way out :)
Just reallocate our initial entry pointing to vector again. This will effectively set vector->size to 0x0, thus vector_delete will not try to delete any entry chunk anymore and happily continue execution.
Now we can just exit the application, which will trigger our initial ropchain, which will pivot into the ropchain on the heap and execute system("/bin/sh").