前言
前几天审计某cms基于ThinkPHP5.0.24开发,反序列化没有可以较好的利用链,这里分享下挖掘ThinkPHP5.0.24反序列化利用链过程.该POP实现任意文件内容写入,达到getshell的目的
环境搭建
Debian
apache2+mysql+ThinkPHP5.0.24+php5.6
下载地址:http://www.thinkphp.cn/donate/download/id/1279.html
文件:application/index/controller/Index.php
<?php
namespace appindexcontroller;
class Index
{
public function index($input='')
{
echo "Welcome thinkphp 5.0.24";
echo $input;
unserialize($input);
}
}
访问:http://127.0.0.1/cms/tp50/public/index.php
简述
Thinkphp 5.0.x反序列化最后触发RCE,要调用的Request
类__call
方法.
但是由于这里self::$hook[$method]
不可控,无法成功利用
我的思路是在找其他的__call
,其他魔术方法搜了一圈没有可以进一步利用.
文件:thinkphp/library/think/console/Output.php
最后选择Output类中的__call
方法,这里调用block
方法.后续可以当做跳板
POP链分析
从头开始分析
反序列化起点:thinkphp/library/think/process/pipes/Windows.php removeFiles
方法
跟进removeFiles
方法
跳板:file_exists
方法能够触发__toString
魔术方法
跳板利用点:thinkphp/library/think/Model.php
Model抽象类的 __toString
跟进toJson
方法至toArray
方法
如下图Model抽象类的toArray
方法,存在三个地方可以执行__call
但是我们目的是调用Output类的__call
且能够继续利用,调试后选择第三处当做调板
$item[$key] = $value ? $value->getAttr($attr) : null;
分析下如何达到该行代码
$item[$key] = $value ? $value->getAttr($attr) : null;
这里直接看else分支
溯源$values
变量,比较关键是下面两行
$modelRelation = $this->$relation();
$value = $this->getRelationData($modelRelation);
$modelRelation
值可以利用Model类中的getError
方法
跟进getRelationData
方法,这里最后传入的$modelRelation
需要Relation
类型
最后返回值$values
需要经过if
语句判断
$this->parent && !$modelRelation->isSelfRelation() && get_class($modelRelation->getModel()) == get_class($this->parent)
全局搜索下,可以利用HasOne类
最后$attr
值,由$bindAttr = $modelRelation->getBindAttr();
执行后的结果.
跟进OneToOne抽象类getBindAttr
方法,binAttr
类变量可控.
至此代码执行到$item[$key] = $value ? $value->getAttr($attr) : null;
就能够执行Output类__call
魔术方法
跟进Output类block
方法
继续跟进writelin
方法,最后会调用write
方法
这里$this->handle
可控,全局搜索write
方法,进一步利用
定位到:thinkphp/library/think/session/driver/Memcached.php
类: Memcached
继续搜索可用set
方法
定位到:thinkphp/library/think/cache/driver/File.php
类:File
最后可以直接执行file_put_contents
方法写入shell
$filename
可控且可以利用伪协议绕过exit
$data
值比较棘手,这里有个坑,由于最后调用set
方法中的参数来自先前调用的write
方法
只能为true
,且这里$expire
只能为数值,这样文件内容就无法写shell
继续执行,跟进下方的setTagItem
方法
会再执行一次set
方法,且这里文件内容$value
通过$name
赋值(文件名)
所以可以在文件名上做手脚
示例:php://filter/write=string.rot13/resource=./<?cuc cucvasb();?>
POP链(图)
EXP
马赛克
复现
写入文件
实战是需要找个可写目录
读取文件
结语
感谢@水泡泡师傅解答问题
整条POP分析下来挺有趣,希望师傅们喜欢.