无参数RCE就类似与在我们经常使用的一句话木马前面,加上了对对参数的过滤,过滤的正则一般类似于一道ctf题目给的这种。
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp']){
eval($_GET['exp']);
}
[a-z,_]+ : 匹配小写字母和下划线_ 1次或多次
\( : 左括号
(?R) : 代表当前表达式,就是这个(/[a-z,_]+\((?R)?\)/),所以会一直递归
(?R)? : 递归当前表达式0次或1次
// (?R)* : 递归当前表达式0次或多次
\) : 右括号
这个正则只会匹配到
a();
a(b());
a(b(c()));
带上参数比如system('ls /');是匹配不到的,过不了判断条件,所以我们只能使用没有参数的函数来进行远程代码执行
我在理解这个正则的时候碰到了一些问题,下面是我的理解和测试的一些结果:
1.首先,这个正则是怎么匹配的呢,顺序是怎么样的
比如a(b(c()))这个格式的函数匹配的顺序如下:
整个正则可以分为三部分
一 [a-z,_]+\( 直到递归前面可以看作一个匹配整体
二 (?R)? 递归部分看作一个匹配整体
三 \) 递归后面的看作一个匹配整体
开始匹配,下面是匹配的顺序
a( //匹配上面第一个匹配整体,然后进入第一次递归
b( //匹配上面第一个匹配整体,进入第二次递归
c( //匹配上面第一个匹配整体,进入第三次递归
//没有匹配第一个匹配整体,递归停止
) //匹配第二次递归剩下的表达式,即第三个匹配的整体\),
) //匹配第一次递归剩下的表达式,即\),
) //匹配原表达式剩下的表达式,即\),
2.(?R),(?R)?,(?R),(?R)+的区别
首先(?R) , (?R)+ 这两个表达式是匹配不到东西的,因为每次匹配的时候都会至少运行一次递归,无法终止,所以匹配不到任何东西。
(?R)?,递归0次或1次,非贪婪,只能匹配a(b())这种一层套一个函数的。
(?R)\,递归0次或多次,贪婪,可以匹配a(b(c()d()))
利用正则表达式递归过滤
参考:https://blog.csdn.net/technofiend/article/details/49906755
利用诸多php函数嵌套实现RCE
参考:https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/#%E4%BB%80%E4%B9%88%E6%98%AF%E6%97%A0%E5%8F%82%E6%95%B0%E5%87%BD%E6%95%B0RCE
个人理解,欢迎指导
下面这些函数有一部分是我从上面大佬的博客整理来的,方便进行整理和翻阅,再进行构造无参数函数RCE的时候可以相互组合使用。
类似的还有:
将整个文件读入一个字符串
读取文件并写入到输出缓冲。
语法高亮一个文件
列出指定路径中的文件和目录
给出一个包含有指向一个文件的全路径的字符串,本函数返回去掉文件名后的目录名。
取得当前工作目录。
将 PHP 的当前目录改为 directory。
此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。
获取一个环境变量的值
返回一包含本地数字及货币格式信息的数组,第一个值一直是.
获取当前的PHP版本
获取/设置当前会话 ID
启动新会话或者重用现有会话
还有一些可以用到的函数,
chr():返回指定的字符
rand():产生一个随机整数
打开网站,只有一句话,其它什么也没有,直接用字典扫dir,发现有.git,使用githacker进行提取。拿到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__);
?>
发现有三层正则匹配,第一层过滤了一些伪协议,第二层就是无参数化了,第三层过滤了一些敏感函数(get,phpinfo,hex2bin,dirname……)。完全绕过后就可以执行代码了。
先得到flag.php文件名。
scandir(‘.’)
. = current(localeconv())
scandir(current(localeconv()))查看当前目录
array_rand(array_flip())每次读取一个随机的文件名,多试几次就是flag.php
再读取文件
最后使用readfile或highlight_file读取文件
payload
?exp=print_r(highlight_file(array_rand(array_flip(scandir(current(localeconv()))))));
先使用session_start()打开session服务,然后使用session_id() 可以获取到当前的 session id。
因此我们手动设置名为 PHPSESSID 的 cookie,并设置值为 flag.php
也可以用它执行部分命令
参考 https://www.gem-love.com/ctf/530.html?replytocom=5
这个也是我博客的一篇文章
用户名 | 金币 | 积分 | 时间 | 理由 |
---|---|---|---|---|
veek | 100.00 | 0 | 2020-12-28 09:09:02 | 期待更多干货~ |
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.