持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第十六天,点击查看活动详情
准备
Windows+phpstudy tp版本:thinkphp_5.0.5_full php版本:5.4.45 phpstorm+xdebug
动态调试
花了两天时间才整好(哭泣.jpg)感谢学长!!pp大佬永远的神!!! 这里简单记录一下在配置debug时候踩的坑
安装xdebug
phpstudy里面就带有xdebug,这里要注意监听的端口,后面配置phpstorm需要用这个端口
php配置文件
设置php.ini里面关于xdebug的内容,重要的点用箭头标记出来了,下面设置的时间是调试的时候有可能出现程序还没走完就自己退出了调试,然后浏览器显示状态码500,这里可以把时间调长一点
xdebug.idekey="PHPSTORM"
xdebug.remote_handler=dbgp
xdebug.remote_autostart = On
# 超时时间改大一点
xdebug.remote_cookie_expire_time = 3600
max_execution_time=3600
max_input_time=3600
default_socket_timeout = 3600
判断xdebug是否配置成功可以在浏览器里面看phpinfo的内容,看里面是否有xdebug模块,也可以使用cmd执行php -m看里面是否有xdebug
phpstorm里的配置
首先需要配置服务器内容,我的tp单独设置了端口
这里的坑一定要记录下,这一个小对勾让我多整了一天!!!
首先设置调试端口和xdebug里面是一样的,不要勾选“通过未注册的服务器配置忽略外部链接”!!!不要勾选“通过未注册的服务器配置忽略外部链接”!!!我之前勾上了然后调试就只会从index入口文件开始,不能调试单独文件
这里的KEY就是配置文件里面的
xdebug插件
浏览器安装就好了,配置下key,选择debug,phpstorm开启监听就可以开始愉快的动态调试啦
开始分析
漏洞代码位于:thinkphp/library/think/Request.php
首先放上payload:
s=whoami&_method=__construct&method=post&filter[]=system
method方法主要用来判断请求方式,首先分析一下这段代码的逻辑:通过$_SERVER和server方法获取请求类型,如果不存在method变量值,那么就用表单请求类型伪装变量覆盖method的值,那么就可以利用这点调用其他函数,预定义里面method为false,那么就会直接走下一步
elseif (!$this->method) {
if (isset($_POST[Config::get('var_method')]))
public function method($method = false)
{
if (true === $method) {
// 获取原始请求类型
return IS_CLI ? 'GET' : (isset($this->server['REQUEST_METHOD']) ? $this->server['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']);
} elseif (!$this->method) {
if (isset($_POST[Config::get('var_method')])) {
$this->method = strtoupper($_POST[Config::get('var_method')]);
$this->{$this->method}($_POST);
} elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
$this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
} else {
$this->method = IS_CLI ? 'GET' : (isset($this->server['REQUEST_METHOD']) ? $this->server['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']);
}
}
return $this->method;
}
下一步进入下层判断是否存在表单覆盖变量
从get方法中获取var_method的值,值为_method
在config.php已经有默认值
// 表单请求类型伪装变量
'var_method' => '_method',
继续往下跟代码,来到__construct构造方法,将数组option进行遍历操作,如果option的键名为该属性的话,则将该同名的属性赋值给$option的键值,如果filter为空的空,就调用默认的default_filter值
filter方法:
public function filter($filter = null)
{
if (is_null($filter)) {
return $this->filter;
} else {
$this->filter = $filter;
}
}
而默认的过滤方法为空
// 默认全局过滤方法 用逗号分隔多个
'default_filter' => '',
在构造函数里面走完filter之后会走input方法,继续跟进
继续往下跟,这里的method已经为post方法,所以进入param方法里的post是直接break的
下一步进入filtervalue方法中,可以看到我们要传入的值已经全部传进了