Tags: pwn cpp 

Rating:

# Raddest DB

There are two bugs we used

## Type confusion
When a (key,value) is added to a db, the db checks if there exists a value with that key. If this is true, the db only changes the `value` field of the object and leaves the `vtable` field initialized. This leads to a type confusion bugs, giving us 3 primitvies:

```
1. Type confusion from `string`->`int`: consider any string pointer as an int, giving us leaks
2. Type confusion from `float`->`string`: consider any address as a string, which gives use an arbitrary read primitive
```

With these two powerful infoleak bugs, we can get the libc base as well as the heap base.

## Fakeobj???
We found a bug that wasn't found via analysis. It occurs if we do the following (One of my teammates found it and I still don't know how it was triggered, but we can make some guesses that it is a UAF)

```
cmd("create db")

cmd("store db int 1 1337")

cmd("getter db 1 2")
p.sendline("echo {}".format("A"*0x10+p64(fakeobj_addr).strip("\x00")))
p.sendline("empty")

cmd("print db")
```

I made some speculations about how this happened:
1. Each entry (key,value) is stored as a 0x18 sized structure, which contains the pointer to the object. So in code it is like this:
```
struct entry {
char unknown[0x10];
struct object *obj;
}
```

```
struct object {
funcptr_t *vtable;
union value {
unsigned long int_form;
char *string_form;
double float_form;
}
}
```

When we empty a db, the entry structure is free'd and the BUF in echo{BUF} takes that chunk if BUF is of appropriate size. Afterwards the reference is not removed and we have a `fakeobj` primitive, which gives us a single RIP control with no arguments controlled.
In code, it is like this: `this->vtable->func1(this, aux);`

We have control to the entire `this` structure, so we can control `func1`. I changed `func1` to all one shot gadgets (found using david942j's `one_gadget`) but none of them popped a shell.

I decided to expand this primitive to an arbitrary write primitive by changing `func1` to `gets`.

Before triggering the vtable function we free the chunk right under where the `this` object is located, turning it into a freed, size 0x20 tcache bin. Overflowing `this` enables us to overwrite a tcache bin, which gives use arbitrary allocation. So, I changed the `fd` field of the next tcache chunk entry to `__free_hook` and got `__free_hook` allocated and wrote `system` to it.

I tried some commands that would trigger `free('/bin.sh;'`. (The way was to enter /bin/sh;[LOTS OF SPACES] as the command because when the string is extended to some length its original buffer is freed (property of c++'s std::vector)

Original writeup (https://github.com/pr0cf5/CTF-writeups/tree/master/2019/fbctf/raddest_db).