最近,看到网上有安全研究人员发布了一篇文章,描述了一款在 tomcat的全新内存马,地址为:https://www.iculture.cc/forum-post/19128.html
,该内存马基于 websocket协议,有别于目前已知的见的filter
型内存马、servle
t 型内存马,该类型的网马从形式上还有功能上都是非常新颖的,且根据该作者描述,目前常规的内存马扫描工具(如memshell scanner
)无法检测出该类型的内存马。为此,笔者对该内存马的原理进行了分析,并在此提出了一种检测该内存马的思路,可以帮助安全人员检测出此种类型内存马,从而降低业务系统安全隐患。
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<String, WsServerContainer.ExactPathMatch>
的类型,通过调试可知,该 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<String, Object> configExactMatchMap = (Map<String, Object>) field.get(wsServerContainer);
// 遍历configExactMatchMap, 打印所有注册的 websocket 服务
Set<String> keyset = configExactMatchMap.keySet();
Iterator<String> 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服务名字
即可:
再此访问检测工具,该服务已经删掉:
本文通过分析该新型内存马原理,提出了对应的检测思路,并实现了一个简陋的内存马检测代码和内存马删除代码,希望该方法能够帮助有此需求的同行安全运维人员,也希望能够抛砖引玉,开展更为深入的讨论。
https://www.iculture.cc/forum-post/19128.html
https://juejin.cn/post/7095918534210879519