Fork me on GitHub

[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师傅发表在合天上面的文章)

  先介绍使用到的函数

  1. localeconv():localeconv()返回一包含本地数字及货币格式信息的数组。这个数组的第一项就是我们需要的"."
  2. current():current()返回数组中的单元,默认取第一个值,在本题中我们只需要使用localeconv(current())便可以构造出我们一直念念不忘的"."

  

payload:?exp=print_r(scandir(current(localeconv())));

 

 

 

  得到以下回显:

  此时我们已经得到该目录下的所有文件,可以看到第四个文件是index.php,而第三个文件就是我们所需要读取的flag.php.接下来介绍我们需要用到的另外两个函数:

  1. array_reverse():将输入的数组反向排序输出,在本题中将index.php作为数组的第一个元素,flag.php作为数组的第二个元素.
  2. 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}

 

 

 

 

 

posted @ 2020-09-17 21:07  M1saka_L1gh73r  阅读(275)  评论(0编辑  收藏  举报