buu打开题目进去看到,只有一行字
只有一行字的话,那么没有可用的信息,那么可以去查看一下源码或者是看一下network里是不是有一些提示,发现都没有,那就利用dirsearch去扫描一下目录
python dirsearch.py -u http://f8716353-f295-45aa-8f8a-d45e2e09ec3c.node5.buuoj.cn:81/
当我们利用dirsearch去扫描目录时,我们发现是存在git信息泄露的,那么接着利用GitHack工具去得到泄露的文件
python GitHack.py http://f8716353-f295-45aa-8f8a-d45e2e09ec3c.node5.buuoj.cn:81/.git
得到的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__);
?>
根据index.php可以看到有三层过滤,第一层if就是简单正则匹配,也就是GET传入的exp中不能存在data: filter: php: phar: 也就是说利用php伪协议这条路应该是不通了。
再来看第二层if,先来解释一下preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])
这部分代码的作用是对传入的exp参数进行正则匹配和替换操作,以确保该参数只包含函数调用,并且这些函数调用的结果最终等于一个分号(;)。具体来说,它使用正则表达式来检测并替换所有符合特定格式的函数调用,然后检查剩余的字符串是否等于分号。
正则表达式解释
preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])
/[a-z,_]+\((?R)?\)/:
[a-z,_]+:匹配一个或多个小写字母、下划线或逗号。
\(:匹配一个左括号(。
(?R)?:递归匹配自身。这里是一个复杂的正则表达式,用于匹配嵌套的括号。(?R)表示递归调用正则表达式本身,用于匹配嵌套的函数调用。
\):匹配一个右括号)。
正则表达式的作用是匹配像func1(func2())这样的嵌套函数调用。preg_replace解释
preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])
preg_replace函数用来将匹配到的部分替换为NULL,即删除匹配到的部分。它会删除所有符合正则表达式的函数调用。
判断是否仅剩分号
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp']))
这行代码的逻辑如下:
使用正则表达式/[a-z,_]+\((?R)?\)/匹配并删除$_GET['exp']中的所有函数调用。
检查删除后的结果是否等于一个分号';'。示例
让我们看一些示例来更好地理解这个逻辑:输入
$_GET['exp'] = 'foo();'
正则表达式匹配并删除foo();,剩下的字符串是';'。
检查';' === ';',条件为真。
输入$_GET['exp'] = 'foo(bar());'
正则表达式匹配并删除foo(bar());,剩下的字符串是';'。
检查';' === ';',条件为真。
输入$_GET['exp'] = 'foo();bar();'
正则表达式匹配并删除foo();,剩下的字符串是'bar();'。
正则表达式再次匹配并删除bar();,剩下的字符串是''。
检查'' === ';',条件为假。需要注意的是
preg_replace
函数只是用于检查条件,并不改变实际传入的参数$_GET['exp'],所以下面的@eval($_GET['exp']);还是可以去执行传入的参数的
我们可以去借助php内置的一些函数来实现读取flag.php,下面是payload需要用到的php函数:
localeconv():
这个函数返回一个数组,包含当前区域设置中与数字格式化相关的信息。例如,千位分隔符,小数点符号等。
current():这个函数返回数组中的当前元素。默认情况下,如果没有先前调用过next()、prev()、reset()等函数,current()返回数组的第一个元素。
在这里,它会返回localeconv()返回的数组的第一个元素。
scandir():这个函数列出指定目录中的文件和目录。
如果传递的参数是一个有效目录路径,它将返回该目录下的文件和目录列表。next():
将数组的内部指针向前移动一位,并返回移动后的当前元素。
print_r():
这个函数输出信息,以便于阅读。它可以打印数组和对象的详细结构。
highlight_file()
:输出指定文件的内容,并且语法高亮显示。(这里用来显示flag.php的内容)
当我们的payload是/index.php?exp=print_r(scandir(current(localeconv())));
可以看到数组的第二个是flag.php,payload加上next()也就是/index.php?exp=print_r(next(scandir(current(localeconv()))));试一下看看能到哪个文件的位置
可以看到已经指向了flag.php文件这里,只需要将print_r函数换成 highlight_file函数来读取flag.php的内容即可。于是paylaod就是/index.php?exp=highlight_file(next(scandir(current(localeconv()))));