• [BUUOJ记录] [HCTF 2018]WarmUp


    BUUOJ Web的第一题,其实是很有质量的一道题,但是不知道为什么成了Solved最多的题目,也被师傅们笑称是“劝退题”,这道题的原型应该是来自于phpMyadmin的一个文件包含漏洞(CVE-2018-12613)

    解题过程

    解题思路

    进入题目查看源代码发现提示:

    跟进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" />";
        }  
    ?> 

    代码分为两部分来看,第一部分是定义了emmm类的一个checkFile函数,用来检查传入的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'])    //用$_REQUEST来接收file参数,如果file参数的值不为空、为字符串、可以通过emmm类checkFile函数检测,则包含该文件
        ) {
            include $_REQUEST['file'];
            exit;
        } else {
            echo "<br><img src="https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg" />";
        }

    在emmm类的checkFile函数中我们可以看到白名单中有两个文件:

    $whitelist = ["source"=>"source.php","hint"=>"hint.php"];

    包含一下hint.php文件来试试 /source.php?file=hint.php

    提示我们Flag在ffffllllaaaagggg中,那么我们解题的关键就是如何包含这个在whitelist外的文件

    从第二部分代码我们可以看出,重点是如何让ffffllllaaaagggg通过checkFile的检查,因此我们就需要从emmm类的checkFile函数入手

    前置知识点

    我们在读这个函数的代码前需要提前了解几个checkFile函数中出现的函数:

    1. in_array() 函数搜索数组中是否存在指定的值

     更多信息参考:https://www.w3school.com.cn/php/func_array_in_array.asp

     2. mb_substr() 函数返回字符串的一部分,之前我们学过 substr() 函数,它只针对英文字符,如果要分割的中文文字则需要使用 mb_substr()

    更多信息参考:https://www.runoob.com/php/func-string-mb_substr.html

    3. mb_strpos() 查找字符串在另一个字符串中首次出现的位置

    更多信息参考:https://www.php.net/manual/zh/function.mb-strpos.php

    核心代码分析

    在了解了上面的几个小点之后我们返回来看核心代码:

        class emmm
        {
            public static function checkFile(&$page)
            {
                $whitelist = ["source"=>"source.php","hint"=>"hint.php"];
                if (! isset($page) || !is_string($page)) {  //page必须不为空或为字符串
                    echo "you can't see it";
                    return false;
                }
    
                if (in_array($page, $whitelist)) {  //in_array()检测page是否在whitelist中
                    return true;
                }
    
                $_page = mb_substr(  //如果page含有?,则获取page第一个?前的值并赋给_page变量
                    $page,
                    0,
                    mb_strpos($page . '?', '?')
                );
                if (in_array($_page, $whitelist)) {  //检测_page是否在whitelist中
                    return true;
                }
    
                $_page = urldecode($page);  //给_page二次赋值,使其等于URL解码之后的page
                $_page = mb_substr(
                    $_page,
                    0,
                    mb_strpos($_page . '?', '?')  //如果_page有?,则截取_page(URL解码后的page)两个?中间的值
                );
                if (in_array($_page, $whitelist)) {  //检测_page是否在whitelist中
                    return true;
                }
                echo "you can't see it";
                return false;
            }
        }

    逻辑可能看着有点乱,所以我们梳理一下逻辑,这里引用王叹之师傅的分析:

    可以看到函数代码中有四个if语句:

    第一个if语句 对变量进行检验,要求$page为字符串,否则返回false  //因为返回False所以这里无用
    第二个if语句 判断$page是否存在于$whitelist数组中,存在则返回true
    第三个if语句 判断截取后的$page是否存在于$whitelist数组中,截取$page中'?'前部分,存在则返回true
    第四个if语句 判断url解码并截取后的$page是否存在于$whitelist中,存在则返回true
    若以上四个if语句均未返回值,则返回false

    有三个if语句可以返回true,第二个语句直接判断$page,不可用

    第三个语句截取'?'前部分,由于?被后部分被解析为get方式提交的参数,也不可利用

    第四个if语句中,先进行url解码再截取,因此我们可以将?经过两次url编码,在服务器端提取参数时解码一次,checkFile函数中解码一次,仍会解码为'?',仍可通过第四个if语句校验。

    只要这四个if语句有一个为true即可包含file,关键点在_page 经过截断后返回true.

    所以我们的突破点就在于第四个if语句中,只要满足他的条件,我们就可以包含文件:

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

    这里URL解码了一次$page值,这里需要注意的是,PHP中$_GET、$_POST、$_REQUEST这类函数在提取参数值时会URL解码一次

    而这里在代码中又一次URL解码了一次,共计解码了两次,所以我们也需要对传入的值进行两次URL编码

    其次我们的突破点就在于这段代码只会截取?之前的字符串拿去和whitelist比对,因此只要确保?前的值是source.php或hint.php即可返回true

    等同于 /source.php?file=source.php%253F123456 便可以使用include()函数包含 source.php?123456 这个文件(%253f是?URL编码两次后的值)

    所以可以构造Payload:

    /index.php?file=source.php%253F/../../../../ffffllllaaaagggg

    需要注意的是,这里之所以可以包含到ffffllllaaaagggg是因为PHP将 source.php%253F/ 视作了一个文件夹,然后 ../ 的用途是返回上级目录

    ffffllllaaaagggg位于根目录下,一般Web服务的文件夹在/var/www/html目录中,再加上source.php?/这个“文件夹”,所以我们总共需要../四次来返回到根目录

    (如果比赛中不知道flag具体位置的话可以一层一层来试)

  • 相关阅读:
    Spark权威指南(中文版)----第11章 Datasets(1)
    左右侧滑动窗口
    解决右侧滑动窗口溢出的问题
    y轴的文字左对齐
    解决echarts图表宽度不够字符被覆盖问题
    Helloworld.JaVa 第一次编程
    二叉排序树:BST: (Binary Sort(Search) Tree)
    赫夫曼编码码(Huffman Coding)
    赫夫曼树(Huffman Tree)
    堆排序
  • 原文地址:https://www.cnblogs.com/yesec/p/12635274.html
Copyright © 2020-2023  润新知