漏洞简介
TP6删除了Windows类,但是可以通过Model类的__destruct()
方法触发save
函数,在checkAllowFields
方法中存在$this->table
与$this->suffix
的拼接,触发__toString()
方法,最后实现$closure
的动态调用
配置
1 | composer create-project --prefer-dist topthink/think=6.0.x-dev tp6x |
将 application/index/controller/Index.php 代码修改成如下:
1 |
|
漏洞演示
利用条件
- 存在反序列化点
- 存在phar反序列化
以上满足一个即可
poc.php
1 |
|
漏洞分析
漏洞的起始点在Model类中的__destruct
方法,然后进入save
方法,此处的$this->exists
需要为true进入到updatedata
方法
需要调用checkAllowFields
方法,需要通过false === $this->trigger('BeforeUpdate')
条件判断,那么只需要将withEvent
设置为false即可
可以看到checkAllowFields
方法中$this->table
和$this->suffix
进行了拼接,会触发__toString()
函数
上半部分分析完成,链子如下
1 | # Model.php |
在Conversion中调用__toString()=>toJson()=>toArray()
,最终进入到elseif
的第二个分支的getAttr
方法,key为$this->data
的键
通过getAttr=>getData=>getFieldName
最终返回的$value
是$this->data[$key]
,然后进入getValue
方法
从$this->withAttr
获取$FieldName
变量为$closure
作为调用函数,从$this->data
获取$FieldName
变量为调用参数$value
,那么只需要令$this->withAttr[$FieldName]
为system
,$this->data[$FieldName]
为参数,即可执行系统命令
漏洞分析至此结束
漏洞修复
增加了对$closure
变量的验证再调用