无参数RCE
一道bytectf的题和一道GXYCTF的题,主要内容为bytectf的wp。两题均为无参数的命令执行,正则匹配如下
/[a-z]+\((?R)?\)/
/[a-z,_]+\((?R)?\)/
第一个较为严格,只允许纯字母函数的无参数调用。
第二个允许了下划线,和逗号,难度直线下降,允许了有参数的函数调用,即a(b(),c())
,允许匹配到,c()
这样子的函数名
(?R)表示引用当前正则表达式,?匹配一次或零次
函数获取字符串
各种各样的神仙技巧,要求对各个函数的功能足够熟练才能做出来了,fuzz跑出内置函数然后做数学题或者各种各样的变换,我只能列一下学到的知识了
获取’.’
思路一
第一步,用chr(47)获取.
,第二步,想办法无中生有一个数字出来,大佬的fuzz跑出来了一个phpversion函数,返回一个当前版本号,7.xxx,然后开始做数学题(我不太清楚这个数学题怎么做的)ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))
这么大一串是47
这样子我们就可以访问当前目录了
思路二
localeconv()函数返回数组的第一位是.
,就能直接current(localeconv())拿到
数组操作有一系列的函数next(),currnt(),array_reverse()等,可以查询array相关函数获得很多信息,还可以利用array_flip()交换键值array_rand()获取随机键以取得任意数组元素
思路三
用crypt函数对字符串进行加密,有概率在最后一位出现一个.
,然后strrev翻转字符串,chr(ord())取得第一位,概率获得一个.
chr(ord(strrev(crypt(($arg)))))
这里的arg好像是啥都行,数字,bool值都可以
所以用phpversion()也好,接着chdir()也好,都能用,就是概率性问题
还有见到用serialize(array())
来获取字符串的
跳完目录还得再获取一个’.’
需要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二进制十六进制应该也能整出数字来