当前位置: 首页 > news >正文

Java Agent动态注入实战:内存马与Shiro密钥热修改技术解析

1. 项目概述与核心价值

最近在整理一些实战中的工具链,发现一个场景特别高频:当我们通过Webshell进入一个Java应用后,经常需要获取或修改一些运行时的关键配置,比如Shiro的加密密钥。手动去翻代码、找配置文件,效率低不说,还可能触发告警。有没有一种方法,能像“外科手术”一样,精准、静默地操作运行中的JVM进程呢?AgentInjectTool就是为解决这类问题而生的一个利器。

简单来说,AgentInjectTool是一个基于Java Agent技术的实战工具包。它的核心能力是向正在运行的Java进程动态注入代码,从而实现内存马的植入、关键信息的提取(如Shiro rememberMe密钥)以及运行时配置的热修改。这相当于给了我们一把“内存手术刀”,可以在不重启服务、不修改磁盘文件的情况下,直接对应用内存进行“微创手术”。对于安全研究人员、红队成员以及需要进行应用诊断和应急响应的开发者而言,这是一个极具价值的工具。

它的核心价值在于“动态”与“无感”。传统上,我们要修改一个Web应用的逻辑,可能需要上传一个JSP马,或者修改一个Class文件然后重启。前者会留下明显的文件痕迹,后者则可能造成服务中断。而Agent技术通过JVMTI接口,直接将修改后的字节码加载到目标JVM中,整个过程对应用本身是透明的,不会产生新的文件,也无需重启,极大地降低了操作的风险和可感知性。接下来,我将详细拆解这个工具的设计思路、使用细节以及背后的原理,并分享一些实战中积累的经验和避坑指南。

2. 核心原理:Java Agent与字节码注入深度解析

要真正用好AgentInjectTool,必须理解其底层依赖的两大核心技术:Java Agent和字节码操作。这不仅是工具使用的理论基础,也能帮助我们在遇到问题时快速定位和解决。

2.1 Java Agent机制:JVM的“后门”

Java Agent并非什么“黑魔法”,它是Java平台自Java 5开始提供的一个标准特性,主要通过java.lang.instrument包来实现。它的设计初衷是为了支持各种监控、性能分析(如Profiler)、诊断工具(如Arthas)的实现。

一个Java Agent本质上是一个特殊的JAR包,其MANIFEST.MF文件中需要声明Premain-ClassAgent-Class。两者的区别在于加载时机:

  • Premain Agent:在目标JVM的main方法之前执行。通常通过-javaagent:agent.jar命令行参数在应用启动时加载。这种方式适用于我们能够控制应用启动参数的场景。
  • AgentMain Agent:在目标JVM运行中动态加载。这正是AgentInjectTool所使用的模式。它依赖于VirtualMachine.attach()API(来自com.sun.tools.attach包),允许一个外部JVM进程连接到另一个正在运行的JVM进程,并将Agent的JAR包注入进去。

AgentInjectTool的inject命令,其内部就是启动了一个新的Java进程,这个进程使用VirtualMachine.attach(pid)连接到目标进程,然后通过loadAgent(agentJarPath, options)方法,将包含我们定制逻辑的Agent动态加载到目标JVM中。一旦Agent被加载,其agentmain方法就会被调用,这是我们植入代码的入口点。

注意com.sun.tools.attach并非Java标准API的一部分,它属于Oracle/Sun JDK的工具包。在OpenJDK或某些精简的JRE环境中,这个包可能不存在,这会导致attach失败。这是实战中需要首先排查的环境问题之一。

2.2 字节码编织:在运行时修改类逻辑

Agent被加载后,如何修改一个已经加载到JVM中的类呢?这就需要用到字节码操作技术。Java程序编译后生成的是.class文件,里面是JVM可以执行的字节码。我们可以通过工具在内存中读取、修改这些字节码,然后让JVM重新定义这个类。

AgentInjectTool主要利用了java.lang.instrument.ClassFileTransformer接口。我们实现一个ClassFileTransformer,在它的transform方法中,我们可以拦截到目标类的字节码(在类被加载或重定义时),对其进行修改,然后返回新的字节码数组,JVM便会加载我们修改后的版本。

修改字节码本身是复杂且易错的,因此我们通常借助成熟的字节码操作库,如ASMJavassist。ASM更偏向底层,性能极高但使用复杂;Javassist提供了基于源代码字符串的API,更易于上手。从项目代码推断,AgentInjectTool很可能使用了ASM,因为它需要对ApplicationFilterChainAbstractRememberMeManager等框架核心类进行非常精细的修改。

以修改Shiro密钥为例,其核心流程是:

  1. Agent的agentmain方法被调用。
  2. 创建一个ClassFileTransformer,专门用于拦截org.apache.shiro.mgt.AbstractRememberMeManager类。
  3. transform方法中,使用ASM库分析该类字节码,找到存储密钥的字段(例如DECRYPT_CIPHER_KEYencryptionCipherKey等,不同版本可能不同)。
  4. 修改该字段的初始化逻辑,或者直接替换其值,将其改为我们指定的新密钥。
  5. 通过Instrumentation.retransformClasses(Class<?>... classes)方法,触发目标类的重转换,使修改生效。

这个过程完成后,目标应用中所有后续的Shiro rememberMe cookie加解密都将使用我们设置的新密钥,而我们无需知道原密钥,也无需重启应用。

2.3 内存马注入:以Tomcat Filter为例

除了修改配置,另一个核心功能是注入内存马。项目更新日志中提到的“Tomcat的ApplicationFilterChain内存马”是一个经典案例。

Tomcat处理请求时,会经过一个过滤器链(FilterChain),ApplicationFilterChain是这个链的实现类。注入内存马的目标是:将一个我们自定义的恶意Filter动态地添加到这个链的最前面,使得所有请求都会先经过我们的Filter。

实现步骤通常如下:

  1. 定位与获取:通过反射,从Tomcat的上下文(ServletContext)中获取当前的FilterChain对象,或者更具体地,获取到ApplicationFilterChain内部的filters数组。
  2. 创建恶意Filter:在内存中动态创建一个实现了javax.servlet.Filter接口的类。这个类的doFilter方法包含了我们的后门逻辑,例如执行命令、上传文件等。
  3. 包装与插入:将创建的Filter实例包装成Tomcat能识别的ApplicationFilterConfig,然后将其插入到filters数组的首位。
  4. 字节码持久化:为了更隐蔽和持久,上述通过反射的操作可能在Agent卸载后失效。因此,高级的内存马会直接修改ApplicationFilterChain类的字节码,在其internalDoFilter方法的最开始处硬编码插入调用我们恶意Filter的逻辑。这样即使JVM重启,只要类没有被还原,内存马就依然存在(直到下次应用重新部署)。AgentInjectTool的“兼容所有Tomcat版本”很可能就是通过适配不同版本Tomcat中ApplicationFilterChain的字节码结构来实现的。

3. 工具实战:从安装到攻防演练

理解了原理,我们来看具体怎么用。工具的使用看似简单,但每个步骤都有需要注意的细节。

3.1 环境准备与工具获取

首先,你需要一个Java运行环境。强烈建议使用JDK而非JRE,因为工具依赖的tools.jar(包含com.sun.tools.attach)在标准的JRE中是不包含的。在Linux上,你可以通过which javajava -version来确认。

# 检查是否是JDK java -version # 输出应包含类似 “Java(TM) SE Runtime Environment” 和 “Java HotSpot(TM) 64-Bit Server VM” # 也可以检查是否存在 tools.jar find /usr/lib/jvm -name "tools.jar" 2>/dev/null

工具本身是一个可执行的JAR包,从项目的GitHub Releases页面下载最新版本即可。下载后,你可以通过java -jar AgentInjectTool.jar查看帮助信息。

3.2 核心命令详解

工具主要提供两个命令:listinject

1.java -jar AgentInjectTool.jar list这个命令用于列出当前系统上所有可附加的Java进程。它会显示每个进程的PID和主类名。这是寻找目标的第一步。

  • 实战技巧:在Linux服务器上,你可能需要以root权限运行,或者当前用户需要具备向目标Java进程发送信号的权限(通常,目标进程的用户和当前用户相同即可)。如果list命令返回为空或权限错误,请检查用户权限。

2.java -jar AgentInjectTool.jar inject <pid> <path_or_key>这是核心功能命令。

  • <pid>:目标Java进程的ID,从list命令获取。
  • <path_or_key>:这个参数有两种用法:
    • 文件路径:指定一个本地文本文件的路径,工具会读取该文件的内容作为新的Shiro密钥。这里有一个至关重要的细节:项目文档中特别强调“一定得使用反斜杠/”。这是因为在Java命令行参数中,反斜杠\是转义字符。在Windows上,路径G:\temp\temp.txt需要写成G:/temp/temp.txt或者G:\\temp\\temp.txt。为了跨平台兼容,统一使用正斜杠/是最佳实践。
    • 密钥字符串:直接传入一个Base64编码的字符串作为新的Shiro密钥,例如ES2ZK5q7qgNrkigR4EmGNg==

注入Tomcat内存马的特殊命令: 根据更新日志,注入内存马的命令格式为:java -jar AgentInjectTool.jar inject <pid> <shell_path>其中<shell_path>是内存马的访问路径,例如/helloshell。这意味着注入后,访问http://target:port/context_path/helloshell就会触发我们的内存马。

3.3 完整操作流程演示:修改Shiro密钥

假设我们已经在目标服务器上获得了Shell,并且发现了一个运行在8080端口的Spring Boot应用,使用了默认的Shiro密钥。

步骤1:确认目标与获取PID首先,我们使用list命令找到目标进程。假设我们看到如下输出:

PID: 12345, MainClass: org.springframework.boot.loader.JarLauncher PID: 23456, MainClass: com.sun.tools.attach.BasicLauncher

很明显,PID为12345的进程是我们的Spring Boot应用。

步骤2:准备新的密钥我们可以生成一个新的Shiro AES密钥。使用任何能生成16、24或32字节Base64字符串的工具。例如,用OpenSSL:

openssl rand -base64 16 # 输出类似:kPH+bIxk5D2deZiIxcaaaA==

我们将这个新密钥保存到服务器上一个临时文件,比如/tmp/new_key.txt

步骤3:执行密钥修改在目标服务器上执行:

java -jar AgentInjectTool.jar inject 12345 /tmp/new_key.txt

如果一切正常,控制台会输出成功的提示信息。

步骤4:触发并验证修改密钥的Agent逻辑,通常需要触发一次Shiro的登录流程(无论成功与否)来激活。这是因为很多Shiro的密钥是懒加载的,只有在加解密操作发生时才会被初始化。 我们可以用curl模拟一个登录请求,或者直接使用Shiro反序列化漏洞检测工具(如ShiroAttack2)的“检测当前密钥”功能,向目标发送一个payload。发送后,Agent会拦截到AbstractRememberMeManager类的初始化或调用,将密钥字段替换。 之后,我们再使用新密钥kPH+bIxk5D2deZiIxcaaaA==来生成rememberMe Cookie,应该就能成功登录或执行命令了。

重要注意事项

  1. 路径问题:文件路径参数务必使用正斜杠/,这是避免因转义字符导致文件读取失败的最稳妥方式。
  2. 权限问题:执行注入的Java进程必须能附加到目标JVM。在Linux上,这意味着用户需要有向目标进程发送ptrace信号的权限。通常,同用户运行或使用root权限可以解决。
  3. Java版本兼容性:高版本Java(如JDK 9+)引入了模块化系统(JPMS),Attach API和Instrumentation API的使用方式可能有变。工具可能对JDK 8及以下版本兼容性最好。在复杂环境下,可能需要调整启动参数或使用更现代的Agent框架。

3.4 防御与检测视角

从蓝队和防御者的角度,了解攻击技术才能更好地防御。针对此类Agent注入攻击,可以采取以下措施:

  1. 禁用Attach API:在不需要动态调试的生产环境中,可以在JVM启动参数中添加-XX:+DisableAttachMechanism来完全禁止外部进程附加。这是最根本的防御。
  2. 使用Java Security Manager:配置严格的安全策略文件,限制com.sun.tools.attach等敏感类的加载。不过,Security Manager在更新的Java版本中已被标记为废弃。
  3. 监控JVM进程:监控服务器上是否有未知的Java进程尝试attach到业务JVM。可以编写脚本监控/proc/<pid>/fd/下的socket连接,或者直接使用RASP(运行时应用自保护)产品,它们能够监控并拦截Instrumentation.retransformClasses等危险调用。
  4. 检测内存马:定期对运行中的JVM进行扫描。可以使用类似java -XX:+StartAttachListener -cp $JAVA_HOME/lib/tools.jar sun.tools.jmap.JMap -dump:live,format=b,file=heap.bin <pid>命令导出堆内存,然后使用MAT、OQL等工具搜索可疑的Filter、Servlet或Controller类名。也可以使用Arthas的sc(search class)或jad(反编译)命令在线检查已加载的类,寻找字节码中被插入的恶意片段。
  5. 应用安全基线:确保应用使用的第三方组件(如Shiro)的密钥在启动时从安全的配置中心获取,而非硬编码在代码中。即使被修改,重启后也会恢复。

4. 高级技巧与深度定制

对于想更进一步的研究者,AgentInjectTool可以作为一个基础框架进行扩展。

4.1 理解项目结构进行二次开发

项目源于对BeichenDream/InjectJDBC的改造,加入了Shiro密钥功能。要添加自己的功能,你需要理解其代码结构:

  1. Agent核心:项目中有个agent模块,里面包含了真正被注入到目标JVM的代码。你需要修改这里的逻辑,例如编写一个新的ClassFileTransformer来修改其他框架的类(比如修改Spring的RequestMappingHandlerMapping来注入一个Controller内存马)。
  2. 注入器:主JAR包(AgentInjectTool.jar)是一个启动器,它负责Attach到目标进程并加载Agent JAR。你可能需要修改这里来传递更多的参数给你的Agent。
  3. 字节码操作:这是最复杂的部分。你需要熟悉ASM或Javassist,并理解目标类的字节码结构。可以通过先写一个普通的Java类,编译后用javap -c查看其字节码,再思考如何用ASM API动态生成等价的修改逻辑。

4.2 扩展功能设想

基于此工具模式,可以扩展出许多实用功能:

  • 动态修改日志级别:在应急排查时,动态将某个关键类的日志级别从INFO调整为DEBUG,无需重启即可获取更详细的日志输出。
  • 热修复漏洞:对于某些无法立即重启修复的漏洞,可以尝试通过注入Agent,在内存中修改漏洞类的逻辑,实现临时热修复。
  • 提取数据库连接池配置:注入代码到HikariCP、Druid等连接池的实现类中,动态提取其内存中的数据库连接字符串、用户名密码。
  • 拦截并修改特定方法返回值:用于测试或故障模拟,例如让某个Service方法总是返回一个特定的异常或值。

4.3 稳定性与隐蔽性优化

在实战中,工具的稳定性和隐蔽性至关重要。

  • 错误处理:确保Agent的agentmain方法和ClassFileTransformertransform方法有完善的try-catch,避免因为某个类转换失败导致整个Agent加载失败,甚至引起目标JVM崩溃。可以将错误信息静默记录到内存或通过某种隐蔽通道传出。
  • 减少影响:修改字节码时,尽量只修改必要的最小部分。例如,修改Shiro密钥时,最好只替换密钥常量,而不是重写整个类的方法,以降低对应用稳定性的影响。
  • 清理痕迹:Agent加载后,可以考虑将自己从Instrumentation的转换器列表中移除,并尝试关闭Attach连接,使得在目标JVM的线程列表或连接列表中更难被发现。
  • 对抗检测:针对RASP的检测,可以尝试使用更底层的JVMTI接口,或者利用Java的反射机制来间接调用InstrumentationAPI,绕过一些基于API Hook的检测。

5. 常见问题排查与实战心得

在实际使用过程中,你可能会遇到各种问题。下面是一些常见问题的排查思路和我个人踩过的坑。

5.1 问题排查速查表

问题现象可能原因排查步骤与解决方案
list命令无输出或报错1. 权限不足
2. 目标进程非HotSpot JVM
3. 使用了JRE而非JDK
1. 尝试使用sudo或以目标进程所属用户身份运行。
2. 检查java -version,确认是Oracle/OpenJDK HotSpot。
3. 确认JAVA_HOME指向JDK,且lib/tools.jar存在。
inject命令失败,提示com.sun.tools.attach.AttachNotSupportedException1. 权限不足
2. 目标PID不存在或不是Java进程
3. 目标JVM以-XX:+DisableAttachMechanism启动
1. 检查权限(同上)。
2. 用ps -ef | grep javajps -l再次确认PID。
3. 此情况无法远程Attach,需寻找其他途径。
inject命令执行成功,但Shiro密钥未改变1. 未触发Shiro类加载/初始化
2. 目标Shiro版本或实现不同,关键类名/字段名不匹配
3. 文件路径错误,密钥未正确读取
1. 发送一个Shiro登录请求(无论对错)以触发类加载。
2. 检查目标应用使用的Shiro版本,Agent逻辑可能需要适配。可尝试先获取密钥看是否成功。
3.重点检查文件路径格式,确保使用/,并且文件内容是正确的Base64密钥。
注入后应用出现异常或崩溃1. 字节码修改错误,导致类结构破坏
2. 与目标JVM版本不兼容(如JDK 11+的模块化)
3. Agent代码存在Bug,产生死循环或内存泄漏
1. 立即停止操作。此类操作应在测试环境充分验证后再用于生产或实战。
2. 在类似版本的环境中测试工具兼容性。
3. 查看目标JVM的日志(如catalina.out, stdout),寻找ClassFormatError,VerifyError等异常堆栈。
工具本身报ClassNotFoundExceptionNoClassDefFoundError依赖缺失,AgentInjectTool.jar不是可执行Fat Jar,或缺少tools.jar确保使用项目发布的完整Release包,并在包含tools.jar的JDK环境下运行。

5.2 实战心得与注意事项

  1. 测试环境先行:任何Agent注入操作都有导致JVM崩溃的风险。务必先在和目标环境(JDK版本、中间件版本、应用框架)尽可能一致的测试环境中进行验证。可以自己搭建一个简单的Spring Boot + Shiro应用进行练习。
  2. 理解“副作用”:修改运行时的类定义是危险操作。它可能导致:
    • 内存泄漏:原来的类定义被替换,但如果仍有线程持有旧类实例的引用,可能导致奇怪的行为或内存无法释放。
    • 验证错误:字节码修改如果不符合JVM规范,会抛出VerifyError
    • 兼容性问题:修改后的类可能与依赖它的其他类不兼容。 因此,这类工具应仅用于安全研究、授权测试和紧急的故障诊断,切忌在重要的生产环境随意使用。
  3. 关于“通用性”:项目宣称“兼容所有的Tomcat版本”,这通常意味着它内置了针对多个Tomcat版本(如7.x, 8.x, 9.x)的ApplicationFilterChain类的字节码修改模板。但软件世界变化快,新版本可能随时引入变化。对于Shiro密钥修改,不同版本(1.2.x, 1.4.x, 1.5+)的AbstractRememberMeManager内部实现也可能有差异。工具不是银弹,遇到不成功的情况,需要结合错误日志和反编译结果进行具体分析。
  4. 法律与授权:再次强调,此类工具功能强大,但必须在合法合规的范围内使用。仅用于自身拥有所有权或已获得明确书面授权的系统进行安全评估、渗透测试和漏洞研究。未经授权对他人系统使用是违法行为。

最后,技术本身是中立的。Java Agent技术是JVM提供给开发者的强大诊断和扩展能力,像Arthas这样的优秀工具正是基于此构建,极大提升了开发运维效率。AgentInjectTool展示了这项技术的另一面。作为安全从业者,我们深入研究它,既是为了在需要时能使用它作为“手术刀”,更是为了能更好地构建“免疫系统”,防御它可能带来的威胁。理解攻击,是为了更好的防御。

http://www.jsqmd.com/news/702100/

相关文章:

  • 安卓虚拟摄像头魔法:如何让手机摄像头看见你想要的画面
  • 如何在 Go 项目中安全、高效地共享 MySQL 数据库连接
  • 裸机驱动开发不再抓狂,VSCode一键生成SVD解析+寄存器智能提示+外设时序图(附NXP i.MX RT1064实测工程包)
  • 分析2026年武汉不错的大巴租赁公司,知名大巴租赁专业公司怎么选 - 工业品牌热点
  • 碧蓝航线Alas自动化脚本:告别重复劳动,实现游戏全托管终极指南
  • XUnity.AutoTranslator:Unity游戏自动翻译插件的终极指南
  • 总结武汉靠谱的大巴租赁企业,夏东通勤哪家好? - mypinpai
  • 如何快速掌握LiveDraw:专业屏幕实时标注工具的完整指南
  • 机器学习平台:实验管理、模型部署与监控一体化
  • 2026年长沙团建活动公司推荐,性价比高的看这里 - 工业品网
  • 2026年梳理我酒吧街氛围彩灯光影设计企业哪个口碑好 - 工业品网
  • 【系统架构师案例题】分布式系统设计与选型
  • 终极指南:5步实现微信平板模式,轻松突破安卓多设备登录限制
  • 探讨2026年郴州团建活动靠谱机构,品牌与价格分析 - myqiye
  • 5分钟掌握DownKyi:B站视频下载与处理的完整解决方案
  • 神经网络在电离层扰动预测中的实践与应用
  • 2026年广东性价比高的工程大数据平台,中策大数据品牌排名 - 工业设备
  • 2026年讲讲四柱货梯生产厂合作案例多的品牌,石家庄优质厂家推荐 - 工业品牌热点
  • 基于LLM的多智能体系统构建:从原理到实践
  • AI 编程工程化:Subagent——给你的 AI 员工打造协作助手
  • Pixel Aurora Engine 多风格对比展示:写实、动漫、水墨等十余种预设效果
  • 知明团建员工流动率低吗,适不适合作为品牌推荐 - mypinpai
  • 解读中策大数据咨询公司,业务覆盖哪些,推荐选择吗 - 工业推荐榜
  • Chord视频分析工具镜像免配置:Docker启动+浏览器直连全流程
  • AgentFlow:模块化智能体框架与Flow-GRPO强化学习实战解析
  • 探寻知明团建研发能力、团队凝聚力和社会责任履行情况靠谱吗 - 工业设备
  • Pixel Language Portal部署教程:基于Hunyuan-MT-7B的16-bit翻译终端保姆级安装指南
  • Keil MDK与STM32开发环境搭建与优化指南
  • 大语言模型嵌入在语义搜索系统中的应用与实践
  • PyTorch 2.8镜像惊艳效果:Qwen2-VL多模态模型图文问答准确率实测展示