查看原文
其他

通过UseAfterFree实现命令执行

2017-10-14 uestcdzy 看雪学院



本贴讲述如何利用UAF漏洞,实现GOT表覆盖,从而实现命令执行,另外漏洞程序由本人通过逆向14年的ctf获得,同时进行了一些功能的精简,从而得到下面的漏洞程序,解决漏洞讲解没有漏洞源码源码的问题。


漏洞程序,是一个用链表实现的简单留言板,用户可以查看消息,并对相关的消息进行:回复、删除、修改。


漏洞代码uaf.c如下:


Uaf程序的基本操作如下:1可以添加留言,2可以查看留言内容,查看完留言内容后,可以选择对浏览内容进行修改,增加回复和删除。

 daizy@daizy-VirtualBox:~/Documents/vuln$ ./uaf

1.leave your message, 2.read the message,3.exit; please input you choice.

1

input you name len:

4

input you name:

test

input you title len:

4

input you title:

test

input you content len:

5

input you content:

hello

1.leave your message, 2.read the message,3.exit; please input you choice.

2

input msgid will read:

1

msg author:test,msg title:test,msg content:hello

Please select the operate: 1.delete 2.modify 3.add reply 4.back

2

input new name len:

5

input new name:

daizy

input new title len:

5

input new title:

hello

input new content len:

11

input new content:

hello,daizy

Please select the operate: 1.delete 2.modify 3.add reply 4.back

4

1.leave your message, 2.read the message,3.exit; please input you choice.

2

input msgid will read:

1

msg author:daizy,msg title:hello,msg content:hello,daizy

Please select the operate: 1.delete 2.modify 3.add reply 4.back

1

Please select the operate: 1.delete 2.modify 3.add reply 4.back

4

1.leave your message, 2.read the message,3.exit; please input you choice.

3

exit 


UseAfterFree漏洞形成原因


链表节点被删除后,可以进入modify_msg函数,modify_msg函数之后可以继续进入modify_msg函数。

while (1) {

                write(STDOUT_FILENO, operate_usage, strlen(operate_usage));

                operate = read_input_uint(input_buffer, sizeof(input_buffer), sizeof(input_buffer));

                //sscanf(input_buffer, "%d", &operate);

                if (operate == 1) {

                    delete_msg(read_msg);

                } else if (operate == 2) {

                    modify_msg(read_msg);

                } else if (operate == 3) {

                    read_msg->reply_count++;

                } else if (operate == 4) {

                    break;

                }

 

}


delete_msg函数如下:


void delete_msg(struct Message * delmsg) {

    //delete linked list msg and free

    struct Message* tmp = head;

    while (tmp->nextMsg != delmsg) {

        tmp = tmp->nextMsg;

    }

    tmp->nextMsg = delmsg->nextMsg;

    //free

    free(delmsg->author);

    free(delmsg->content);

    free(delmsg->title);

    free(delmsg);

}


delete_msg函数中对节点进行了free操作,如果在循环代码中,进行delete操作,释放节点后,在选择2进入modify_msg函数,modify_msg会根据用户输入的内容,重新分配堆内存。

modify_msg函数如下:


void modify_msg(struct Message * modifymsg) {

    int size = 0;

    char temp[0x100];

 

    write(STDOUT_FILENO, "input new name len:\n", 20);

    size = read_input_uint(input_buffer, sizeof(input_buffer),

            sizeof(input_buffer));

    if (size > 0x100)

        return;

    write(STDOUT_FILENO, "input new name:\n", 16);

    read_input(temp, size, 0x100);

    memcpy(modifymsg->author, temp, size);

    modifymsg->author_size= size;

 

    write(STDOUT_FILENO, "input new title len:\n", 21);

    size = read_input_uint(input_buffer, sizeof(input_buffer),

            sizeof(input_buffer));

    if (size > 0x100)

        return;

    write(STDOUT_FILENO, "input new title:\n", 17);

    read_input(temp, size, 0x100);

    memcpy(modifymsg->title, temp, size);

    modifymsg->title_size= size;

 

    write(STDOUT_FILENO, "input new content len:\n", 23);

    size = read_input_uint(input_buffer, sizeof(input_buffer),

            sizeof(input_buffer));

    if (size > 0x100)

        return;

    write(STDOUT_FILENO, "input new content:\n", 19);

    read_input(temp, size, 0x100);

    modifymsg->content = (char *) malloc(size);          //新分配一个content

    memcpy(modifymsg->content, temp, size);

    modifymsg->content_size= size;

}


modify_msg函数从用户读取数据,然后拷贝到对应的指针中,但此时使用的是一个已经释放的msg结构指针。当输入content时,会取content的长度作为大小分配内存,当分配内存大小等于msg结构大小(x86上是40字节,会将刚才释放的内存分配给content指针。此外由于msg结构指针刚好是40个字节,再给msg分配堆内存是,由于需要8字节对齐,而40个字节+8字节[prev_size+size],刚好8字节对齐,另外由于40字节,在堆中属于fastbin管理,不会发生合并,free后再分配时,就会返回相同的堆块)。


接着会将用户输入的内容(content)拷贝到content指针中,即我们构造的恶意内容,覆盖了原来的Message中的char* author、char* title等地址内容。

整个msg变化过程,如下图所示:




在循环代码中,modify_msg完之后可以继续进入modify_msg,此时msg中相关地址,如author、title和content地址已经变成free函数在got表中的位置,也就是我们输入的内容可以覆盖GOT表!我们把free函数的GOT表地址覆盖成system函数地址,下次在执行free函数时,就会执行system函数,从而达到命令执行。


最终漏洞的exp代码如下:


由于print_msg函数在modify_msg函数的上一层,也就是无法通过print指定地址上的内容造成信息泄露,所以上述free函数的地址是通过运行时,gdb -pid 5519获得,得到free函数地址后,需要退出gdb程序,否则主程序无法进行下一步。


当然也可以通过指定一个free函数地址,爆破n次,也可以获得成功。

exp运行结果如下:






本文由看雪翻译小组 uestcdzy  原创

转载请注明来自看雪社区

热门阅读


点击阅读原文/read,

更多干货等着你~


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存