内存马检测与排查

源码检测

在java中,只有被JVM加载后的类才能被调用,或者在需要时通过反射通知JVM加载。所以特征都在内存中,表现形式为被加载的class。需要通过某种方法获取到JVM的运行时内存中已加载的类, Java本身提供了Instrumentation类来实现运行时注入代码并执行,因此产生一个检测思路:注入jar包-> dump已加载class字节码->反编译成java代码-> 源码webshell检测
这样检测比较消耗性能,我们可以缩小需要进行源码检测的类的范围,通过如下的筛选条件组合使用筛选类进行检测:
①新增的或修改的
②没有对应class文件的
③xml配置中没注册的
④冰蝎等常见工具使用的
⑤filterchain中排第一的filter类

还有一些比较弱的特征可以用来辅助检测,比如类名称中包含shell或者为随机名,使用不常见的classloader加载的类等等

另外,有一些工具可以辅助检测内存马,如java-memshell-scanner是通过jsp扫描应用中所有的filter和servlet,然后通过名称、对应的class是否存在来判断是否是内存马

image-20230515161128350

内存马排查

如果通过检测工具或者其他手段发现了一些内存马的痕迹,需要有一个排查的思路来进行跟踪分析,根据各类型的原理,列出一个排查思路

如果是filter或者listener类型,可能会有较多的404但是带有参数的请求,或者大量请求不同url但带有相同的参数,或者页面并不存在但返回200

如果是jsp注入,日志中排查可疑jsp的访问请求

如果是代码执行漏洞,排查中间件的error.log,查看是否有可疑的报错,判断注入时间和方法

根据业务使用的组件排查是否可能存在java代码执行漏洞以及是否存在过webshell,排查框架漏洞、反序列化漏洞

如果是servlet或者spring controller类型,根据上报webshell的url查找日志

在动态注册组件的时候不管是注册的是Servlet还是Filter、Listener、Controller,其都要创建新的类并继承相应组件的父类,新创建的类加载到jvm内存中之后,可以通过java tools中的Instrumentation.getAllLoadedClasses()获取到,所以可以通过javaagent技术来实现查杀;查杀agent型内存马的时候,通过检查常见通用类的字节码实现是否发生了改变来实现内存马的排查

1、首先先判断加载到内存中的类,从类继承的角度去判断是否继承了如javax.servlet.Servlet、javax.servlet.Filter、javax.servlet.ServletRequestListener接口

2、对于1中匹配到的类,因为正常实现的组件都会匹配到,所以我们还要进一步的检测内存马的特征:

  • 类名关键词检测,检测类名是否存在如:shell、memshell、noshell、cmd等敏感词
  • 对关键方法字节码实现关使用键词检测,检测类关键方法(Filter类里面的doFilter方法,Servlet里面的services方法)字节码实现中是否存在一些敏感词:如cmd、shell、exec
  • 命令执行类检测,检测器字节码实现中是否存在调用Runtime、ProcessBuilder类可以用来执行命令的类

查找目前服务器上的内存马

多个维度的判断方式,配合 JavaAgent 技术来进行内存马的查杀

防御内存马的添加与访问

对应接口 Hook,以监听者模式监控系统关键属性

杀掉目前存在的内存马

对于非Agent马两种思路:

  • 从系统中移除该对象(推荐)
  • 访问时抛异常(或跳过调用),中断此次调用

对于Agent马:retransform