PbootCMS2.07前台任意文件包含漏洞(复现)
Posted wangtanzhi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PbootCMS2.07前台任意文件包含漏洞(复现)相关的知识,希望对你有一定的参考价值。
0x00
审计对象:PbootCMS2.07
cms下载地址:
https://github.com/hnaoyun/PbootCMS/releases/tag/V2.0.7
参考文章:
https://xz.aliyun.com/t/7744#toc-1
漏洞代码
漏洞发生在 core/view/view.php
即解析模板文件的parser函数:
// 解析模板文件
public function parser($file)
{
// 设置主题
$theme = isset($this->vars[‘theme‘]) ? $this->vars[‘theme‘] : ‘default‘;
$theme = preg_replace(‘/..(/|)/‘, ‘‘, $theme); // 过滤掉相对路径
$file = preg_replace(‘/..(/|)/‘, ‘‘, $file); // 过滤掉相对路径
if (strpos($file, ‘/‘) === 0) { // 绝对路径模板
$tpl_file = ROOT_PATH . $file;
} elseif (! ! $pos = strpos($file, ‘@‘)) { // 跨模块调用
$path = APP_PATH . ‘/‘ . substr($file, 0, $pos) . ‘/view/‘ . $theme;
define(‘APP_THEME_DIR‘, str_replace(DOC_PATH, ‘‘, $path));
if (! is_dir($path)) { // 检查主题是否存在
error(‘模板主题目录不存在!主题路径:‘ . $path);
} else {
$this->tplPath = $path;
}
$tpl_file = $path . ‘/‘ . substr($file, $pos + 1);
} else {
// 定义当前应用主题目录
define(‘APP_THEME_DIR‘, str_replace(DOC_PATH, ‘‘, APP_VIEW_PATH) . ‘/‘ . $theme);
if (! is_dir($this->tplPath .= ‘/‘ . $theme)) { // 检查主题是否存在
error(‘模板主题目录不存在!主题路径:‘ . APP_THEME_DIR);
}
$tpl_file = $this->tplPath . ‘/‘ . $file; // 模板文件
}
$note = Config::get(‘tpl_html_dir‘) ? ‘<br>同时检测到您系统中启用了模板子目录‘ . Config::get(‘tpl_html_dir‘) . ‘,请核对是否是此原因导致!‘ : ‘‘;
file_exists($tpl_file) ?: error(‘模板文件‘ . APP_THEME_DIR . ‘/‘ . $file . ‘不存在!‘ . $note);
$tpl_c_file = $this->tplcPath . ‘/‘ . md5($tpl_file) . ‘.php‘; // 编译文件
// 当编译文件不存在,或者模板文件修改过,则重新生成编译文件
if (! file_exists($tpl_c_file) || filemtime($tpl_c_file) < filemtime($tpl_file) || ! Config::get(‘tpl_parser_cache‘)) {
$content = Parser::compile($this->tplPath, $tpl_file); // 解析模板
file_put_contents($tpl_c_file, $content) ?: error(‘编译文件‘ . $tpl_c_file . ‘生成出错!请检查目录是否有可写权限!‘); // 写入编译文件
$compile = true;
}
ob_start(); // 开启缓冲区,引入编译文件
$rs = include $tpl_c_file;
if (! isset($compile)) {
foreach ($rs as $value) { // 检查包含文件是否更新,其中一个包含文件不存在或修改则重新解析模板
if (! file_exists($value) || filemtime($tpl_c_file) < filemtime($value) || ! Config::get(‘tpl_parser_cache‘)) {
$content = Parser::compile($this->tplPath, $tpl_file); // 解析模板
file_put_contents($tpl_c_file, $content) ?: error(‘编译文件‘ . $tpl_c_file . ‘生成出错!请检查目录是否有可写权限!‘); // 写入编译文件
ob_clean();
include $tpl_c_file;
break;
}
}
}
$content = ob_get_contents();
ob_end_clean();
return $content;
}
我们可以看到这个函数首先对传过来的参数进行过滤:
$file = preg_replace(‘/..(/|)/‘, ‘‘, $file); // 过滤掉相对路径
这个函数过滤的实际上是过滤2个.和/与(就是)
但是却可以绕过,我们本地测试一下:
这样就得到了后续我们想要的字符。
继续看代码:
之后生成模板文件
?$tpl_file?=?$this->tplPath?.?‘/‘?.?$file;?//?模板文件
然后编译文件
$tpl_c_file?=?$this->tplcPath?.?‘/‘?.?md5($tpl_file)?.?‘.php‘;?//?编译文件
之后就到了关键点
当编译文件不存在,或者模板文件修改过,则重新生成编译文件
我们可以看到他首先从缓存中判断编译文件存不存在,不存在则解析模板重新生成编译文件。
file_put_contents这个函数将$content写入,生成编译文件,include?$tpl_c_file; 包含我们的编译文件。
到这里我们的攻击思路已经很明确了,我们需要让parser函数的参数可以被控制,造造成任意文件包含。
找漏洞执行点,也就是找一个调用了parser函数的地方,且参数可控,比如能让我们传入入上面的目录穿越恶意字符造成任意文件读取。
漏洞执行点
前台控制器TagController中的index方法:
apps/home/controller/tagcontroller.php
$tagstpl?=?request(‘tagstpl‘);
直接传入导致我们前台$tagstpl可控
这里::用来直接调用类中的属性或方法
所以调用parser函数,且直接拼接,那个正则不影响我们的paylaod。
$content?=?parent::parser($this->htmldir?.?$tagstpl);?//?框架标签解析
然后就是虽然这个$content前面被拼接了$this->htmldir,但是函数内部可以出现目录穿越,所以$this->htmldir这个路径并不影响。也就是说他是在生成编译文件时穿越的。
验证成功
漏洞修复
官方在2.08版本修改
直接对TagController的正则进行了修改,强制限制了后缀名为html。
总结
这个漏洞用P3师傅的话来说就是
PbootCMS2.07内核处理缺陷导致的一个前台任意文件包含漏洞
他的内核函数在生成编译文件的时候造成任意文件读取,我们只要找到可控参数的parser调用就可以触发漏洞。
同时这次修复并没有对内核做出调整,所以想要绕过补丁可以往这个思路来找,看看哪里同样调用了可控参数的parser。
最后,膜P3师傅!
参考
https://xz.aliyun.com/t/7744#toc-0
以上是关于PbootCMS2.07前台任意文件包含漏洞(复现)的主要内容,如果未能解决你的问题,请参考以下文章