基于逆向分析的缓冲区溢出实验设计与研究

2020-12-28 11:53李春秋何军朱静怡
电脑知识与技术 2020年31期
关键词:缓冲区漏洞安全

李春秋 何军 朱静怡

摘要:随着云计算、大数据和人工智能的飞速发展,安全问题也日益突出。“没有网络安全就没有国家安全”,缓冲区溢出作为网络安全漏洞中的高发地带,在操作系统和众多的应用软件中广泛存在,且很难被主动发现。缓冲区溢出的学习成本较低,造成的破坏力极大。该实验构建两个虚拟网络,模拟搭建含有缓冲区溢出漏洞的Linux服务器,使用GDB逆向分析技术,探测并计算缓冲区buffer的长度,计算出ESP,手工编写程序,利用缓冲区溢出漏洞发起进攻的演示。阐述缓冲区溢出漏洞造成的危害以及如何检测和防御缓冲区溢出漏洞,以降低缓冲区溢出带来的安全风险。

关键词:逆向分析;缓冲区;漏洞;安全;溢出

中图分类号:TP311        文献标识码:A

文章编号:1009-3044(2020)31-0240-04

Abstract: With the rapid development of cloud computing, big data and artificial intelligence, security issues are increasingly prominent. "Without network security, there is no national security". As a high incidence area of network security vulnerabilities, buffer overflow exists widely in the operating system and many application software, and it is difficult to be found actively. The learning cost of buffer overflow is low, which causes great damage. In this experiment, we build two virtual networks, simulate the Linux server with buffer overflow vulnerability, use GDB reverse analysis technology to detect and calculate the length of buffer, calculate ESP, write programs manually, and use buffer overflow vulnerability to launch attack demonstration. This paper expounds the harm caused by buffer overflow and how to detect and defend buffer overflow to reduce the security risk brought by buffer overflow.

Key words: reverse analysis;buffer;vulnerability;security;overflow

当我们在计算机上执行程序时,操作系统会在内存区域分配一段连续的区域用来存放需要的数据,然后将计算机的外存传输到内存中用于等待指令操作,这一段连续的区域就是缓冲区区域。缓冲区的区域由程序本身定义,所以当攻击者探测出缓冲区的长度,并将大于缓冲区长度的数据恶意写入,就会触发缓冲区溢出。对于存在这种问题的程序,就有缓冲区溢出漏洞,攻击者往往会根据缓冲区溢出漏洞,基于探测或者逆向分析技术,精准找出RET的ESP,然后构造shellcode,对目标机器发起攻击。攻击者使用未授权的管理员权限,可以对目标机器执行任何操作,如开后门、挂木马、暴露数据库、安装挖矿程序等,更大的威胁是可以对目标机器的内网进行渗透,对整个单位的数据和应用程序安全形成巨大的隐患。

缓冲区溢出已经成为十分严重的安全问题,产生的原因是冯·诺伊曼结构的计算机内存中并未对存储的数据进行校验,无法区分内存中的数据和代码,且现阶段去更换计算机结构的概率几乎为零,所以只能从现有的技术中去发掘检测和防御缓冲区溢出漏洞的方法,而DEP(数据执行保护)就能从区分数据和代码这一功能上进行防御。

缓冲区溢出常见类型一般分为栈溢出和堆溢出[1]。堆溢出发生的主要原因是堆在接收到内存分配指令后,没有对写入内存中的数据进行检查;栈溢出产生的主要原因是C语言的标准库中没有对复制到缓冲区中数据的长度进行检查[2],所以当复制到缓冲区中数据的长度大于原本设定的缓冲区长度,就会触发溢出。

1理论分析

1.1缓冲区溢出

能够查到记载的缓冲区溢出漏洞的惡意利用是在1988年,这是Morris蠕虫在互联网上少量的几个漏洞其中之一,含有漏洞的是一个叫finger的服务程序,源自Unix操作系统,利用的是栈溢出漏洞。2017年,一个名为“永恒之蓝”的勒索病毒造成的波及范围极大,这款病毒同样也是利用了缓冲区溢出漏洞[3],较近的CVE2018-6789漏洞是利用Linux上的邮件传输代理服务造成的,该漏洞是由于base64d()函数中b64decode的缓冲区长度没有有效检测,当恶意触发计算错误并无法解析时,base64字符串则可能会覆盖堆内存,进而覆盖关键数据,base64解码是一种广泛使用的函数,并且字节是开发者自行设计,因此增加了易用性,可以广泛用于使用远程代码执行来控制目标机器。

2018年OWASP(开放式Web应用程序安全项目)的Web应用十大最严重的安全风险中,使用含有已知漏洞的组件C(Using Components with KnownVulnerabilities)排名第9[5]。2010-2011年,CWE/SANS组织统计的数据中显示,缓冲区溢出漏洞在最危险的程序设计错误中排第3[6],并且,CWE公布自身的软件脆弱性枚举库中,涉及缓冲区溢出漏洞的有21条[7]。2018年12月,CNNVD(国家信息安全漏洞库)报告公布新增安全漏洞1275个,其中缓冲区漏洞就有196个,占比约为15.37%。

1.2 windows缓冲区溢出

首先,需要了解windows环境中函数的执行原理。当系统需要去调用某个函数的时候,计算机需要按下列步骤执行:

(1)首先架设有4个参数,名称分别为parameter1,parameter2、parameter3和parameter4,按照从右到左的顺序依次入栈;

(2)EIP(指令寄存器)的数据作为RET入栈;

(3)EBP(基址寄存器)的数据入栈;

(4)将当前ESP的数据赋值给EBP;

(5)假设有两个变量var1,var2,其数据类型为int,为函数的变量开辟内存空间时,var1的变量地址为[EBP-4],var2的变量地址为[EBP-8]。

当函数执行周期结束,根据先进后出的栈规则,出栈顺序为:var2,var1,EBP的数据,EIP的数据,parameter1,parameter2、parameter3和parameter4。

若定义一个数组buffer长度为8,那么buffer本身8个元素在内存中的地址的从高到低分别是[EBP-4],[EBP-8],…,[EBP-32]。当使用函数strcpy()给buffer数组赋值时,入栈的顺序就是从低地址向高地址,若此时入栈的字符串长度大于8个字符,多出部分的字符串就会去覆盖原有的EBP和RET,从而触发缓冲区溢出。

1.3 linux缓冲区溢出

程序通常都是由代码和数据构成的,默认情况下代码和数据都是静态。需要运行程序时,操作系统会首先创建所需进程,接着会在内存中为进程开辟虚拟地址空间,为代码段和数据段建立映射关系。程序想要正式运行,仅有代码段和数据段是无法完成的,还需要很多动态环境,堆和栈就是最重要的环境。Linux的进程地址空间布局见图1。

首先会由execve(2)为进程的代码段和数据段建立映射关系,接下来操作系统的缺页异常处理会将代码段和数据段读入内存,同时execve(2)会清零bss段,这也就合理解释了当未赋值的全局变量及静态变量默认初值为零的原因。进程空间的最高位地址用于存放命令参数和环境变量等,堆和栈就在中间位置,作为进程动态运行环境,栈向下伸展,而堆向上伸展。堆栈中存放了C语言标准库中的函数对应的堆栈帧,当需要调用函数时,被调用函数的堆栈帧被压入,当需要返回时,被压入的堆栈帧从堆栈中弹出。一般堆栈帧的结构如图2所示。由高往低,依次是函数的实参、函数的返回地址、前一个堆栈帧的指针和函数局部变量使用的空间。一般堆栈帧有两个指针,一个是堆栈帧指针,指向的位置是固定的,一个是栈顶指针,在函数运行过程中指向的位置是可变的。所以,在函数访问实参和局部变量时,都是以堆栈指针为基础地址,加上偏移量确定。

函数调用的过程如下:

(1)调用函数时,堆栈从由高往低依次是被调用函数的实参、EIP原始值、EBP原始值、被调用函数的局部变量。

(2)由于被调用函数的局部变量的地址是已知的,EIP原始的位置就和局部变量地址位置较近。

(3)被调用函数需要返回时,由返回地址报对战中的EIP原始值重新赋予EIP。所以,只要找到堆栈中的EIP原始值的位置,修改为Shellcode的地址就能触发攻击。

1.4软件逆向工程

软件逆向工程(Software Reverse Engineering),又被称软件反向工程。泛指从已设计成型且可运行的程序系统研究开始,综合运用密码学加密解密、汇编和反汇编、操作系统分析和程序逻辑分析等多种计算机技术,对程序的编程语言、代码结构、流程算法等方面进行逆向分析,从而推导出程序的源代码、设计思想、流程算法、过程处理等相关内容。这个综合分析的过程一般被统称为软件逆向工程技术。在实际操作中,推导出源代码和算法的概率极小,软件逆向工程的目的是用于研究和学习。

2实验设计

本次实验设计使用strcpy()函数作为产生缓冲区溢出的不安全因素。strcpy()的API函数实现如下:

char* strcpy(char *d, const char *s)

{

char *r=d;

while((*d++=*s++));

return r;

}

可以看出,strcpy这个函数通过while循环将所有字符依次赋值给了指针d,再将字符指针r指向d的开始位置(d[0]的地址),并返回r指针的地址。

举例说明,假设A值8字符、B值16字符。若使用strcpy()函数将B赋值给A,由于B的字符串长度大于A的字符串長度,就会产生溢出。若当溢出的值刚好覆盖到ret的返回地址时,再将需要的地址准确地覆盖到ret的esp处,就会执行溢出的值。通过构造shellcode,从而获取计算机的控制权限。

我们将虚拟机VM1作为服务器,虚拟机VM2作为攻击机。VM1操作系统为CentOS Linux 5.5,仅安装工具GCC、GDB和Nginx。VM2操作系统为CentOS Linux 5.5,安装工具GCC、GDB和Python3/Python2.

2.1网络环境设计

为了实现不同网络的拓扑设计,我们将VM1的网络连接模式设置为“仅主机模式”,通过共享物理网卡信息,连接网络。将VM2的网络连接模式设置为“NAT模式”, 将VM2的网卡连接到虚拟交换机Vmnet8,然后将连接Vmnet8的虚拟NAT设备与虚拟DHCP服务器一起连接在物理网卡上,实现VM2的网络连接。这样就可以虚拟出两个不同的网络,摆脱了局域网实验的困境。

使用Socket编程中的sockaddr_in结构体完成漏洞程序端口的设计,首先给sockaddr_in变量赋值,因为类型不同,需要进行强制类型转换,然后给sockaddr做参数。sockaddr_in的作用是定义socket和赋值,sockaddr的作用是函数参数,最后使用accept绑定端口。

使用strcpy()函数的漏洞缺陷,设计buffer长度为1000,通过传参的形式,触发缓冲区溢出漏洞,漏洞程序设计如图3所示。

将端口绑定程序和缓冲区溢出漏洞程序整合为vuln.c,使用GCC编译该文件后,执行并设置绑定任意端口,但不得与现有端口重合。同时将编译后的文件vuln放置Nginx目录,因为常规情况下,做逆向分析都会有原始的安装程序,此编译后的程序用于逆向分析,所以必须提供。

2.2 逆向分析探测设计

使用python编写ARP扫描工具,使得VM2可以对VM1进行端口探测,找出Nginx的目录,下载编译后的程序,同时根据网络基础知识和社会工程学查找含有缓冲区溢出漏洞的服务端口。通过VM2的GDB调试工具对可执行文件vuln进行逆向分析调试。

#gdbvlun

(gdb) run 666

此时系统的666端口已经自动开启监听模式,尝试使用netcat工具进行连接。

nc 127.0.0.1 666

这个探测过程可能会出现中断信息,可以重新运行GDB,开启指定端口。为 "My name is:.." 命令行提供超过1000个字节长的输入,发现已经溢出。这个探测过程相对简单,真实情况下探测溢出长度也不需要很多时间。

根据GDB调试得到的缓冲区长度信息,构造拒绝服务攻击代码dos.c。

执行dos脚本,使用GDB工具运行666端口,发现已经溢出,找到返回地址。

#./dos 127.0.0.1 666

#gdb vuln

(gdb) run 666

找到返回地址,在本步骤中,尽量选择中间靠上的位置,使跳转到该地址后,会顺延到shellcode位置。缓冲区的构造如图所示:

|NOPS|NOPS|NOPS|……|shellcode|RET|RET|……|RET|

构造攻击代码exploit.c,步骤如下:

(1)使用汇编编写提取或者使用kalilinux生成一个shellcode代码;

(2)定义一个大于1000字节的缓冲区,比如1088字节,要保证能够覆盖EIP;

(3)因为NOP填充不影响程序整体执行,所以采取NOP填充,执行代码memset(buffer, 0x90, 1088);

(4)将构造的shellcode復制到缓冲区中,执行代码memcpy(buffer+1001-sizeof(shellcode), shellcode, sizeof(shellcode));

(5)构造一个buffer[1000] = 0x90,因为NOP的十六进制值为0x90;

(6)将返回地址复制到到缓冲区的结尾,其中RET就是返回地址,前面的代码中用#define定义过的。

for(i = 1022; i< 1083; i+=4)

{((int *)&buffer) = RET;}

由于漏洞程序vuln的缓冲区是1000字节,程序从998开始复制RET,直到1083个字节的位置为止。

(7)在缓冲区的最后以'\0'结束:buffer[1087] = 0x0;

至此,构造的缓冲区代码完成。最后使用exploit攻击服务器VM1。由于本次生成的shellcode是执行cat /etc/passwd ,所以在VM1上显示结果如图2.可以更换shellcode,采用正向获取或者反向弹射的方式,达到获取ROOT权限的目的。

3缓冲区检测与防御

面对缓冲区溢出漏洞的频发,检测和防御成为研究热点,很多世界知名厂商也推出了诸多措施来预防该类漏洞。软件层面,例如Microsoft公司在软件开发工具中增加了用于检测缓冲区溢出的编译选项,用于开发阶段测试是否存在缓冲区溢出漏洞;在Windows操作系统中使用了结构化异常处理覆盖的保护机制,通过监测判断是否恶意修改数据结构,进而进行异常处理,从而阻止恶意修改,增强系统安全性。硬件层面以AMD公司的64位CPU为例,引入了NX机制,在内存空间中将数据区和代码区加以区分,当攻击者利用溢出漏洞执行时,会进入数据区,进行异常处理并终止程序运行。

产生缓冲区溢出漏洞的人为因素,是由软件开发项目管理不规范和开发人员安全意识薄弱引起的。所以想要从根本上防御缓冲区溢出漏洞,就是需要采用工程的概念,将开发安全的需求加入项目规划中,和软件业务功能同步进行,并且在信息化建设活动中有关加强系统安全性活动的集合。《中华人民共和国网络安全法》明确指出“建设关键信息基础设施应当确保其具有支持业务稳定、持续运行的性能,并保证安全技术措施同步规划、同步建设、同步使用。”当管理层面的防御问题解决后,需要解决技术层面的防御,因为安全是一个相对概念,所以使用一种技术防御或者多种技术防御,都能在一定程度上降低缓冲区溢出漏洞出现的概率,从而达到更安全的目的。目前主要的缓冲区溢出防御措施如下。

3.1 StackGuard 保护技术

最早提出StackGuard 防护理论的是《StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks》,发表在1998年的7th USENIX Security Conference上,作者由 Crispan Cowan[4]等 10 人联合署名。该文介绍了GCC编译器上检测栈溢出的研究成果,并且首次提出在栈返回地址前加入canary技术,用于防护检测,这就是StackGuard技术的前身。StackGuard技术检测原理是当攻击者使用溢出漏洞去覆盖函数返回地址时,必然会覆盖其上方地址的空间,这时canary必然发生改变,若检测到canary发生改变,则说明已经发生了缓冲区溢出攻击。

3.2 DEP 保护技术

由于产生缓冲区溢出的根本原因是由于冯·诺依曼结构的计算机内存中并未对存储的数据进行校验,无法区分内存中的数据和代码,而DEP(数据执行保护)就能从区分数据和代码这一功能上进行防御。DEP从攻击者的角度出發,研究攻击中的共同特征。通过研究发现,当发动缓冲区溢出时,系统的指令会跳转到攻击者构造的shellcode的地址开始执行,若能够中断执行,就可以保护程序不受攻击。所以,DEP标记数据所在的内存页为不可执行状态,这样就有效区分了数据和代码混淆的问题。当程序被攻击者引导进入shellcode的时候,由于数据区域被标记为不可执行代码,CPU执行到这一段指令时,就会出现异常错误,从而中断执行。

DEP主要解决的问题是在数据区去执行代码。目前分为硬件DEP和软件DEP。硬件DEP是目前DEP技术的核心,由CPU提供硬件支持,操作系统可以通过对内存页的NX/XD属性标记进行设置,将内存页标识为不可执行。而软件DEP,就是微软引入了S.E.H 的校验机制 SafeSEH,主要针对SHE攻击,没有CPU硬件支持。DEP是一种针对冯·诺依曼结构计算机核心问题的解决方法,通过严格区分数据和代码区域,限制恶意代码在函数堆栈上运行,能够保障缓冲区的安全。但是由于硬件和操作系统的多样性,为了更好的兼容,所以DEP保护是可以设置的,一旦关闭了DEP,计算机就处在了危险之中。

3.3 ASLR 保护技术

由于所有的缓冲区溢出漏洞的利用都离不开返回地址,因为只有找到准确的地址,才能引导至shellcaode的开始地址执行,所以为了解决其他缓冲区溢出保护措施的问题,Microsoft公司提出了ASLR(地址空间配置随机化)技术。ASLR使用随机的方式将传统不变的基址地址打乱,使得跳转地址不再是固定位置,从而干扰攻击者定位Shellcode。地址随机化是ASLR的核心,当ASLR硬件支持开启后,攻击者所需要的返回地址就会随时发生变换,增大了探测的困难,也就很难被利用。早在VisualStudio2005 中,Microsoft公司已经在编译程序时加入了/dynamicbase 链接属性的设置。若开发者在编译程序时添加了此选项,编译后的程序就支持ASLR技术。目前ASLR技术主要分为映像随机化、堆栈随机化、PEB与TEB随机化,较为常见的是映像随机化和堆栈随机化。映像随机化是在PE文件映射到内存时,对其加载的虚拟地址进行随机化分配处理,在系统启动时才能确定该地址,当系统重启后,该地址会发生变化。堆栈随机化是将原来固定的栈堆指针基址进行随机化处理,当程序执行时才能确定该地址,所以一个程序每次运行的时候,栈堆指针基址都是不相同的。ASRL技术的出现,由于程序每次运行的地址是随机的,只有在程序运行的一次操作中探测到所有的信息才能攻击成功,大大增加了攻击者的难度。有研究数据表明,ASLR技术已经成功减少了缓冲区溢出的攻击次数。

4结语

本实验模拟了无任何攻击工具的情况下,使用GDB的反编译来探测缓冲区溢出的长度及返回地址,通过构造Shellcode及攻击代码,最终实现攻击成功。在现实中,缓冲区溢出一直是信息安全领域的热点。由于其伴随C语言而来的属性,成为不可避免的问题,因为C语言天生缺少自动控制边界的机制,并且不能通过指针控制数据元素的个数。

当《中华人民共和国网络安全法》发布时,网络安全就已经上升到国家战略的高度。等级保护制度、护网行动等,均是管理层面对网络安全的重视。对于技术层面,我们需要进一步规范软件系统的开发规范,将安全工程和系统工程同步开展,针对有可能出现的漏洞,加强软件投产前的测试工作,进一步提高安全性。

参考文献:

[1] 唐成华,彭灿,刘猛,等.一种抗地址淹没的缓冲区栈溢出算法[J].计算机应用研究,2017,34(12):3758-3761.

[2] 邵必林,燕佳芬,边根庆.缓冲区溢出漏洞综合检测方法研究[J].微电子学与计算机,2015,32(7):99-102.

[3] 李安安,杨德芹,王学健.软件系统缓冲区溢出漏洞防范研究[J].高技术通讯,2017,27(8):718-726.

[4]Cowan C,Pu C,Maier D,et al.StackGuard:Automatic Adaptive Detection and Prevention ofBuffer-Overflow Attacks[C].Usenix Security,1998,98:63-78.

[5] 邵思豪,高庆,马森,等.缓冲区溢出漏洞分析技术研究进展[J].软件学报,2018,29(5):1179-1198.

[6]傅建明.计算机病毒分析与对抗(第二版)[M].武汉:武汉大学出版社2009.

[7] 乐德广,章亮,郑力新,等.面向 RTF 文件的 Word 漏洞分析[J].华侨大学学报(自然科学版),2015,36(1):17-22.

【通联编辑:代影】

猜你喜欢
缓冲区漏洞安全
漏洞
嫩江重要省界缓冲区水质单因子评价法研究
三明:“两票制”堵住加价漏洞
高铁急救应补齐三漏洞
关键链技术缓冲区的确定方法研究
地理信息系统绘图缓冲区技术设计与实现
AVS标准中的视频码流缓冲区校验模型分析