ThinkPHP5远程代码执行漏洞分析与利用

历时半年的考研复习结束,被数学一棒子打死。。。 来年再战

0x01 背景

2018年12月9日 Thinkphp官方发布ThinkPHP5.* 版本更新(https://blog.thinkphp.cn/869075)

本次版本更新主要涉及一个安全更新,由于框架对控制器名没有进行足够的检测会导致在没有开启强制路由的情况下可能的getshell漏洞,受影响的版本包括5.0和5.1版本,推荐尽快更新到最新版本。

0x02 分析

根据官方所提供补丁,可以把审计重点放在框架对控制器名的检测和不开启强制路由所导致的结果上。
ThinkPHP的路由规则(三种模式、五种方法)相信了解过phpMVC(TP5)的都心中有数,这里不再赘述。

不开启强制路由的情况下(默认不开启)采用默认的PATH_INFO 模式访问URL:

http://serverName/index.php/module/controller/action/param/value/...

如果服务器环境不支持pathinfo方式的url访问,可以使用兼容模式。

http://serverName/index.php?s=/module/controller/action

在TP5.0中,追踪到 /library/think/Loader.php 文件

protected static function getModuleAndClass($name, $layer, $appendSuffix)
{
if (false !== strpos($name, '\\')) {
$module = Request::instance()->module();
$class  = $name;
} else {
if (strpos($name, '/')) {
list($module, $name) = explode('/', $name, 2);
} else {
$module = Request::instance()->module();
}
$class = self::parseClass($module, $layer, $name, $appendSuffix);
}
return [$module, $class];
}

public static function parseClass($module, $layer, $name, $appendSuffix = false)
{
$array = explode('\\', str_replace(['/', '.'], '\\', $name));
$class = self::parseName(array_pop($array), 1);
$class = $class . (App::$suffix || $appendSuffix ? ucfirst($layer) : '');
$path  = $array ? implode('\\', $array) . '\\' : '';
return App::$namespace . '\\' .
($module ? $module . '\\' : '') .
$layer . '\\' . $path . $class;
}

$name 中存在反斜杠时,直接将$name 赋值到$class ,不再调用parseClass 函数来限定命名空间(调用范围),从而导致可将任意命名空间下的类作为controller,使得任意public类都可被用户访问到,通过调用ThinkPHP5的敏感的类内方法便可造成远程命令执行。

0x03 利用

payload:
12

0x04 修复

更新框架修复

如果你使用composer安装,并且一直保持最新版本使用的话,使用下面的指令更新到最新版本即可

composer update topthink/framework

如果你使用了git版本库安装,也请及时更新你所用的仓库版本。

如果各种原因暂时无法更新到最新版本(早期版本升级到最新版本可能存在兼容性问题,请首先参考官方手册的升级指导章节),可以参考下面的方式进行手动修正。

手动修复

5.0版本
在think\\App类的module方法的获取控制器的代码后面加上

if (!preg_match('/^[A-Za-z](\\w|\\.)*$/', $controller)) {
throw new HttpException(404, 'controller not exists:' . $controller);
}

5.1版本
在think\\route\\dispatch\\Url类的parseUrl方法,解析控制器后加上

if ($controller && !preg_match('/^[A-Za-z](\\w|\\.)*$/', $controller)) {
throw new HttpException(404, 'controller not exists:' . $controller);
}

参考链接:
1.https://p0sec.net/index.php/archives/125/
2.https://www.kancloud.cn/manual/thinkphp5

1 评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注