Agent内存马的自动分析与查杀(一)

简介: Agent内存马的自动分析与查杀

出发点是Java Agent内存马的自动分析与查杀,实际上其他内存马都可以通过这种方式查杀

本文主要的难点主要是以下三个,我会在文中逐个解答

  1. 如何dumpJVM真正的当前的字节码
  2. 如何解决由于LAMBDA表达式导致非法字节码无法分析的问题
  3. 如何对字节码进行分析以确定某个类是内存马

基于静态分析动态,打破规则之道                            -- Java King 对本文的评价

背景

对于Java内存马的攻防一直没有停止,是Java安全领域的重点

回顾TomcatSpring内存马:FilterController等都需要注册新的组件

针对于需要注册新组件的内存马查杀起来比较容易:

例如c0ny1师傅的java-memshell-scanner项目,利用了Tomcat API删除添加的组件。优点在于一个简单的JSP文件即可查看所有的组件信息,结合人工审查(类名和ClassLoader等信息)对内存马进行查杀,也可以对有风险的Class进行dump后反编译分析

或者LandGrey师傅基于Alibaba Arthas编写的copagent项目,分析JVM中所有的Class,根据危险注解和类名等信息dump可疑的组件,结合人工反编译后进行分析


但实战中,可能并不是以上这种注册新组件的内存马

例如师傅们常用的冰蝎内存马,是Java Agent内存马。以下这段是冰蝎内存马一段代码,简单分析后可以发现冰蝎内存马是利用Java Agent注入到javax.servlet.http.HttpServletservice方法中,这是JavaEE的规范,理论上部署在Tomcat的都要符合这个规范,简单来理解这是Tomcat处理请求最先且总是经过的地方,在该类加入内存马的逻辑,可以保证稳定触发

0720a98a5016990af0494aea8a84c82d_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

类似的逻辑,可以使用Java Agent将内存马注入org.apache.catalina.core.ApplicationFilterChain类中,该类位于Filter链头部,也就是说经过Tomcat的请求都会交经过该类的doFilter方法处理,所以在该方法中加入内存马逻辑,也是一种稳定触发的方式(据说这是老版本冰蝎内存马的方式)

还可以对类似的类进行注入,例如org.springframework.web.servlet.DispatcherServlet类,针对于Spring框架的底层进行注入。或者一些巧妙的思路,比如注入Tomcat自带的Filter之一org.apache.tomcat.websocket.server.WsFilter类,这也是Java Agent内存马可以做到的


上文简单地介绍了各种内存马的利用方式与普通内存马的查杀,之所以最后介绍Java Agent内存马的查杀,是因为比较困难。宽字节安全的师傅提出查杀思路:基于javaAgent内存马检测查杀指南

引用文章讲到Java Agent内存马检测的难点:

调用retransformClass方法的时候参数中的字节码并不是调用redefineClass后被修改的类的字节码。对于冰蝎来讲,根本无法获取被冰蝎修改后的字节码。我们自己写Java Agent清除内存马的时候,同样也是无法获取到被redefineClass修改后的字节码,只能获取到被retransformClass修改后的字节码。通过JavaassistASM工具获取到类的字节码,也只是读取磁盘上响应类的字节码,而不是JVM中的字节码

宽字节安全的师傅找到了一种检测手段:sa-jdi.jar

借用公众号师傅的图片,这是一个GUI工具,可以查看JVM中所有已加载的类。区别在于这里获取到的是真正的当前的字节码,而不是获取到原始的,本地的字节码,所以是可以查看被Java Agent调用redefineClass后被修改的类的字节码。进一步可以dump下来认为存在风险的类然后反编译人工审核

f438bb946a834eaf9896fff648e7cd5c_640_wx_fmt=other&wxfrom=5&wx_lazy=1&wx_co=1.jpg


介绍

以上是背景,接下来介绍我做了些什么,能够实现怎样的效果

不难看出,以上内存马查杀手段都是半自动结合人工审核的方式,当检测出内存马后

是否可以找到一种方式,做到一条龙式服务:

  • 检测(同时支持普通内存马和Java Agent内存马的检测)
  • 分析(如何确定该类是内存马,仅根据恶意类名和注解等信息不完善)
  • 查杀(当确定内存马存在,如何自动地删除内存马并恢复正常业务逻辑)

大致看来,实现起来似乎不难,然而实际中遇到了很多坑,接下来我会逐个介绍


SA-JDI分析

我尝试通过Java Agent技术来获取当前的字节码,发现如师傅所说拿不到被修改的字节码

所以为了可以检测Agent马需要从sa-jdi.jar本身入手,想办法dump得到当前字节码(这样不止可以分析被修改了字节码的Agent马也可以分析普通类型的内存马)

注意到其中一个类:sun.jvm.hotspot.tools.jcore.ClassDump并通过查资料发现该类功能正是dump当前的Class(根据类名也可猜测出)其中的main方法提供一个dump class的命令行工具

于是我想了一些办法,用代码实现了命令行工具的功能,并可以设置一个Filter

ClassDump classDump = new ClassDump();
// my filter
classDump.setClassFilter(filter);
classDump.setOutputDirectory("out");
// protected start method
Class<?> toolClass = Class.forName("sun.jvm.hotspot.tools.Tool");
Method method = toolClass.getDeclaredMethod("start", String[].class);
method.setAccessible(true);
// jvm pid
String[] params = new String[]{String.valueOf(pid)};
try {
   method.invoke(classDump, (Object) params);
} catch (Exception ignored) {
   logger.error("unknown error");
   return;
}
logger.info("dump class finish");
// detach
Field field = toolClass.getDeclaredField("agent");
field.setAccessible(true);
HotSpotAgent agent = (HotSpotAgent) field.get(classDump);
agent.detach();

上文提到设置一个Filter是用于确定需要对哪些类进行dump操作(dump过多会导致性能等问题)

public class NameFilter implements ClassFilter {
   @Override
   public boolean canInclude(InstanceKlass instanceKlass) {
       String klassName = instanceKlass.getName().asString();
       // 在黑名单中的类需要dump
       if (blackList.contains(klassName)) {
           return true;
      }
       // 包含了关键字的类也需要dump
       for (String k : Constant.keyword) {
           if (klassName.contains(k)) {
               return true;
          }
      }
       return false;
  }
}

以上包含了类的黑名单和关键字:

  • 黑名单:Java Agent内存马通常会Hook的地方,需要dump下来进行分析
  • 关键字:类名如果出现memshellshell等关键字认为可能是普通内存马,需要分析
public class Constant {
   // BLACKLIST (Analysis Target)
   // CLASS_NAME#METHOD_NAME
   public static List<String> blackList = new ArrayList<>();
   // SHELL KEYWORD
   public static List<String> keyword = new ArrayList<>();
   static {
       blackList.add("javax/servlet/http/HttpServlet#service");
       blackList.add("org/apache/catalina/core/ApplicationFilterChain#doFilter");
       blackList.add("org/springframework/web/servlet/DispatcherServlet#doService");
       blackList.add("org/apache/tomcat/websocket/server/WsFilter#doFilter");
       keyword.add("shell");
       keyword.add("memshell");
       keyword.add("agentshell");
       keyword.add("exploit");
       keyword.add("payload");
       keyword.add("rebeyond");
       keyword.add("metasploit");
  }
}

另外如果想在Maven项目中加入JDK/lib下的依赖,需要特殊配置

<dependency>
   <groupId>sun.jvm.hotspot</groupId>
   <artifactId>sa-jdi</artifactId>
   <version>jdk-8</version>
   <scope>system</scope>
   <systemPath>${env.JAVA_HOME}/lib/sa-jdi.jar</systemPath>
</dependency>

在打包成工具Jar包时默认情况下不会加入system scope的依赖,所以需要特殊处理

<artifactId>maven-assembly-plugin</artifactId>
<configuration>
   <appendAssemblyId>false</appendAssemblyId>
   <descriptors>
       <descriptor>assembly.xml</descriptor>
   </descriptors>
   <archive>
       <manifest>
           <mainClass>org.sec.Main</mainClass>
       </manifest>
   </archive>
</configuration>

编写assembly.xml文件

<!-- 省略部分 -->
<dependencySets>
   <dependencySet>
       <outputDirectory>/</outputDirectory>
       <unpack>true</unpack>
       <scope>system</scope>
   </dependencySet>
</dependencySets>

接着就可以通过代码的方式,根据黑名单和关键字来确定需要dump哪些类然后进行dump操作了

我在测试中遇到一个小问题,值得分享:HttpServlet是正常可以dump的但是ApplicationFilterChain类没有找到。这是因为SpringBoot的懒加载问题,需要手动请求下某个接口就可以了


相关文章
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
存储 弹性计算 缓存
阿里云服务器ECS经济型、通用算力、计算型、通用和内存型选购指南及使用场景分析
本文详细解析阿里云ECS服务器的经济型、通用算力型、计算型、通用型和内存型实例的区别及适用场景,涵盖性能特点、配置比例与实际应用,助你根据业务需求精准选型,提升资源利用率并降低成本。
763 3
|
8月前
|
设计模式 缓存 Java
【JUC】(4)从JMM内存模型的角度来分析CAS并发性问题
本篇文章将从JMM内存模型的角度来分析CAS并发性问题; 内容包含:介绍JMM、CAS、balking犹豫模式、二次检查锁、指令重排问题
222 1
|
11月前
|
存储 人工智能 自然语言处理
AI代理内存消耗过大?9种优化策略对比分析
在AI代理系统中,多代理协作虽能提升整体准确性,但真正决定性能的关键因素之一是**内存管理**。随着对话深度和长度的增加,内存消耗呈指数级增长,主要源于历史上下文、工具调用记录、数据库查询结果等组件的持续积累。本文深入探讨了从基础到高级的九种内存优化技术,涵盖顺序存储、滑动窗口、摘要型内存、基于检索的系统、内存增强变换器、分层优化、图形化记忆网络、压缩整合策略以及类操作系统内存管理。通过统一框架下的代码实现与性能评估,分析了每种技术的适用场景与局限性,为构建高效、可扩展的AI代理系统提供了系统性的优化路径和技术参考。
831 4
AI代理内存消耗过大?9种优化策略对比分析
|
JavaScript
如何使用内存快照分析工具来分析Node.js应用的内存问题?
需要注意的是,不同的内存快照分析工具可能具有不同的功能和操作方式,在使用时需要根据具体工具的说明和特点进行灵活运用。
748 159
|
存储 Java
课时4:对象内存分析
接下来对对象实例化操作展开初步分析。在整个课程学习中,对象使用环节往往是最棘手的问题所在。
161 4
|
Java 编译器 Go
go的内存逃逸分析
内存逃逸分析是Go编译器在编译期间根据变量的类型和作用域,确定变量分配在堆上还是栈上的过程。如果变量需要分配在堆上,则称作内存逃逸。Go语言有自动内存管理(GC),开发者无需手动释放内存,但编译器需准确分配内存以优化性能。常见的内存逃逸场景包括返回局部变量的指针、使用`interface{}`动态类型、栈空间不足和闭包等。内存逃逸会影响性能,因为操作堆比栈慢,且增加GC压力。合理使用内存逃逸分析工具(如`-gcflags=-m`)有助于编写高效代码。
290 2
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
666 1
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
420 5
|
数据采集 存储 自然语言处理
基于Qwen2.5的大规模ESG数据解析与趋势分析多Agent系统设计
2022年中国上市企业ESG报告数据集,涵盖制造、能源、金融、科技等行业,通过Qwen2.5大模型实现报告自动收集、解析、清洗及可视化生成,支持单/多Agent场景,大幅提升ESG数据分析效率与自动化水平。
1197 0