Pwnable.kr UAF
复制本地路径 | 在线编辑
C++ 虚函数
C++ 虚函数 vtable
知道对象在继承父类的时候,在子类的属性前面会有一个 vtable, 指向继承父类的值。
题目
#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;
}
关键点:
可以看出 Man 和 Woman 都是继承了 Human 类,并且可以看出 Human 类的私有虚函数 give_shell 就是 system, 所以想办法执行这个函数。
程序给了我们 3 个选项:use 使用指针指向的函数; after 分配一段地址空间,我们可以用其将已经被 free 的内存,重新 allocate; free 将指针指向的内存释放。
利用思路
很明显,只有子类 introduce 会执行,所以就是把子类 introduce 改为父类 give_shell 的地址即可。
- 假设
introduce在子类的偏移量为 n,give_shell在父类的偏移量为 m. - 假设
子类虚函数表地址为 X,父类虚函数表为 Y.
做法:把 X 指向 Y+m-n 即可。因为要保证 X + n == Y + m.
里面的内容就是 p64(Y+m-n), 然后执行 3-2-2-1.
- 第一步执行 3, 会把那两个指针执行的内存删除掉,可以调试发现大小是 24Bytes.
- 第二步第三步执行 2, 会把之前删掉的内存块中添加
Y+m-n, 这样那两个指针开头为Y+m-n. - 第四步执行 1, 让那两个指针指向的对象执行
introduce, 而找这个函数是通过对象开头的虚函数表,而这个表被我们改掉了,偏移 n 就是 Y+m, 所以成功执行了give_shell.