Cisco RV110W UPnP 0day 分析

百家 作者:Chamd5安全团队 2021-10-02 12:07:29

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/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接