ThinkPHP5.0.*版本代码执行漏洞


我们先看处理请求的Request类,在thinkphp/ library/think/Request.php。

Method方法来获取当前的请求类型。

isGet、isPOST、isPUT等方法都调用了method方法来做请求类型的判断。
而在默认情况下,是有表单伪装变量的

在默认情况下,该变量值为_method

在Method方法中,将表单伪装变量对该方法的变量进行覆盖,可以实现对该类的所有函数进行调用。

在Request类中,我们看一下__construct析构方法。

如果该类属性不存在,就会调用默认配置文件中的default_filter的值。

如果该类属性存在,就会对$option数组进行遍历,将该类属性赋值为$option数组中键对应的值 。

因此,我们可以构造一个Payload:s=ipconfig&_mehthod=__construct$method=&filter[]=system

这个Payload只适用于thinkphp5.0.10版本,为什么?我们来看看thinkphp5.0.23版本的改进。

ThinkPHP5.0.23版本代码执行漏洞
在5.0.23版本中,App类 在thinkphp/ library/think/App.php中,module方法新增设置了
filter属性值,初始化了filter属性值,也导致在5.0.10版本中被重新覆盖为配置文件中的默认值导致无法进行利用。

在返回到Request类中,Param方法也调用了method方法,它用于获取当前请求的参数,传入了默认值true。

再看看method方法

当传过来的值为true时,会执行下面的代码。

再去看看server方法

那么,在此时server方法中的name值为REQUEST_METHOD,然后会调用input方法。

再进行跟踪input方法

$filter = $this->getFilter($filter, $default);
看注释是解析过滤器

再跟踪getFilter方法

此时的$filter= ''和$default=null

因此,执行了$filter = $filter ?: $this->filter;这段代码。

$this->filter被赋值给了$filter,也就是请求中的filter参数。

再回到input方法中,在解析过滤器之后,判断了$data是否为数组,如果不是,则将每个值作为一个参数并且用filterValue方法进行过滤

在server方法中,$this->server接受的是$_SERVER超全局变量。那么在调用析构方法的时候,我们也可以对$this->server的值进行覆盖。

在filterValue方法中,调用call_user_func方法导致了代码执行。

再回到Param方法,哪个地方会调用Param方法呢?

用phpstorm搜索一下 
在App类中,找到一处调用了$request->param();


如果开启了debug模式,就可以实现代码执行了。

在调用param方法之前,进行了未设置调度信息则进行 URL 路由检测的功能。用routeCheck方法来设置$dispatch。然后用exec方法


$config变量是通过initCommon方法中的init方法初始化的。

再来看看routeCheck方法

加载config文件导入路由配置
然后通过Route::import加载路由。

在入口文件 public/index.php中加载了/thinkphp/start.php文件

在start.php文件中加载了基础文件base.php

在base.php中有注册自动加载功能

跟踪register方法

对vendor下的php文件注册系统自动加载

在vendor/topthink/think-captcha/src/helper.php中加载了验证码路由类。

上面执行了Route::import方法之后,此时,我们打印一下Route::rules的值看看。


然后我们再返回到Route::check路由检测(根据路由定义返回不同的URL调度)跟踪check方法

Check方法调用了Request类中的method方法,把method方法执行的结果小写赋值给$method变量。

在Request类中method方法判断了如果$this->method存在的话,那么就直接返回。


那么我们在调用析构函数覆盖变量的时候,可以直接将method覆盖掉。$method是可控的了

将$method变量的值传入$rules的方法中获取对应的路由信息,赋值给$rules这个变量。


返回路由规则检测,$method给的值不同返回的结果也会不同。$method=get的时候


$rules的值是不一样的。


$url = str_replace($depr, '|', $url);
$item = str_replace('|', '/', $url);
$url在check方法中为参数,在App类中routeCheck方法调用

$path是经过$request->path();调用Request类中的path方法得到的。

如果is_null($this->path)为真的时候,会调用pathinfo函数来获取。

$_SERVER['PATH_INFO']的值为Config配置文件中
默认的值's'


url中的s参数可以设置App类中的routeCheck方法中的$path参数。
Router类中的check方法控制了check函数中的$item变量也就控制了check方法最终返回的值,同时也控制了App类中的调度信息$dispath

$dispath在App类中的run方法中被exec方法调用。

跟踪exec方法

这里使用了switch语句判断$dispath['type']来执行相应的代码。

之前,我们需要调用Request类中的param方法来对filter变量的覆盖。

如果$dispath['type']是controller或者是method的时候可以直接调用param方法。

当我们让$dispath['type']=function的时候,调用了invokeFunction方法, invokeFunction方法调用了bindParams方法也对param方法进行了调用。

我们控制了url中的s参数的值可以设置不同的$method,让routeCheck返回$dispath。
我们将控制的url参数s的设置为captcha,并且设置post数据_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ipconfig

此时,调用了check方法后会进入checkRoute方法。

再调用了CheckRule方法

又调用了parseRule方法

最后经过路由处理后,返回$result

在调用exec方法以后,触发调用了Request类中的param方法,将Request类中的server变量覆盖,再经过Request类中的input方法的调用导致了代码执行漏洞。

发布了19 篇原创文章 · 获赞 12 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/xuandao_ahfengren/article/details/86333189