打开页面,得到了一只滑稽。

这种情况下,一般就只有两种方法:

  • 打开F12看源码
  • 抓包

打开F12得到source.php

<?php
    highlight_file(__FILE__);
    class emmm
    {
        public static function checkFile(&$page)
        {
            $whitelist = ["source"=>"source.php","hint"=>"hint.php"];
            if (! isset($page) || !is_string($page)) {
                echo "you can't see it";
                return false;
            }

            if (in_array($page, $whitelist)) {
                return true;
            }

            $_page = mb_substr(
                $page,
                0,
                mb_strpos($page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }

            $_page = urldecode($page);
            $_page = mb_substr(
                $_page,
                0,
                mb_strpos($_page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
            echo "you can't see it";
            return false;
        }
    }

    if (! empty($_REQUEST['file'])
        && is_string($_REQUEST['file'])
        && emmm::checkFile($_REQUEST['file'])
    ) {
        include $_REQUEST['file'];
        exit;
    } else {
        echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
    }  
?> 

又是代码审计orz....

那么简单审计一下,这里采用了白名单的形式,而且又是一道文件包含题目(我为什么要说又呢)

根据白名单,先打开hint.php试试,得到了一个hint:

flag not here, and flag in ffffllllaaaagggg

得到了flag在ffffllllaaaagggg

很明显,我们需要越权访问。

这里有几个关键点:

  • 变量$whitelist也就是白名单里规定了只能通过source.phphint.php
  • checkFile(&page)函数里会检查$page
    • $page为空或者不是字符串会返回false
    • $page在白名单里会直接返回true
  • $_page里又对$page进行处理,检查的是$page?前的东西并取出。
 mb_substr()返回字符串的一部分,
eg: echo mb_substr("kodosan",0,4); 
// 输出 : kodo
mb_strpos()返回查找的字符在其字符串首次出现的位置
eg : echo mb_strpos("kodosan",'o');
// 输出 : 1 

也就是说,$_page取出的是$page从0到?前的所有字符

  • 然后再对$_page进行urldecode处理
  • 进行上面相同的操作
  • $_page进行白名单校验,如果在白名单中返回ture。否则返回"you can't see it"

然后最为关键的来了

  if (! empty($_REQUEST['file'])
        && is_string($_REQUEST['file'])
        && emmm::checkFile($_REQUEST['file'])
    ) {
        include $_REQUEST['file'];
        exit;

在if里对file进行检查,file需要满足三个条件

  • $_REQUEST['file']不为空
  • $_REQUEST['file']是字符串
  • $_REQUEST['file']需要通过emm类的checkFile()检查

才能包含成功。

分析完成,下面来进行payload的分析

首先,我们要让file通过白名单的检查,就要先构造source.php?让我们通过白名单。然后根据hint,flag在ffffllllaaaagggg下,很容易考虑到要用../来表示上一级目录的文件或文件夹,所以payload应该为:

source.php?file=source.php?../../../../../ffffllllaaaagggg经过测试,至少需要五个../才能读到flag。

根据上述函数以及服务的编码问题,我们要讲?进行两次url编码。

我们不妨逆向思维一下,当传入%253f,也就是?两次编码后的值,服务器会先进行解码,变为%3f,经过urldecode就会变为?

所以如下payload也是可以的:

source.php?file=source.php%253f../../../../../ffffllllaaaagggg

参考:phpmyadmin 4.8.1 远程文件包含漏洞(CVE-2018-12613)


花びらは笑った.