更新中… 11.1:还在调😅
Pre
漏洞信息:Cisco Small Business RV110W、RV130、RV130W 和 RV215W 路由器远程命令执行和拒绝服务漏洞。
UART调试
上一篇 CVE-2020-3331复现 用的设备是 RV110W,在升级固件的过程中发现有的固件没有开启 telnet 服务,有的会默认开启 telnet 服务。应该是厂商在打补丁更新的时候加入了 telnet 服务,又刚好把哈希明文写si在固件代码中,所以才能在较新的版本中通过 telnet 后门远程连接。
但并非每台设备、每个版本都这样。搜索 RV130W ,几乎所有的 CVE 里都没提到过 telnet 后门。我往里面刷了几个不同版本的固件,发现都没有开启。也许是出于安全问题的考虑,没有加上 telnet 后门。
除了远程连接调试的方式外,还可以通过 UART 口进行调试。参考:
RV130W 的 UART 口不太好找,于是我拿 110W 的对比了下。上一篇博客提到过,130W 比 110W 的网更快、更稳定、更安全。下图中左边是 RV130W,右边是 RV110W。从两者用的芯片型号、大小以及整个电路板的设计可以直观地感受出区别。整体布局还是很相似的(RV130W的芯片分布是我根据印刷的型号判断的,正确与否还得拆除散热片进行验证,这点留到复现完再做),最终找到的 UART 口都是在闪存旁边。
虽然 130W 的 UART 口附近只标注了电阻名,但我们可以借助 标注详细的 110W 的 UART口来分析。以下是 RV110W 上的 UART 口,可以判断出它用的是 RS232 标准。焊上排针后,用万用表分别测 110W 和 130W 的 UART 口,发现它们的顺序是一样的,只是整体排列方向不同。调试的时候用 TX、RX、GND 就够了。
连接到 PC 上需要用 FT232。买的时候商家一般会发使用手册,里面有驱动和串口调试工具。我用的是 HyperTerminal ,putty也行,支持串口通讯的都行。受这篇文章影响,一开始选择了默认的波特率9600,一直接收到乱码。后来想起在通信原理课上用过这个软件,其实收发数据的过程也是完整的信号调制-传输-解调的过程,波特率不一致就会导致解调结果错误。因此逐个波特率尝试了一遍,最终试出了正确的数值 。
开机的时候会打印出一些配置信息。因此我先关闭电源,串口通信连接成功后(此时会接收到一堆 \x00),再接通电源,这样就能接收到完整的开机信息了。
38400
UPnP协议
参考:
SSDP协议是UPnP协议的核心,主要是完成了服务发现的工作。消息类型可以分为 m-search 和 notify。SSDP 协议是 UDP 传输实现的,设备端总是在 239.255.255.250:1900
这个组播地址上监听。因为是建立在无连接基础上的传输,消息总是会不断地重复发送,我本地抓包的时候发现确实如此。
假设我们现在有一台 PC(控制点)和路由器(UPnP设备)。当 UpnP 设备进入网络时,会广播发送 NOTIFY 信息给网络上的所有设备。当PC接入网络的时候,首先通过路由器的 DHCP 服务获得局域网内的一个 ip,接着发送 ssdp 的 m-search 消息,寻找 UPnP 设备。ST 字段指定了搜索设备类型,抓包过程中发现一般都有网关设备、根设备这几种类型。
为了理清楚这些包之间的关系,我们可以用 upnpy 运行对应的服务看看。以下是在 ubuntu 上运行的discover服务,然后在 VMnet8 网卡截取流量。
1 | import upnpy |
一开始是从 ens33 网卡的 ip 发出 discover 包的,寻找的是根设备。但我们的虚拟机用的是 NAT 模式,这个包最终会通过 VMnet8 虚拟网卡转发出来。可以看到后续发的包全是代理转发的包,加上了 UASER-AGENT 字段,ST也变了。
随后在WLAN网卡抓取到由它进一步转发的包,ST类型变化如下:
1 | ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n |
查看前面定义的 devices 变量,发现可用设备为空。因为我们的测试环境是在校园网下的,没有开启upnp服务,也收不到答应包。但是我连接了路由器的 LAN 接口后,发现 devices 还是空。在以太网网卡截取不到ssdp流量,倒是从WLAN网卡转发出去了,应该是没跟 192.168.1.1 建立起 socket 连接的原因。
因此我们需要手动发送 discover 的 socket 包到网关上测试一下。根据前面的测试来看,本机发送的ST字段应该是根设备。当这个包被代理转发后,ST字段才会改变。
这里的脚本我用的是前面参考链接中的脚本。把每个变量输出一下大概知道得到的是什么内容了。简而言之就是:
- device:
Device <(device name)>
- device.get_services():service数组,有服务名和服务id两个元素,且名字相似。
- service.get_actions():action数组,有每个服务对应的功能和需要的参数。
1 | import upnpy |
从运行结果可以看到UPnP设备能提供的服务,基本都是与端口映射、建立连接相关的。随后能抓到路由器发来的回复包,说明这台路由器就是根设备。
端口扫描
UPnP 漏洞影响严重的其中一个原因是很多路由器都默认开次该服务,思科官网针对停产设备提出的解决方案是用户禁用此服务。在 1.2.44 的版本下测试的时候,我发现 RV130W 是默认关闭此服务的。打开后,只是多了一个 444 端口,似乎并没有什么用。
1 | nmap 192.168.1.1 |
尝试扫了所有端口,也没有结果。但是通过前面的流量分析可以肯定 UPnP 服务是开启的,找不到端口也没有关系。
漏洞程序
同一类路由器的设计思路可能是相似的,因此这里借助了《Cisco RV110w UPnP stack overflow》进行分析。
只开启了NX保护。从运行结果来看,程序用的是 uClibc 库。这个库可以通过buildroot交叉编译出来。
1 | file upnp |
IDA 打开后发现大部分函数都去了符号表。但是 RV110W 的 upnp 文件没有去除,可以用 bindiff 对比着还原些函数名(右键>import symbols
)。
ida对下面这段地址的解析出现错误,无法进行反汇编。观察一下汇编指令,基本没什么问题,但是从PUSH指令开始解析失败。在这里右键 create function就可以了。
这段程序还原符号表后,应该是 parse_uri 函数,结尾调用了ssdp_msearch_response,看来是msearch包响应的处理程序。下面这段代码使用了危险函数 strcpy:
1 | if ( !strcmp(device, "upnp:rootdevice") ) |
从 "upnp:rootdevice"
可以判断注入点在 ST 字段。但是传入 uuid 的时候,返回相应包之前会做一个检查。这里的 v11 具体是什么呢,我是用gdb调出来的。
1 | case 2: |
漏洞利用
参考:
- SSD Advisory – DD-WRT UPNP Buffer Overflow
- PSV-2020-0211-Netgear-R8300-UPnP栈溢出漏洞分析
- DD-WRT 45723 - UPNP Buffer Overflow (PoC)
……
调试
(跟上一篇的步骤差不多)
先上传 gdbserver。
发现 netstat 也没有 -p 参数,从 busybox官网 下载对应的版本再上传。
1 | uname -a |
测出偏移(160)
发短一些的数据时还是会崩,这次我们把断点下在strcpy函数 (0xb79c) 和 strcmp(0xb59c) .跟进分析找到了 strcmp 用的 id 号,为了通过检查把它写在payload头部,后面先用 ‘\x00’ 填充。
0x000B79C: strcpy
0x0000B764: call msearch
0x0000B59C: strcmp
0x0000B5B8: response
0x0000B770: return 0
发现这个设备跟 RV110W 一样,每个程序的 libc 基址都是固定的。所以我们同样地用 libc 的 gadget ,而不用程序本身的(地址含’\x00’,会被截断)。
en 调着调着win10 不给连了,“收集到未知错误信息”emm。调之前应该想好怎么调,减少错误,不然一直 reboot(
利用
Unsolved
官网发布的漏洞解决方案提到,鼓励用户迁移到 RV132W、RV160 或 RV160W 路由器,那么他们在代码上有什么变化呢?是直接把 UPnP 功能删除了还是用其他函数替代危险函数呢?
7.6对bindiff的支持挺好。
130W有三块芯片用屏蔽或散热片封起来,尚未确定具体是什么芯片。
怕拆坏,复现完再拆。