ThinkPHP5.1.X反序列化利用链
字数 1308 2025-08-26 22:11:15

ThinkPHP5.1.X反序列化利用链分析

环境搭建

  1. 使用Composer创建项目:
composer create-project --prefer-dist topthink/think tp5137
cd tp5137
  1. 修改composer.json文件,将框架版本指定为5.1.37:
"topthink/framework": "5.1.37"
  1. 更新依赖:
composer update
  1. 修改控制器代码(application/index/controller/Index.php):
<?php
namespace app\index\controller;

class Index
{
    public function index()
    {
        $u = unserialize($_GET['c']);
        return 'hhh';
    }
}

利用条件

  • 必要条件:

    • 存在完全可控的反序列化点(如unserialize(可控变量)
  • 可选条件(满足任意一个即可):

    • 存在文件上传且文件名完全可控,并使用了文件操作函数(如file_exists('phar://恶意文件')

漏洞链分析

1. 入口点:Windows类的__destruct方法

think\process\pipes\Windows类的__destruct方法中存在文件删除功能,其中$filename变量可控。如果将类对象赋值给$filename,在file_exists($filename)时会触发该对象的__toString方法。

2. __toString方法利用

think\model\concern\Conversion类的__toString方法中:

  • $relation来自$this->data[$name](可控)
  • $name来自$this->append(可控)
  • 最终调用形式:可控类->visible(可控变量)

3. 寻找visible或__call方法

由于没有直接可利用的visible方法,转而寻找__call方法:

  • think\Request类的__call方法中call_user_func_array的第一个参数完全可控
  • 可构造为call_user_func_array(array(任意类,任意方法),$args)

4. 方法链调用

虽然Request类的input方法常被用于RCE,但由于$args的第一个元素固定为类对象,需要间接调用:

  • 通过request方法间接调用input方法
  • 进一步通过isAjaxisPjax方法调用param方法

5. 最终利用

构造Request对象:

  • $this->filter = 'system'
  • $this->param = array('id')

影响版本

  • 5.1.16 <= ThinkPHP <= 5.1.37
  • 5.1.14 <= ThinkPHP <= 5.1.15
  • 5.1.3 <= ThinkPHP <= 5.1.13

漏洞链流程图

  1. Windows::__destruct -> file_exists
  2. 触发Conversion::__toString
  3. 调用Request::__call
  4. 通过isAjax/isPjax -> param -> input
  5. 执行任意命令

防御建议

  1. 升级到安全版本
  2. 避免直接反序列化用户输入
  3. 对文件操作函数进行严格过滤
  4. 使用类型检查确保file_exists等函数参数为字符串

参考

  • 官方GitHub issue
  • 挖掘暗藏thinkphp中的反序列利用链
ThinkPHP5.1.X反序列化利用链分析 环境搭建 使用Composer创建项目: 修改composer.json文件,将框架版本指定为5.1.37: 更新依赖: 修改控制器代码(application/index/controller/Index.php): 利用条件 必要条件: 存在完全可控的反序列化点(如 unserialize(可控变量) ) 可选条件(满足任意一个即可): 存在文件上传且文件名完全可控,并使用了文件操作函数(如 file_exists('phar://恶意文件') ) 漏洞链分析 1. 入口点:Windows类的__ destruct方法 think\process\pipes\Windows 类的 __destruct 方法中存在文件删除功能,其中 $filename 变量可控。如果将类对象赋值给 $filename ,在 file_exists($filename) 时会触发该对象的 __toString 方法。 2. __ toString方法利用 think\model\concern\Conversion 类的 __toString 方法中: $relation 来自 $this->data[$name] (可控) $name 来自 $this->append (可控) 最终调用形式: 可控类->visible(可控变量) 3. 寻找visible或__ call方法 由于没有直接可利用的 visible 方法,转而寻找 __call 方法: think\Request 类的 __call 方法中 call_user_func_array 的第一个参数完全可控 可构造为 call_user_func_array(array(任意类,任意方法),$args) 4. 方法链调用 虽然 Request 类的 input 方法常被用于RCE,但由于 $args 的第一个元素固定为类对象,需要间接调用: 通过 request 方法间接调用 input 方法 进一步通过 isAjax 或 isPjax 方法调用 param 方法 5. 最终利用 构造 Request 对象: $this->filter = 'system' $this->param = array('id') 影响版本 5.1.16 <= ThinkPHP <= 5.1.37 5.1.14 <= ThinkPHP <= 5.1.15 5.1.3 <= ThinkPHP <= 5.1.13 漏洞链流程图 Windows::__destruct -> file_exists 触发 Conversion::__toString 调用 Request::__call 通过 isAjax/isPjax -> param -> input 执行任意命令 防御建议 升级到安全版本 避免直接反序列化用户输入 对文件操作函数进行严格过滤 使用类型检查确保 file_exists 等函数参数为字符串 参考 官方GitHub issue 挖掘暗藏thinkphp中的反序列利用链