
1. 概述
本文档详细介绍了Java中各种序列化/反序列化方式的漏洞成因和代码特征。
2. 序列化/反序列化方式
2.1 原生Java序列化
原生Java序列化使用 ObjectOutputStream
/ ObjectInputStream
,基于 Serializable
接口的二进制协议。
特点:
常见漏洞:
- Commons Collections, Spring, JDK内置Gadget等
- ysoserial工具里大量payload就是针对原生序列化
2.1.1 原生反序列化
1. 直接反序列化未验证输入
1 2 3 4 5 6 7 8 9 10
| public void deserializeData(InputStream inputStream) { try { ObjectInputStream ois = new ObjectInputStream(inputStream); Object obj = ois.readObject(); } catch (Exception e) { e.printStackTrace(); } }
|
2. 缺少安全过滤器
1 2 3 4 5 6 7 8 9
| public class UnsafeDeserializer { public Object deserialize(byte[] data) throws Exception { ByteArrayInputStream bis = new ByteArrayInputStream(data); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); } }
|
3. 白名单实现不完整
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class IncompleteWhitelistFilter implements ObjectInputFilter { private static final Set<String> WHITELIST = new HashSet<>(); static { WHITELIST.add("com.safe.User"); WHITELIST.add("com.safe.Order"); }
@Override public Status checkInput(FilterInfo filterInfo) { if (filterInfo.serialClass() != null) { String className = filterInfo.serialClass().getName(); return WHITELIST.contains(className) ? Status.ALLOWED : Status.REJECTED; } return Status.UNDECIDED; } }
|
4. 黑名单更新不及时
1 2 3 4 5 6 7 8 9
| public class OutdatedBlacklistFilter implements ObjectInputFilter { private static final Set<String> BLACKLIST = new HashSet<>(); static { BLACKLIST.add("org.apache.commons.collections.Transformer"); BLACKLIST.add("org.apache.commons.collections.functors.ChainedTransformer"); } }
|
5. 异常处理不当
1 2 3 4 5 6 7 8 9 10 11 12
| public Object deserializeWithBadExceptionHandling(byte[] data) { try { ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); return ois.readObject(); } catch (Exception e) { e.printStackTrace(); return null; } }
|
2.2 基于JSON的序列化
2.2.1 Fastjson
阿里巴巴开源,支持自动类型(autoType
)特性。
常见漏洞:
- 利用
autoType
加载恶意类/Gadget
- 版本不同防御机制差异很大
漏洞成因与代码特征:
1. autoType功能开启
1 2 3 4 5 6 7
| ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
ParserConfig config = new ParserConfig(); config.setAutoTypeSupport(true); JSON.parseObject(jsonString, Object.class, config);
|
2. 白名单配置不完整
1 2 3 4 5 6 7 8
| ParserConfig config = new ParserConfig(); config.put("com.safe.User", User.class); config.put("com.safe.Order", Order.class);
JSON.parseObject(jsonString, Object.class, config);
|
3. 直接反序列化为Object类型
1 2 3 4 5 6 7 8 9
| public Object parseJson(String jsonString) { return JSON.parseObject(jsonString, Object.class); }
public Object parseJsonWithTypeRef(String jsonString) { return JSON.parseObject(jsonString, new TypeReference<Object>(){}); }
|
4. 版本过低或配置不当
1 2 3 4 5 6
|
ParserConfig config = new ParserConfig();
|
2.2.2 Jackson
高性能JSON序列化/反序列化库。
常见漏洞:
- 利用
DefaultTyping
特性
- 利用可反序列化的gadget类,例如
org.springframework.beans.factory.ObjectFactory
漏洞成因与代码特征:
1. DefaultTyping配置不当
1 2 3 4 5 6 7
| ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS);
|
2. 多态反序列化配置
1 2 3 4 5 6 7 8 9 10 11
| @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) public class BaseClass { }
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY) public class PolymorphicClass { }
|
3. 直接反序列化为Object
1 2 3 4 5
| public Object parseJson(String jsonString) throws Exception { ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(jsonString, Object.class); }
|
4. 缺少安全模块配置
1 2 3 4
| ObjectMapper mapper = new ObjectMapper();
|
2.2.3 Gson
谷歌出品,通常默认安全性较好。Gson默认情况下不支持反序列化为任意类型,不易利用,但也可能被组合攻击。
漏洞成因与代码特征:
1. 自定义TypeAdapter存在风险
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class UnsafeTypeAdapter<T> extends TypeAdapter<T> { @Override public T read(JsonReader in) throws IOException { String className = in.nextString(); try { Class<?> clazz = Class.forName(className); return (T) clazz.newInstance(); } catch (Exception e) { return null; } }
@Override public void write(JsonWriter out, T value) throws IOException { } }
|
2. 反射使用不当
1 2 3 4 5 6 7 8 9 10 11 12
| public class ReflectionBasedDeserializer { public Object deserialize(String json, String className) { try { Class<?> clazz = Class.forName(className); Gson gson = new Gson(); return gson.fromJson(json, clazz); } catch (Exception e) { return null; } } }
|
2.3 基于XML的序列化
2.3.1 XStream
将对象转为XML或从XML转回对象。
特点:
常见漏洞:
- 加载可利用的gadget类
- SSRF/任意文件读写(如果配置不当)
漏洞成因与代码特征:
1. 默认配置未修改
1 2 3 4 5
| public Object deserializeXml(String xml) { XStream xstream = new XStream(); return xstream.fromXML(xml); }
|
2. 白名单配置不完整
1 2 3 4 5
| XStream xstream = new XStream(); xstream.allowTypes(new Class[]{User.class, Order.class});
|
3. 允许外部实体解析
1 2 3 4
| XStream xstream = new XStream();
|
4. 直接反序列化用户输入
1 2 3 4 5 6
| public Object processUserXml(String userXml) { XStream xstream = new XStream(); return xstream.fromXML(userXml); }
|
2.3.2 JAXB
Java官方标准。也可能被利用反序列化gadget攻击,但相比XStream要安全一些(要看配置)。
漏洞成因与代码特征:
1. 不安全的Unmarshaller配置
1 2 3 4 5 6 7
| public Object unmarshalXml(String xml) throws Exception { JAXBContext context = JAXBContext.newInstance(Object.class); Unmarshaller unmarshaller = context.createUnmarshaller(); return unmarshaller.unmarshal(new StringReader(xml)); }
|
2. 允许外部实体
1 2 3
| Unmarshaller unmarshaller = context.createUnmarshaller();
|
2.4 基于YAML的序列化
2.4.1 SnakeYAML
流行的YAML库。
常见漏洞:
- 默认情况下可能反序列化为任意Java类型
- 利用gadget加载恶意对象
漏洞成因与代码特征:
1. 默认构造函数使用
1 2 3 4 5
| public Object loadYaml(String yamlContent) { Yaml yaml = new Yaml(); return yaml.load(yamlContent); }
|
2. 不安全的Constructor配置
1 2 3 4 5 6 7
| public Object loadYamlUnsafe(String yamlContent) { Constructor constructor = new Constructor(); Yaml yaml = new Yaml(constructor); return yaml.load(yamlContent); }
|
3. 缺少类型限制
1 2 3 4 5 6
| public Object loadYamlWithoutTypeRestriction(String yamlContent) { Yaml yaml = new Yaml(); return yaml.load(yamlContent); }
|
4. 直接加载用户输入
1 2 3 4 5 6
| public Object processUserYaml(String userYaml) { Yaml yaml = new Yaml(); return yaml.load(userYaml); }
|
2.5 基于其他格式的序列化
2.5.1 Hessian
二进制序列化协议,跨语言。部分历史版本可被反序列化利用。
漏洞成因与代码特征:
1. 使用存在漏洞的版本
1 2 3 4 5 6 7 8
|
public Object deserializeHessian(byte[] data) throws Exception { ByteArrayInputStream bis = new ByteArrayInputStream(data); Hessian2Input input = new Hessian2Input(bis); return input.readObject(); }
|
2. 缺少类型验证
1 2 3 4 5 6
| public Object deserializeWithoutValidation(byte[] data) throws Exception { Hessian2Input input = new Hessian2Input(new ByteArrayInputStream(data)); return input.readObject(); }
|
2.5.2 Kryo
高性能二进制序列化库。如果允许外部输入数据,可能被利用。
漏洞成因与代码特征:
1. 不安全的Kryo配置
1 2 3 4 5 6 7 8 9 10
| public Object deserializeKryo(byte[] data) { Kryo kryo = new Kryo();
ByteArrayInputStream bis = new ByteArrayInputStream(data); Input input = new Input(bis); return kryo.readObject(input, Object.class); }
|
2. 允许任意类反序列化
1 2 3 4 5 6 7 8 9
| public Object deserializeAnyClass(byte[] data, Class<?> clazz) { Kryo kryo = new Kryo(); kryo.setRegistrationRequired(false);
ByteArrayInputStream bis = new ByteArrayInputStream(data); Input input = new Input(bis); return kryo.readObject(input, clazz); }
|
2.5.3 protobuf/Thrift/Avro
通常需要配合定义好的schema。不易出现传统反序列化漏洞,但schema本身若可控也有风险。
漏洞成因与代码特征:
1. 动态Schema加载
1 2 3 4 5 6 7
| public Object deserializeWithDynamicSchema(byte[] data, String schemaDefinition) { Schema schema = Schema.parse(schemaDefinition); DatumReader<Object> reader = new GenericDatumReader<>(schema); return reader.read(null, DecoderFactory.get().binaryDecoder(data, null)); }
|
2. 反射使用不当
1 2 3 4 5 6 7 8 9 10
| public Object deserializeWithReflection(byte[] data, String className) { try { Class<?> clazz = Class.forName(className); Method parseFrom = clazz.getMethod("parseFrom", byte[].class); return parseFrom.invoke(null, data); } catch (Exception e) { return null; } }
|
3. 总结
在进行Java反序列化代码审计时,需要重点关注:
- 反序列化入口点:识别所有可能触发反序列化的代码位置
- 安全控制机制:检查是否实现了有效的安全控制措施
- 输入验证:确保反序列化数据的来源和完整性
- 配置管理:验证安全配置的正确性和及时性
- 异常处理:避免因异常处理不当导致的安全问题
通过系统性的审计,可以有效识别和防范Java反序列化漏洞风险。