堆利用学习之unlink

原理

unlink是内存操作中的一个宏, 用来从双向链表中取出一个free chunk,其过程中的指针操作存在任意写的漏洞。
从双向链表中取出节点的过程,若是学过数据结构应该都比较清楚,主要代码是:

P->fd->bk = P->bk
P->bk->fd = P->fd

但是在unlink宏中,为了安全性还增加了一些检查:
unlink源码:

if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))
    malloc_printerr ("corrupted size vs. prev_size"); 
FD = P->fd;
BK = P->bk;
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
    malloc_printerr (check_action, "corrupted double-linked list", P);
else {
FD->bk = BK;
BK->fd = FD;
...
}

要利用该漏洞,先要绕过这里的两处检查(64位为例)

  • 第一处检查chunksize(P) != prev_size (next_chunk(P)),检查下一个chunk的prevsize是否等于当前chunk的size,所以需要通过溢出等手段设置下一chunk的prevsize
  • 第二处检查__builtin_expect (FD->bk != P || BK->fd != P, 0),检查当前chunk是否是前一个chunk的后继,同时也是后一个chunk的前驱,也就是检查链表是否真的是链接好的。但是这个检查有个致命的缺点:

    因为FD->bk == *(FD+0x18)BK->fd == *(BK+0x10)
    若在FD中存入&P-0x18,那么表达式将变为FD->bk == *((&P-0x18)+0x18) == *&P == P
    若在BK中存入&P-0x10,那么表达式将变为BK->fd == *((&P-0x10)+0x10) == *&P == P
    从而就绕过了检查
    注意:&P是表示指向目标chunk的指针的地址

绕过检查后,满足FD->bk == P && BK->fd == P,所以有:

FD->bk = BK; // P = &P-0x10
BK->fd = FD; // P = &P-0x18

在此之后,P就指向了比自己的地址低0x18个字节的位置,可以通过再次向P写入从而覆盖掉P本身,将其修改为任意地址,第三次向P写入则实现了任意地址写。

触发前提

  1. 堆指针是全局变量,或其地址是可泄露的
  2. 能够free一个smallchunk(可以是伪造的)(largebin增加对fd_nextsize和bk_nextsize检查,unsortedbin没有检查)
  3. 能够控制下一chunk的prevsize和size

利用过程(64位为例)

法一

存在堆溢出时:

  1. 分配连续3个chunk a, b, c
  2. 在a中伪造chunkp64(0) + p64(0x80) + p64(head_ptr-0x18) + p64(head_ptr-0x10) + padding
  3. 覆盖b的prevsize和size,修改inuse位,使之可以与a合并p64(0x80) + p64(0x90)
  4. free掉b,触发unlink
  5. 写入a,覆盖指针为任意地址,p64(0)*3 + p64(addr)
  6. 再次写入a,任意地址写入

法二

不能溢出,但有double free

  1. 分配连续3个fastchunk a, b, c
  2. 释放a,此时并不会修改后面的inuse位和prevsize
  3. 申请一个大内存,触发fastbin合并,这时会将fastbin中的a取出放入unsortedbin,可以实现对b的inuse位进行修改
  4. 触发double free,将a再次释放,放入fastbin
  5. 再申请与a相同大小的chunk,就会从fastbin中将a返回,同时不会改变b的inuse
  6. 布置a内存,释放b,触发unlink

题目

  1. 一道典型的unlink例题
  2. HITCON CTF 2014-stkof

欢迎访问我的博客:Hpasserby’s blog

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值