反序列化漏洞复现 – 作者:mazihan

一、前言

之前看到关于thinkphp5反序列化链的分析,很多文章并没有公布payload,并且感觉有很多细节没有阐述清楚,跳跃性很大,今天站在前人的肩膀上,就探讨一下ThinkPHP的反序列化问题!这里详细分析一下5.1.37版本。

二、利用条件

有一个内容完全可控的反序列化点,例如: unserialize(可控变量)

三、环境搭建

1、直接GitHub下载所需版本。

1582452297_5e524e495585e.png!small2、更改composer.json

1582452307_5e524e537159a.png!small

3、执行命令——composer install【在composer.json的目录】

4、代码如下

1582452321_5e524e61c8cce.png!small

5、页面效果如下

1582452330_5e524e6a335f6.png!small

四、漏洞复现

1、通过构建get参数payload,执行反序列化,最终实现代码执行【不明白为啥大佬都不公布这个payload,所以这里也打下码】

五、漏洞分析

1、首先我们进入到入口文件,构造反序列化的入口点。

1582452593_5e524f71656b6.png!small

2、该框架有个非常好玩的点,file_exists函数触发类__toString方法,这个也算是漏洞的入口了。通过全局搜索file_exists,在windows.php的文件中,存在一个被动触发的点,这个点在对象销毁时会触发。

1582452602_5e524f7a608ee.png!small

下面这张图实现了被动触发

1582452610_5e524f8296df8.png!small

3、当一个对象以字符串的形式被执行时,包含__toString魔术方法的类,便被执行,同样搜索关键词__toString,全局搜索到的 __toString 方法其实不多,这里有两处都可以利用。它们的区别在于利用 think\Collection 构造的链要多构造一步,我们这里只分析链较短的 think\model\concern\Conversion

1582452618_5e524f8a8c2e6.png!small

1582452626_5e524f929316e.png!small

通过代码的跳转,__toSting的最终落脚点在toArray()的方法体里。要利用的代码如下:

1582452634_5e524f9a66507.png!small

分析getAttr()方法的结构体,我们可以控制$relation的值。

1582452642_5e524fa27619a.png!small

跳转到getData里面

1582452650_5e524faa7738c.png!small

这里的$this->append是可以赋值的,还有$this-data【提示:这个值是在payload里面构造的】,也就是完全可控,所以这个$key也是可控,如果$key可控,也就是$relation,因为它来自$this->data[$key]【转型后的样子】,总体变成$可控类->visible(可变变量)。这里通过发散思维,就可以调用visible方法和__call的方法,理论实践,__call的方法可行,所以全局搜索,这里选择request.php里的魔术函数。

1582452658_5e524fb2946c0.png!small

因为这里$this->hook是可控的,可以直接在payload中直接构建,在call_user_func_array()函数中,传达一个数组为[‘’类名:’方法名’]的形式,既可以调用任一类中的方法。

在之前的漏洞中,该框架存在RCE的漏洞,这个位置在request类里的input中。里面存在一个call_user_func($filter,$data)的接入点,如果直接调用input方法,在$name = (string) $name;转换的时候就会报错,所以需要找其他的input调用点。在param    方法调用了input,如果直接调用param方法和input出现同样的错误,在接着找调用param的方法,这里有isAjax和isPjax,因为第一参数可控【固定值】如下图:

1582452668_5e524fbc81870.png!small

1582452676_5e524fc4157d0.png!small

通过触发这个函数,就可以顺利来到input方法中,执行下面一段代码

1582452684_5e524fcc1f889.png!small

在getFilter中,通过构建payload进行$filter赋值,这里的值一般是可执行的系统函数,如system。赋值过程见下图:

1582452692_5e524fd46edbf.png!small

那么上图中的$data是可以通过get传值来赋值的,它是通过param方法传值的,里面合并了get参数的值,并传递给input方法,如下图所示

1582452701_5e524fdd76290.png!small

到这里,array_walk_recursive里的参数,都可以控制了,那filterValue这个函数附张截图简单说明下,

1582452710_5e524fe6698c8.png!small

其中,value的形参对应的为data的值,filters为payload中构建的system函数,那么通过修改data的值,就行执行不同的系统命令,比如文章的第一张图,传值为whoami。最后附上流程导图。

1582452718_5e524feee1a20.png!small

六、漏洞修复

控制反序列化参数的值,必要时可以使用白名单的方式进行过滤控制。

来源:freebuf.com 2020-02-26 13:18:46 by: mazihan

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论