论坛首页 漏洞分析研究区 阅读主题

[原创] cgibin中与upnp协议有关的一些漏洞分析与复现

100 浏览 6 回复
#1 楼主 2026-06-01 21:09:05
UPNP,全称为:Universal Plug and Play,中文为:通用即插即用,是一套基于TCP/IP、UDP和HTTP的网络协议。简单来说,就和它的名字一样,UPNP的目的就是为了在某个设备接入网络后,该网络中的所有设备都知道有新设备加入,这些设备之间能互相沟通,甚至可直接使用或控制对方。UPNP的一大亮点就是,只要某设备支持并开启了UPNP,当主机向其发出端口映射请求的时候,该设备就会自动为主机分配端口并进行端口映射。在D-Link,TRENDnet等apache struct的路由器的/htdocs目录下都存在一个cgibin二进制文件,它会有很多.cgi文件的软链接,通过运行这些软链接,其名字会作为第一个参数传入cgibin,就会调用到cgibin中对应的函数。cgibin会作为 “请求验证文件” ,对用户的请求进行验证并解析,再将解析后的数据传给对应的文件,进行下一步的操作。下图为UPNP协议栈的结构示意图: 可以看到其中的 SSDP(简单服务发现协议),SOAP(简单对象访问协议)与GENA(通用事件通知体系) ,其分别对应ssdpcgi(在/htdocs/upnp目录下),soap.cgi(在/htdocs/upnp/docs/LAN-1目录下),gena.cgi(在/htdocs/upnp/docs/LAN-1目录下),本文也主要是分析这几个cgi在cgibin中对应函数的漏洞。由于牵涉到UPNP协议,用qemu来模拟是比较复杂的,需要手动初始化一些东西,因此笔者为了方便,选择使用FAP来仿真模拟固件运行,这个平台基于firmadyne,对其做了一些优化及改进,GitHub的项目地址为:firmware-analysis-plus 。该平台的优点是:可以做到一键仿真模拟固件运行,缺点是:适配性较差,最好在Kali上安装使用,笔者所用的物理机是Kali 2021.11的版本。此外,经过笔者测试,该平台对大部分MIPS架构的固件模拟都没有问题,但是对部分ARM架构(特别是D-Link系列高版本路由)的固件模拟好像会出一些问题。关于ARM无法成功仿真模拟的问题,笔者已经在github上提交了issue咨询了作者,并且得到了回复: 笔者后来又找到了另一个优秀的“固件仿真框架”EMUX,这是一个基于docker的框架,主要针对于arm架构的仿真模拟,近期也支持了mips架构,根据官方的描述,可以对DIR860以上的arm架构路由进行模拟运行。2022.5.7更新:这篇文章其实是挺久前写的了,昨天刚发出来,今天就收到了FAP项目作者的回复,说是已经修复了D-Link系列高版本arm路由无法仿真的问题: 笔者立即测试了一下,的确是修复了该问题,接着,笔者又尝试用FAP模拟运行了TP-Link,Tenda等品牌中多款arm的固件,都能够成功。从目前各方面综合来看,FAP项目是仿真模拟IoT固件的极好的选择。注:以下复现的CVE所影响的路由器为D-Link DIR-859及以下的版本,以及部分D-Link DIR-859以上的较低小版本,TRENDnet的很多路由器因框架相同,也受其影响。漏洞信息:CVE-2020-15893这个CVE与ssdpcgi有关,我们先来分析cgibin中的ssdpcgi_main函数,可以很轻松地定位到可能的漏洞点在LABEL_17这里: 进入lxmldbc_system函数: 可以看到这里是用vsnprintf对传进来的格式化字符串进行了拼接,其中va是通过va_arg取当前栈上的元素组成的va_list,通过动态调试不难发现,这里取的栈上的元素就是存放在栈上的环境变量: 在真机环境中,这里只有HTTP_ST是我们可控的。当我们向HTTP_ST注入恶意指令,那么拼接好的字符串v6作为system参数,就可以导致任意命令执行(RCE)漏洞了。再回到ssdpcgi_main详细分析一下该如何构造payload: 可以发现,进行一堆匹配验证,最后的格式化字符串只有下面两个会多出一个参数%s的拼接,我们再看到汇编: 这里的第二个参数为/etc/scripts/upnp/M-SEARCH.sh,第三四个参数可以往上查找到,分别是REMOTE_ADDR和REMOTE_PORT: 结合格式化字符串,可以猜测并通过动调验证出,最后在lxmldbc_system函数中拼接好的system的参数应为 /etc/scripts/upnp/M-SEARCH.sh XXX REMOTE_ADDR:REMOTE_PORT SERVER_ID HTTP_ST & ,因此,想要造成RCE,也就是要让HTTP_ST拼接上去,就必须要选用后面两个格式化字符串(device和service),也就需要之前有urn:才行。综上,我们初步构造的payload可以是向HTTP_ST注入urn:device:;telnetd -p 8888,由于此busybox自带了telnetd,这里用telnetd开一个端口,再从主机远程登陆进去是最方便的。我们知道ssdpcgi和UPNP协议有关,也就是要发送报文到UPNP相关的端口,所以先用FAP模拟运行起固件,然后打开/var/run/httpd.conf文件,可以找到: 也就是说,要向1900端口发送报文,才能走到ssdpcgi。然而,发送一段报文,肯定是需要请求方式的,在cgibin中不好直接看出来,可以到/usr/sbin/upnp文件中去找ST字段的关键词定位: 可以看到sub_41BFDC函数中有对其的操作,再交叉引用到调用sub_41BFDC的sub_41C2A0函数,这里要求我们的请求方式是M-SEARCH: 上图中的v10是调用ILibParsePacketHeader对a1 + 108的数据包解析的结果,而a1 + 108是接收到的socket套接字储存的地方: 在sub_415C9C中也可以看到,把socket绑定到了1900端口: 再回到有对ST字段进行匹配操作的sub_41BFDC函数,可以看到首先需要绕过下面圈出的判断,这里的1.1显然就是HTTP版本: 综上,从upnp二进制文件中可以看到,我们得是M-SEARCH请求方式,故:报文头应为M-SEARCH * HTTP/1.1。POC:最终成功开启了8888端口,利用telnet远程登陆到了路由器固件中,可执行任意命令: 通过ps命令查看进程,可以发现telnetd -p 8888命令的确已经被成功执行: 漏洞信息:CVE-2019-17621这个漏洞与gena.cgi有关,还是先看到cgibin中的genacgi_main函数: 可以看到v5是service=后面的内容,再先看到SUBCRIBE请求方式对应的sub_41A390函数: 看到这里,拼接好的字符串v16作为了xmldbc_ephp的参数,xmldbc_ephp函数在这里显然就是运行了/htdocs/upnp/run.NOTIFY.php文件,于是,我们再来分析这个文件: 当SID为空的时候,调用了GENA_subscribe_new函数,这个函数在/htdocs/upnpinc/gena.php中: 这里有对H

...(已截断)

---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-272634.htm
#2 2026-06-01 21:09:05
师傅想问一下CVE-2022-25106 分析中,函数sub_40E9BC是怎么分析出来的,师傅用的是哪个版本的固件?在分析1.05b的时候,看不到sub_40E9BC这个函数
#3 2026-06-01 21:09:05
感谢分享,我何时才能变得和你一样优秀

最后于 2022-9-2 16:53
被pureGavin编辑

,原因:
#4 2026-06-01 21:09:05
太强了 我何时才能分析到这个程度
#5 2026-06-01 21:09:05
IOTの真神!
#6 2026-06-01 21:09:05
IOTの真神!
#7 2026-06-01 21:09:05
IOTの真神!

请登录后参与讨论

立即登录 注册账号