fastjson及其反序列化分析
源碼取自
https://www.github.com/ZH3FENG/PoCs-fastjson1241
參考
(23條消息) Json詳解以及fastjson使用教程_srj1095530512的博客-CSDN博客_fastjson
parse方法和parseObject方法區別:
parse()及parseObject()進行反序列化時的細節區別在於,parse() 會識別並調用目標類的 setter 方法,而 parseObject() 由於要將返回值轉化為JSONObject,多執行了 JSON.toJSON(obj),所以在處理過程中會調用反序列化目標類的getter 方法來將參數賦值給JSONObject
ParserConfig類:配置反序列化信息
Autotype:
Fastjson提供了autotype功能,允許用戶在反序列化數據中通過“@type”指定反序列化的Class類型。
AutoType安全校驗
知道了AutoType的作用后,假設如下場景,
服務端接收到的請求Json串中包含了指定惡意代碼Class的@Type,
服務端調用JSON.parseObject()時觸發了該Class中的構造函數、或者是getter、setter方法中的惡意代碼
AutoType黑名單機制:在反序列化時,會校驗指定的class是否在黑名單中,若在,則拋出異常
Safemode機制:配置safeMode后,無論白名單和黑名單,都不支持autoType,可一定程度上緩解反序列化Gadgets類變種攻擊。
TemplatesImpl 利用鏈
fastjson反序列化TemplatesImpl - Afant1 - 博客園 (cnblogs.com)
在fastjson中調用Templateslmpl可以構造一條反序列化攻擊鏈。
攻擊鏈分析
TemplatesImpl攻擊調用鏈路
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl是歷史上出現過存在FastJson反序列漏洞的一個第三方類
TemplatesImpl中存在一個get方法為getOutputProperties(),其在調用FastJson.parseObject()序列化為Java對象時會被調用
getOutputProperties內部調用了newTransformer()方法,newTransformer()內部調用了getTransletInstance()方法獲取Translet對象
獲取Translet對象時,其通過內部的私有變量_bytecodes生成返回的Translet對象
這里這個_bytecodes私有變量就是整個攻擊設計的核心所在,雖然FastJson默認只能反序列化公有屬性,但是可以在JSON串中指定_bytecodes為我們惡意攻擊類的字節碼,同時調用JSON.parseObject(json, Object.class, Feature.SupportNonPublicField)來反序列化私有屬性,那么_bytecodes就可以是任意指定代碼
也就是說,如果事先定義好了Translet返回Class類的內容,並且在自定義的Translet類的構造函數中實現攻擊代碼,並且把攻擊代碼轉化成字節碼,傳入TemplatesImpl的私有變量_bytecodes中,那么反序列化生成TemplatesImpl時就會使用我們自定義的字節碼來生成Translet類,從而觸發Translet構造函數中的攻擊代碼
首先使用parseObject對payload進行反序列化。parseObject會調用payload中存儲的@type信息,即Templateslmpl的getter,setter,和構造方法。
在TypeUtil.class中下斷點,此處加載Templateslmpl類。

這里調用了getter方法,getOutputProperties

調用newTransformer方法

調用getTransletInstance方法

這里會調用defineTransletClasses,通過傳入的_bytecodes生成 _class

此處可以看到成功傳入類名,調用newInstance實例化為tranlet對象

此處調用惡意構造方法

完整的利用鏈


payload構造分析
String payload = "{\"@type\":\"" + NASTY_CLASS +
"\",\"_bytecodes\":[\""+evilCode+"\"],'_name':'a.b','_tfactory':{ },\"_transletIndex\":0,\"_auxClasses\":{},\"_outputProperties\":{}}";
這里NASTY_CLASS是指要加載的類,這里evilCode時包含惡意代碼的類路徑,需要使用字節碼在進行base64編碼。getTransletInstance方法中會判斷_name是否為空,因此這里需要設置 _name字段。
public static String readClass(String cls){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
IOUtils.copy(new FileInputStream(new File(cls)), bos);
} catch (IOException e) {
e.printStackTrace();
}
return Base64.encodeBase64String(bos.toByteArray());
}
這里對整個_bytecodes部分做了base64解碼,所以payload要進行base64編碼
