溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

php unserialize反序列化漏洞分析

發(fā)布時(shí)間:2022-03-15 14:34:00 來源:億速云 閱讀:613 作者:iii 欄目:網(wǎng)絡(luò)管理

今天小編給大家分享一下php unserialize反序列化漏洞分析的相關(guān)知識點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

題目如下:

php unserialize反序列化漏洞分析

漏洞解析: (上圖代碼第11行正則表達(dá)式應(yīng)改為:'/O:\d:/')

題目考察對php反序列化函數(shù)的利用。在第10行 loadData() 函數(shù)中,我們發(fā)現(xiàn)了 unserialize 函數(shù)對傳入的 $data 變量進(jìn)行了反序列。在反序列化前,對變量內(nèi)容進(jìn)行了判斷,先不考慮繞過,跟蹤一下變量,看看變量是否可控。在代碼 第6行 ,調(diào)用了 loadData() 函數(shù),$data變量來自于 __construct() 構(gòu)造函數(shù)傳入的變量。代碼第32行,對 Template 類進(jìn)行了實(shí)例化,并將 cookie 中鍵為'data'數(shù)據(jù)作為初始化數(shù)據(jù)進(jìn)行傳入,$data數(shù)據(jù)我們可控。開始考慮繞過對傳入數(shù)據(jù)的判斷。

代碼 11行 ,第一個(gè)if,截取前兩個(gè)字符,判斷反序列化內(nèi)容是否為對象,如果為對象,返回為空。php可反序列化類型有String,Integer,Boolean,Null,Array,Object。去除掉Object后,考慮采用數(shù)組中存儲對象進(jìn)行繞過。

第二個(gè)if判斷,匹配 字符串為 \'O:任意十進(jìn)制:',將對象放入數(shù)組進(jìn)行反序列化后,仍然能夠匹配到,返回為空,考慮一下如何繞過正則匹配,PHP反序列化處理部分源碼如下:

php unserialize反序列化漏洞分析

php unserialize反序列化漏洞分析

php unserialize反序列化漏洞分析

在PHP源碼var_unserializer.c,對反序列化字符串進(jìn)行處理,在代碼568行對字符進(jìn)行判斷,并調(diào)用相應(yīng)的函數(shù)進(jìn)行處理,當(dāng)字符為'O'時(shí),調(diào)用 yy13 函數(shù),在 yy13 函數(shù)中,對‘O‘字符的下一個(gè)字符進(jìn)行判斷,如果是':',則調(diào)用 yy17 函數(shù),如果不是則調(diào)用 yy3 函數(shù),直接return 0,結(jié)束反序列化。接著看 yy17 函數(shù)。通過觀察yybm[]數(shù)組可知,第一個(gè)if判斷是否為數(shù)字,如果為數(shù)字則跳轉(zhuǎn)到 yy20 函數(shù),第二個(gè)判斷如果是'+'號則跳轉(zhuǎn)到 yy19 ,在 yy19 中,繼續(xù)對 +號 后面的字符進(jìn)行判斷,如果為數(shù)字則跳轉(zhuǎn)到 yy20 ,如果不是則跳轉(zhuǎn)到 yy18 , y18 最終跳轉(zhuǎn)到 yy3 ,退出反序列化流程。由此,在'O:',后面可以增加'+',用來繞過正則判斷。

繞過了過濾以后,接下來考慮怎樣對反序列化進(jìn)行利用,反序列化本質(zhì)是將序列化的字符串還原成對應(yīng)的類實(shí)例,在該過程中,我們可控的是序列化字符串的內(nèi)容,也就是對應(yīng)類中變量的值。我們無法直接調(diào)用類中的函數(shù),但PHP在滿足一定的條件下,會自動觸發(fā)一些函數(shù)的調(diào)用,該類函數(shù),我們稱為魔術(shù)方法。通過可控的類變量,觸發(fā)自動調(diào)用的魔術(shù)方法,以及魔術(shù)方法中存在的可利用點(diǎn),進(jìn)而形成反序列化漏洞的利用。

在代碼31行,對象銷毀時(shí)會調(diào)用 createCache() 函數(shù),函數(shù)將 $template 中的內(nèi)容放到了 $cacheFile 對應(yīng)的文件中。 file_put_contents() 函數(shù),當(dāng)文件不存在時(shí),會創(chuàng)建該文件。由此可構(gòu)造一句話,寫入當(dāng)前路徑。

$cacheFile$template 為類變量,反序列化可控,由此,構(gòu)造以下反序列化內(nèi)容,別忘了加'+'號

php unserialize反序列化漏洞分析

放入cookie需進(jìn)行URL編碼

a:1:{i:0;O:+8:"Template":2:{s:9:"cacheFile";s:10:"./test.php";s:8:"template";s:25:"<?php eval($_POST[xx]);?>";}}

文件成功寫入:

php unserialize反序列化漏洞分析

實(shí)例分析

本次實(shí)例分析,選取的是 Typecho-1.1 版本,在該版本中,用戶可通過反序列化Cookie數(shù)據(jù)進(jìn)行前臺Getshell。該漏洞出現(xiàn)于 install.php 文件 230行 ,具體代碼如下:

php unserialize反序列化漏洞分析

在上圖代碼 第3行 ,對Cookie中的數(shù)據(jù)base64解碼以后,進(jìn)行了反序列化操作,該值可控,接下來看一下代碼觸發(fā)條件。文件幾個(gè)關(guān)鍵判斷如下:

php unserialize反序列化漏洞分析

第一個(gè)if判斷,可通過GET傳遞 finish=任意值 繞過 ,第二if判斷是否有GET或者POST傳參,并判斷Referer是否為空,第四個(gè)if判斷Referer是否為本站點(diǎn)。緊接著還有判斷,如下圖:

php unserialize反序列化漏洞分析

第一個(gè)if判斷 $_GET['finish'] 是否設(shè)置,然后判斷 config.inc.php文件 是否存在,安裝后已存在,第三個(gè)判斷cookie中 __typecho_config 參數(shù)是否為空,不為空。進(jìn)入else分支。綜上,具體構(gòu)造如下圖:

php unserialize反序列化漏洞分析

$config = unserialize(base64_decode(Typecho_Cookie::get('__typecho_config')));
Typecho_Cookie::delete('__typecho_config');
$db = new Typecho_Db($config['adapter'], $config['prefix']);

反序列化結(jié)果存儲到 $config 變量中,然后將 $config['adapter']$config['prefix'] 作為 Typecho_Db 類的初始化變量創(chuàng)建類實(shí)例。我們可以在 var/Typecho/Db.php 文件中找到該類構(gòu)造函數(shù)代碼,具體如下:

php unserialize反序列化漏洞分析

上圖代碼 第6行 ,對傳入的 $adapterName 變量進(jìn)行了字符串拼接操作,對于PHP而言,如果 $adapterName 類型為對象,則會調(diào)用該類 __toString() 魔術(shù)方法。可作為反序列化的一個(gè)觸發(fā)點(diǎn),我們?nèi)炙阉饕幌?__toString() ,查看是否有可利用的點(diǎn)。實(shí)際搜索時(shí),會發(fā)現(xiàn)有三個(gè)類都定義了 __toString() 方法:

php unserialize反序列化漏洞分析

  • 第一處 var\Typecho\Config.php

    php unserialize反序列化漏洞分析

    調(diào)用 serialize() 函數(shù)進(jìn)行序列化操作,會自動觸發(fā) __sleep() ,如果存在可利用的 __sleep() ,則可以進(jìn)一步利用。

  • 第二處 var\Typecho\Db\Query.phpphp unserialize反序列化漏洞分析

    該方法用于構(gòu)建SQL語句,并沒有執(zhí)行數(shù)據(jù)庫操作,所以暫無利用價(jià)值。

  • 第三處var\Typecho\Feed.php

    php unserialize反序列化漏洞分析

    在代碼 19行 , $this->_items 為類變量,反序列化可控,在代碼 27行$item['author']->screenName ,如果 $item['author'] 中存儲的類沒有'screenName'屬性或該屬性為私有屬性,此時(shí)會觸發(fā)該類中的 __get() 魔法方法,這個(gè)可作為進(jìn)一步利用的點(diǎn),繼續(xù)往下看代碼,未發(fā)現(xiàn)有危險(xiǎn)函數(shù)的調(diào)用。

記一波魔術(shù)方法及對應(yīng)的觸發(fā)條件,具體如下:

__wakeup() //使用unserialize時(shí)觸發(fā)
__sleep() //使用serialize時(shí)觸發(fā)
__destruct() //對象被銷毀時(shí)觸發(fā)
__call() //在對象上下文中調(diào)用不可訪問的方法時(shí)觸發(fā)
__callStatic() //在靜態(tài)上下文中調(diào)用不可訪問的方法時(shí)觸發(fā)
__get() //用于從不可訪問的屬性讀取數(shù)據(jù)
__set() //用于將數(shù)據(jù)寫入不可訪問的屬性
__isset() //在不可訪問的屬性上調(diào)用isset()或empty()觸發(fā)
__unset() //在不可訪問的屬性上使用unset()時(shí)觸發(fā)
__toString() //把類當(dāng)作字符串使用時(shí)觸發(fā)
__invoke() //當(dāng)腳本嘗試將對象調(diào)用為函數(shù)時(shí)觸發(fā)

var/Typecho/Request.php 的  Typecho_Request 類中,我們發(fā)現(xiàn) __get() 方法,跟蹤該方法的調(diào)用,具體如下圖:php unserialize反序列化漏洞分析

array_map() 函數(shù)和 call_user_func 函數(shù),都可以作為利用點(diǎn),$filter 作為調(diào)用函數(shù),$value 為函數(shù)參數(shù),跟蹤變量,看一下是否可控。這兩個(gè)變量都來源于類變量,反序列化可控。從上面的分析中,可知當(dāng) $item['author'] 滿足一定條件會觸發(fā) __get 方法。

假設(shè) $item['author'] 中存儲 Typecho_Request 類實(shí)例,此時(shí)調(diào)用 $item['author']->screenName ,在Typecho_Request 類中沒有該屬性,就會調(diào)用類中的 __get($key) 方法,$key 傳入的值為 scrrenName 。參數(shù)傳遞過程如下:$key='scrrenName'=>$this->_param[$key]=>$value

我們將 $this->_param['scrrenName'] 的值設(shè)置為想要執(zhí)行的函數(shù),構(gòu)造 $this->_filter 為對應(yīng)函數(shù)的參數(shù)值,具體構(gòu)造如下:

php unserialize反序列化漏洞分析

接下來我們?nèi)タ匆幌?Typecho_Feed 類的構(gòu)造,該類在 var/Typecho/Feed.php 文件中,代碼如下:

php unserialize反序列化漏洞分析

上圖代碼 第7行 ,滿足 self::RSS2$this->_type 相等進(jìn)入該分支,所以 $this->_type 需要構(gòu)造,item['author'] 為觸發(fā)點(diǎn),需要構(gòu)造 $this_items ,具體構(gòu)造如下:

php unserialize反序列化漏洞分析

代碼 22行 在實(shí)際利用沒必要添加,install.php在代碼 54行 調(diào)用 ob_start() 函數(shù),該函數(shù)對輸出內(nèi)容進(jìn)行緩沖,反序列化漏洞利用結(jié)束后,在var\Typecho\Db.php代碼121行,觸發(fā)異常,在 var\Typecho\Common.php 代碼237行調(diào)用 ob_end_clean()函數(shù) 清除了緩沖區(qū)內(nèi)容,導(dǎo)致無法看見執(zhí)行結(jié)果,考慮在進(jìn)入到異常處理前提前報(bào)錯(cuò)結(jié)束程序。由此構(gòu)造該數(shù)據(jù)。執(zhí)行結(jié)果如下:

php unserialize反序列化漏洞分析

修復(fù)建議

造成該漏洞的原因主要有兩點(diǎn):

  • 當(dāng) config.inc.php 文件存在的時(shí),可繞過判斷繼續(xù)往下執(zhí)行代碼。

  • 傳入反序列化函數(shù)的參數(shù)可控

修復(fù)方法:在 install.php 文件第一行判斷 config.inc.php 是否存在,如果存在,則退出代碼執(zhí)行。

<?php 
if (file_exists(dirname(__FILE__) . '/config.inc.php'))
    exit('Access Denied');
?>

結(jié)語

看完了上述分析,不知道大家是否對反序列化利用有了一定的了解,文中用到的CMS可以從 這里 下載,當(dāng)然文中若有不當(dāng)之處,還望各位斧正。如果你對我們的項(xiàng)目感興趣,歡迎發(fā)送郵件到 hongrisec@gmail.com 聯(lián)系我們。Day11 的分析文章就到這里,我們最后留了一道CTF題目給大家練手,題目如下:

<?php
include "config.php";

class HITCON{
    public $method;
    public $args;
    public $conn;

    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
        $this->__conn();
    }

    function __conn() {
        global $db_host, $db_name, $db_user, $db_pass, $DEBUG;
        if (!$this->conn)
            $this->conn = mysql_connect($db_host, $db_user, $db_pass);
        mysql_select_db($db_name, $this->conn);
        if ($DEBUG) {
            $sql = "DROP TABLE IF  EXISTS  users";
            $this->__query($sql, $back=false);
            $sql = "CREATE TABLE IF NOT EXISTS users (username VARCHAR(64),
            password VARCHAR(64),role VARCHAR(256)) CHARACTER SET utf8";

            $this->__query($sql, $back=false);
            $sql = "INSERT INTO users VALUES ('orange', '$db_pass', 'admin'), ('phddaa', 'ddaa', 'user')";
            $this->__query($sql, $back=false);
        }
        mysql_query("SET names utf8");
        mysql_query("SET sql_mode = 'strict_all_tables'");
    }

    function __query($sql, $back=true) {
        $result = @mysql_query($sql);
        if ($back) {
            return @mysql_fetch_object($result);
        }
    }

    function login() {
        list($username, $password) = func_get_args();
        $sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, md5($password));
        $obj = $this->__query($sql);

        if ( $obj != false ) {
            define('IN_FLAG', TRUE);
            $this->loadData($obj->role);
        }
        else {
          $this->__die("sorry!");
        }
    }

    function loadData($data) {
        if (substr($data, 0, 2) !== 'O:' && !preg_match('/O:\d:/', $data)) {
            return unserialize($data);
        }
        return [];
    }

    function __die($msg) {
        $this->__close();
        header("Content-Type: application/json");
        die( json_encode( array("msg"=> $msg) ) );
    }

    function __close() {
        mysql_close($this->conn);
    }

    function source() {
        highlight_file(__FILE__);
    }

    function __destruct() {
        $this->__conn();
        if (in_array($this->method, array("login", "source"))) {
            @call_user_func_array(array($this, $this->method), $this->args);
        }
        else {
            $this->__die("What do you do?");
        }
        $this->__close();
    }

    function __wakeup() {
        foreach($this->args as $k => $v) {
            $this->args[$k] = strtolower(trim(mysql_escape_string($v)));
        }
    }
}
class SoFun{
    public $file='index.php';

    function __destruct(){
        if(!empty($this->file)) {
            include $this->file;
        }
    }
    function __wakeup(){
        $this-> file='index.php';
    }
}
if(isset($_GET["data"])) {
    @unserialize($_GET["data"]);
}
else {
    new HITCON("source", array());
}

?>
//config.php
<?php
    $db_host = 'localhost';
    $db_name = 'test';
    $db_user = 'root';
    $db_pass = '123';
    $DEBUG = 'xx';
?>
// flag.php
<?php
!defined('IN_FLAG') && exit('Access Denied');
echo "flag{un3eri@liz3_i3_s0_fun}";

?>

以上就是“php unserialize反序列化漏洞分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學(xué)習(xí)更多的知識,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI