本文章转载:how2heap-2.23-08-large_bin_attack_pwn largbin图解-CSDN博客
欢迎各位读者前往原文章阅读,本处只用作笔记记录
pwngdb命令,方便源码调试
|
|
粗略理解chunk进入large bin的过程(先看图解,再回头看代码
)
下面是从unsorted bin链中取出的chunk,插入到large bin链的代码
基本逻辑如下:
-
从unsorted bin链中取出的chunk大小,是否属于small bin的大小
- small bin相关的处理
-
从unsorted bin链中取出的chunk大小,是否属于large bin的大小
- 计算出当前chunk大小从属的main_arean->bins的下标
- 获取该bins下标的large bin的头结点 bck
- 通过large bin头节点的fd,找到large bin链中size最大的chunk fwd(large bin链中第一个chunk)
- 当前large bin链不为空(操作的是fd_nextsize和bk_nextsize形成的链)
- 当前chunk的size < large bin链中最小的chunk
- (总结:将当前chunk插入到large bin链的尾部,即插入到最小的chunk的后面)
- fwd = bck; 令 fwd 指向 large bin 头结点
- bck = bck->bk; 令 bck 指向 largin bin 尾部 chunk,就是当前已在large bin链中最小的这个chunk
- victim->fd_nextsize = fwd->fd; 当前chunk 的 fd_nextsize 指向 largin bin 的第一个 chunk
- victim->bk_nextsize = fwd->fd->bk_nextsize; 当前chunk的 bk_nextsize 指向原来链表的第一个 chunk 指向的 bk_nextsize(当前chunk的bk_nextsize指向原先最小的chunk)
- fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
- victim->bk_nextsize->fd_nextsize = victim; 原先最小chunk的fd_nextsize指向当前chunk
- fwd->fd->bk_nextsize = victim large bin中第一个chunk的bk_nextsize指向当前chunk
- 当前chunk的size >= large bin链中最小的chunk
- 从large bin链中size最大的chunk fwd(large bin链中第一个chunk),从大到小遍历,找到首个不大于当前chunk的chunk
- 如果找到的chunk的大小等于当前chunk
- **fwd = fwd->fd;**将当前chunk 插入到该chunk的后面,并不修改 nextsize 指针
- 如果找到的chunk的大小小于当前chunk
- victim->fd_nextsize = fwd; 当前chunk的fd_nextsize指向这个找到的chunk
- victim->bk_nextsize = fwd->bk_nextsize; 当前chunk的bk_nextsize指向这个找到的chunk的bk_nextsize
- fwd->bk_nextsize = victim; 找到chunk的bk_nextsize指向当前chunk
- victim->bk_nextsize->fd_nextsize = victim; 找到chunk原先前面的chunk的fd_nextsize指向当前chunk
- 获取找到的chunk的前面的一个chunk(通过bk找到当前chunk插入前,最小的比找到的chunk大的chunk)
- 当前chunk的size < large bin链中最小的chunk
- 当前large bin链为空(操作的是fd_nextsize和bk_nextsize形成的链)
- 当前chunk的fd_nextsize和bk_nextsize均指向自己
-
将当前chunk链入large bin链中(fd,bk形成的链)
图解入large bin链的逻辑
large bin链中放入首个chunk:chunk a
这里fd,bk,fd_nextsize,bk_nextsize指向都是chunk的首地址
放入 size 比 chunk a 大的 chunk c
large bin fd总是指向size最大的chunk
large bin bk总是指向size最小的chunk
large bin链中的chunk,通过large bin fd 从大到小排序
放入size 比 chunk a 小的 chunk b
large bin链中的chunk,通过large bin fd 从大到小排序
只不过在代码实现中,通过large bin bk 快速来实现这一步
放入 size 和 chunk a 一样的 chunk d
在代码中,有这么一个注释:
/ Always insert in the second position. /,
就是大小相同的chunk,紧临着相同大小首个进入large bin链的chunk放置(可以看下面chunk e一起理解
)
大小相同(重复
)的chunk,不会有fd_nextsize,bk_nextsize(忘掉横向链,竖向链
)
放入 size 和 chunk a 一样的 chunk e
如上情况就是**入large bin链的所有逻辑
**
回顾一下unsafe_unlink
和unsorted_bin_attack
how2heap-2.23-05-unsorted_bin_attack
how2heap-2.23-06-unsorted_bin_into_stack
unsafe_unlink
是因为一个被破坏
的chunk脱链,重新修整指针时引发的问题
unsorted_bin_attack
也是一个被破坏
的chunk脱链,重新修整指针时引发的问题
large_bin_attack
原理与上面的两个差不多,是一个chunk入链,与一个**被破坏
**的chunk重新修整指针时引发的问题
从large_bin_attack被挖掘出的思路出发只考虑2.23的源码
unsorted bin chunk进入large bin链的四种逻辑:
- large bin链为空,chunk进入large bin链
- chunk的size比large bin链中size最小的chunk还要小
- large bin中存在和chunk size一样大小的chunk
- chunk的size大于large bin链中size最小的chunk
large bin chunk出large bin链
- 从恰好大小合适的largebin中获取chunk,发生unlink
- 从比malloc要求大的largebin中取chunk,发生unlink
好像入链归于large_bin_attack
,出链归于unsafe_unlink
,不清楚,只看入链的逻辑
下面通过被破坏的large bin chunk实现漏洞利用
的角度来观察不同入链的逻辑(一般是堆溢出修改large bin的fd,bk,fd_nextsize,bk_nextsize
)
1)首个chunk入large bin链
- 此时还没有被破坏的chunk
- large bin fd,large bin bk 指向这个chunk的首地址
- chunk的fd,bk指向large bin 头结点的首地址
- fd_nextsize,bk_nextsize指向chunk自身
这里没有利用场景
2)比最小的chunk还小的chunk入large bin链
先看看该入链逻辑所涉及的代码,寻找可被破坏-用于利用的chunk(从而有堆溢出产生时,构造这种堆结构)
,以及利用方式
|
|
-
【0】victim为要入链的chunk
-
【1】bck为large bin链的头结点,这个位置是一个固定死的值(除非篡改了arena的位置)
-
【2】fwd为large bin链中size最大的chunk,也就是紧挨着large bin头结点的chunk
-
【3】fwd为large bin链的头结点
-
【4】bck为large bin链中size最小的chunk
现在bck,victim,fwd,三要素集齐,下面就是构造如下的双链
考虑到fwd时large bin链的头结点,实际的图形如下的两种形式
-
【5】**
victim->fd_nextsize = fwd->fd;
**fwd为large bin链的头结点,fwd->fd指向的是一个chunk的地址,即size最大的chunk(如果原先large bin链中只有一个chunk,就是指向的那个size最小的chunk的地址),这里没有办法伪造 -
【6】
victim->bk_nextsize = fwd->fd->bk_nextsize;
与【5】中描述的类似,fwd->fd指向的是与large bin头节点最近的chunk,而fwd->fd->bk_nextsize
是该chunk中的数据,如果有堆溢出漏洞,就可以篡改这个chunk的bk_nextsize
字段,并赋值给要链入的chunk的bk_nextsize
字段
- 【7】
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
分成两部分看
fwd->fd->bk_nextsize = victim
仅是给紧挨着large bin头结点的chunk的bk_nextsize
字段赋值
victim->bk_nextsize->fd_nextsize = victim
对篡改的bk_nextsize
chunk的fd_nextsize
字段赋值为victim
如果有一个unsigned long stack_var = 0;
变量,将篡改的bk_nextsize赋值为(unsigned long )(&stack_var -4)
,那就可以修改这个变量的值了
【8】,【9】,【10】,【11】都是不能控制的
下面是相关代码,可以自己调试看看
|
|
|
|
3)放入相同大小的 chunk
所涉及的代码如下
|
|
-
【1】
bck
为large bin链的头结点,这个位置是一个固定死的值(除非篡改了arena的位置
) -
【2】
fwd
为large bin链中size最大的chunk,也就是紧挨着large bin头结点的chunk -
【3】通过
fwd
从大到小,寻找到第一个不大于victim size的chunk
如下就是fwd和victim
- 【4】如果fwd和victim大小相等,就会把fwd中的fd取出来用,从上图可以可以想到,假设存在堆溢出漏洞,那就可以修改fwd->fd
经过赋值,伪造的fd指向的chunk,成为fwd
- 【5】将伪造的fwd的bk取出,作为bck
这里有个问题,伪造的fwd chunk的bk字段必须是要有内容的,最好是个可写内存的地址,否则执行【9】时程序会崩溃
-
【6】,【7】给victim chunk的bk,fd赋值,这个不管
-
【8】,伪造的fwd chunk的bk赋值为victim chunk的地址
-
【9】,这个太不稳定了,不管
这种攻击场景,适用于覆盖栈中保存的堆地址的变量,将其保存的堆地址修改为victim chunk的地址
|
|
4)放入不和其他chunk size相等,且不是最小的chunk
所涉及的代码如下
|
|
-
【1】bck为large bin链的头结点,这个位置是一个固定死的值(除非篡改了arena的位置)
-
【2】fwd为large bin链中size最大的chunk,也就是紧挨着large bin头结点的chunk
-
【3】通过fwd从大到小,寻找到第一个不大于victim size的chunk
如下就是fwd和victim
假设存在堆溢出漏洞,可以溢出修改fwd中的数据
-
【4】victim->fd_nextsize = fwd,victim的fd_nextsize存储fwd chunk的首地址,这个没有办法伪造
-
【5】victim->bk_nextsize = fwd->bk_nextsize,victim的bk_nextsize存储fwd chunk中伪造的bk_nextsize
-
【6】fwd->bk_nextsize = victim,fwd的bk_nextsize字段赋值为victim chunk的首地址,这个正常
- 【7】
victim->bk_nextsize->fd_nextsize = victim
,将伪造的bk_nextsize chunk的fd_nextsize字段覆盖为victim chunk的首地址
- 【8】
bck = fwd->bk
,bck指向fwd伪造的bk
- 【8】和【9】,给victim chunk 的fd和bk赋值
- 【11】给fwd的bk字段赋值
- 【12】给伪造的bk chunk的fd字段赋值为victim chunk的首地址
如果存在两个变量(一个也行)stack_var1,stack_var2
通过堆溢出漏洞,将fwd的bk修改为&stack_var1-2,fwd的bk_nextsize修改为&stack_var2-4,则stack_var1和stack_var2存储的内容在漏洞触发后都能修改为victim chunk的首地址
测试代码如下:
|
|
现在how2heap中的large_bin_attack应该可以完全理解了