關于JAVA的Apache Commons Collections組件反序列漏洞的分析文章已經有很多了,當我看完很多分析文章后,發現JAVA反序列漏洞的一些要點與細節未被詳細描述,還需要繼續分析之后才能更進一步理解并掌握這個漏洞。
上述的要點與細節包括:
為了方便和我一樣的小白們理解這個漏洞,我將JAVA反序列化漏洞完整過程的分析與調試進行了整理。分析過程中利用的類為TransformedMap與AnnotationInvocationHandler。發現漏洞不是我等小白能力所及,因此本文不以挖掘漏洞的角度來進行分析,而是在已知漏洞存在的情況下分析漏洞。
為了分析JAVA的反序列化漏洞,首先需要了解JAVA的序列化與反序列化機制。
以下內容來自JDK1.6 API文檔中對ObjectOutputStream的說明。
ObjectOutputStream 將 Java 對象的基本數據類型和圖形寫入 OutputStream。可以使用 ObjectInputStream 讀取(重構)對象。通過在流中使用文件可以實現對象的持久存儲。如果流是網絡套接字流,則可以在另一臺主機上或另一個進程中重構對象。
只能將支持 java.io.Serializable 接口的對象寫入流中。每個 serializable 對象的類都被編碼,編碼內容包括類名和類簽名、對象的字段值和數組值,以及從初始對象中引用的其他所有對象的閉包。
writeObject 方法用于將對象寫入流中。所有對象(包括 String 和數組)都可以通過 writeObject 寫入。可將多個對象或基元寫入流中。必須使用與寫入對象時相同的類型和順序從相應 ObjectInputstream 中讀回對象。
即使用ObjectOutputStream.writeObject方法可對實現了Serializable接口的對象進行序列化,序列化后的數據可存儲在文件中,或通過網絡傳輸。
以下內容來自JDK1.6 API文檔中對ObjectInputStream的說明。
ObjectInputStream 對以前使用 ObjectOutputStream 寫入的基本數據和對象進行反序列化。
ObjectOutputStream 和 ObjectInputStream 分別與 FileOutputStream 和 FileInputStream 一起使用時,可以為應用程序提供對對象圖形的持久存儲。ObjectInputStream 用于恢復那些以前序列化的對象。其他用途包括使用套接字流在主機之間傳遞對象,或者用于編組和解組遠程通信系統中的實參和形參。
ObjectInputStream 確保從流創建的圖形中所有對象的類型與 Java 虛擬機中顯示的類相匹配。使用標準機制按需加載類。
只有支持 java.io.Serializable 或 java.io.Externalizable 接口的對象才能從流讀取。
readObject 方法用于從流讀取對象。應該使用 Java 的安全強制轉換來獲取所需的類型。在 Java 中,字符串和數組都是對象,所以在序列化期間將其視為對象。讀取時,需要將其強制轉換為期望的類型。
即使用ObjectInputStream.readObject方法可對序列化的數據進行反序列化。當實現了Serializable接口的對象被反序列化時,該對象的readObject方法會被調用。
String實現了Serializable接口,可進行序列化。
以下測試代碼會對String類的對象進行序列化,將序列化的數據保存在文件中,再從文件讀取序列化的數據進行反序列化。執行上述代碼后,能夠正確輸出原String類的對象的值。
java.io.ObjectStreamConstants類中定義了STREAM_MAGIC與STREAM_VERSION,查看JDK1.5、1.6、1.7、1.8的ObjectStreamConstants類,STREAM_MAGIC值均為0xaced,STREAM_VERSION值均為5。JDK1.6的源碼中,上述變量的代碼如下。
#!java
package java.io;
/**
* Constants written into the Object Serialization Stream.
*
* @author unascribed
* @version %I%, %G%
* @since JDK 1.1
*/
public interface ObjectStreamConstants {
/**
* Magic number that is written to the stream header.
*/
final static short STREAM_MAGIC = (short)0xaced;
/**
* Version number that is written to the stream header.
*/
final static short STREAM_VERSION = 5;
即0xaced為JAVA對象序列化流的魔數,0x0005為JAVA對象序列化的版本號,JAVA對象序列化數據的前4個字節為“AC ED 00 05”。
查看上一步驟生成的保存了序列化數據的文件,文件內容開頭為“AC ED 00 05”,與上述描述相符。
以下測試代碼為test.SerializeMyClass類,在其中定義了內部類MyObject。MyObject類實現了Serializable接口,SerializeMyClass類會對MyObject類的對象進行序列化,將序列化的數據保存在文件中,再從文件讀取序列化的數據進行反序列化。
執行結果如下
#!bash
MyObject(String name) tttest
MyObject-readObject!!!!!!!!!!!!!! tttest
tttest!
可以看到MyObject類實現的Serializable接口的readObject方法會被調用,且對象被序列化再反序列化后,對其值不影響。
生成的保存了序列化數據的文件,文件內容開頭也為“AC ED 00 05”,可以看到文件內容包含了包名與類名、類中包含的變量名稱、類型及變量的值。
調用FileOutputStream類寫文件時,常用的代碼如下:
#!java
FileOutputStream fos = new FileOutputStream("1.txt");
fos.write("abc".getBytes());
若需要使用JAVA反射機制調用FileOutputStream類寫文件,且只允許調用Class.getMethod與Method.invoke方法,上述代碼需修改為如下形式。
調用Runtime類執行程序時,常用的代碼如下:
#!java
Runtime runtime = Runtime.getRuntime();
runtime.exec("calc");
若需要使用JAVA反射機制調用Runtime類執行程序件,且只允許調用Class.getMethod與Method.invoke方法,上述代碼需修改為如下形式。
當需要操作無法直接訪問的類時,需要使用JAVA的反射機制。即對無法直接訪問的類進行序列化時,需要使用JAVA的反射機制。
以下測試代碼為testReflection.TestReflection類,與前文中的test.MyObject類不在同一個包中,在TestReflection類中對MyObject類進行序列化時,需要使用JAVA的反射機制。
以下為執行結果,可以看到使用JAVA的反射機制后,能夠對無法直接訪問的類進行序列化。
#!bash
-before newInstance-
MyObject(String name) tttest
-after newInstance-
byteOut.toByteArray().length:71
MyObject-readObject!!!!!!!!!!!!!! tttest
object:class test.MyObject
-before newInstance-
MyObject(String name) no name-default
-after newInstance-
byteOut.toByteArray().length:80
MyObject-readObject!!!!!!!!!!!!!! no name-default
object:class test.MyObject
breenmachine在“What Do WebLogic, WebSphere, JBoss, Jenkins, OpenNMS, and Your Application Have in Common This Vulnerability”中列出了以下會使用JAVA反序列化的場景。
Java LOVES sending serialized objects all over the place. For example:
In HTTP requests – Parameters, ViewState, Cookies, you name it.
RMI – The extensively used Java RMI protocol is 100% based on serialization.
RMI over HTTP – Many Java thick client web apps use this – again 100% serialized objects.
JMX – Again, relies on serialized objects being shot over the wire.
Custom Protocols – Sending an receiving raw Java objects is the norm – which we’ll see in some of the exploits to come.
JAVA中間件通常通過網絡接收客戶端發送的序列化數據,JAVA中間件在對序列化數據進行反序列化數據時,會調用被序列化對象的readObject方法。如果某個對象的readObject方法中能夠執行任意代碼,那么JAVA中間件在對其進行反序列化時,也會執行對應的代碼。如果能夠找到滿足上述條件的對象進行序列化并發送給JAVA中間件,JAVA中間件也會執行指定的代碼,即存在反序列化漏洞。
JAVA反序列化漏洞需要滿足兩個條件:
利用JAVA反序列化漏洞可以使服務器執行任意代碼,可以直接控制服務器,危害非常大。
下文中出現的以下類均包含在Apache Commons Collections組件中。
org.apache.commons.collections.functors.ConstantTransformer
org.apache.commons.collections.functors.InvokerTransformer
org.apache.commons.collections.functors.ChainedTransformer
org.apache.commons.collections.map.TransformedMap
org.apache.commons.collections.map.AbstractInputCheckedMapDecorator
org.apache.commons.collections.map.AbstractMapDecorator
org.apache.commons.collections.set.AbstractSetDecorator
org.apache.commons.collections.collection.AbstractCollectionDecorator
org.apache.commons.collections.iterators.AbstractIteratorDecorator
org.apache.commons.collections.keyvalue.AbstractMapEntryDecorator
Apache Commons Collections組件原生的jar包為commons-collections-xxx.jar。
本文中分析的commons-collections-xxx.jar版本為3.2.1,JDK版本為1.6。
通過對commons-collections-xxx.jar中涉及的代碼進行反編譯,增加輸出或進行調試,可以跟蹤漏洞觸發時的代碼執行情況。
org.apache.commons.collections.functors.ConstantTransformer類的transform方法會返回構造函數傳入的參數。ConstantTransformer類相關代碼如下。
#!java
public class ConstantTransformer implements Transformer, Serializable {
private final Object iConstant;
public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
public Object transform(Object input) {
return this.iConstant;
}
...
}
org.apache.commons.collections.functors.InvokerTransformer類的transform方法可以通過JAVA反射機制執行指定的代碼,能指定所需執行的類、方法及參數,且在transform方法中未進行任何驗證或限制。transform方法中執行的代碼的方法名、參數類型及參數值在InvokerTransformer類的構造函數中指定。InvokerTransformer類相關代碼如下。
#!java
public class InvokerTransformer implements Transformer, Serializable {
private final String iMethodName;
private final Class[] iParamTypes;
private final Object[] iArgs;
public InvokerTransformer(String methodName, Class[] paramTypes,
Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
public Object transform(Object input) {
if (input == null)
return null;
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
}
...
}
}
以下為利用org.apache.commons.collections.functors.ChainedTransformer類執行任意代碼的示例,當執行最后的“chain.transform(chain);”后,會執行傳入的Transformer數組指定的代碼。在該示例中,會啟動計算器程序。
ConstantTransformer與InvokerTransformer數組可被轉換為org.apache.commons.collections.functors.ChainedTransformer對象。在ChainedTransformer類的帶參數構造函數中,會將參數中的ConstantTransformer與InvokerTransformer數組保存為this.iTransformers對象。在ChainedTransformer類的transform方法中,會依次調用this.iTransformers對應的ConstantTransformer與InvokerTransformer數組的transform方法,且前一次執行transform方法的返回值object,會作為下一次執行transform方法的參數object。ChainedTransformer類的相關代碼如下。
#!java
public class ChainedTransformer implements Transformer, Serializable {
public ChainedTransformer(Transformer[] transformers) {
this.iTransformers = transformers;
}
...
public Object transform(Object object) {
for (int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
...
}
對于上述的示例代碼,在執行最后的“chain.transform(chain);”方法時,會首先調用ConstantTransformer.transform方法獲取其構造函數中傳入的類,再依次調用InvokerTransformer.transform方法執行其構造函數中傳入的方法,等價于下面的代碼。
上述代碼與前文“使用JAVA反射機制調用Runtime類執行程序”中的代碼相同,已經過驗證可以成功執行,能夠調用指定的程序。ChainedTransformer也能夠調用FileOutputStream類進行寫文件操作,相關代碼見前文“使用JAVA反射機制調用FileOutputStream類寫文件”部分。由此可見,利用ChainedTransformer類能夠執行指定的代碼。
以下為通過org.apache.commons.collections.map.TransformedMap類執行任意代碼的示例,當執行最后的“localEntry.setValue(null);”后,會執行傳入的Transformer數組指定的代碼。在該示例中,會啟動計算器程序。
上述示例代碼中涉及的變量及類型如下。
變量 | 類型 |
outerMap | org.apache.commons.collections.map.TransformedMap |
set | org.apache.commons.collections.map.AbstractInputCheckedMapDecorator$EntrySet |
localIterator | org.apache.commons.collections.map.AbstractInputCheckedMapDecorator$EntrySetIterator |
localEntry | org.apache.commons.collections.map.AbstractInputCheckedMapDecorator$MapEntry |
org.apache.commons.collections.map.TransformedMap類直接繼承自org.apache.commons.collections.map.AbstractInputCheckedMapDecorator類,間接繼承自java.util.Map類。org.apache.commons.collections.map.TransformedMap類的繼承關系如下。
org.apache.commons.collections.map.TransformedMap
└org.apache.commons.collections.map.AbstractInputCheckedMapDecorator
└org.apache.commons.collections.map.AbstractMapDecorator
└java.util.Map
上述示例中第33行代碼TransformedMap.decorate調用了TransformedMap類的decorate方法。TransformedMap類的decorate方法中創建了TransformedMap對象,以調用decorate方法的參數一map作為參數調用了父類AbstractInputCheckedMapDecorator的構造函數,并將調用decorate方法的參數三valueTransformer保存為this.valueTransformer變量。TransformedMap類相關代碼如下。
#!java
public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable {
protected final Transformer keyTransformer;
protected final Transformer valueTransformer;
public static Map decorate(Map map, Transformer keyTransformer,
Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
protected TransformedMap(Map map, Transformer keyTransformer,
Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
...
}
在TransformedMap類的父類AbstractInputCheckedMapDecorator的構造函數中,以自身類構造函數的參數為參數調用了父類的構造函數。AbstractInputCheckedMapDecorator類相關代碼如下。
#!java
abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {
protected AbstractInputCheckedMapDecorator(Map map) {
super(map);
}
...
}
在AbstractInputCheckedMapDecorator類的父類AbstractMapDecorator的構造函數中,將構造函數的參數保存為this.map對象。AbstractMapDecorator類相關代碼如下。
#!java
public abstract class AbstractMapDecorator implements Map {
protected transient Map map;
public AbstractMapDecorator(Map map) {
if (map == null) {
throw new IllegalArgumentException("Map must not be null");
}
this.map = map;
}
...
}
可以看出,上述示例代碼中,第33行代碼調用TransformedMap類的decorate方法時,參數一innerMap被保存為生成的TransformedMap對象的map變量,參數三chain被保存為valueTransformer變量。
上述示例中第35行代碼outerMap.entrySet調用了TransformedMap類的父類AbstractInputCheckedMapDecorator的entrySet方法。AbstractInputCheckedMapDecorator類為抽象類,在其entrySet方法中,創建了EntrySet類的對象并返回。在調用EntrySet類的構造函數時,參數二為this,由于AbstractInputCheckedMapDecorator類為抽象類。在上述示例代碼執行時,參數二this即為TransformedMap類的對象outerMap。
EntrySet類為AbstractInputCheckedMapDecorator類的內部類,在其構造函數中,會將參數二保存為this.parent變量。在上述示例代碼執行時,TransformedMap類的對象outerMap會被保存為EntrySet類的this.parent變量。
AbstractInputCheckedMapDecorator類相關代碼如下。
#!java
abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {
protected boolean isSetValueChecking() {
return true;
}
public Set entrySet() {
if (isSetValueChecking()) {
return new EntrySet(this.map.entrySet(), this);
}
return this.map.entrySet();
}
...
static class EntrySet extends AbstractSetDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected EntrySet(Set set, AbstractInputCheckedMapDecorator parent) {
super(set);
this.parent = parent;
}
...
}
}
上述示例代碼中第37行代碼set.iterator調用了AbstractInputCheckedMapDecorator$EntrySet類的iterator方法。在EntrySet類的iterator方法中,創建了AbstractInputCheckedMapDecorator$EntrySetIterator類的對象并返回,在調用EntrySetIterator類的構造函數時,參數二為this.parent。在上述示例代碼中,this.parent即為TransformedMap類的對象outerMap。
EntrySetIterator類為AbstractInputCheckedMapDecorator類的內部類,在其構造函數中,會將參數二保存為this.parent變量。在上述示例代碼執行時,TransformedMap類的對象outerMap會被保存為EntrySetIterator類的this.parent變量。
AbstractInputCheckedMapDecorator類相關代碼如下。
#!java
abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {
static class EntrySet extends AbstractSetDecorator {
private final AbstractInputCheckedMapDecorator parent;
public Iterator iterator() {
return new AbstractInputCheckedMapDecorator.EntrySetIterator(
this.collection.iterator(), this.parent);
}
...
}
static class EntrySetIterator extends AbstractIteratorDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected EntrySetIterator(Iterator iterator,
AbstractInputCheckedMapDecorator parent) {
super(iterator);
this.parent = parent;
}
...
}
...
}
上述示例代碼中第39行代碼localIterator.next調用了AbstractInputCheckedMapDecorator$EntrySetIterator類的next方法。在EntrySetIterator類的next方法中,創建了AbstractInputCheckedMapDecorator$MapEntry類的對象并返回,在調用MapEntry類的構造函數時,參數二為this.parent。在上述示例代碼中,this.parent即為TransformedMap類的對象outerMap。
MapEntry類為AbstractInputCheckedMapDecorator類的內部類,在其構造函數中,會將參數二保存為this.parent變量。在上述示例代碼執行時,TransformedMap類的對象outerMap會被保存為MapEntry類的this.parent變量。
AbstractInputCheckedMapDecorator類相關代碼如下。
#!java
abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {
static class EntrySetIterator extends AbstractIteratorDecorator {
private final AbstractInputCheckedMapDecorator parent;
public Object next() {
Map.Entry entry = (Map.Entry) this.iterator.next();
return new AbstractInputCheckedMapDecorator.MapEntry(entry,
this.parent);
}
...
}
...
static class MapEntry extends AbstractMapEntryDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Map.Entry entry,
AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}
...
}
}
上述示例代碼中第43行代碼localEntry.setValue調用了AbstractInputCheckedMapDecorator$MapEntry類的setValue方法。在MapEntry類的setValue方法中,調用了this.parent的checkSetValue方法。在上述示例代碼中,MapEntry類的this.parent即為TransformedMap類的對象outerMap,因此會調用TransformedMap類的checkSetValue方法。AbstractInputCheckedMapDecorator類相關代碼如下。
#!java
abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {
static class MapEntry extends AbstractMapEntryDecorator {
private final AbstractInputCheckedMapDecorator parent;
public Object setValue(Object value) {
value = this.parent.checkSetValue(value);
return this.entry.setValue(value);
}
...
}
}
在TransformedMap類的checkSetValue方法中,會調用this.valueTransformer.transform方法。在前文的示例代碼中,TransformedMap類的對象outerMap的this.valueTransformer變量對應ChainedTransformer類對象chain。前文“利用ChainedTransformer執行代碼分析”部分已經說明,調用ChainedTransformer類的transform方法時,會執行其在構造時傳入的ConstantTransformer與InvokerTransformer數組中指定的方法。TransformedMap類相關代碼如下。
#!java
public class TransformedMap extends AbstractInputCheckedMapDecorator implements
Serializable {
protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value);
}
...
}
綜上所述,上述示例代碼最后的“localEntry.setValue(null);”時,會執行ConstantTransformer與InvokerTransformer數組指定的方法。
上述漏洞在觸發時的完整調用過程如下。
//調用TransformedMap類的decorate方法
TransformedMap.decorate
AbstractMapDecorator.AbstractMapDecorator
AbstractInputCheckedMapDecorator.AbstractInputCheckedMapDecorator
TransformedMap.TransformedMap
//調用AbstractInputCheckedMapDecorator類的entrySet方法
AbstractInputCheckedMapDecorator.entrySet
TransformedMap.isSetValueChecking
AbstractInputCheckedMapDecorator$EntrySet.EntrySet
//調用AbstractInputCheckedMapDecorator$EntrySet類的iterator方法
AbstractInputCheckedMapDecorator$EntrySet.iterator
AbstractInputCheckedMapDecorator$EntrySetIterator.EntrySetIterator
//調用AbstractInputCheckedMapDecorator$EntrySetIterator類的next方法
AbstractInputCheckedMapDecorator$EntrySetIterator.next
AbstractMapEntryDecorator.AbstractMapEntryDecorator
AbstractInputCheckedMapDecorator$MapEntry.MapEntry
//調用AbstractInputCheckedMapDecorator$MapEntry類的setValue方法
AbstractInputCheckedMapDecorator$MapEntry.setValue
TransformedMap.checkSetValue
ChainedTransformer.transform
InvokerTransformer.transform
在確定了利用TransformedMap類可以執行代碼以后,再來關注上述示例代碼中調用最后的“localEntry.setValue”之前的localEntry的鍵值對。之所以需要關注localEntry的鍵值對,是因為在通過AnnotationInvocationHandler類執行代碼時,這是一個重要的變量。
從上述示例代碼第35行“outerMap.entrySet”開始分析,之前的步驟不再重復。
上述示例中第35行代碼outerMap.entrySet調用了TransformedMap類的父類AbstractInputCheckedMapDecorator的entrySet方法。在AbstractInputCheckedMapDecorator類的entrySet方法中,創建了EntrySet類的對象并返回。在調用EntrySet類的構造函數時,參數一為this.map.entrySet()。在上述示例代碼中,AbstractInputCheckedMapDecorator類的this.map.entrySet()對應Map對象innerMap的entrySet()。
在AbstractInputCheckedMapDecorator$EntrySet類的構造函數中,會將參數一set作為參數調用父類org.apache.commons.collections.set.AbstractSetDecorator的構造函數。
AbstractInputCheckedMapDecorator類相關代碼如下。
#!java
abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {
protected boolean isSetValueChecking() {
return true;
}
public Set entrySet() {
if (isSetValueChecking()) {
return new EntrySet(this.map.entrySet(), this);
}
return this.map.entrySet();
}
...
static class EntrySet extends AbstractSetDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected EntrySet(Set set, AbstractInputCheckedMapDecorator parent) {
super(set);
this.parent = parent;
}
...
}
}
在AbstractSetDecorator類的構造函數中,會將參數一set作為參數調用父類org.apache.commons.collections.collection.AbstractCollectionDecorator的構造函數。
AbstractSetDecorator類相關代碼如下。
#!java
public abstract class AbstractSetDecorator extends AbstractCollectionDecorator
implements Set {
protected AbstractSetDecorator(Set set) {
super(set);
}
...
}
在AbstractCollectionDecorator類的構造函數中,會將參數一coll保存為this.collection變量,即AbstractCollectionDecorator類的this.collection變量保存了示例代碼中Map對象innerMap的entrySet()。
AbstractCollectionDecorator類相關代碼如下。
#!java
public abstract class AbstractCollectionDecorator implements Collection {
protected Collection collection;
protected AbstractCollectionDecorator(Collection coll) {
if (coll == null) {
throw new IllegalArgumentException("Collection must not be null");
}
this.collection = coll;
}
...
}
上述示例代碼中第37行代碼set.iterator調用了AbstractInputCheckedMapDecorator$EntrySet類的iterator方法。在EntrySet類的iterator方法中,創建了AbstractInputCheckedMapDecorator$EntrySetIterator類的對象并返回,在調用EntrySetIterator類的構造函數時,參數一為this.collection.iterator()。在上述示例代碼中,this.collection.iterator()即為Map對象innerMap的entrySet().iterator()。
在AbstractInputCheckedMapDecorator$EntrySetIterator類的構造函數中,會將參數一iterator作為參數調用父類org.apache.commons.collections.iterators.AbstractIteratorDecorator的構造函數。
AbstractInputCheckedMapDecorator類相關代碼如下。
#!java
abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {
static class EntrySet extends AbstractSetDecorator {
private final AbstractInputCheckedMapDecorator parent;
public Iterator iterator() {
return new AbstractInputCheckedMapDecorator.EntrySetIterator(
this.collection.iterator(), this.parent);
}
...
}
static class EntrySetIterator extends AbstractIteratorDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected EntrySetIterator(Iterator iterator,
AbstractInputCheckedMapDecorator parent) {
super(iterator);
this.parent = parent;
}
...
}
...
}
在AbstractIteratorDecorator類的構造函數中,會將參數一iterator保存為this.iterator變量,即AbstractIteratorDecorator類的this.iterator變量保存了示例代碼中Map對象innerMap的entrySet().iterator()。
AbstractIteratorDecorator類相關代碼如下。
#!java
public class AbstractIteratorDecorator implements Iterator {
protected final Iterator iterator;
public AbstractIteratorDecorator(Iterator iterator) {
if (iterator == null) {
throw new IllegalArgumentException("Iterator must not be null");
}
this.iterator = iterator;
}
...
}
上述示例代碼中第39行代碼localIterator.next調用了AbstractInputCheckedMapDecorator$EntrySetIterator類的next方法。在EntrySetIterator類的next方法中,創建了AbstractInputCheckedMapDecorator$MapEntry類的對象并返回,在調用MapEntry類的構造函數時,參數一為this.iterator.next()。在上述示例代碼中,this.iterator.next()即為Map對象innerMap的entrySet().iterator().next(),即示例代碼中第30行通過innerMap.put添加的鍵值對。
在AbstractInputCheckedMapDecorator$EntrySet類的構造函數中,會將參數一entry作為參數調用父類org.apache.commons.collections.keyvalue.AbstractMapEntryDecorator的構造函數。
AbstractInputCheckedMapDecorator類相關代碼如下。
#!java
abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {
static class EntrySetIterator extends AbstractIteratorDecorator {
private final AbstractInputCheckedMapDecorator parent;
public Object next() {
Map.Entry entry = (Map.Entry) this.iterator.next();
return new AbstractInputCheckedMapDecorator.MapEntry(entry,
this.parent);
}
...
}
...
static class MapEntry extends AbstractMapEntryDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Map.Entry entry,
AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}
...
}
}
在AbstractMapEntryDecorator類的構造函數中,會將參數一entry保存為this.entry變量,在getKey與getValue方法會分別返回this.entry.getKey()與this.entry.getValue()。
AbstractMapEntryDecorator類相關代碼如下。
#!java
public abstract class AbstractMapEntryDecorator implements Map.Entry, KeyValue {
protected final Map.Entry entry;
public AbstractMapEntryDecorator(Map.Entry entry) {
if (entry == null) {
throw new IllegalArgumentException("Map Entry must not be null");
}
this.entry = entry;
}
public Object getKey() {
return this.entry.getKey();
}
public Object getValue() {
return this.entry.getValue();
}
...
}
綜上所述,在示例代碼中執行第39行localIterator.next后,執行localEntry.getKey()與localEntry.getValue()可獲取示例代碼中第30行通過innerMap.put添加的鍵值對。
已知TransformedMap類為Map類的子類,為了觸發JAVA反序列化漏洞,需要找到某個類提供了方法接收Map對象,且在readObject方法中會調用Map對象的Entry的setValue方法。
sun.reflect.annotation.AnnotationInvocationHandler類滿足上述的要求。sun.reflect.annotation.AnnotationInvocationHandler類為JRE中原生的類,不需要第三方支持。
以下為通過TransformedMap與AnnotationInvocationHandler類執行任意代碼的示例,當執行第57行的“ois.readObject();”后,會執行傳入的Transformer數組指定的代碼。在該示例中,會啟動計算器程序。
sun.reflect.annotation.AnnotationInvocationHandler類無法直接訪問,因此在構造需要序列化的對象時,需要使用JAVA反射機制。
在上述觸發漏洞的示例代碼中,會調用AnnotationInvocationHandler類的帶參數構造函數與反序列化時會被調用的readObject函數。
AnnotationInvocationHandler類的重要的變量及方法如下。
#!java
1. class AnnotationInvocationHandler implements InvocationHandler, Serializable {
2. private final Class type;
3. private final Map<String, ObjectmemberValues;
4. ...
5.
6. AnnotationInvocationHandler(Class paramClass, Map<String, ObjectparamMap) {
7. this.type = paramClass;
8. this.memberValues = paramMap;
9. }
10. ...
11.
12. private void readObject(ObjectInputStream paramObjectInputStream)
13. throws IOException, ClassNotFoundException {
14. paramObjectInputStream.defaultReadObject();
15. AnnotationType localAnnotationType = null;
16. try {
17. localAnnotationType = AnnotationType.getInstance(this.type);
18. } catch (IllegalArgumentException localIllegalArgumentException) {
19. return;
20. }
21. Map localMap = localAnnotationType.memberTypes();
22. Iterator localIterator = this.memberValues.entrySet().iterator();
23. while (localIterator.hasNext()) {
24. Map.Entry localEntry = (Map.Entry) localIterator.next();
25. String str = (String) localEntry.getKey();
26. Class localClass = (Class) localMap.get(str);
27. if (localClass != null) {
28. Object localObject = localEntry.getValue();
29. if ((!(localClass.isInstance(localObject)))
30. && (!(localObject instanceof ExceptionProxy)))
31. localEntry.setValue(new AnnotationTypeMismatchExceptionProxy(
32. localObject.getClass() + "[" + localObject
33. + "]")
34. .setMember((Method) localAnnotationType
35. .members().get(str)));
36. }
37. }
38. }
示例代碼中第43行執行newInstance方法時,對應AnnotationInvocationHandler類代碼的第6行的帶參數構造方法。示例代碼中第43行執行newInstance方法構造AnnotationInvocationHandler對象時,參數一為java.lang.annotation.Retention.class,參數二為TransformedMap類的對象outerMap。因此AnnotationInvocationHandler類代碼中構造函數中保存的this.type對應java.lang.annotation.Retention.class,this.memberValues對應示例代碼中的outerMap。
當AnnotationInvocationHandler類的readObject方法執行時,過程如下。
第17行代碼中的this.type為java.lang.annotation.Retention.class。
第21行代碼的localMap變量存在一個鍵值對,key為字符串"value",value為class"java.lang.annotation.RetentionPolicy"。
第22行代碼的this.memberValues對應示例代碼中TransformedMap類的對象outerMap
第24行代碼的localEntry等價于outerMap.entrySet().iterator().next(),根據前文“AbstractInputCheckedMapDecorator$MapEntry對象的鍵值對”部分的分析結果,localEntry對應示例代碼中Map對象innerMap的entrySet().iterator().next(),即示例代碼中第34行通過innerMap.put添加的鍵值對。
第25行代碼的str等于示例代碼中第34行通過innerMap.put添加的鍵值對的key,即字符串"value"。
第26行代碼的localClass等于localMap變量中的鍵值對的value,即class"java.lang.annotation.RetentionPolicy"。
第27行代碼的判斷,需要localClass非空,滿足該條件。
第28行代碼的localObject等于示例代碼中第34行通過innerMap.put添加的鍵值對的value,即字符串"tttest"。
第29行代碼的判斷,需要localObject不是localClass的實例,localObject為String對象,localClass為class"java.lang.annotation.RetentionPolicy",滿足該條件。
第30行代碼的判斷,需要localObject不是sun.reflect.annotation.ExceptionProxy的實例,localObject為String對象,滿足該條件。
第31行代碼調用了localEntry變量的setValue方法,localEntry為AbstractInputCheckedMapDecorator$MapEntry類的實例,根據前文”調用AbstractInputCheckedMapDecorator$MapEntry類的setValue方法“部分的分析,在調用AbstractInputCheckedMapDecorator$MapEntry類的setValue方法時,會執行ConstantTransformer與InvokerTransformer數組指定的方法,此時漏洞觸發。
綜上所述,在利用TransformedMap與AnnotationInvocationHandler類觸發JAVA反序列化漏洞時,有以下幾點應滿足條件。
綜合前文的分析,利用TransformedMap與AnnotationInvocationHandler類觸發JAVA反序列化漏洞的大致步驟如下。
簡而言之,當攻擊者將構造好的包含攻擊代碼序列化數據發送給使用了Apache Commons Collections組件的JAVA中間件時,JAVA中間件在對其進行反序列化操作時,會觸發反序列化漏洞,執行攻擊者指定的任意代碼。
不同JAVA中間件的JAVA反序列化漏洞利用與防護分析,之后再繼續。
What Do WebLogic, WebSphere, JBoss, Jenkins, OpenNMS, and Your Application Have in Common This Vulnerability | http://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/ |
common-collections中Java反序列化漏洞導致的RCE原理分析 WooYun知識庫 | http://drops.wooyun.org/papers/10467 |
Commons Collections Java反序列化漏洞深入分析 - 博客 - 騰訊安全應急響應中心 | http://security.tencent.com/index.php/blog/msg/97 |
JAVA Apache-CommonsCollections 序列化漏洞分析以及漏洞高級利用 隨風'S Blog | http://www.iswin.org/2015/11/13/Apache-CommonsCollections-Deserialized-Vulnerability/ |
Java反序列化漏洞技術分析 天融信阿爾法實驗室 | http://blog.topsec.com.cn/ad_lab/java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E6%8A%80%E6%9C%AF%E5%88%86%E6%9E%90/ |
Java反序列化漏洞之Weblogic、Jboss利用教程及exp - HereSecurity | http://www.heresec.com/index.php/archives/127/ |
Java反序列化漏洞之weblogic本地利用實現篇 - FreeBuf_COM 關注黑客與極客 | http://www.freebuf.com/vuls/90802.html |
Lib之過?Java反序列化漏洞通用利用分析 - Cnlouds的個人空間 - 開源中國社區 | http://my.oschina.net/u/1188877/blog/529611 |
WebLogic之Java反序列化漏洞利用實現二進制文件上傳和命令執行 WooYun知識庫 | http://drops.wooyun.org/papers/11690 |