ThinkPHP5.0.x反序列化利用链
字数 1591 2025-08-26 22:11:51

ThinkPHP5.0.x反序列化利用链分析

漏洞概述

本文详细分析ThinkPHP5.0.x版本中的反序列化漏洞利用链。该漏洞通过精心构造的反序列化数据,可以实现任意文件写入,最终获取Webshell权限。

环境要求

  • PHP版本:5.6
  • 操作系统:Linux
  • ThinkPHP版本:5.0.24

漏洞测试代码

<?php
namespace app\index\controller;

class Index {
    public function index() {
        $c = unserialize($_GET['c']);
        var_dump($c);
        return 'Welcome to thinkphp5.0.24';
    }
}

POP链分析

入口点:think\process\pipes::__destruct

  • 通过__destruct方法里调用的removeFiles方法
  • 利用file_exists函数触发任意类的__toString方法
  • 选择think\Model类来触发(需使用其子类如think\Model\Pivot

__toString触发点

  • 在ThinkPHP5.1.X中,通常在调用think\Model::toArray()时触发think\Request::__call()
  • 但在5.0.X中,think\Request::__call()写法不同,成员变量不可控

替代__call方法选择

选择think\console\Output类:

  • __call方法最终调用$this->handle->write()
  • $this->handle可控,需要找到具有可利用write方法的类

文件写入点选择

think\console\Output类的handler属性设置为think\session\driver\Memcached类对象:

  • think\session\driver\Memcached::write()中调用set方法
  • think\cache\driver\File::set()方法可以写文件

文件写入细节

  1. $filename的前一部分$this->options['path']可控
  2. $valuethink\console\Output::writeln()中固定为true
  3. $this->setTagItem($filename)中又调用set方法写文件
    • 此时$value为部分可控的$filename
    • 可利用PHP伪协议写shell

示例payload:

php://filter/write=string.rot13/resource=<?cuc @riny($_TRG[_]);?>

(访问webshell时需将文件名中的?URL编码为%3f

toArray中的__call触发

通过$value->getAttr()触发think\console\Output::__call()

  1. $value必须是think\console\Output类对象
  2. $this->getRelationData($modelRelation)中被赋值
  3. $modelRelation = $this->$relation(),选择返回结果简单可控的getError方法

getRelationData方法分析

  • $this->parent需为think\console\Output类对象
  • think\model\Relation类的isSelfRelationgetModel方法返回值可控
  • 使用think\model\Relation的子类完成利用

替代利用方式

如果网站不允许写文件,可以:

  1. 修改payload创建0755权限目录(利用think\cache\driver\File::getCacheKey()中的mkdir函数)
  2. 然后往该目录写文件

防御建议

  1. 避免反序列化用户可控数据
  2. 升级到最新安全版本
  3. 对文件操作进行严格权限控制
  4. 禁用危险PHP函数和协议

总结

该漏洞利用链通过精心构造的反序列化数据,利用多个类的魔术方法和特性,最终实现任意文件写入。理解该漏洞有助于开发者更好地防范类似安全问题。

ThinkPHP5.0.x反序列化利用链分析 漏洞概述 本文详细分析ThinkPHP5.0.x版本中的反序列化漏洞利用链。该漏洞通过精心构造的反序列化数据,可以实现任意文件写入,最终获取Webshell权限。 环境要求 PHP版本:5.6 操作系统:Linux ThinkPHP版本:5.0.24 漏洞测试代码 POP链分析 入口点: think\process\pipes::__destruct 通过 __destruct 方法里调用的 removeFiles 方法 利用 file_exists 函数触发任意类的 __toString 方法 选择 think\Model 类来触发(需使用其子类如 think\Model\Pivot ) __toString 触发点 在ThinkPHP5.1.X中,通常在调用 think\Model::toArray() 时触发 think\Request::__call() 但在5.0.X中, think\Request::__call() 写法不同,成员变量不可控 替代 __call 方法选择 选择 think\console\Output 类: 其 __call 方法最终调用 $this->handle->write() $this->handle 可控,需要找到具有可利用 write 方法的类 文件写入点选择 将 think\console\Output 类的 handler 属性设置为 think\session\driver\Memcached 类对象: think\session\driver\Memcached::write() 中调用 set 方法 think\cache\driver\File::set() 方法可以写文件 文件写入细节 $filename 的前一部分 $this->options['path'] 可控 $value 在 think\console\Output::writeln() 中固定为 true $this->setTagItem($filename) 中又调用 set 方法写文件 此时 $value 为部分可控的 $filename 可利用PHP伪协议写shell 示例payload: (访问webshell时需将文件名中的 ? URL编码为 %3f ) toArray 中的 __call 触发 通过 $value->getAttr() 触发 think\console\Output::__call() : $value 必须是 think\console\Output 类对象 在 $this->getRelationData($modelRelation) 中被赋值 $modelRelation = $this->$relation() ,选择返回结果简单可控的 getError 方法 getRelationData 方法分析 $this->parent 需为 think\console\Output 类对象 think\model\Relation 类的 isSelfRelation 、 getModel 方法返回值可控 使用 think\model\Relation 的子类完成利用 替代利用方式 如果网站不允许写文件,可以: 修改payload创建0755权限目录(利用 think\cache\driver\File::getCacheKey() 中的 mkdir 函数) 然后往该目录写文件 防御建议 避免反序列化用户可控数据 升级到最新安全版本 对文件操作进行严格权限控制 禁用危险PHP函数和协议 总结 该漏洞利用链通过精心构造的反序列化数据,利用多个类的魔术方法和特性,最终实现任意文件写入。理解该漏洞有助于开发者更好地防范类似安全问题。