JAVA内存马查杀
客户端请求流程
JAVA内存马类型
Servlet-API
-
listener 内存马
-
filter内存马
-
Servlet内存马
特定框架分类
如Spring/Struts2等框架,按照位置分类可以有
-
interceptor
-
controller
中间件分类
-
Tomcat的Pipeline&Valve
-
Grizzly的FilterChain&Filter等等
字节码增强型
-
Java Agent内存马
内存马的原理
顺序:listener -> filter -> servlet
Servlet
Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。它负责处理用户的请求,并根据请求生成相应的返回信息提供给用户。
Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
-
Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第4步,否则,执行第2步。
-
装载并创建该Servlet的一个实例对象。
-
调用Servlet实例对象的init()方法。
-
创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
-
WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
Filter
Filter译为过滤器。过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理,通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理。
web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
Listener
监听器用于监听Web应用中某些对象的创建、销毁、增加,修改,删除等动作的发生,然后作出相应的响应处理。当监听范围的对象的状态发生变化的时候,服务器自动调用监听器对象中的方法。常用于统计网站在线人数、系统加载时进行信息初始化、统计网站的访问量等等。
主要由三部分构成:
-
事件源:被监听的对象
-
监听器:监听的对象,事件源的变化会触发监听器的响应行为
-
响应行为:监听器监听到事件源的状态变化时所执行的动作
在初始化时,需要将事件源和监听器进行绑定,也就是注册监听器。
可以使用监听器监听客户端的请求、服务端的操作等。通过监听器,可以自动出发一些动作,比如监听在线的用户数量,统计网站访问量、网站访问监控等。
Filter内存马例子
生命周期
public void init(FilterConfig filterConfig) throws ServletException;//初始化 和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;//拦截请求 这个方法完成实际的过滤操作。当客户请求访问与过滤器关联的URL的时候,Servlet过滤器将先执行doFilter方法。FilterChain参数用于访问后续过滤器。 public void destroy();//销毁 Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。在Web容器卸载 Filter 对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。
JAVA代码
package com.evalshell.Filter; import javax.servlet.*; import java.io.IOException; public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("Filter 创建"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("执行过滤过程"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { System.out.println("销毁!"); } }
内存马查杀方式
Tomcat内存马的排除与查杀
原理• 利用Java Agent技术遍历所有已经加载到内存中的class。先判断是否是内存马,是则进入内存查杀。
public class Transformer implements ClassFileTransformer { public byte[] transform(ClassLoader classLoader, String s, Class<?> aClass, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException { // 识别内存马 if(isMemshell(aClass,bytes)){ // 查杀内存马 byte[] newClassByte = killMemshell(aClass,bytes); return newClassByte; }else{ return bytes; } } }
• 访问时抛异常(或跳过调用),中断此次调用
• 从系统中移除该对象
排查方式
• 如果是jsp注入,日志中排查可疑jsp的访问请求。
• 如果是代码执行漏洞,排查中间件的error.log,查看是否有可疑的报错,判断注入时间和方法
• 根据业务使用的组件排查是否可能存在java代码执行漏洞以及是否存在过webshell,排查框架漏洞,反序列化漏洞。
• 如果是servlet或者spring的controller类型,根据上报的webshell的url查找日志(日志可能被关闭,不一定有),根据url最早访问时间确定被注入时间。
• 如果是filter或者listener类型,可能会有较多的404但是带有参数的请求,或者大量请求不同url但带有相同的参数,或者页面并不存在但返回200
查杀方式
可以使用哥斯拉,冰蝎,或者上文提到的内存马进行一个生成,然后再进行查杀。
1. VisualVM(远程调试)
VisualVM是一个集成多个JDK命令行工具的可视化工具。可以作为Java应用程序性能分析和运行监控的工具。开发人员可以利用它来监控、分析线程信息,浏览内存堆数据。系统管理员可以利用它来监测、控制Java应用程序横跨整个网络的情况。
2. arthas
arthas是Alibaba开源的Java诊断工具 https://github.com/alibaba/arthas
3. Copagent
这个项目是上面的改进版本,直接可以确定风险等级,并且将内存中的信息全部输出。项目地址:https://github.com/LandGrey/copagent试了一下,只有jdk1.8能够运行,之后会生成一个.copagent,里面有扫描结果。
4. java-memshell-scanner
https://github.com/c0ny1/java-memshell-scanner通过jsp脚本扫描并查杀各类中间件内存马