新聞中心
輕量級博客系統(tǒng)Typecho install.php 反序列化導(dǎo)致任意代碼執(zhí)行
作者 / 無憂主機(jī) 時間 2017-12-07 21:32:35
Typecho是一個輕量級的開源博客系統(tǒng),近日有網(wǎng)友爆出該系統(tǒng)的安裝文件install.php存在反序列化可控點(diǎn),能導(dǎo)致任意代碼執(zhí)行。 快速修補(bǔ)方法: 刪除install.php及install目錄 漏洞細(xì)節(jié): 在 install.php 文件的 288-235行
$config = unserialize(base64_decode(Typecho_Cookie::get('__typecho_config'))); Typecho_Cookie::delete('__typecho_config'); $db = new Typecho_Db($config['adapter'], $config['prefix']); $db->addServer($config, Typecho_Db::READ | Typecho_Db::WRITE); Typecho_Db::set($db);第230行獲取cookie中的__typecho_config值base64解碼,然后反序列化。想要執(zhí)行,只需isset($_GET['finish'])并且__typecho_config存在值。 反序列化后232行把$config['adapter']和$config['prefix']傳入Typecho_Db進(jìn)行實(shí)例化。然后調(diào)用Typecho_Db的addServer方法,調(diào)用Typecho_Config實(shí)例化工廠函數(shù)對Typecho_Config類進(jìn)行實(shí)例化。 跟蹤 Typecho_Db 類,構(gòu)造方法,Db.php 114-135行
public function __construct($adapterName, $prefix = 'typecho_') { /** 獲取適配器名稱 */ $this->_adapterName = $adapterName; /** 數(shù)據(jù)庫適配器 */ $adapterName = 'Typecho_Db_Adapter_' . $adapterName; if (!call_user_func(array($adapterName, 'isAvailable'))) { throw new Typecho_Db_Exception("Adapter {$adapterName} is not available"); } $this->_prefix = $prefix; /** 初始化內(nèi)部變量 */ $this->_pool = array(); $this->_connectedPool = array(); $this->_config = array(); //實(shí)例化適配器對象 $this->_adapter = new $adapterName(); }發(fā)現(xiàn)第120行對傳入的$adapterName進(jìn)行了字符串的拼接操作。那么如果$adapterName傳入的是個實(shí)例化對象,就會觸發(fā)該對象的__toString()魔術(shù)方法。 全局搜索__toString():發(fā)現(xiàn)三處,跟進(jìn)發(fā)現(xiàn) Typecho_Query類的__toString()魔術(shù)方法,Query.php 488-519行:
public function __toString() { switch ($this->_sqlPreBuild['action']) { case Typecho_Db::SELECT: return $this->_adapter->parseSelect($this->_sqlPreBuild); case Typecho_Db::INSERT: return 'INSERT INTO ' . $this->_sqlPreBuild['table'] . '(' . implode(' , ', array_keys($this->_sqlPreBuild['rows'])) . ')' . ' VALUES ' . '(' . implode(' , ', array_values($this->_sqlPreBuild['rows'])) . ')' . $this->_sqlPreBuild['limit']; case Typecho_Db::DELETE: return 'DELETE FROM ' . $this->_sqlPreBuild['table'] . $this->_sqlPreBuild['where']; case Typecho_Db::UPDATE: $columns = array(); if (isset($this->_sqlPreBuild['rows'])) { foreach ($this->_sqlPreBuild['rows'] as $key => $val) { $columns[] = "$key = $val"; } } return 'UPDATE ' . $this->_sqlPreBuild['table'] . ' SET ' . implode(' , ', $columns) . $this->_sqlPreBuild['where']; default: return NULL; } }第492行$this->_adapter調(diào)用parseSelect()方法,如果該實(shí)例化對象在對象上下文中調(diào)用不可訪問的方法時觸發(fā),便會觸發(fā)__call()魔術(shù)方法。 繼續(xù)跟進(jìn)發(fā)現(xiàn)Typecho_Plugin類的__call()魔術(shù)方法存在回調(diào)函數(shù),Plugin.php 479-494行:
public function __call($component, $args) { $component = $this->_handle . ':' . $component; $last = count($args); $args[$last] = $last > 0 ? $args[0] : false; if (isset(self::$_plugins['handles'][$component])) { $args[$last] = NULL; $this->_signal = true; foreach (self::$_plugins['handles'][$component] as $callback) { $args[$last] = call_user_func_array($callback, $args); } } return $args[$last]; }$component是調(diào)用失敗的方法名,$args是調(diào)用時的參數(shù)。均可控,但是根據(jù)上文,$args必須存在array('action'=>'SELECT'),然后加上我們構(gòu)造的payload,最少是個長度為2的數(shù)組,但是483行又給數(shù)組加了一個長度,導(dǎo)致$args長度至少為3,那么call_user_func_array()便無法正常執(zhí)行。所以此路就不通了。 繼續(xù)跟進(jìn)Typecho_Feed類的__toString()魔術(shù)方法,F(xiàn)eed.php 340-360行
} else if (self::ATOM1 == $this->_type) { $result .= '_baseUrl . '" >' . self::EOL; $content = ''; $lastUpdate = 0; foreach ($this->_items as $item) { $content .= '' . self::EOL; $content .= '<![CDATA[' . $item['title'] . ']]>' . self::EOL; $content .= '' . self::EOL; $content .= '' . $item['link'] . '' . self::EOL; $content .= '' . $this->dateFormat($item['date']) . '' . self::EOL; $content .= '' . $this->dateFormat($item['date']) . '' . self::EOL; $content .= ' ' . $item['author']->screenName . ' ' . $item['author']->url . ' ' . self::EOL;第358行$item['author']調(diào)用screenName屬性,如果該實(shí)例化對象用于從不可訪問的屬性讀取數(shù)據(jù),便會觸發(fā)__get()魔術(shù)方法。 全局搜索__get(): 發(fā)現(xiàn)了幾處,最終確定Typecho_Request類存在可利用的地方 __get()魔術(shù)方法調(diào)用get()方法,Request.php 293-309行:
public function get($key, $default = NULL) { switch (true) { case isset($this->_params[$key]): $value = $this->_params[$key]; break; case isset(self::$_httpParams[$key]): $value = self::$_httpParams[$key]; break; default: $value = $default; break; } $value = !is_array($value) && strlen($value) > 0 ? $value : $default; return $this->_applyFilter($value); }308行調(diào)用_applyFilter()方法,傳入的$value是$this->_params[$key]的值,$key就是screenName。 跟進(jìn)_applyFilter(),Request.php 159-171行:
private function _applyFilter($value) { if ($this->_filter) { foreach ($this->_filter as $filter) { $value = is_array($value) ? array_map($filter, $value) : call_user_func($filter, $value); } $this->_filter = array(); } return $value; }第163行array_map和164行call_user_func均可造成任意代碼執(zhí)行。
本文地址:http://www.gle-technology.com/safety/25764.html
上一篇: ECTouch幻燈片設(shè)置
下一篇: ECTouch如何去除版權(quán)信息