Some Tricks of Bypass php waf

起因是年前看了一篇How To Exploit PHP Remotely To Bypass Filters & WAF Rules,现在搜了一下发现已经有翻译了。感觉升华也没什么好扩展的,也不太好拿去投稿了,思考了一下,感觉还是当作学习笔记来写算了。

以下实现环境均在 php 7.0.31 版本上,并且把 waf 因素考虑在内。

引入

首先来看一段 php 代码:

<?php
if(preg_match('/system|exec|passthru/',$_GET['code'])){
    echo 'invalid syntax';
}
else {
    eval($_GET['code']);
}

执行命令不只这几个函数,还有很多,这里就拿这几个来举例。毫无疑问,这里肯定是可以执行命令。

那我们先直接尝试读取/etc/passwd,显然,这里还没到代码层面就直接被 waf 了。

原因可能是因为/etc/passwd的敏感原因

绕过 waf

那我们接下来先尝试绕过 waf ,可以尝试使用/etc/pas\swd绕过,也可以使用其他空变量,当然方法很多

可以发现,我们是成功绕过 waf ,来到了代码层面,接下来我们就需要考虑怎么绕过命令执行函数了

PHP 转义序列

在谈绕过函数过滤前,我们先了解一下 PHP 的转义序列,简单来说就是使用反斜线转义各种字符形成特定的意义。比如说:

  • \040 空格的另外一种用法
  • 以八进制表示的\[0–7]{1,3}转义字符会自动适配二进制字符(如\377,八进制377是10进制255, 因此代表一个全1的字符)
  • \x[0–9A-Fa-f]{1,2} 表示十六进制转义字符表示法(如\x41
  • 以Unicode表示的\u{[0–9A-Fa-f]+}字符,会输出为UTF-8字符串

虽然看起来很普通,但是我们可以使用各种语法来表示字符串,再配合 php 可变函数来绕过各种防御

php 可变函数

​ PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。

可变函数不能用于例如 echoprintunset()isset()empty()includerequire 以及类似的语言结构。需要使用自己的包装函数来将这些结构用作可变函数。

看一下官方示例

<?php
function foo() {
    echo "In foo()<br />\n";
}

function bar($arg = '') {
    echo "In bar(); argument was '$arg'.<br />\n";
}

// 使用 echo 的包装函数
function echoit($string)
{
    echo $string;
}

$func = 'foo';
$func();        // This calls foo()

$func = 'bar';
$func('test');  // This calls bar()

$func = 'echoit';
$func('test');  // This calls echoit()
?>

也就是说$var(args)“string”(args);是与function(args)等效的。于是我们可以有

尝试"\x73\x79\x73\x74\x65\x6d"("whoami")

确实可以执行无误,然后我们就可以利用这个特性绕过对system的检测了

改进输入检测

上述我们的 payload 中还是用到了双引号,而大多数的时候 waf 不会允许使用双引号的,我们将 php 文件内容修改为

<?php
if(preg_match('/system|exec|passthru|[\"\']/',$_GET['code'])){
    echo 'invalid syntax';
}
else {
    eval($_GET['code']);
}

增加了对单引号与双引号的检测,这时候应该怎么办呢

让我们再来看一个特性

也就是说在 PHP 中,字符串表达可以有以上这几种方法,于是我们可以用以上的方式尝试替代双引号。第一种是类似(system)(whoami);,然而在 php 中我们可以用.来拼接字符串,于是也可以有(sys.(te).m)(whoami);

也可以使用/?a=system&b=ls&code=$_GET[a]($_GET[b]);的解析方法来绕过

这里我们还可以使用其他技巧,比如我们可以在函数名和参数内插入注释(这种方法在绕过某些WAF规则集方面非常有用,这些规则集会拦截特定的PHP函数名

php -r "echo/*this is a comment*/(foo);"
php -r "system/*this is a comment*/(whoami);"
php -r "system/*this is a comment*/(wh./*foo*/(oa)/*bar*/.mi);"
php -r "(s/*foo*/.(ys)./*bar*/tem)/*this is a comment*/(wh./*foo*/(oa)/*bar*/.mi);"

get_defined_functions

​ (PHP 4 >= 4.0.4, PHP 5, PHP 7)

​ get_defined_functions — 返回所有已定义函数的数组

​ get_defined_functions ([ bool $exclude_disabled = FALSE ] ) : array

​ 获取所有已定义函数的数组。

这个函数是可将用户定义的和内置函数均返回的。获取内置函数可以使用$arr[“internal”],获取用户定义的函数可以使用$arr[“user”]

所以我们可以尝试找到system函数

配合前面的方法,效果拔群,不过system函数不一定都是 1077 ,使用的时候最好可以grep查找一下system函数的下标

字符数组

我们可以将PHP中的每个字符串当成一组字符来使用(基本上与 Python 相同),并且我们可以使用$string[2]或者$string[-3]语法来引用单个字符。这种方法也有可能绕过基于PHP函数名的防护规则。比如,我们可以使用$a="eimstdy";这个字符串构造出system("id");语句。需要空格的话就在$a中加入空格就好了。

也可以使用一些内置变量,不再赘述,如下图所示

参考

How To Exploit PHP Remotely To Bypass Filters & WAF Rules

Sqli-lab速刷记录(1-53) Hexo Template render error 解决方案

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×