题目简介

  • 题目名称:[HCTF 2018]WarmUp 1
  • 题目平台:BUUCTF
  • 题目类型:Web
  • 考察知识点:
    • 代码审计
    • 本地文件包含漏洞

解题步骤

  1. 启动并访问靶机,前端只有一个滑稽的表情

image.png

  1. 右击或F12审计一下代码,可以看到source.php,我们访问一下该页面

image.png

  1. 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\" />";
    }  
?>
  1. 可以看到include函数没有任何过滤措施,那么我们可以利用本地文件包含漏洞来访问任意文件,以获取flag,但是想要执行include函数,我们需要满足三个条件:
    1. file不为空
    2. file的值是字符串
    3. file通过emmm类的checkFile方法校验
  2. 第一和第二个条件都很简单,主要是第三个条件checkFile方法的校验规则非常麻烦,我们先来逐行解释一emmm类每一行代码的作用:
<?php
highlight_file(__FILE__);
class emmm
{
    //$page前面加&代表引用,这里是引用传递
    public static function checkFile(&$page)
    {
        //定义whitelist数组
        $whitelist = ["source"=>"source.php","hint"=>"hint.php"];

        //如果page变量未声明或为null,或者不是字符串,则进入if
        if (! isset($page) || !is_string($page)) {
            echo "you can't see it";
            return false;
        }
        //如果page的值在$whitelist数组里则进入if
        if (in_array($page, $whitelist)) {
            return true;
        }

        //截取从0到?首次出现的位置的字符串
        $_page = mb_substr(
            $page,
            0,
            mb_strpos($page . '?', '?') //点在php中用来连接两个字符串或变量,此代码意思为拼接page和?的值,并返回?字符首次出现的位置
        );

        //如果_page的值在$whitelist数组里则进入if
        if (in_array($_page, $whitelist)) {
            return true;
        }
        //对page解码
        $_page = urldecode($page);

        //截取从0到?首次出现的位置的字符串
        $_page = mb_substr(
            $_page,
            0,
            mb_strpos($_page . '?', '?')
        );
        //如果_page的值在$whitelist数组里则进入if
        if (in_array($_page, $whitelist)) {
            return true;
        }
        echo "you can't see it";
        return false;
    }
}
//如果file变量为空 且 是字符串  且 通过checkFile 校验,则进入if

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. 读懂了emmm类的代码后,我们需要构造file的值。本地文件包含漏洞的格式一般是file=../../../.../xxx,其中../的个数不固定,xxx为flag文件名,那么这个xxx具体是什么呢?我们尝试访问一下hint.php,发现提示:flag not here, and flag in ffffllllaaaagggg,可以猜测到服务器存放flag的文件名可能为ffffllllaaaagggg,我们可以构造的file=../ffffllllaaaagggg,将其带入checkFile中看一下是否可以返回true。
  2. 步骤一:因为我们定义了file的值且是string类型,所以第一个if 的return false 顺利绕过
if (! isset($page) || !is_string($page)) {
    echo "第一个if未通过";
    eturn false;
 }
  1. 步骤二:因为我们的file的值既不是souce.php也不是hint.php,所以是无法通过该if的,直接放弃这个return true的机会,继续往下看。
if (in_array($page, $whitelist)) {
  return true;
}
  1. 步骤三:mb_substr会将file的值通过一定的规则截取后,再次放入if判断,那么我们可以在file的值前面主动拼接一个souce.php?或者hint.php?,也就是file=source.php?../ffffllllaaaagggg,通过mb_substr截取后的值是souce.php,此时checkFile返回true
//截取从0到?首次出现的位置的字符串
$_page = mb_substr(
    $page,
    0,
    mb_strpos($page . '?', '?') //点在php中用来连接两个字符串或变量,此代码意思为拼接page和?的值,并返回?字符首次出现的位置
);


//如果_page的值在$whitelist数组里则进入if
if (in_array($_page, $whitelist)) {
    return true;
}
  1. 我们逐个添加…/的个数,最终发现当file=source.php?../../../../../ffffllllaaaagggg的时候,输出flag:flag{784155f9-68a0-4772-ac02-3eaa0105f3ac}

知识详解

一:php常见函数

  • isset:检测变量是否已声明并且其值不为 null
  • in_array:检查数组中是否存在某个值,第一个参数为待搜索的值,第二个参数为要搜索的数组
  • is_string:检测变量是否是字符串
  • empty:检查一个变量是否为空
  • R E Q U E S T : _REQUEST: REQUEST_REQUEST可以获取以POST方法和GET方法提交的数据
  • mb_strcut() :根据 start 和 length 参数返回 str 的一部分
  • mb_strpos():查找字符串在另一个字符串中首次出现的位置
  • urldecode(): 解码已编码的 URL 字符串
  • include():包含并运行指定文件
  • include_once() :include_once 表达式和 include 表达式完全相同,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含
  • require() :require 和 include 几乎完全一样,除了处理失败的方式不同之外。require 在出错时脚本会中止,而include会继续运行。
  • require_once():require_once 表达式和 require 表达式完全相同,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含。

二:文件包含漏洞

文件包含漏洞

漏洞原因:

  • php代码中使用了文件包含函数
  • 文件包含函数参数可以自定义且没有经过过滤

漏洞分类:

  • 本地文件包含漏洞(LFI):指包含本地服务器中的文件
    • 格式:?file=../../../xxxx
  • 远程文件包含漏洞(RFI):指包含远程服务器中的文件
    • php.ini中的allow_url_fopen = On和 allow_url_include = On 时,才可以利用该漏洞
    • 格式:?file=http://xxxx.xxx/xxxx
  • php中引发文件包含漏洞的通常是以下四个函数:
    • include():包含并运行指定文件
    • include_once() :include_once 表达式和 include 表达式完全相同,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含
    • require() :require 和 include 几乎完全一样,除了处理失败的方式不同之外。require 在出错时脚本会中止,而include会继续运行。
    • require_once():require_once 表达式和 require 表达式完全相同,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含。
    • 当利用这四个函数来包含文件时,不管文件是什么类型(图片、txt等等),都会直接作为php文件进行解析。[1]

参考资料

分享网络安全学习笔记和CTF真题详解,更多内容欢迎大家关注公众号:【张甲】

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐