本文目录导读:

- 案例一:SSRF + 云服务元数据泄露(业务逻辑漏洞)
- 案例二:Spring Boot Actuator + JNDI注入(配置不当 + 反序列化)
- 案例三:MyBatis/MyBatis-Plus SQL注入(最经典但易忽视)
- 案例四:Java原生反序列化漏洞(RCE终极形态)
- 总结:Java审计的“黄金三角”
这是一份关于 Java审计(Java Security Audit/Code Review) 的实战案例分析合集。
在Java开发中,由于框架的成熟和第三方组件的丰富,安全问题往往隐藏在 业务逻辑缺陷、框架配置不当、序列化/反序列化漏洞 以及 复杂框架组合利用 中。
以下整理了四个具有代表性的Java审计案例,涵盖不同层级和攻击面。
SSRF + 云服务元数据泄露(业务逻辑漏洞)
场景: 一个“头像/图片抓取”功能,允许用户输入URL,服务端Java代码去读取该URL的图片并返回。
漏洞代码(伪代码):
// 对外暴露接口,接受imageUrl参数
@RequestMapping("/fetchImage")
public ResponseEntity fetchImage(@RequestParam String imageUrl) {
// 无任何协议验证和IP白名单
URL url = new URL(imageUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
InputStream is = conn.getInputStream();
// 读取并返回图片流...
}
审计发现:
- SSRF (Server Side Request Forgery): 攻击者输入
http://169.254.169.254/latest/meta-data/(AWS云元数据地址)。 - Java行为: JDK内置的
URL类直接发起GET请求到内网IP。 - 攻击效果: 服务器返回云服务器的临时凭证 (Access Key / Secret Key)。
- 利用链:
http://10.0.0.1:8080/actuator/env(探测内网Spring Boot Actuator)。 - 更深层: 利用
file:///etc/passwd协议读取本地文件。
修复方案:
- 协议白名单: 强制只能使用
http://或https://,禁止file://、ftp://。 - IP黑名单/白名单: 使用
InetAddress解析URL中的域名,判断是否为内网IP(127.0.0.1, 10.x.x.x, 172.16.x.x, 192.168.x.x),是则拦截。 - DNS Rebinding防御: 进行两次IP解析校验(第一次校验,第二次连接)。
Spring Boot Actuator + JNDI注入(配置不当 + 反序列化)
场景: 某Spring Boot应用开启了所有Actuator端点,且Spring版本较低(Log4j、Fastjson或Tomcat本地)。
审计发现:
- Actuator 未授权访问:
/actuator/env可查看环境变量,/actuator/beans可查看Bean列表,/actuator/dump 可下载堆转储。 - 意外功能:
POST /actuator/refresh(Config Client端)可动态修改配置。 - JNDI注入点: 代码中有类似
InitialContext.lookup(request.getParameter("url"))或使用了存在漏洞的日志库(Log4j2 CVE-2021-44228)。
攻击链(综合利用):
- 访问
/actuator/env获取数据库密码、Redis密码等。 - 如果存在远程配置中心(如Spring Cloud Config Server)且可写,攻击者修改配置属性(如
spring.datasource.url指向恶意MySQL服务器)。 - 利用
POST /actuator/refresh刷新配置。 - 应用重启或触发数据源连接时,恶意MySQL服务器返回包含恶意反序列化payload的数据包,触发RCE。
修复方案:
- 生产环境禁用: 关闭所有Actuator端点或使用
management.endpoints.web.exposure.exclude=*。 - 强认证: 引入Spring Security,为
/actuator/**路径添加角色校验(如ADMIN角色)。 - 升级依赖: 升级Log4j、Fastjson、Jackson等库,避免JNDI漏洞。
MyBatis/MyBatis-Plus SQL注入(最经典但易忽视)
场景: 项目使用MyBatis,查询接口使用了过多的 拼接。
漏洞代码(DAO层):
// Mapper接口
@Select("SELECT * FROM user WHERE ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);
审计发现:
- 开发者为了方便动态传递排序字段或表名,使用了
${column}。 - 攻击者请求参数:
/api/user?column=id&value=1变更为/api/user?column=id OR 1=1--&value=1。 - 结果: SQL变更为
SELECT * FROM user WHERE id OR 1=1-- = ?,注入了万能密码或绕过逻辑。
另一个变种(Order By注入):
// 使用MyBatis-Plus的Wrapper,但直接拼接用户输入 queryWrapper.orderByAsc(request.getSortField()); // 如果sortField是 user_id; DROP TABLE orders--
修复方案:
- 严苛使用#{ }: 坚持传参必须使用(预编译占位符),只有当表名、字段名、
ORDER BY子句等不能预编译时,才需白名单验证。 - 白名单验证: 如果必须用,在Java代码层对参数进行枚举白名单校验。
List<String> allowedColumns = Arrays.asList("id", "name", "create_time"); if (!allowedColumns.contains(column)) { throw new IllegalArgumentException(); }
Java原生反序列化漏洞(RCE终极形态)
场景: 一个老的内部系统,接收用户POST的Base64字符串,直接进行反序列化,使用了Commons-Collections 3.2.1。
漏洞代码:
@RequestMapping("/deserialize")
public String deserializeData(@RequestBody String data) {
byte[] bytes = Base64.getDecoder().decode(data);
// 危险!使用了 ObjectInputStream
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
Object obj = ois.readObject(); // 在此触发RCE
// ...
}
审计发现:
- 技术预判: 项目依赖了
commons-collections-3.2.1,该版本存在著名的InvokerTransformer链。 - 入口点: 任何读取
ObjectInputStream.readObject()的地方都是高危入口,常见场景:- RMI接口调用(绑定对象)。
- JMX连接。
- Session持久化(Tomcat反序列化Session)。
- Fastjson/Jackson自动反序列化。
攻击利用:
- 攻击者使用
ysoserial工具生成payload:java -jar ysoserial.jar CommonsCollections5 "calc.exe" > payload.bin。 - 将payload Base64编码后POST给接口。
- 服务端反序列化时,执行命令(windows弹出计算器,Linux反弹shell)。
修复方案:
- 替代序列化协议: 使用JSON(Jackson/Fastjson,注意配置SafeMode)替代Java原生序列化。
- 黑白名单过滤: 重写
ObjectInputStream的resolveClass方法,禁止反序列化危险类(如InvokerTransformer、Runtime等)。 - 升级库: 升级
commons-collections到4.x(修复了Gadget链)或使用jackson-databind的SafeMode。
Java审计的“黄金三角”
在进行Java审计时,重点关注以下三个维度:
-
数据流入口(Input Source):
@RequestParam,@RequestBody,HttpServletRequest.getParameter()ObjectInputStream.readObject(),JMS消息,RMI接口- 多部分文件上传
MultipartFile
-
危险函数/中间件(Sink Point):
Runtime.exec(),ProcessBuilder.start()(命令执行)InitialContext.lookup()(JNDI注入)URLClassLoader(动态类加载)XMLReader.parse(),DocumentBuilder.parse()(XXE)@Select ${}(SQL注入)FileInputStream,FileOutputStream(任意文件读取/写入)
-
配置与依赖(Configuration & Dependency):
Spring Boot Actuator是否暴露敏感端点。log4j2、fastjson、shiro、struts2等历史漏洞组件的版本。Web.xml/Security Configuration中路径权限配置是否遗漏(如全部放行)。
审计核心逻辑: 找到User Input ( 用户可控)如何流向Dangerous Functions ( 危险函数),中间经过了哪些Filter/Validation ( 过滤/校验),并判断该过滤是否可以被绕过。