Cisco RV110W UPnP 0day 分析
0x01 前言
最近 UPnP 比较火,恰好手里有一台 Cisco RV110W,在 2021 年 8 月份思科官方公布了一个 Cisco RV 系列关于 UPnP 的 0day,但是具体的细节并没有公布出来。于是想要用手中的设备调试挖掘一下这个漏洞,漏洞的公告可以在官网 https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-cisco-sb-rv-overflow-htpymMB5 看到。
0x02 准备工作
首先将固件更新到最新版本 1.2.2.8,传送门:https://software.cisco.com/download/home/283879340/type/282487380/release/1.2.2.8?i=!pp,接下来面临的一个问题就是如何调试和定位漏洞。
首先解决调试的问题,调试的首要工作就是拿到设备的 shell,不过最新的固件并没有提供调试的接口,笔者这里通过 UART 串口和修改固件包的方式拿到了最新固件的调试权限,具体的方法参考之前写过的一篇文章路由器调试之getshell,可见:https://badmonkey.site/archives/router-debug-getshell
调试准备
Cisco RV110W 是 mipsel 架构,所以需要先找一个对应的 gdb-server,可以自己交叉编译也可以使用别人编译好的,这里推荐 gdb-static-cross,传送门:https://github.com/stayliv3/gdb-static-cross
漏洞定位
官方公告指出漏洞存在于 UPnP 服务中,首先进入后台管理,FireWall 的 Basic Settings 打开 UPnP 的配置
然后 nmap 扫一下端口,并没有发现 UPnP 的端口,但是测试发现 UPnP 的确打开了。
这里笔者使用 UPnPy (https://upnpy.readthedocs.io/en/latest/)进行漏洞的测试和利用。
import socket
msg = \
b'M-SEARCH * HTTP/1.1\r\n' \
b'HOST:239.255.255.250:1900\r\n' \
b'ST:upnp:rootdevice\r\n' \
b'MX:2\r\n' \
b'MAN:"ssdp:discover"\r\n' \
b'\r\n'
# Set up UDP socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.bind((b"192.168.2.100",23333)) #本机IP
s.settimeout(2)
s.sendto(msg, (b'239.255.255.250', 1900))
addr = ('192.168.2.1', 1900) # 网关IP
try:
while True:
data, addr = s.recvfrom(65507)
print(addr,data)
except socket.timeout:
pass
发现确实得到了 UPnP 的响应,而且在设备的进程中存在对应的 UPnP 进程
服务分析
UPnP 是一种通用的协议标准,厂商大多按照标准实现,即很多 action 都是一致的,但是也有必要对设备提供的服务进行分析以便于漏洞的定位和利用,同样使用 UPnPy 进行信息的收集
import upnpy
import socket
import requests
from upnpy.ssdp.SSDPDevice import SSDPDevice
msg = \
b'M-SEARCH * HTTP/1.1\r\n' \
b'HOST:239.255.255.250:1900\r\n' \
b'ST:upnp:rootdevice\r\n' \
b'MX:2\r\n' \
b'MAN:"ssdp:discover"\r\n' \
b'\r\n'
# Set up UDP socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.bind((b"192.168.2.100",23333))
s.settimeout(2)
s.sendto(msg, (b'239.255.255.250', 1900))
addr = ('192.168.2.1', 1900)
data = b""
try:
while True:
data, addr = s.recvfrom(65507)
print(addr,data)
except socket.timeout:
pass
# data = b'HTTP/1.1 200 OK\r\nCache-Control: max-age=120\r\nDate: Fri, 01 Jan 2010 00:44:16 GMT\r\nExt: \r\nLocation: http://192.168.2.1:1780/InternetGatewayDevice.xml\r\nServer: POSIX UPnP/1.0 linux/5.70.48.16\r\nST: upnp:rootdevice\r\nUSN: uuid:31474a87-67ea-dae4-2f73-f157fb06d22b::upnp:rootdevice\r\n\r\n'
# data = b'HTTP/1.1 200 OK\r\nCache-Control: max-age=3600\r\nST: upnp:rootdevice\r\nUSN: uuid:824ff22b-8c7d-41c5-a131-8c3bad401726::upnp:rootdevice\r\nEXT:\r\nServer: Unspecified, UPnP/1.0, Unspecified\r\nLocation: http://192.168.3.1:56688/rootDesc.xml\r\n\r\n'
device = SSDPDevice(addr, data.decode())
services = device.get_services()
services_id = [services[i].id.split(":")[-1] for i in range(len(services))]
for id in services_id:
service = device[id]
actions = service.get_actions()
for action in actions:
for argument in action.arguments:
print(id,action.name,argument.name)
可以得到一系列的服务信息,部分信息如下
WANIPConn1 AddPortMapping NewRemoteHost
WANIPConn1 AddPortMapping NewExternalPort
WANIPConn1 AddPortMapping NewProtocol
WANIPConn1 AddPortMapping NewInternalPort
WANIPConn1 AddPortMapping NewInternalClient
WANIPConn1 AddPortMapping NewEnabled
WANIPConn1 AddPortMapping NewPortMappingDescription
WANIPConn1 AddPortMapping NewLeaseDuration
WANIPConn1 DeletePortMapping NewRemoteHost
WANIPConn1 DeletePortMapping NewExternalPort
WANIPConn1 DeletePortMapping NewProtocol
WANIPConn1 GetExternalIPAddress NewExternalIPAddress
这些服务大致可以分为 get 类和 set 类,以及少数的 delete 类服务。由于 UPnP 的主要目的之一是将内网设备暴露给公网设备,这就需要进行一定的配置(端口映射等),既然需要配置,那么配置的参数和信息必不可缺,而这些参数其实就是 set 类服务中的参数。
服务定位
由于并不是所有服务,厂商都有实现,因此需要自己逆向一下固件。首先定位到upnp_mainloop
所有的处理逻辑都是在 upnp_dispatch 中实现的,跟进分析发现了存在 ssdp 和 http 的请求处理部分
ssdp_process
对应寻址时 M-Search 的请求,upnp_http_process
对应 http 请求的处理,由于正常的服务调用都是 http 请求,因此判断upnp_http_process
可能存在漏洞,服务调用示例如下图所示
在upnp_http_process
中进一步调用了upnp_http_fsm_emgine
跟进分析,发现会执行 off_45ab80 处的几个函数
这些函数包括了初始化和解析协议头的功能,最后一个函数为upnp_http_fsm_dispatch
,猜测会执行对应的服务函数。
跟进upnp_http_fsm_dispatch
,发现确实调用了函数方法,不过是根据 a1 和 a2 执行的函数调用,无法确定具体的被调用函数,需要动态调试。
如果是正常的 ssdp 寻址请求那么会调用SUB_405B34
,description_process
,如果是服务相关请求,会调用soap_process
,在soap_process
中根据请求头信息,调用query_process
或者action_process
主要关注action_process
,根据服务调用的请求头,猜测action_process
中的soap_control
对应服务调用
在 soap_control 中仍然需要动态调试,确定具体的函数信息。
最终经过动态调试确定sub_414C28
对应AddPortMapping
action,其函数调用链为
sub_414c28->upnp_portmap_add->upnp_osl_nat_config->strcpy(stack overflow)
不过在 upnp_portmap_add 中检查了一下本机的 wan 口地址,由于笔者在测试的时候并没有配置 wan 口,所以直接用 nvram 设置了一下 wan 口 ip。
栈溢出的时候需要控制程序流走到红色的方块中,因为蓝色方块的函数会访问被溢出的栈导致程序在 RCE 之前崩掉,因此需要控制 *(a2+11) 的值为 0,幸运的是此处的值是可控的
0x03 漏洞利用
由于最新固件没有 telnetd,可以反弹 shell 然后自己上一个 utelnetd,然后开启 telnet
end
招新小广告
ChaMd5 Venom 招收大佬入圈
新成立组IOT+工控+样本分析 长期招新
欢迎联系admin@chamd5.org
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
随时掌握互联网精彩
- 1 澳门是伟大祖国的一方宝地 7940849
- 2 36岁女子看高血压查出怀孕34周 7925913
- 3 日本火山喷发灰柱高达3400米 7809295
- 4 中国为全球经济增长添动能 7737140
- 5 刘诗诗方辟谣离婚 7689279
- 6 女子8年生6个女儿第7胎再产女 7543138
- 7 肖战新片射雕英雄传郭靖造型曝光 7427284
- 8 女法官遇害案凶手被判死刑 7310619
- 9 蒋欣生图更是妈妈级别 7296335
- 10 太原市原市长耿彦波再获新头衔 7116709