Category: pwnable | Points: 124 | Solves: 49 | Challenge files
> checksec mario
This is a classic C++ menu challenge that features a UAF and heap overflow with a vtable pointer overwrite. The main heap techniques involve using a free unsorted bin chunk to leak a libc and heap address, as well as some feng shui to place an object in an overflow-able region on the heap. fortenforge, qzqxq, and I combined to reverse the binary and discover the 3 separate vulnerabilities.
When you first create a new Customer or login to this pizza kitchen, you have the option to
admire Pizzas. When you order, you can specify Unicode emoji ingredients like 🍍 and 🍅. If you try to order a pizza with 🍍, you are kicked out and banned from ever logging in again.
Wellcom my friende!! It's-a me, Mario! Ready for pizza italiana vera?
>> Welcome ghostly
When you ask to have your pizzas cooked, the ingredients are
strcated together and checked with
strstr — if a pizza contains only 🍅, it’s “good”, if it has 🍍, it’s “criminal” — then, a new Pizza object of the correct type is created. You can also provide an explanation string, which is
malloced onto the heap and stored in the Customer. This explanation is then freed at the end of
cook, if the number of good pizzas == total number of pizzas.
In pseudo-code, the Customer struct looks like
You want to trick Mario into cooking a pineapple pizza first. We make a pizza with two ingredients, one ending in
\xf0\x9f and another starting with
\x8d\x8d that, when
strcated together, give a Unicode pineapple (
\xf0\x9f\x8d\x8d). Now you’ve made Mario upset! Your customer is then stored as a global variable
upset_user. You also unlock a menu option to print the
explanation field, which would be a UAF if the
explanation were freed in
cook. We can ensure that the explanation is freed by overflowing the 1-byte counter of total number of pizzas, with (16 bad + 1 good pizza) = 17 total pizzas % 16 = 1 total pizza.
What are the contents of a freed chunk that we can leak? If the chunk is >fastbin size (>0x80), when it is freed, it will be inserted into the unsorted bin, which is a doubly-linked list of recently freed chunks. The
bk pointers will thus be populated with the address of the unsorted bin, which is in the
main_arena struct in libc. This gives us our libc leak.
0x555555773120: 0000000000000000 0000000000000111 | <-- explanation chunk. size of explanation chunk=0x111
We can also get a heap leak if the unsorted bin has another chunk when we free our explanation. In that case, when the explanation chunk is inserted, the
fd pointer will be set to the other free chunk. The free
explanation chunk then looks as follows:
0x555555773120: 0000000000000000 0000000000000111 | <-- explanation chunk
This image from sploitfun is great for understanding the linked list structure of heap bins.
If we successfully cook a pineapple pizza, we are also given a new menu option to “explain ourselves” by overwriting our explanation. It reads 300 bytes into the explanation on the heap, even though our explanation was previously
malloced with a smaller length (the length of our original explanation). IDA can’t detect/decompile jump tables for some reason, so here’s the assembly of the overflow:
.text:00000000000020BB cmp [rbp+var_1], 0
This overflow allows us to overwrite whatever is after the explanation in the heap. With some grooming, we can try to get a Pizza object placed right after our explanation, so we can overwrite its contents — namely, the C++ vtable pointers for class functions.
A Pizza on the heap looks like this:
0x5555557731C0: 0000000000000000 0000000000000041 | ........A> <---- Pizza chunk. size of Pizza chunk=0x41
The goal, of course, is to overwrite the printPizza vtable ptr to a pointer to
one_gadget, so that the next time we print the pizza in
admire, we get a shell.
How do we ensure that the Pizza is placed right after our Customer’s explanation? If we
free a large explanation, its free space will be used for other objects, and if we are lucky, a Pizza.
0x555555773120: 0000000000000000 0000000000000041 | ........A> <---- Customer's explanation chunk
Lovely! Now, we perform our overflow, first writing
one_gadget to the heap, then overflowing the Pizza with the heap ptr to
0x555555773120: 0000000000000000 0000000000000041 | ........A>
admire is called,
------------------- USER MENU -------------------
I learned the unsorted bin leak trick from Samurai’s Gulshan Singh in one of his writeups, who in turn learned it from PPP.