前言
看到一篇文章 TP5 RCE利用链改造 - 先知社区 觉得挺有意思的,吐槽下:看了一下自己以前写的TP文章,太拉跨了,有空重新写一遍
改造后版本影响 5.0.8-5.0.23
环境
- WIN11
- php7.3.4
- TPv5.0.23
回顾
开启debug
先贴个payload
_method=__construct&method=GET&filter[]=system&get[]=whoami |
TP5的RCE的原因就是因为在Request类中的method方法可以调用当前类的任意方法
调用了__construct方法
覆盖了参数 method=GET、filter=array(‘system’)、get=array(‘whoami’) 这是construct最重要的作用:覆盖参数
然后走到了filterValue
然后触发rce
整体流程为:
补充:
还可以这样的payload
http://127.0.0.1/public/?a=whoami |
因为在二次进入input时,调用get(),然后参数合并
所以当没有覆盖$this->get时,会去获取url中的值
未开启debug
未开启debug情况下跟 captcha 验证码有分不开的联系,环境配置如下,composer引入
composer require topthink/think-captcha 1.* |
application/config.php添加
'captcha' => [ |
先给出正常的poc
http://127.0.0.1/public/index.php?s=captcha |
经过 __construct()覆盖后返回到run()**,需要注意的是请求的路由是 **?s=captcha,它对应的注册规则为 \think\Route::get。在method方法结束后,返回值应为get,所以在 __construct() 覆盖值为get
captcha路由对应的**$dispatch[‘type’]=method**,这里进入下面的exec()
顺利**进入Request::param()**,往下就流程同未开启debug一样
改造
如果不能利用 __construct方法,也就是不能覆盖 filter 参数,而call_user_func中的value可以通过URL传入经过get()从而可控,尝试寻找一种新的方法去控制$filter即可,
开启debug改造
命令执行
给出命令执行的poc
POST: |
分析:任意调用方法
调用filter()
由App:run()进入param()
进入$this->filterValue(),但是这里由于没有走到参数合并处,并没有成功rce
走到参数合并
再次进入input(),调用filterValue
命令执行
流程:
phpinfo
server函数中 将全局变量$_SERVER,赋值给$this->server ,传入input()
在input()中 $data为传入的$server,这里取获取请求方式,也就是POST
在首次进入filterValue时,调用情况是这样的,对于filter是一种循环调用,上次call_user_func的结果作为filter的参数进行调用
虽然value不可控,能否尝试利用 filter对一个不可控的参数进行调用后产生的结果不影响下次filter的调用
这里说两个函数 error_reporting 和 phpinfo
error_reporting('test') 返回数字 |
对于filter的控制我们可以用 filter($filter = null) 函数来进行控制
给出poc
0=error_reporting&1=phpinfo&_method=filter |
任意方法调用
调用filter,设置$this->filter
返回到run(),一路进入到input()
循环调用
相当于是流程图中的 二 中实现的
sesison文件写入
poc
POST |
session文件包含
poc
http://127.0.0.1/public/ |
如果构造的poc为这样,会导致在include时报错退出程序,利用 \think\Cookie::get 去改变POST的值。
http://127.0.0.1/public/?a=D:/phpstudy_pro/Extensions/tmp/tmp/sess_nccaieuoqs98rdv8r72hstqfh1 |
未开启debug改造(失败)
这样的改造,感觉是很难找出的,这里的$this->method我们指向filter的话,虽然调用了filter函数,但是返回值 $this->method 不等于get,所以在switch时,$dispatch[‘type’]=module,无法进入param()函数