RSS Feed
更好更安全的互联网

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

2020-07-31

作者:LoRexxar'@知道创宇404实验室
时间:2020年3月31日
English Version: https://paper.seebug.org/1268

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


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

https://hackerone.com/reports/198734

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

GMP类型混淆漏洞

漏洞利用条件

  • php 5.6.x
  • 反序列化入口点
  • 可以触发__wakeup的触发点(在php < 5.6.11以下,可以使用内置类)

漏洞详情

gmp.c

zend_object_handlers.c

从gmp.c中的片段中我们可以大致理解漏洞发现者taoguangchen的原话。

__wakeup等魔术方法可以导致ZVAL在内存中被修改。因此,攻击者可以将**object转化为整数型或者bool型的ZVAL,那么我们就可以通过Z_OBJ_P访问存储在对象储存中的任何对象,这也就意味着可以通过zend_hash_copy覆盖任何对象中的属性,这可能导致很多问题,在一定场景下也可以导致安全问题。

或许仅凭借代码片段没办法理解上述的话,但我们可以用实际测试来看看。

首先我们来看一段测试代码

在代码中我展示了多种不同情况下的环境。

让我们来看看结果是什么?

我成功修改了第一个声明的对象。

但如果我将反序列化的类改成b会发生什么呢?

很显然的是,并不会影响到其他的类变量

如果我们给class b加一个__Wakeup函数,那么又会产生一样的效果。

但如果我们把wakeup魔术方法中的变量设置为2

返回的结果可以看出来,我们成功修改了第二个声明的对象。

但如果我们把ryat改为4,那么页面会直接返回500,因为我们修改了没有分配的对象空间。

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

如果我们有一个可控的反序列化入口,目标后端PHP安装了GMP插件(这个插件在原版php中不是默认安装的,但部分打包环境中会自带),如果我们找到一个可控的__wakeup魔术方法,我们就可以修改反序列化前声明的对象属性,并配合场景产生实际的安全问题。

如果目标的php版本在5.6 <= 5.6.11中,我们可以直接使用内置的魔术方法来触发这个漏洞。

真实世界案例

在讨论完GMP类型混淆漏洞之后,我们必须要讨论一下这个漏洞在真实场景下的利用方式。

漏洞的发现者Taoguang Chen提交了一个在mybb中的相关利用。

https://hackerone.com/reports/198734

这里我们不继续讨论这个漏洞,而是从头讨论一下在ecshop中的利用方式。

漏洞环境

  • ecshop 4.0.7
  • php 5.6.9

反序列化漏洞

首先我们需要找到一个反序列化入口点,这里我们可以全局搜索unserialize,挨个看一下我们可以找到两个可控的反序列化入口。

其中一个是search.php line 45

这是一个前台的入口,但可惜的是引入初始化文件在反序列化之后,这也就导致我们没办法找到可以覆盖类变量属性的目标,也就没办法进一步利用。

还有一个是admin/order.php line 229

后台的表单页的这个功能就满足我们的要求了,不但可控,还可以用urlencode来绕过ecshop对全局变量的过滤。

这样一来我们就找到了一个可控并且合适的反序列化入口点。

寻找合适的类属性利用链

在寻找利用链之前,我们可以用

来确定在反序列化时,已经声明定义过的类。

在我本地环境下,除了PHP内置类以外我一共找到13个类

从代码中也可以看到在文件头引入了多个库文件

这里我们主要关注init.php,因为在这个文件中声明了ecshop的大部分通用类。

在逐个看这里面的类变量时,我们可以敏锐的看到一个特殊的变量,由于ecshop的后台结构特殊,页面内容大多都是由模板编译而成,而这个模板类恰好也在init.php中声明

回到order.php中我们寻找与$smarty相关的方法,不难发现,主要集中在两个方法中

而这里我们主要把视角集中在display方法上。

粗略的浏览下display方法的逻辑大致是

比较重要的代码会在make_compiled这个函数中被定义

当流程走到这一步的时候,我们需要先找到我们的目标是什么?

重新审视cls_template.php的代码,我们可以发现涉及到代码执行的只有几个函数。

get_para只在select中调用,但是没找到能触发select的地方。

然后是pop_vars

恰好配合GMP我们可以控制$this->_temp_key变量,所以我们只要能在上面的流程中找到任意地方调用这个方法,我们就可以配合变量覆盖构造一个代码执行。

在回看刚才的代码流程时,我们从编译后的PHP文件中找到了这样的代码

order_info.htm.php

在遍历完表单之后,正好会触发pop_vars

这样一来,只要我们控制覆盖cls_template变量的_temp_key属性,我们就可以完成一次getshell

最终利用效果

Timeline

  • 2020.03.31 发现漏洞。
  • 2020.03.31 将漏洞报送厂商、CVE、CNVD等。
  • 2020.07.08 符合90天漏洞披露期,公开细节。

Paper

本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1267/

作者:叶霖 杨 | Categories:安全研究技术分享 | Tags:

发表评论