WarmUp

进来一个滑稽表情包,查看页面源码,得到提示

<-- source.php -->

访问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\" />";
    }  
?> 

审计一下php代码,需要使用GET方式提交一个 $file 变量,进行文件包含。

看到白名单,随即访问hint.php,得到ffffllllaaaagggg文件名。

多次if语句判断,第一次判断是否存在 $page 变量且是否为字符串,简单绕过。

第二层 if 用到了bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ) 函数:

如果满足了就 return true。关于 bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ) 函数有它的设计缺陷,当进行弱比较的时候会进行弱类型转换,从而绕过对数字的审查。大致可以看这篇参考文章:PHP代码审计Day1 - in_array函数缺陷

在这里由于数组类型是字符串类型,所以不存在这样的弱类型转换漏洞,这里如果需要返回 true,则必须 $page 的值为 hint.php 或者 source.php。

这部分用到了两个比较新的 php 函数:

substr() 函数不同的是,mb_substr() 通常用来分割多种字符,用于兼容字符集。例如:

这看起来似乎更符合中国人的习惯。

该函数用来返回字符串 $needle$haystack 字符串中第一次出现的位置。这里就存在绕过,只要题目中的字符串中出现问号,就可以对字符串进行截断,从而绕过白名单的审查。

于是只要构造这样的字符串:

就可以绕过白名单审计,返回 true

接下来就是简单的目录穿越完事,可以先读取 /etc/passwd:

穿越回根目录经过五次,大概推测文件结构,然后读取flag。

Last updated