The binary turns out to be a “ticket vending machine”
So, we can reserve a ticket, view the reserved tickets and cancel them. We’re also able to define a name (which can be changed by doing a logout and login again).
Let’s start with leaking some addresses here.
Obviously, the allocated space for the comment doesn’t get zeroed out, before reading our comment, so this can be used to read some data from the heap.
For leaking heap addresses, we just have to create some comments, which will be stored in fastbin chunks and then cancel our tickets, which will put the comment chunks into the fastbin list and populate their FD pointer with the address for the next chunk.
This will create four chunks. One for the ticket “header” and one for the “ticket comment” per ticket.
By cancelling those tickets, the allocated chunks will be freed and put into the fastbin list.
So, when we now allocate a new ticket with a comment length of 0, it will first allocate a chunk for the ticket “header” (Fastbin 1) and then another for our comment (Fastbin 2). But since getnline checks for the length of the data to read, it won’t read anything and just lets the data currently at that address untouched (which happens to be the FD pointer of Fastbin 2).
Thus, we can now just read the confirmation of our created ticket and the comment will be the FD pointer of Fastbin 2.
We can now do the same thing to leak a pointer to main_arena. The only difference is, that we create bigger comments, so our comment chunks won’t get put into the fastbin list, but into unsorted bin list, thus populating the FD pointer with a pointer back to main_arena.
But the idea stays the same, by reserving a ticket with comment length 0, we’ll get an existing chunk without changing its content, thus reading the stored pointer from there.
Ok, with the leaks out of the way, we need to think of a way to corrupt the heap.
Let’s take a look at the cancel function
cancel fails at checking the boundary of the list array. It reads the index from the user, then subtracts 1 of it and uses this as the index of the element to free.
By this, it’s possible to free list[-1], so let’s take a look, whats just before the list array in memory.
As in many other challenges, which let us enter a name, it’s pretty obvious, that we could do some mischief with it, and here it is.
Since the name is directly aligned with our list, we could specify a name long enough to fill up the entire struct and thus put an arbitrary address directly before the list array, which we can then free.
Let’s put something useful into our name now
Now we have a fake chunk in our name object, and an address point to it at list[-1].
At name+32 we’ll put a pointer to a fake comment chunk on the heap, which doesn’t exist by now, but will be created by our next reservation (We have to create it after the logout, because it would have been freed already otherwise)
For that we now just create a big comment, and inside of that comment we forge another fake chunk
So, now we constructed a fake chunk on the heap (at 0x603220) and we have our fake chunk in the name object, whose comment pointer points to it.
We’ll now free our fake name chunk.
As we’ve seen in the cancel function, this will first free our fake comment chunk (putting it into 0x71 fastbin list) and then frees our fake ticket chunk in the name chunk (putting it into 0x21 fastbin list).
But we’re not able to manipulate the content of that fastbin (yet).
So let’s just free the big chunk we created around it and recreate it.
Since we created another comment of the size, we just freed, it will overwrite the same data as our previous big chunk, and since that contained our fake chunk we’ll also be able to overwrite the freed fake chunk, thus overwriting its FD pointer.
We’ll be using that, to overwrite __malloc_hook by putting a misaligned address there, pointing to the highest byte of __memalign_hook (which will be 0x7f, thus tricking malloc into thinking that would be a valid fastbin chunk. See BabyHeap2017 from uafio for more details on this).
So, now everything’s prepared for our finale. We now just allocate another comment chunk with a size around 100, which will then get our chunk at 0x63210 served from malloc, which will then put our fake FD pointer (MALLOC_HOOK_TARGET) into the fastbin list.
The next allocation for a chunk with a size around 100, will now be served with a chunk overlapping __malloc_hook, so we can use that comment to overwrite it with something useful (like an one gadget).
The next time, malloc tries to allocate memory, it will execute the function in __malloc_hook, resulting in executing our one gadget, triggering a shell. So all we have to do now is to allocate some memory, by reserving another ticket