warm_up

warm_up

本来想着倒头睡觉的,但是万分愧疚,觉得得再做个题才行,于是点开了这道warm up

先谈谈感受吧,(自己还是懂的太少了),这种用到CVE的题,需要理解的知识点不是单一的,而且要有很缜密的思维,可以说是需要很多的经验积累的。加油吧!

看题,打开是个滑稽标签

1

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\" />";
    } 
 ?> flag not here, and flag in ffffllllaaaagggg

得安排时间学学代码审计的姿势了

提示代码在ffffllllaaaagggg中

看题,想要访问传入的file需要满足几个条件:

  • file非空
  • file是string
  • emmm::checkFile返回true
    • (checkFile返回true的条件)file能通过in_array()检验

前两个条件没有问题,但第三个没有思路,仔细看了两边代码,去查了下in_array()和mb_substr()

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

根据字符数执行一个多字节安全的 substr() 操作。 位置是从 str 的开始位置进行计数。 第一个字符的位置是 0。第二个字符的位置是 1,以此类推。

in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ) : bool

在array中查询needle,有则返回true。如果没有设置 strict ,那么比较时只会比较值,不会比较类型。

补充:in_array()函数的弱类型比较利用:

<?php

// Example array

$array = array(
  'egg' => true,
  'cheese' => false,
  'hair' => 765,
  'goblins' => null,
  'ogres' => 'no ogres allowed in this array'
);

// Loose checking -- return values are in comments

// First three make sense, last four do not

in_array(null, $array); // true
in_array(false, $array); // true
in_array(765, $array); // true
in_array(763, $array); // true
in_array('egg', $array); // true
in_array('hhh', $array); // true
in_array(array(), $array); // true

// Strict checking

in_array(null, $array, true); // true
in_array(false, $array, true); // true
in_array(765, $array, true); // true
in_array(763, $array, true); // false
in_array('egg', $array, true); // false
in_array('hhh', $array, true); // false
in_array(array(), $array, true); // false

?>

回到题目中,想尝试下in_array()弱类型的漏洞,但无法构造有效的payload,只好放弃,另寻他路。

搜索到一位前辈的wp,记录了解题思路和要点(感谢),也提到了这是CVE-2018-12613

下面是我在读过wp之后的结题思路:

bypass重点在checkFile函数,看函数的代码

  • 检测page的类型必须是set或者string
  • 传入page后,把page和白名单对比,如果在白名单里就直接返回true,不在则进入下一步,因为要访问ffffllllaaaagggg
  • 提取page里第一个问号前的字符串并拿来和白名单对比
  • urldecode,然后重复上一步的操作
  • 前边没有返回的话,最后会返回false

这里有个前置的知识点:

传入的参数(比如这里的hint.php)后边加上(可以是两次urlencode)任意字符和目录,那么服务器会把这个参数当作目录处理

例如:include 'x.x/../../../../../1.txt';,php会将include_path常量与该路径组合起来,即path = include_path.'x.x/../../../../../1.txt',然后去访问该文件,判断其是否存在,这个机制给予了我们实现目录穿越的机会。(x为除过截止字符、目录分隔符、系统不允许的空白字符之外的任意字符)

两次urlencode的原因是,服务器接收到数据包后会自动decode一次

既然有了访问目录的法子,就来想下怎么绕过检测函数呢?

这里刚好有问号,而检测函数里会截取file参数里问号之前的内容和白名单比对,所以两次urlencode的问号(一次被服务器decode,一次等函数中的decode)刚好符合情况。

函数中:

  • 第一次白名单没过
  • 提取的字符串等于本身(没有变化)
  • urldecode,让file里出现了问号
  • 提取了file中问号前的字符并与白名单比对,到这里就可以构造payload了

payload :

  • ?file=hint.php%253f/../../../../../../ffffllllaaaagggg(最后一次比对时返回true)
  • ?file=hint.php?/../../../../../../ffffllllaaaagggg(第一次比对时返回true)

自己的基础真的还差很多,肝!

原文地址:https://www.cnblogs.com/R3col/p/12452513.html