So, it’s Mario, ready for some serious pizza cooking.
aegis and some other people already reversed most of the binary, when I joined the challenge, so I just went from there, picked up the available information and created an exploit for it.
When we order a pizza containing only “valid” ingredients and cook it, Mario will ask us for a declaration. It will then allocate a buffer with the length of the declaration we came up with. When the pizza is approved, this declaration buffer will get freed.
We can use this, to define how big the chunk should be, that gets allocated (and it also gives us some control over, where the chunk will be allocated on the heap, if we maintain an overview over the freed chunks on the heap).
But, when we order a pizza with pineapples on it, Mario will get angry at us and asks for an explanation, why we did such a criminal thing and ban us afterwards.
The trick here is to get to both code parts, such as cooking a criminal pizza, so Mario will be asking us for an explanation as well as cooking only valid pizzas, so Mario will free the declaraion buffer for us (so we can fill it with our explanation for the criminal pizza).
aegis pointed out, this can be achieved by cooking 16 pineapple pizzas and 1 tomato pizza:
If we now just back out, the declaration buffer will be freed (thus having a populated FD pointer at the start), and we’re able to leak it from the main menu, asking Mario, why he’s so angry.
Still, we would also like to know where libc is allocated. To achieve this, we’ll just create a criminal order again.
But this time, we only create a fastbin chunk size declaration, which will serve us a fastbin, that’s located above the current user chunk.
If we now use the cooking trick again, the declaration buffer will be freed again, but since we’re still able to write into it. And since we can always enter 300 bytes for the explanation, we can overwrite the user chunk on the heap with this.
So we’ll just be putting a pointer into the name, pointing to a libc address currently lurking around on the heap.
If we ask Mario now, why he’s offended, he’ll tell us, that our friend libc main_arena pointer has done something wrong:
Parsing this:
So, now we have all needed leaks to start doing something useful.
We can now use the cooking trick to actually write into the already freed buffers, overwriting their FD pointer to create a fastbin attack.
For achieving the chunks being allocated in the right order, I went this way:
Prepare a user with an offending order (don’t cook it by now)
Login with another user, order and cook an offending order
Use size 0x70 for the declaration, which will then get freed giving us a 0x70 freed fastbin
Write a misaligned address into the FD of this chunk pointing above stderr
If we use 0x7ffff783a4fd for overwriting the FD pointer of the currently freed 0x70 fastbin chunk, it will think that there’s a valid fastbin (with size 0x7f) on the next allocations.
Then, we’ll create another customer, which will just try to cook an invalid pizza (not criminal though) and also specifying a declaration of 0x70 size. This will allocate the currently freed fastbin chunk and putting its FD into fastbin list (which happens to be our misaligned pointer into _nl_global_locale).
Now, we’ll switch back to the user, which already placed his criminal order but didn’t cook it by now.
If we now cook this, the declaration can overwrite the stderr file structure. But we have to keep in mind, that we’re not allowed to put any null bytes there, since the binary will check the length of the declaration buffer with strlen first and only allocate that much space (so we won’t get that 0x70 buffer, if we used null bytes in it).
BUT in the explanation, why we cooked a criminal pizza, we’re allowed to just put 300 bytes there (no strlen or strcpy involved), so we can abuse that to get the final overwrite.
Cook the criminal order with a 0x70 declaration
This will now allocate a chunk overlapping stderr
The declaration buffer will get freed again
Put 0x70 As there via explanation to get the chunk right
Mario now asks us for an explanation (the buffer now points to the just freed declaration)
We’re now free to put 300 arbitrary bytes overwriting sdterr
Preparing this
Mario will now be waiting for a proper explanation. Well, here’s our poor excuse for making him angry:
We’re overwriting the _IO_file struct for stderr, which will now look like this
Overwriting the address for _IO_file_jumps in stderr pointing back into itself (just reused the space to put the needed pointers in one write).
When we now exit the binary, it will try to clean up the open file handles, calling _IO_fflush on our stderr file struct.
This will try to call _IO_SYNC(fp) (which is _IO_file_jumps + 0x60), so it will call the function located at offset 0x60 from _IO_file_jumps (where we just put the address of system). fp points to the top of the _IO_file struct, where we put the string /bin/sh.