Category: pwnable | Points: 124 | Solves: 49 | Challenge files
1 | > checksec mario |
Summary
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.
Reversing
When you first create a new Customer or login to this pizza kitchen, you have the option to order
, cook
, or 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.
1 | Wellcom my friende!! It's-a me, Mario! Ready for pizza italiana vera? |
1 | >> Welcome ghostly |
When you ask to have your pizzas cooked, the ingredients are strcat
ed 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 malloc
ed 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
1 | Customer { |
Stage 1: Leak
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 strcat
ed 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.
1 | int angry_leak() |
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 fd
and 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.
1 | 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:
1 | 0x555555773120: 0000000000000000 0000000000000111 | <-- explanation chunk |
This image from sploitfun is great for understanding the linked list structure of heap bins.
Stage 2: Overflow
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 malloc
ed 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:
1 | .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:
1 | 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 malloc
and free
a large explanation, its free space will be used for other objects, and if we are lucky, a Pizza.
1 | 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 one_gadget
.
1 | 0x555555773120: 0000000000000000 0000000000000041 | ........A> |
And when admire
is called,
1 | ------------------- 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.