ThinkPHP5.x RCE 复现
2021-04-02 23:54:59 Author: xz.aliyun.com(查看原文) 阅读量:143 收藏

其实去年开始是复现过这个漏洞的,但是总觉得并没有吃透,分析写得漏洞百出,于是再来审计一遍。

漏洞影响范围

5.x < 5.1.31
5.x < 5.0.23

复现环境

php7.3  thinkphp5.1.29 phpstorm

漏洞复现

测试成功

漏洞分析

进入入口文件,

$this->dispatch 无初始值。

直接跟进路由检测routeCheck

直接跟进 path() 方法

跟进pathinfo()

pathinfo 从当前类中的 config 中获取,

也就是我们可以通过 ?s的方式传入路由信息,这在tp的文档中也有说明。

然后我们继续跟进此处的路由检测。

跟进route类中的 check 方法。

在检查完路由后会返回

url类,这个类是继承 dispatch类,然后会执行这个对象的init() 方法。

然后又会实例化 Module类并执行 init() 方法返回,init() 返回值是当前实例化的对象。

所以我们在一开始的路由检测后

$dispatch值是 实例化的Molude对象。

继续往下看,当程序执行到这里时,

$data 在上面已经被赋值为null,会去执行Module类的 run()方法,run()方法在其父类中被调用。

又会去执行 exec() 方法,

继续跟进

注意这里,又去调用了当前appcontroller方法,

继续跟进 parseModuleAndClass 方法

如果控制器的名字中存在 \或者以\开头,会被会被当作一个类,可以利用命名空间,实例化任意类。。

回到controller方法,会检查类是否存在,存在就会去调用__get()方法,其实一开始有很多地方都会调用到 __get 方法,比如这些

$this->route->check() $this->request->module(),这些属性不存在,就会去调用 __get() 方法,

make方法用来将类实例化。

继续看module类里exec方法的后半部分,

获取我们的操作名,也就是我们需要执行的实例化类的方法,然后方法里面对应的参数通过get请求传入。

tp的路由规则是 ?s=模块/控制器/操作名

寻找可以利用的类以及方法

think\app

container类里,存在 invokeFunction 方法,用来动态调用函数。

payload:

?s=index/\think\app/invokefunction?function=call_user_func&vars[0]=system&vars[1]=whoami

think\request

在 request 类里,其实也有一个很好的rce利用点,

跟进 filterValue()

这里存在回调函数,且参数可控。

payload

?s=index/\think\request/input?data[]=-1&filter=phpinfo

think\template\driver\file

可以利用此方法写马,

payload

?s=index/\think\template\driver\file/write?cacheFile=shell.php&content=<?php%20phpinfo();?>

然后当前目录访问shell.php 就ok了。

写在后面

这个rce漏洞归根结底就是因为 把控制器名字的 \ 开头作为类名导致我们可以实例化任意类,后面的payload也不过是基于此漏洞的利用。如有问题还请及时告知。


文章来源: http://xz.aliyun.com/t/9361
如有侵权请联系:admin#unsafe.sh