全国咨询电话

02389108633

关注官方微信

新型Tomcat Websocket内存马检测与防护思路探究

近期,看到网上有安全研究人员发布了一篇文章,描述了一款在Tomcat的全新内存马,地址为:https://www.iculture.cc/forum-post/19128.html,该内存马基于Websocket协议,有别于目前已知常见的filter型内存马、servlet型内存马,该类型的网马从形式上还有功能上都是非常新颖的,且根据描述,目前常规的内存马扫描工具(如 Memshell Scanner)无法检测出该类型的内存马。为此,对该内存马的原理进行了分析,并在此提出了一种检测该内存马的思路,可以帮助安全人员检测出此种类型内存马,从而降低业务系统安全隐患。



Websocket 内存马原理



Tomcat服务器在启动时会通过 WsSci 中的 WsServerContainer 将 classpath 注解下带有 @ServerEndpoint 的类加入到 Websocket 服务中。如:





通过调试可以看到添加位置如下:





对该段代码进行分析,注册websocket服务步骤如下,

首先要初始化一个 WsServerContainer ,





通过扫描 classpath 下的带注解的类并加入一个 iterator  之中,





然后遍历该列表,针对每个元素,然后创建一个 ServerEndpointConfig,然后通过 addEndpoint 将该类加入到 Websocket服务中。





至此,就将一个 ws 节点加入的服务中,根据这个思路,攻击者也可以利用这种方式在运行时动态注入 ws 节点,注入的思路与此一致。



检测思路

既然是内存马,那在内存中肯定会有实体,所以只要找到存储该实体的位置,就能检测出此类内存马。

下面通过动态调试分析该实体的具体存储位置,前面提到,最终是通过 addEndpoint 函数将该内存马实体加入到 ws 服务中,于是跟进该函数,





在 addEndpoint 中通过 (WsServerContainer.ExactPathMatch)this.configExactMatchMap.put(path,newMatch) 加入到一个名为 configExactMatchMap 的 Map 之中,进一步查看该成员变量定义:





是一个 Map 的类型,通过调试可知,该 map 的 key 就是对应 ws 服务的 url, 后面的 WsServerContainer.ExactPathMatch 则存放了 ws 服务的实体,该类定义如下:





其中的 config 变量就是之前扫描注解时生成的实体类。

于是,检测思路就呼之欲出了:找到对应 context 的 configExactMatchMap, 里面保存了所有注册的 ws 服务,每个服务就是该 map 中的一个元素。

一个简单的 jsp 检测脚本,访问该 jsp 就能打印出所有的 ws 服务。



<%@ page import="org.apache.tomcat.websocket.server.WsServerContainer" %>

<%@ page import="javax.websocket.server.ServerContainer" %>

<%@ page import="java.lang.reflect.Field" %>

<%@ page import="java.util.Map" %>

<%@ page import="java.util.Set" %>

<%@ page import="java.util.Iterator" %>

<%@ page import="javax.websocket.server.ServerEndpointConfig" %><%-- Created by IntelliJ IDEA. --%>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%

  // 通过 request 的 context 获取 ServerContainer

  WsServerContainer wsServerContainer = (WsServerContainer) request.getServletContext().getAttribute(ServerContainer.class.getName());

  // 利用反射获取 WsServerContainer 类中的私有变量 configExactMatchMap

  Class obj = Class.forName("org.apache.tomcat.websocket.server.WsServerContainer");

  Field field = obj.getDeclaredField("configExactMatchMap");

  field.setAccessible(true);

  Map configExactMatchMap = (Map) field.get(wsServerContainer);

  // 遍历configExactMatchMap, 打印所有注册的 websocket 服务

  Set keyset = configExactMatchMap.keySet();

  Iterator iterator = keyset.iterator();

  while (iterator.hasNext()){

    String key = iterator.next();

    Object object = wsServerContainer.findMapping(key);

    Class wsMappingResultObj = Class.forName("org.apache.tomcat.websocket.server.WsMappingResult");

    Field configField = wsMappingResultObj.getDeclaredField("config");

    configField.setAccessible(true);

    ServerEndpointConfig config1 = (ServerEndpointConfig)configField.get(object);

    Class clazz = config1.getEndpointClass();

    // 打印 ws 服务 url, 对应的 class

    out.println(String.format("websocket name:%s,  websocket class: %s", key, clazz.getName()));

  }

  // 如果参数带name, 删除该服务,名字为name参数值

  if(request.getParameter("name")!= null){

    configExactMatchMap.remove(request.getParameter("name"));

    out.println(String.format("delete ws service: %s", request.getParameter("name")));

  }

%>



效果如下:





如果想要删除一个 ws 服务,直接将该实体从 map 中移除即可,

configExactMatchMap.remove(request.getParameter("name"));


通过该 jsp 就是在后面加入 ?name=websocket服务名字即可:





再此访问检测工具,该服务已经删掉: