[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具體位置的話可以一層一層來試)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM