通读审计之DOYOCMS
字数 2173 2025-08-15 21:30:29
DOYOCMS代码审计与漏洞分析教学文档
一、前言
本教学文档基于对DOYOCMS建站系统的完整代码审计过程,详细记录了审计思路、漏洞发现过程及利用方法。文档采用"通读审计"方式,将整个审计过程完整呈现,特别适合对MVC框架不熟悉的审计者学习。
二、MVC框架基础分析
1. 核心文件结构
-
index.php:入口文件
- 包含
config.php(定义常量和数据库信息) - 设置模板路径:
$doyoConfig['view']['config']['template_dir'] = APP_PATH.'/template/'.$doyoConfig['ext']['view_themes']; - 包含
sys.php(核心系统文件)
- 包含
-
sys.php:核心系统文件
- 包含
functions.php(功能函数) - 初始化全局配置:
$GLOBALS['G_DY'] = spConfigReady(require(DOYO_PATH."/inc.php"), $doyoConfig); - 定义MVC核心类:
syController、syModel、syView
- 包含
2. 关键函数分析
spConfigReady函数
function spConfigReady($useconfig, $preconfig = array()) {
if(is_array($useconfig)) {
foreach($useconfig as $key => $val) {
$preconfig[$key] = $val;
}
}
return $preconfig;
}
作用:合并两个配置数组
import函数
function import($sfilename) {
if(TRUE == @is_readable($sfilename)) {
require($sfilename);
$GLOBALS['G_DY']['import_file'][md5($sfilename)] = TRUE;
return TRUE;
}
return FALSE;
}
作用:安全包含文件,确保文件可读后才包含
syClass函数
function syClass($class_name, $args = null, $sdir = null, $force_inst = FALSE) {
// 类名合法性检查
if(preg_match("/^[a-zA-Z0-9_]+$/", $class_name) == 0) syError("类定义不存在,请检查。");
// 单例模式处理
if(TRUE != $force_inst) if(isset($GLOBALS['G_DY']["inst_class"][$class_name]))
return $GLOBALS['G_DY']["inst_class"][$class_name];
// 尝试加载类文件
if(null != $sdir && !import($sdir) && !import($sdir.'/'.$class_name.'.php')) return FALSE;
// 类是否存在检查
$has_define = FALSE;
if(class_exists($class_name, false) || interface_exists($class_name, false)) {
$has_define = TRUE;
} else {
if(TRUE == import($class_name.'.php')) {
$has_define = TRUE;
}
}
// 实例化类
if(FALSE != $has_define) {
$argString = ''; $comma = '';
if(null != $args) for($i = 0; $i < count($args); $i++) {
$argString .= $comma . "\$args[$i]"; $comma = ', ';
}
eval("\$GLOBALS['G_DY']['inst_class'][\$class_name]= new \$class_name($argString);");
return $GLOBALS['G_DY']["inst_class"][$class_name];
}
syError($class_name."类定义不存在,请检查。");
}
作用:动态加载和实例化类,实现MVC框架的核心功能
三、漏洞分析与利用
1. PHP任意文件替换漏洞
漏洞位置:自定义session处理机制中的文件写入操作
漏洞文件:sysession.php
关键代码:
function write($id, $data) {
$filedir = $this->savePath;
$filename = $filedir."/".$id.".php";
$data = "<?php die();?>".$data;
return file_put_contents($filename, $data);
}
漏洞分析:
- 程序使用自定义session存储机制,将session数据写入文件
- 写入前会在数据前添加
<?php die();?>防止代码执行 - 但未对
$id(即session ID)进行过滤,导致路径可控 - 利用
file_put_contents特性,可以替换任意文件内容
利用条件:
- 能够控制session ID
- 知道目标文件路径
利用方法:
- 设置session ID为
../include/inc.php - 程序会将session数据写入
/include/inc.php,替换原文件 - 由于写入内容包含
<?php die();?>,会导致网站无法正常运行
防御建议:
- 对session ID进行严格过滤,禁止目录穿越字符
- 使用固定目录存储session文件
- 使用PHP原生session机制
2. SSRF漏洞
漏洞位置:a_sys控制器的template_cache方法
漏洞文件:source/admin/a_sys.php
关键代码:
function template_cache() {
$y = $this->syArgs('y', 2);
$url = "http://".$y."/template/admin/index/index.html";
$html = spUrlOpen($url);
// ...
}
漏洞分析:
- 使用
syArgs('y', 2)获取参数,其中第二个参数2表示过滤级别 - 过滤级别2存在缺陷(后面详细分析)
- 直接将用户输入的
y参数拼接到URL中 - 使用
spUrlOpen函数发起请求,导致SSRF
过滤问题分析:
syArgs方法中:
case 2: // 仅安全过滤
$this->args = $this->filters($this->args, 'safe');
break;
filters方法中的safe过滤:
function filters($value, $type) {
// ...
case 'safe':
if(is_array($value)) {
array_walk_recursive($value, array($this, 'arrays'));
} else {
$value = $this->arrays($value);
}
break;
// ...
}
function arrays($value) {
if(is_array($value)) {
foreach($value as $k => $v) {
$value[$k] = $this->arrays($v);
}
} else {
$value = htmlspecialchars(addslashes($value));
}
return $value;
}
问题:array_walk_recursive不会修改原始数组,导致数组参数过滤失效
利用方法:
admin.php?c=a_sys&a=template_cache&y=127.0.0.1:3306
可探测内网服务
防御建议:
- 对URL参数进行严格校验,限制域名和端口
- 使用白名单方式限制可访问的地址
- 修复
array_walk_recursive过滤问题
3. SQL注入漏洞
漏洞位置:a_classtypes控制器的alledit方法
漏洞文件:source/admin/a_classtypes.php
关键代码:
function alledit() {
$orders = $this->syArgs('orders', 2);
// ...
$this->db->update(array('types'=>$orders), $where);
}
漏洞分析:
- 使用
syArgs('orders', 2)获取参数,同样存在过滤级别2的问题 - 直接将用户输入的
orders参数用于SQL更新操作 - 框架自带的update方法未充分过滤输入
利用方法:
?c=a_classtypes&a=alledit&orders[]=1' or updatexml(2,concat(0x7e,(version())),0) or'
防御建议:
- 使用预处理语句替代直接拼接SQL
- 加强输入过滤,特别是对数组参数的过滤
- 对数据库操作进行统一的参数绑定处理
四、审计技巧总结
-
MVC框架审计要点:
- 跟踪控制器、模型、视图的调用关系
- 关注路由解析过程(
$_REQUEST['c']和$_REQUEST['a']) - 分析核心函数如
syClass的动态加载机制
-
漏洞挖掘方法:
- 全局搜索危险函数(
file_put_contents、eval等) - 分析自定义过滤函数的实现缺陷
- 跟踪用户输入从获取到使用的完整流程
- 全局搜索危险函数(
-
重点关注:
- 文件操作相关的路径控制
- 数据库操作是否使用预处理
- 网络请求相关的参数过滤
五、修复建议
-
整体安全加固:
- 实现严格的输入过滤机制
- 使用PHP原生session替代自定义实现
- 对文件操作进行路径检查和限制
-
代码层面改进:
- 修复
array_walk_recursive过滤问题 - 加强
syArgs方法的过滤能力 - 对数据库操作进行统一安全处理
- 修复
-
架构层面建议:
- 实现前后端分离,减少服务端动态渲染
- 使用成熟的框架替代自主实现MVC
- 引入自动化安全测试流程
本教学文档详细记录了DOYOCMS系统的审计过程和发现的漏洞,可作为代码审计学习的典型案例。通过此案例,可以学习到MVC框架的审计方法、常见漏洞的挖掘技巧以及安全防御的最佳实践。