Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

Thinkphp5-x远程代码执行漏洞分析

0
0
漏洞介绍

Thinkphp5.x版本(5.0.20)中没有对路由中的控制器进行严格过滤,在存在admin、index模块、没有开启强制路由的条件下(默认不开启),导致可以注入恶意代码利用反射类调用命名空间其他任意内置类,完成远程代码执行。

影响版本

THINKPHP 5.0.5-5.0.22

THINKPHP 5.1.0-5.1.30

漏洞分析

这里选择对5.0.20版本进行分析,关键函数开头

/thinkphp/library/think/App.php:120

$dispatch=self::$dispatch; //未设置调度信息则进行URL路由检测 if(empty($dispatch)){ $dispatch=self::routeCheck($request,$config); } //记录当前调度信息 $request->dispatch($dispatch); //记录路由和请求信息 if(self::$debug){ Log::record('[ROUTE]'.var_export($dispatch,true),'info'); Log::record('[HEADER]'.var_export($request->header(),true),'info'); Log::record('[PARAM]'.var_export($request->param(),true),'info'); } //监听app_begin Hook::listen('app_begin',$dispatch); //请求缓存检查 $request->cache( $config['request_cache'], $config['request_cache_expire'], $config['request_cache_except'] ); $data=self::exec($dispatch,$config);

进入self::routeCheck函数,进行路由检查

/thinkphp/library/think/App.php:120

publicstaticfunctionrouteCheck($request,array$config) { $path=$request->path(); ........................ }

进入$request->path()函数

/thinkphp/library/think/Request.php:416行

publicfunctionpath() { if(is_null($this->path)){ $suffix=Config::get('url_html_suffix'); $pathinfo=$this->pathinfo(); if(false===$suffix){ //禁止伪静态访问 $this->path=$pathinfo; }elseif($suffix){ //去除正常的URL后缀 $this->path=preg_replace('/\.('.ltrim($suffix,'.').')$/i','',$pathinfo); }else{ //允许任何后缀访问 $this->path=preg_replace('/\.'.$this->ext().'$/i','',$pathinfo); } } return$this->path; }

进入$this->pathinfo()函数

/thinkphp/library/think/Request.php:384行

publicfunctionpathinfo() { if(is_null($this->pathinfo)){ if(isset($_GET[Config::get('var_pathinfo')])){ //判断URL里面是否有兼容模式参数 $_SERVER['PATH_INFO']=$_GET[Config::get('var_pathinfo')]; unset($_GET[Config::get('var_pathinfo')]); }elseif(IS_CLI){ //CLI模式下index.phpmodule/controller/action/params/... $_SERVER['PATH_INFO']=isset($_SERVER['argv'][1])?$_SERVER['argv'][1]:''; } //分析PATHINFO信息 if(!isset($_SERVER['PATH_INFO'])){ foreach(Config::get('pathinfo_fetch')as$type){ if(!empty($_SERVER[$type])){ $_SERVER['PATH_INFO']=(0===strpos($_SERVER[$type],$_SERVER['SCRIPT_NAME']))? substr($_SERVER[$type],strlen($_SERVER['SCRIPT_NAME'])):$_SERVER[$type]; break; } } } $this->pathinfo=empty($_SERVER['PATH_INFO'])?'/':ltrim($_SERVER['PATH_INFO'],'/'); } return$this->pathinfo; }

Config::get('var_pathinfo')是配置文件中的设置的参数,默认值为s,从GET中获取键值,然后赋值给routeCheck中的$path,这里也就是index/think\app/invokefunction。


Thinkphp5-x远程代码执行漏洞分析

/thinkphp/library/think/App.php:120

publicstaticfunctionrouteCheck($request,array$config) { $path=$request->path(); $depr=$config['pathinfo_depr']; $result=false; //路由检测 $check=!is_null(self::$routeCheck)?self::$routeCheck:$config['url_route_on']; if($check){ //开启路由 if(is_file(RUNTIME_PATH.'route.php')){ //读取路由缓存 $rules=includeRUNTIME_PATH.'route.php'; is_array($rules)&&Route::rules($rules); }else{ $files=$config['route_config_file']; foreach($filesas$file){ if(is_file(CONF_PATH.$file.CONF_EXT)){ //导入路由配置 $rules=includeCONF_PATH.$file.CONF_EXT; is_array($rules)&&Route::import($rules); } } } //路由检测(根据路由定义返回不同的URL调度) $result=Route::check($request,$path,$depr,$config['url_domain_deploy']); $must=!is_null(self::$routeMust)?self::$routeMust:$config['url_route_must']; if($must&&false===$result){ //路由无效 thrownewRouteNotFoundException(); } } //路由无效解析模块/控制器/操作/参数...支持控制器自动搜索 if(false===$result){ $result=Route::parseUrl($path,$depr,$config['controller_auto_search']); } return$result; }

这里会开始进行路由检测,检查$check后会进入else分支导入路由配置,然后接着检测路由表url调度结果$result,如果调度失败并且开启了强制路由$must,就抛出异常,这就是漏洞利用条件之一,接着进入了Route::parseUrl函数,根据$path(我们可控的url)进行模块/控制器解析。

进入Route::parseUrl函数

/thinkphp/library/think/Route.php:1204行

publicstaticfunctionparseUrl($url,$depr='/',$autoSearch=false) { if(isset(self::$bind['module'])){ $bind=str_replace('/',$depr,self::$bind['module']); //如果有模块/控制器绑定 $url=$bind.('.'!=substr($bind,-1)?$depr:'').ltrim($url,$depr); } $url=str_replace($depr,'|',$url); list($path,$var)=self::parseUrlPath($url); }

进入self::parseUrlPath函数

/thinkphp/library/think/Route.php

privatestaticfunctionparseUrlPath($url) { //分隔符替换确保路由定义使用统一的分隔符 $url=str_replace('|','/',$url); $url=trim($url,'/'); $var=[]; if(false!==strpos($url,'?')){ //[模块/控制器/操作?]参数1=值1&参数2=值2... $info=parse_url($url); $path=explode('/',$info['path']); parse_str($info['query'],$var); }elseif(strpos($url,'/')){ //[模块/控制器/操作] $path=explode('/',$url); }else{ $path=[$url]; } return[$path,$var]; }

对包含模块/控制器/操作的URL进行分割成数组进行返回


Thinkphp5-x远程代码执行漏洞分析
list($path,$var)=self::parseUrlPath($url); $route=[null,null,null]; if(isset($path)){ //解析模块 $module=Config::get('app_multi_module')?array_shift($path):null; if($autoSearch){ //自动搜索控制器 $dir=APP_PATH.($module?$module.DS:'').Config::get('url_controller_layer'); $suffix=App::$suffix||Config::get('controller_suffix')?ucfirst(Config::get('url_controller_layer')):''; $item=[]; $find=false; foreach($pathas$val){ $item[]=$val; $file=$dir.DS.str_replace('.',DS,$val).$suffix.EXT; $file=pathinfo($file,PATHINFO_DIRNAME).DS.Loader::parseName(pathinfo($file,PATHINFO_FILENAME),1).EXT; if(is_file($file)){ $find=true; break; }else{ $dir.=DS.Loader::parseName($val); } } if($find){ $cont

Viewing all articles
Browse latest Browse all 12749