freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

jfinal框架——ofcmsv1.1.3代码审计
2024-09-05 13:04:34

项目地址:

https://gitee.com/oufu/ofcms/tree/V1.1.3/

参考文章:

https://www.yijinglab.com/specialized/20220526170417

https://mp.weixin.qq.com/s/dZAqUVpKr6fA_Lq7nJTmbw

https://mp.weixin.qq.com/s/1agUDocOUbFgbiO83Qz5-w

本篇文章是个人在代码审计学习阶段对于jfinal下的一个CMS的审计,漏洞的审计还是比较经典的过程,存在的问题希望大家可以提出,共同交流学习

代码审计思路

  1. pom.xml的依赖 dependency

  2. ofcms-V1.1.3\ofcms-admin\src\main\webapp\WEB-INF\web.xml 过滤器 filter

  3. 全局搜索关键字 配合 网页 功能点灰盒 审计

1. SQL注入

1.1. jfinal框架下操作数据库简介

首先分析他使用的数据库框架是Mybatis还是JDBC
1.png
随便找了一条语句,发现是使用Db这个类去执行的SQL语句,去Db看一下

2.png

发现是jfinal框架下的ORM

ORM(Object-Relational Mapping,对象关系映射)是一种技术,用于在面向对象编程语言(如 Java、C# 等)和关系数据库之间建立关联。通过 ORM,开发者可以使用面向对象的编程方式来操作数据库,而无需编写大量的 SQL 语句。

该框架下的数据库配置在JFWebConfig.java中

3.png

跟入ADMIN_CONFIG,可以看到数据库配置文件的存放位置

4.png

这里简介一些关于Db的操作,这样方便搜索关键字进行审计

Db.save: 插入一条记录。该方法需要提供表名和一个Record对象。

Record user = new Record().set("name", "Alice").set("age", 25);
        Db.save("user", user);

Db.find:查询所有记录

List<Record> users = Db.find("SELECT * FROM user");
for (Record user : users) {
    System.out.println(user.get("name"));

Db.findFirst:查询单条记录

Record user = Db.findFirst("SELECT * FROM user WHERE id = ?", 1);
        if (user != null) {
            System.out.println(user.get("name"));
        }

Db.query:查询单个字段

String name = Db.queryStr("SELECT name FROM user WHERE id = ?", 1);
System.out.println(name);

Db.update: 更新记录。该方法可以直接执行更新 SQL 语句,也可以传入Record对象。

直接执行更新 SQL 语句

int updatedRows = Db.update("UPDATE user SET age = ? WHERE id = ?", 30, 1);
System.out.println("Updated rows: " + updatedRows);

使用Record对象

Record user = Db.findById("user", 1).set("age", 30);
        Db.update("user", user);

下文用到了Db.update,这里来看一下源码

可以看到使用了prepareStatement,但是要注意的是,这里虽然使用了prepareStatement但是没有使用??占位符,所以直接将sql语句输入依然可以执行

5.png

Db.delete:

直接执行删除 SQL 语句

int deletedRows = Db.delete("DELETE FROM user WHERE id = ?", 1);
System.out.println("Deleted rows: " + deletedRows);

根据主键删除记录

boolean success = Db.deleteById("user", 1);
System.out.println("Deleted successfully: " + success);

Db.tx:

可以执行事务操作: 事务是一组操作,这些操作要么全部成功,要么全部失败,从而保证数据的一致性和完整性。

该方法接受一个实现了IAtom接口的匿名类或 lambda 表达式,所有的数据库操作将在一个事务中执行

import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.IAtom;

public class DbExample {
    public static void main(String[] args) {
        boolean success = Db.tx(new IAtom() {
            @Override
            public boolean run() throws SQLException {
                Db.update("UPDATE user SET age = ? WHERE id = ?", 30, 1);
                Db.update("UPDATE user SET age = ? WHERE id = ?", 35, 2);
                return true; // 返回 true 表示提交事务,返回 false 表示回滚事务
            }
        });
        System.out.println("Transaction success: " + success);
    }
}

Db.batch:

import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;
import java.util.ArrayList;
import java.util.List;

public class DbExample {
    public static void main(String[] args) {
        List<Record> users = new ArrayList<>();
        users.add(new Record().set("name", "Bob").set("age", 20));
        users.add(new Record().set("name", "Charlie").set("age", 22));
        int[] results = Db.batchSave("user", users, 100);
        for (int result : results) {
            System.out.println("Batch result: " + result);
        }
    }
}

Db.getSqlPara: 用于获取带有参数的 SQL 语句,结合了 SQL 模板和参数的功能,将动态生成的 SQL 语句与参数安全地绑定在一起,从而避免了 SQL 注入风险。

import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.SqlPara;

import java.util.HashMap;
import java.util.Map;

public class Example {
    public static void main(String[] args) {
        // 假设 params 包含查询参数
        Map<String, Object> params = new HashMap<>();
        params.put("sqlid", "findUserById");
        params.put("id", 1);

        // 获取 SqlPara 对象
        SqlPara sqlPara = Db.getSqlPara(params.get("sqlid").toString(), params);

        // 使用 SqlPara 执行查询
        List<Record> users = Db.find(sqlPara);
        for (Record user : users) {
            System.out.println(user.get("name"));
        }
    }
}

1.2. 关键字查找注入点

根据上面学习到的内容,我们基本上可以确定思路了,先查找执能够行SQL语句的关键字,然后看执行语句时传入的参数(非Db.getSqlPara、或是预编译传参)笔者这里写了一个表达式

Db.(find|findFirst|update|delete|save|batch)\s*(

用来搜索jfinal框架下对于SQL语句操作的方法

6.png

都看了一遍,大部分是用了Db.getSqlPara或者是预编译,但是有一处直接使用getPara进行传参

7.png

跟过去,找到控制器路径

8.png

抓包控制器路径,传入恶意参数,判断确实存在SQL注入漏洞

9.png

payload:

update of_cms_link set link_name=updatexml(1,concat(0x7e,(user())),0) where link_id = 4

10.png

2. XSS

2.1. jfinal框架下接收参数关键字

挖掘XSS漏洞时,要寻找"输入点"与"输出点",由于"输入点"和"输出点"可能不在同一个业务流中,在挖掘这类漏洞时,可以考虑以下方法提高效率:

  1. 黑白盒结合。(定位可能存在漏洞的地点)。

  2. 通过功能,接口名,表名,字段名等角度去搜索(关键字)。

这里的话重点是代码审计,就不去黑盒的测试了,介绍一下白盒下的思路

首先寻找接收参数的关键字:getParameter,但是别忘了当

# 网络安全 # web安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录