标题 | 简介 | 类型 | 公开时间 | ||||||||||
|
|||||||||||||
|
|||||||||||||
详情 | |||||||||||||
[SAFE-ID: JIWO-2024-3147] 作者: 羽毛 发表于: [2022-07-24]
本文共 [151] 位读者顶过
最近,看到网上有安全研究人员发布了一篇文章,描述了一款在 tomcat的全新内存马,地址为:https://www.iculture.cc/forum-post/19128.html,该内存马基于 websocket协议,有别于目前已知的见的filter型内存马、servlet 型内存马,该类型的网马从形式上还有功能上都是非常新颖的,且根据该作者描述,目前常规的内存马扫描工具(如memshell scanner)无法检测出该类型的内存马。为此,笔者对该内存马的原理进行了分析,并在此提出了一种检测该内存马的思路,可以帮助安全人员检测出此种类型内存马,从而降低业务系统安全隐患。 websocket 内存马原理
Tomcat 服务器在启动时会通过WsSci中的 WsServerContainer 将 classpath 注解下带有@ServerEndpoint的类加入到 websocket 服务中。如:[出自:jiwo.org]
通过调试可以看到添加位置如下:
对该段代码进行分析,注册 websocket 服务步骤如下,
通过扫描 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 |