从TemplatesImpl类Gadget中提取bytecode的三种方法
前言
在Java反序列化漏洞利用中,TemplatesImpl类是一个常用的gadget组件,出现在多个反序列化利用链中(如CommonsCollections2-4、CommonsBeanutils1、Jdk7u21及变种JRE8u20等)。这类gadget的核心原理是通过反序列化触发TemplatesImpl类的newTransformer或getOutputProperties方法,最终调用defineTransletClasses方法,将_bytecodes字段中存储的字节码重新在JVM中定义为类,并在实例化时执行其中的静态代码块或无参构造方法中的代码。
方法一:使用IDEA调试提取bytecode
准备工作
- 安装IDEA开发环境
- 部署目标环境(如shiro的samples-web.war)
- 准备Xray高级版(用于触发Shiro扫描)
操作步骤
-
在IDEA中打开目标项目
-
在
TemplatesImpl类的内部类TransletClassLoader的defineClass方法上设置断点 -
启动调试模式
-
使用Xray执行扫描命令:
xray webscan --plugins shiro --url http://127.0.0.1:8080/samples_web_war/(注意URL结尾必须加斜杠)
-
当断点触发时,在IDEA的调试表达式窗口中执行以下代码:
var f = new FileOutputStream("d:/test.class"); // 修改为实际保存路径 f.write(b); // b是defineClass方法的参数 f.close(); -
使用反编译工具(如JD-GUI或Luyten)查看保存的class文件
方法二:使用Java Agent动态修改类
实现原理
通过Java Agent技术,在运行时修改TemplatesImpl$TransletClassLoader类的defineClass方法,添加字节码导出功能。
示例代码
package org.fnmsd;
import javassist.*;
import java.io.ByteArrayInputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
public class DumperAgent {
public static void premain(String args, Instrumentation instrumentation) {
DumperTransformer transformer = new DumperTransformer();
instrumentation.addTransformer(transformer);
}
public static class DumperTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if (className.equals("com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl$TransletClassLoader")){
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(new LoaderClassPath(loader));
try {
CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
CtMethod defineClass = ctClass.getDeclaredMethod("defineClass");
// 插入导出字节码的代码
defineClass.insertBefore(
"long l = new java.util.Random().nextLong();" +
"System.out.println(\"Output Class:\"+l);" +
"new java.io.FileOutputStream(l+\".class\").write($1);"
);
return ctClass.toBytecode();
} catch (Exception e) {
System.err.println("Agent:Patch Method Error");
e.printStackTrace();
}
}
return classfileBuffer;
}
}
}
使用说明
- 将上述代码编译为Java Agent
- 在JVM启动参数中添加Agent:
-javaagent:/path/to/agent.jar - 当
defineClass方法被调用时,会自动将字节码保存为随机文件名的.class文件
方法三:直接解析Java序列化数据
实现原理
通过分析Java序列化数据的二进制格式,直接从中提取TemplatesImpl类的_bytecodes字段值。
工具准备
可以使用修改版的SerializationDumper工具(基于NickstaDB的实现)
关键修改点
- 添加识别
TemplatesImpl类的逻辑 - 添加提取
_bytecodes字段的功能 - 添加保存字节码的功能
操作步骤
- 获取目标序列化数据(如Shiro rememberMe cookie)
- 使用修改后的SerializationDumper解析数据
- 工具会自动识别并导出
_bytecodes字段中的字节码
总结对比
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| IDEA调试 | 直观、无需额外工具 | 需要调试环境、手动操作 | 研究分析、调试场景 |
| Java Agent | 自动化、可批量处理 | 需要Agent部署 | 生产环境监控、自动化分析 |
| 序列化解析 | 无需运行环境 | 需要处理加密数据 | 离线分析、加密数据研究 |
注意事项
-
ysoserial生成的payload通常包含两个类:
- 实际执行的恶意类
- 一个无用的Foo类(用于满足某些条件)
-
字节码中的执行逻辑通常位于静态代码块中,这是此类gadget的标准做法
-
对于加密的序列化数据(如Shiro),需要先解密才能使用方法三
-
在实际分析中,可能需要结合多种方法才能完整提取和分析bytecode