ThinkPHP6.0.13反序列化漏洞分析

2022-10-11 14:51 栏目: 行业交流 查看()
ThinkPHP6.0.13反序列化漏洞分析(图1)

1. 前言

最近有点闲下来了,不找点事干比较难受,打算找点漏洞分析一下,于是就打算看看TP的一些漏洞,ThinkPHP6.0.13TP的最新版,八月份有师傅提交了一个issue指出TP存在反序列化问题,网上也有些师傅分析了一波,不过断点下的比较多,而且部分方法没有阐明其用途,所以我也尝试详细的分析一波。下面先给出POC

ThinkPHP6.0.13反序列化漏洞分析(图2)

2. 分析

首先看看POC的起始点

ThinkPHP6.0.13反序列化漏洞分析(图3)
ThinkPHP6.0.13反序列化漏洞分析(图4)

发现起始点在Psr6Cache这个类,我们进入这个类,不过没有发现__destruct或者__wakeup等常见的反序列化起始魔术方法,推测应该在其父类AbstractCache这个抽象类中。跟入AbstractCache类

ThinkPHP6.0.13反序列化漏洞分析(图5)

如图,成功发现本次反序列化链子的起始类。这里我们可以控制autosave这个属性为false,从而进入save方法。

【----帮助网安学习,以下所有学习资料关注我,私信回复“资料”获取----】

 ① 网安学习成长路径思维导图

 ② 60+网安经典常用工具包

 ③ 100+SRC漏洞分析报告

 ④ 150+网安攻防实战技术电子书

 ⑤ 最权威CISSP 认证考试指南+题库

 ⑥ 超1800页CTF实战技巧手册

 ⑦ 最新网安大厂面试题合集(含答案)

 ⑧ APP客户端安全检测指南(安卓+IOS)

回到Psr6Cache类查看这个方法

ThinkPHP6.0.13反序列化漏洞分析(图6)

可以发现,pool属性和key属性我们都可控。因此可能存在两种路线,调用不同类的同名方法(getItem)。或者是直接尝试触发__call方法。我们来看看POC作者是怎么让反序列化进行下去的。

ThinkPHP6.0.13反序列化漏洞分析(图7)

作者用构造方法传入了exp,exp其实就是在实例化Channel类。我们进入Channel类查看

ThinkPHP6.0.13反序列化漏洞分析(图8)

Channel类中有一个__call方法,那么作者是选择触发__call来让链子继续下去。这个call方法接受了两个参数,method是写死的(getItem)parameters是可控的(即前面可控的key属性)

ThinkPHP6.0.13反序列化漏洞分析(图9)

跟入log方法查看,其接受三个传参(但是其实对后续的链子没啥用),传入record方法

ThinkPHP6.0.13反序列化漏洞分析(图10)

跟入record方法

ThinkPHP6.0.13反序列化漏洞分析(图11)

再返回查看作者的POC,发现其控制lazy属性为false,让函数进入最后一个if分支执行save方法

ThinkPHP6.0.13反序列化漏洞分析(图12)

那么save方法应该是比较关键的方法了,跟入save方法,这里面有三个可能被利用的点,作者选择了哪一个呢?

ThinkPHP6.0.13反序列化漏洞分析(图13)

根据POC不难发现作者选择了控制logger属性,利用构造函数对其赋值,令其为Socket类的对象

ThinkPHP6.0.13反序列化漏洞分析(图14)
ThinkPHP6.0.13反序列化漏洞分析(图15)

在这个类中,我们找到了一个复杂的同名方法,其中有大量的操作。

ThinkPHP6.0.13反序列化漏洞分析(图16)

我们继续来看作者是怎么构造的,作者控制config属性,给其赋值为数组。数组有如下内容

ThinkPHP6.0.13反序列化漏洞分析(图17)

关键在于这两个键值,作者控制config,让程序运行到调用invoke方法的分支

ThinkPHP6.0.13反序列化漏洞分析(图18)

同时,app属性可控,作者令app属性为App类的对象,我们进入App类

ThinkPHP6.0.13反序列化漏洞分析(图19)

这里先看看App类的的exists方法的情况,在其父类中找到了这个方法

ThinkPHP6.0.13反序列化漏洞分析(图20)

继续往后,这里对App类进行了唯一一个操作,控制了instances属性的值。这里控制其值是为了进入Request类,并且执行url方法

ThinkPHP6.0.13反序列化漏洞分析(图21)
ThinkPHP6.0.13反序列化漏洞分析(图22)

作者在这里对Request类做出唯一的操纵,就是控制url属性的值。可以看出,如果url属性存在,那么就会进入第一个分支,其值等于本身。

ThinkPHP6.0.13反序列化漏洞分析(图23)

同时又注意到,complete我们之前传入的是true。因此最终返回的结果就是$this->domain().$urlurl我们已经控制了,那么domain方法返回什么呢?

ThinkPHP6.0.13反序列化漏洞分析(图24)

OK,这点我们就不用看了。分析了这么多,我们得到了$currentUri最后的值,就是:

http://localhost/<?php system(\calc\); exit(); ?>

ThinkPHP6.0.13反序列化漏洞分析(图25)

currentUri作为一个数组被传入invoke了,根据链子的长度,达到invoke,我们的反序列化之旅就快结束了

ThinkPHP6.0.13反序列化漏洞分析(图26)

查看invoke,App类找不到这个方法,在他的父类里找到了这个方法

ThinkPHP6.0.13反序列化漏洞分析(图27)

这里可以看到。这个函数内有三分支走向,那么最终会走向哪里呢?根据我们之前$config[‘format_head’]的传入, 首先我们传入的这个对象不是Closure的实例或者子类,并且也不满足第二个分支的条件

ThinkPHP6.0.13反序列化漏洞分析(图28)

因此进入到到第三个分支。我们跟进invokeMethod()方法。这里传入的$callabel就是[new \think\view\driver\Php,display]、而$vars就是[‘http://localhost/<?php system(\calc\); exit(); ?>’]

注意,我们传入的$method是数组,因此进入第一个分支。把new \think\view\driver\Php (即对象)赋值给$class,’display’(即方法名)赋值给新的$method。

然后下面进行了一个判断,如果$class是对象,那么其值就为它本身,因为我们传入的是对象,所以这里没什么变化。然后进入最关键的代码

可以看到,把对象new \think\view\driver\Php 以及方法display传入了ReflectionMethod。

在最后,调用invokeArgs方法,传入了new \think\view\driver\Php对象,同时传入了$args

那么args是什么呢?

ThinkPHP6.0.13反序列化漏洞分析(图29)

我们跟入之后发现是一个处理函数,因为本人比较懒,而且到这都快分析完了,就不去硬读了,直接给结论,总之我们传入的 $vars ,也即 [‘http://localhost/<?php system(\calc\); exit(); ?>’] 其中的关键部分<?php system(\calc\); exit(); ?>保留了下来,并且进入到了后续的传参中

ThinkPHP6.0.13反序列化漏洞分析(图30)

继续往后看,对于这个函数(invokeArgs),可以简单的类比call_user_func(),因此最后的关键代码其实只有这两行

ThinkPHP6.0.13反序列化漏洞分析(图31)

也即

$reflect = new ReflectionMethod(new \think\view\driver\Php,’display’);

return $reflect->invokeArgs(new \think\view\driver\Php,’ <?php system(\calc\); exit(); ?>’)

常看tp反序列化的朋友就知道,已经结束咧!毕竟调用display方法了。但是上述这个调用ReflectionMethod类的操作到底是什么呢?我们可以借助如下实例来演示。所以说这玩意和call_user_func很像

ThinkPHP6.0.13反序列化漏洞分析(图32)

最后是display方法,没什么好说的了,content传入display方法中,eval执行命令了

ThinkPHP6.0.13反序列化漏洞分析(图33)

3. 结语

TP的链子一如既往的有意思(以及复杂),特别是最后的ReflectionMethod类的用法上,如果不了解这个类以及类中的方法组合可以实现类似call_user_func函数的作用的话,那么就很容易错过这样一个精彩的漏洞。

更多靶场实验练习、网安学习资料请访问 合天网安实验室。

举报/反馈
扫二维码与项目经理沟通

全景拍摄制作、网站/小程序/App开发

家装/婚庆/餐饮/教育/公共医疗等行业解决方案

郑重申明:元创全景以外的任何单位或个人,不得使用该内容作为工作成功案例展示!部分素材来源网络,如有侵权,请联系删除。