在一些pwn的题目中,有些题目最主要的漏洞点在off by null,所以就会存在一些必须触发向上合并才能进行利用的情况,在此简述一下触发向上合并所需条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//glibc-2.24 malloc.c:4036
if (!prev_inuse(p)) {
prevsize = p->prev_size;
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);
}

if (nextchunk != av->top) {
/* get and clear inuse bit */
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

/* consolidate forward */
if (!nextinuse) {
unlink(av, nextchunk, bck, fwd);
size += nextsize;
} else
clear_inuse_bit_at_offset(nextchunk, 0);

一般的利用方法就是,先malloc多个块,结构如下 (x86_64) :

malloc(size)
>= 0x80
<= 0x70
<= 0x70
<= 0x70
……
<= 0x70 | 8
0xf0
  • 先free第一块,此时第二块的prev_size会变成第一块的size,且当前块的fd与bk会变成main_arena相应的地址
  • 填充倒数第三快,将最后一块的prev_size填写为第一块到最后一块的offset,然后再利用off by null将最后一块的prev_inuse覆盖为0
  • free最后一块

参照如上代码此时,会先检查最后一块的prev_inuse位,此时它是0,所以会进入第一个if中

1
2
3
4
5
6
if (!prev_inuse(p)) {
prevsize = p->prev_size;
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);
}

它会根据prev_size找到前一块,即我们malloc的第一块,然后会将它及进行unlink

由于第一块的fd与bk都是它在main_arena的相应地址,因此unlink顺利通过

但是,事实上,不知为何,有一个检查并没有在源码上体现(可能是我没看见),它会在unlink之前检查一下prev_chunk即第一块的next_chunk的prev_size是否它的size相同。

接下来检查下一块是否是top chunk,很明显,是,所以进else

1
2
3
4
5
6
7
8
9
10
   if (nextchunk != av->top) {
/* get and clear inuse bit */
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

/* consolidate forward */
if (!nextinuse) {
unlink(av, nextchunk, bck, fwd);
size += nextsize;
} else
clear_inuse_bit_at_offset(nextchunk, 0);

之后基本就无需操心了,它会直接进行下去