前面陸續學習了CC1,CC3,CC6,以及TemplatesImpl
以及改造,有點亂,正所謂溫故而知新嘛,所以這篇就回顧一下,捋一捋,解決一些細節問題。
由於CC1要介紹CC鏈的幾個關鍵類,所以寫了三篇
InvokerTransformer
,其transform
可以執行任意方法,ConstantTransformer
作用是拿到一個危險類,如RunTime
等等,ChainedTransformer
作用是將幾個Transformer串聯起來這三種搭配就可以執行任意方法
TransformedMap
:用來修飾Map,被修飾過的Map在新增新的元素時,將可以執⾏⼀個回撥,也就是說可以呼叫其他的tramsform
AnnotationInvocationHandler
:第四點說了,觸發漏洞的核心是向Map加入新的元素,在實際反序列化利用的時候,我們需要找到一個類,它在反序列化的readObject
邏輯裡有類似的寫入操作。這個類剛好符合條件。
到這兒,算是一條完整的CC利用鏈了。
LazyMap
:作用和TransformedMap
類似,都是為了執行transform
,區別就是TransformedMap是在寫入元素的時候執行會transform
,而LazyMap是在其get
方法中執行的factory.transform
,原因是當我們呼叫某個動態代理物件的方法時,都會觸發代理類的
invoke`方法,並傳遞對應的內容分析一下利用過程:這裡1,2,3,4,5是一條利用鏈,邏輯很清晰。1,2,3,6,7是一條利用鏈。這裡還是比較繞的,分析一下利用過程:
只需要找到某個地方呼叫了LazyMap#get
方法,並且傳遞了任意值。
首先在readObject時,會觸發AnnotationInvocationHandler#readObject
方法,其中呼叫了this.memberValues.entrySet
而this.memberValues
是構造好的proxyMap
,由於這是一個代理物件,所以呼叫其方法時,會去呼叫其建立代理時設定的handler
的invoke
方法
proxyMap設定的handler為下面這個handler
,同樣是InvocationHandler
這個類,接著會呼叫他的invoke方法
InvocationHandler#invoke
的78行程式碼中呼叫了this.memberValues#get
,此時的this.memberValues
為之前設定好的lazymap,所以這裡呼叫的是lazymap#get
,從而觸發後邊的rce鏈。
代理後的物件叫做proxyMap
,但我們不能直接對其進行序列化,因為我們入口點是 sun.reflect.annotation.AnnotationInvocationHandler#readObject
,所以我們還需要再用 AnnotationInvocationHandler
對這個proxyMap
進行包裹:
這裡還是比較繞的,因為設定了兩個handler,但是第一個handler是為了觸發lazymap#get
,而第二個handler實際上只是為了觸發代理類所設定handler的invoke方法
接著解釋一些細節的問題:
為什麼這用反射的方式來建立AnnotationInvocationHandler的範例?
因為AnnotationInvocationHandler並不是public類,所以無法直接通過new的方式來建立其範例。
2.為什麼建立handler時傳入的第一個引數是Retention.class?
因為在建立範例的時候對傳入的第一個引數呼叫了isAnnotation方法來判斷其是否為註解類:
public boolean isAnnotation() {
return (getModifiers() & ANNOTATION) != 0;
}
而Retention.class正是java自帶的一個註解類,所以這裡可以直接用上,當然要是換成其他註解類也是ok的。
CommonsCollections1
利用鏈,兩種方法,LazyMap
以及TransformedMap
,但是在Java 8u71
以後,這個利⽤鏈不能再利⽤了,主要原因是 sun.reflect.annotation.AnnotationInvocationHandler#readObject
的邏輯變化了
所以關注點如何調⽤LazyMap#get()
?
找到的類是org.apache.commons.collections.keyvalue.TiedMapEntry
,在其getValue
⽅法中調⽤了 this.map.get
,⽽其hashCode
⽅法調⽤了getValue
⽅法
又在 java.util.HashMap#readObject
中就可以找到 HashMap#hash()
的調⽤
/*
Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashMap#readObject
java.util.HashMap#hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
*/
而ysoserial中使用HashSet.readObject()來呼叫
java.util.HashSet.readObject()
java.util.HashMap.put()
java.util.HashMap.hash()
邏輯也很清晰
這條鏈是Java7和8高版本通殺
首先利⽤TemplatesImpl
鏈是可以通過TemplatesImpl#newTransformer()
執行程式碼的。
在一個為了繞過⼀些規則對InvokerTransformer
的限制。所以CC3並沒有使⽤到InvokerTransformer
來調⽤任意⽅法,⽽是⽤到了另⼀個 類, com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter
這個類的構造⽅法中調⽤(TransformerImpl) templates.newTransformer()
,免去了我們使⽤InvokerTransformer⼿⼯調⽤newTransformer()⽅法這⼀步
⽤到⼀個新的Transformer,就是 org.apache.commons.collections.functors.InstantiateTransformer
。InstantiateTransformer
也是⼀個實現了Transformer
接⼝的類,他的作⽤就是調⽤構造⽅法.
利⽤InstantiateTransformer
來調⽤到TrAXFilter
的構造⽅法,再利⽤其構造⽅法⾥的templates.newTransformer()
調⽤到TemplatesImpl
⾥的位元組碼
知識點比較多,每一個都捋清楚後,就可以重分發散思維,各種組合利用