news 2026/4/26 3:59:52

Java Agent技术实战:无侵入获取Shiro密钥与注入内存马

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java Agent技术实战:无侵入获取Shiro密钥与注入内存马

1. 项目概述

在红队攻防演练和日常安全测试中,我们经常会遇到一些“卡脖子”的难题。比如,费尽周折拿到一个Webshell,却发现目标系统的数据库连接密码要么藏在某个晦涩的配置文件深处,要么被开发者用自定义逻辑加密了,想要解密还得逆向分析代码,耗时耗力。再比如,面对一个存在Shiro反序列化漏洞的系统,我们想修改其加密密钥(Key)来巩固后门,或者想在不重启服务的情况下悄无声息地获取当前密钥,传统方法要么需要修改代码、重启应用,动静太大;要么就需要深入分析内存结构,门槛太高。这些场景都指向一个核心痛点:如何在目标Java应用运行时,以无侵入、高隐蔽性的方式,动态地读取或修改其内存中的关键数据?

AgentInjectTool 正是为了解决这类问题而生的。它本质上是一个基于Java Agent技术的“内存手术刀”。通过Java Agent的Instrumentation机制,它可以在目标JVM进程运行时,动态地将我们编写的字节码“注射”进去,从而拦截、修改或增强特定类的行为。这个项目最初灵感来源于BeichenDream的InjectJDBC工具,我在其基础上进行了深度改造和功能扩展,核心新增了针对Apache Shiro框架的密钥获取与动态修改功能,并后续加入了兼容性更强的Tomcat内存马注入能力。简单来说,它让你能像“外科医生”一样,对正在运行的Java进程进行精准的“内存级”操作,无论是窃取机密(如Shiro Key),还是植入后门(如内存马),都能做到静默、快速、无需重启。

对于安全研究人员、红队队员和负责内部渗透测试的工程师而言,这个工具的价值在于它将一些高难度的、需要深厚Java底层知识的技术,封装成了简单的命令行操作。你不需要完全理解JVMTI、ClassFileTransformer这些底层概念,也能利用它完成一些高级的渗透动作。当然,强大的能力也意味着重大的责任,请务必在合法授权的范围内使用,本文所有内容仅用于技术研究和安全防御能力的提升。

2. 核心原理与技术选型解析

2.1 为什么选择Java Agent技术?

在深入功能之前,我们必须先理解为什么Agent技术是解决此类问题的“银弹”。传统上,我们要影响一个已经运行的Java应用,无非几种方法:修改源代码并重新部署、通过JMX等管理接口调用、或者利用反射进行动态修改。但这几种方法各有局限:改源码重启动静大;JMX接口可能未开启或权限不足;反射虽然灵活,但无法修改已加载类的字节码,且对Shiro Key这种存储在静态常量或通过复杂初始化逻辑生成的场景无能为力。

Java Agent技术则提供了在JVM层面进行类加载拦截和字节码转换的能力。它的核心优势在于:

  1. 无侵入性:无需修改目标应用源码,无需重启服务。对于需要保持隐蔽性的红队行动或线上问题诊断,这是黄金准则。
  2. 底层权限:Agent运行在JVM内部,拥有极高的权限,可以访问和修改几乎所有已加载和即将加载的类。
  3. 时机精准:通过ClassFileTransformer接口,我们可以在类被加载进JVM的瞬间,对其字节码进行修改。这意味着我们可以“偷梁换柱”,在目标类(如Shiro处理登录的类)执行关键逻辑时,插入我们自己的代码逻辑。

项目选择Agent技术,正是看中了它在运行时动态修改程序行为的强大能力和隐蔽性。这比依赖特定框架漏洞(如某些内存马注入依赖特定版本漏洞)更为通用和稳定。

2.2 Shiro Key的存储与获取原理

Apache Shiro是一个广泛使用的Java安全框架。其核心功能之一是利用一个对称密钥(CipherKey)来加密和解密RememberMe Cookie。在Shiro 1.2.4及之前,默认使用硬编码的密钥kPH+bIxk5D2deZiIxcaaaA==,这也是Shiro反序列化漏洞广为流传的原因。高版本Shiro会在应用启动时生成一个随机密钥。

那么,这个Key存储在哪里?如何获取?这是AgentInjectTool能够生效的前提。Shiro的密钥通常存储在org.apache.shiro.mgt.AbstractRememberMeManager类的cipherKey私有字段中。当用户登录并勾选“记住我”时,Shiro会用这个密钥加密用户信息并放入Cookie。当校验Cookie时,再用它解密。

因此,获取Key的思路就清晰了:我们需要在目标JVM中,找到已加载的AbstractRememberMeManager类(或其子类,如CookieRememberMeManager)的实例,然后通过反射读取其cipherKey字段的值。但是,如何让目标JVM执行这段“读取”代码呢?这就是Agent的用武之地。我们可以注入一个“触发器”,例如,修改某个处理HTTP请求的类(比如org.apache.shiro.web.servlet.ShiroHttpServletRequest的相关方法),当该请求被触发时(例如我们发送一个登录请求),执行我们插入的、用于读取并输出Key的代码。

注意:不同版本的Shiro,其内部类结构和字段名可能有细微差别。一个健壮的工具需要兼容多个版本。AgentInjectTool在实现时,通常会采用模糊查找类名、遍历字段等方式来提高兼容性,这也是其技术难点之一。

2.3 内存马注入:从Filter到ApplicationFilterChain

除了Shiro Key操作,项目后期加入的Tomcat内存马功能是另一个亮点。内存马是一种无文件、驻留在内存中的Webshell,重启即失效,但隐蔽性极强。传统的基于Filter的内存马需要修改web.xml或动态注册Filter,这些操作在某些环境下可能受限。

ApplicationFilterChain是Tomcat处理请求过滤器的核心类。每个请求都会经过它的doFilter方法。注入ApplicationFilterChain内存马的思路是:修改其internalDoFilter方法的字节码,在调用过滤器数组之前,插入我们自己的恶意过滤器逻辑。因为ApplicationFilterChain是Tomcat的核心类,几乎不受Web应用自身配置的影响,所以这种内存马的兼容性非常好,号称“兼容所有Tomcat版本”。

这种注入方式的优势在于:

  1. 高兼容性:不依赖特定应用的Filter注册机制,直击Tomcat请求处理的核心链路。
  2. 高隐蔽性:没有在StandardContext等容易检查的地方留下明显的Filter映射记录。
  3. 执行早:在应用自定义Filter之前执行,可以拦截到最原始的请求。

工具将这一复杂的内存马注入过程,简化成一条命令,极大地降低了技术门槛。

3. 工具使用详解与实战演练

3.1 环境准备与工具获取

首先,你需要一个目标环境。为了演示,我们可以在本地使用JDK 8(建议8以下,高版本JDK对Agent的权限管理更严格,可能需要额外参数)启动一个存在Shiro漏洞的Web应用进行测试。这里以一个简单的Spring Boot + Shiro 1.2.4的演示应用为例。

工具的获取非常简单,前往项目的GitHub Release页面下载最新的AgentInjectTool.jar文件即可。确保你的操作机器上安装了与目标进程相同或兼容版本的Java运行环境(JRE)。

3.2 核心命令全解析

AgentInjectTool采用命令行交互,核心命令结构如下:

java -jar AgentInjectTool.jar [command] [args...]

1. 列出Java进程 (list命令)在动手之前,你需要知道目标应用的进程ID(PID)。

java -jar AgentInjectTool.jar list

执行这个命令,它会调用类似jps -l的功能,列出当前系统上所有的Java进程及其主类信息。你需要从中找到你的目标Web应用(例如,demo.ShiroApplication)对应的PID。这是所有后续操作的基础。

2. 注入并获取Shiro Key (inject pid file.txt)这是获取密钥的核心操作。命令格式为:

java -jar AgentInjectTool.jar inject {PID} {文件路径}
  • {PID}: 上一步获取的目标Java进程ID。
  • {文件路径}: 一个本地文件的绝对路径,用于存储捕获到的Shiro Key。这里有一个至关重要的细节:路径必须使用正斜杠/,即使是Windows系统。例如:G:/temp/shiro_key.txt

操作流程与原理剖析:

  1. 执行命令java -jar AgentInjectTool.jar inject 12345 G:/temp/key.txt
  2. Agent附着:工具会通过VirtualMachine.attach(PID)接口,将我们的Agent动态加载到目标JVM中。
  3. 字节码转换:Agent中的ClassFileTransformer开始工作。它会定位到Shiro处理请求的关键类(例如RememberMeManager相关类),并修改其某个方法(如处理登录的方法),在方法入口或出口处插入一段“钩子”代码。
  4. 触发与捕获这个“钩子”代码被设计成:当该方法被调用时,会通过反射获取当前实例的cipherKey字段值,然后将这个值写入你指定的文件路径。所以,你需要去“触发”这个钩子。最直接的方式就是向目标应用发送一个Shiro登录请求(无论是成功还是失败)。你可以使用任何Shiro漏洞检测工具(如ShiroAttack2)的“检测当前密钥”功能,或者手动构造一个登录的HTTP请求。
  5. 查看结果:触发请求后,检查你指定的文件(如G:/temp/key.txt)。如果一切顺利,文件里应该已经写入了Shiro的Base64编码的密钥。

实操心得:路径与触发我第一次用时,在Windows上习惯性地写了反斜杠\,结果工具报错。后来看源码才发现,工具内部对路径进行了统一处理,要求正斜杠。这是个小坑,但很关键。另外,“触发”这一步容易被忽略。注入成功后,工具控制台可能没有明显提示,你必须主动发送一个能流经被修改方法的请求,才能激活钩子代码。耐心等待几秒再查看输出文件。

3. 动态修改Shiro Key (inject pid shirokey)更强大的功能是动态修改运行中Shiro的密钥。命令格式为:

java -jar AgentInjectTool.jar inject {PID} {新的ShiroKey}
  • {新的ShiroKey}: 一个Base64编码的字符串,作为你想要设置的新密钥。例如:ES2ZK5q7qgNrkigR4EmGNg==

操作流程:

  1. 执行命令java -jar AgentInjectTool.jar inject 12345 ES2ZK5q7qgNrkigR4EmGNg==
  2. 附着与转换:同样,Agent会附着并修改字节码。但这次插入的“钩子”代码逻辑是设置密钥。它可能会修改AbstractRememberMeManagersetCipherKey方法,或者直接在初始化逻辑里将cipherKey字段的值替换为我们指定的新值。
  3. 验证:修改完成后,立即使用新密钥去构造RememberMe Cookie进行漏洞利用,你会发现可以成功执行命令。而用旧密钥则会失败。这在内网横向移动中非常有用:拿到一个Shell后,将目标的Shiro密钥改为你知道的一个,就等于为自己留下了一个稳定的、只有你知道的后门。

4. 注入Tomcat内存马 (inject pid /shell_path)这是针对Tomcat容器的功能。命令格式为:

java -jar AgentInjectTool.jar inject {PID} {内存马路径}
  • {内存马路径}: 你希望访问内存马的URL路径,例如/helloshell

操作流程:

  1. 执行命令java -jar AgentInjectTool.jar inject 12345 /helloshell
  2. 字节码修改:Agent会定位到org.apache.catalina.core.ApplicationFilterChain类,修改其internalDoFilter方法。注入的代码会检查当前请求的路径是否匹配/helloshell。如果匹配,则直接接管请求,执行内置的恶意逻辑(如命令执行),并返回响应,不再走后续的过滤器链。
  3. 连接内存马:注入成功后,直接访问http://target:port/helloshell?cmd=whoami,即可执行命令。这种内存马存活在Tomcat进程内存中,在文件系统上找不到对应的Servlet或Filter类文件,重启后消失。

3.3 实战案例:从获取Key到权限维持

假设我们作为红队,已经通过Shiro反序列化漏洞在目标服务器(PID: 8888)上获得了命令执行能力,但这是一个临时漏洞点,我们需要建立更稳定的后门。

第一步:信息收集,获取当前Key

java -jar AgentInjectTool.jar list # 找到目标Tomcat进程,PID为8888 java -jar AgentInjectTool.jar inject 8888 /tmp/current_key.txt

然后,用BurpSuite或漏洞工具向目标发送一个登录POST请求。查看/tmp/current_key.txt文件,得到当前密钥abc123...==

第二步:权限维持,修改为已知Key我们打算将其修改为我们自己密钥库中的一个已知密钥my_backdoor_key==

java -jar AgentInjectTool.jar inject 8888 my_backdoor_key==

执行成功后,目标应用的Shiro加密密钥就变成了my_backdoor_key==。之后,即使原来的漏洞被修复,我们依然可以使用这个密钥构造有效的RememberMe Cookie,随时重新进入系统。

第三步:植入深度后门,注入内存马为了在Web层面保留一个更直接的入口,我们注入一个ApplicationFilterChain内存马。

java -jar AgentInjectTool.jar inject 8888 /api/health

选择/api/health这种看似正常的监控接口路径作为内存马路径,可以规避一些简单的URL扫描。注入后,访问http://target.com/api/health?cmd=ipconfig即可获得命令回显。

通过这三步,我们实现了从信息窃取(Key)到主动权限维持(改Key),再到部署隐蔽后门(内存马)的完整链条,且全程无需重启目标服务,隐蔽性极高。

4. 高级技巧、排查与防御

4.1 使用技巧与注意事项

  1. JDK版本兼容性:该工具对高版本JDK(特别是JDK 9+)的支持可能存在问题。高版本Java引入了模块化(JPMS)和更强的安全限制。在实战中,优先选择在JDK 8环境下使用。如果目标为高版本JDK,可能需要尝试添加JVM启动参数来放宽权限,但这在攻击中通常不可控。
  2. 路径与权限:指定输出文件路径时,确保工具进程有对该路径的写入权限。在Linux下,/tmp目录通常是安全的选择。在Windows下,注意避免写入受保护目录。
  3. “触发”的艺术:获取Key时,务必理解“触发”机制。不是注入完就万事大吉。你需要让目标应用执行被修改的代码路径。对于Shiro Key获取,发送登录请求是最通用的触发方式。如果应用有特定的登录API,最好针对该API进行触发。
  4. 进程选择:使用list命令时,仔细辨认进程。在Tomcat服务器上,可能有多个Java进程(如Tomcat主进程、子应用进程)。通常,部署Web应用的是Tomcat的主进程。不确定时,可以结合进程命令行参数和端口号判断。
  5. 内存马的隐蔽性ApplicationFilterChain内存马虽然兼容性好,但近年来已成为防守方重点排查的对象。高级的RASP(运行时应用自保护)或内存马查杀工具可以检测到ApplicationFilterChain类字节码的异常修改。因此,它更适合用于短期任务或作为备用手段。

4.2 常见问题与排查实录

即使理解了原理,实战中还是会遇到各种问题。下面是我在多次使用中踩过的坑和解决方案:

问题现象可能原因排查步骤与解决方案
执行list命令无输出或报错1. 没有安装或配置JAVA_HOME。
2. 工具jar包损坏。
3. 权限不足(Linux下非root用户可能无法查看其他用户的进程)。
1. 命令行输入java -version确认Java可用。
2. 重新下载jar包。
3. 在Linux下尝试使用sudo执行,或在有权限的用户下运行。
inject命令执行后,目标进程崩溃或报错1. Agent与目标JVM版本不兼容。
2. 目标JVM启动了安全管理器(SecurityManager)并禁止Agent。
3. 注入的字节码存在错误,导致类验证失败。
1. 检查目标JDK版本,尽量在JDK 8环境测试。
2. 这通常意味着攻击失败,目标环境防护较严。
3. 查看目标应用日志(如catalina.out)获取具体的JVM错误信息。这可能是工具对特定Shiro或Tomcat版本兼容性问题。
获取Key时,触发请求后文件仍为空1. 文件路径错误或权限不足。
2. 注入的“钩子”类或方法在目标应用中不存在或未被加载。
3. 触发的请求没有经过被修改的代码路径。
1. 检查文件路径(用正斜杠),并确认可写。
2. 使用list命令确认PID正确。目标应用可能使用了非标准的Shiro集成方式。
3.这是最常见原因。尝试多种触发方式:登录、注销、访问需认证的页面等。用Burp抓包,确保请求确实经过了Shiro的认证过滤器。
修改Key后,新Key无法利用1. 修改未成功,旧Key仍生效。
2. 新Key格式错误,不是有效的Base64或长度不符合AES要求。
3. 目标应用有集群或缓存,Key未同步。
1. 再次执行获取Key的命令,确认当前Key是否已变更。
2. 确保新Key是合法的Base64编码,且长度是AES算法要求的(如16, 24, 32字节)。
3. 对于集群环境,可能需要向多个节点注入。
注入内存马后,访问路径返回4041. 注入失败。
2. 内存马路径被其他Filter或Servlet优先处理。
3. 请求未到达Tomcat核心过滤器链(如被Nginx等前置代理拦截了特定路径)。
1. 检查注入命令是否有报错。尝试注入一个简单路径如/test
2. 尝试换一个生僻的、大概率不会被应用的URL路径。
3. 确保你的请求直接打到了Tomcat服务端口,或者前置代理将你设定的路径转发给了Tomcat。

4.3 防御视角:如何检测与防护

知己知彼,百战不殆。从防御者角度,了解攻击手法才能有效防护。

  1. Agent注入检测

    • 启动参数检查:检查JVM启动参数中是否有非授权的-javaagent选项。这是最直接的检测方式,但攻击者使用的是动态附着(Attach),不会修改启动参数。
    • 运行时检测:使用jcmd <PID> VM.command_line可以查看进程的命令行。动态附着的Agent会在jcmd <PID> VM.agent_properties中留下痕迹。可以编写定时任务脚本扫描服务器上Java进程的Agent列表。
    • RASP(运行时应用自保护):部署RASP产品,它本身就是一个Agent,可以监控和阻止其他恶意Agent的附着和字节码修改行为。
  2. Shiro Key修改检测

    • 基线比对:在应用启动后,立即通过管理接口或安全Agent读取一次RememberMeManagercipherKey并保存为基线。定期(或通过钩子)检查运行时该Key是否与基线一致。
    • 流量监控:监控突然出现的、使用新密钥的成功RememberMe Cookie认证请求,这可能是攻击者在测试修改后的密钥。
  3. 内存马检测

    • ApplicationFilterChain类校验:定期计算ApplicationFilterChain.class文件的字节码哈希值(或内存中该类字节数组的哈希),与标准版本进行比对。这是检测此类内存马最有效的方法。
    • 行为监控:监控是否存在对非常见URL路径(如/helloshell,/api/health)且带有命令执行参数(cmd,exec等)的访问请求。
    • 使用专业工具:部署如CopagentJava-Memshell-Scanner等开源内存马查杀工具,或商业版的EDR、HDR产品。

对于企业来说,最根本的防御是:

  • 及时升级:尽快将Shiro升级到安全版本(至少1.5.0以上,并确保使用了随机生成的密钥)。
  • 最小权限:确保运行Java应用的系统用户权限最小化,防止攻击者上传和执行AgentInjectTool.jar这类工具。
  • 网络隔离:严格限制生产服务器上的出网和横向移动能力,增加攻击者利用工具和回连的难度。

5. 项目演进与个人思考

AgentInjectTool从一个单一功能的工具,演进到集成Shiro Key操作和Tomcat内存马注入,反映了红队技术栈的两个重要方向:信息获取权限维持。它的设计哲学很清晰:将复杂、底层的JVM攻防技术产品化、命令行化,降低使用门槛。

我个人在复现和研究这个工具的过程中,最大的体会有两点:

第一,对底层原理的理解是突破防御的关键。无论是通过反射获取Shiro的私有字段,还是修改ApplicationFilterChain的字节码,都需要对Java类加载机制、JVM内存结构、Tomcat架构有深入的理解。工具只是封装,背后的原理才是核心。防守方同样如此,只有知道攻击者是如何修改字节码的,才能设计出有效的检测方案(比如校验核心类的字节码哈希)。

第二,攻防的对抗永无止境,且越来越趋向“内存战场”。随着文件上传查杀、日志审计、流量监控的完善,传统的文件型Webshell生存空间被压缩。无文件攻击、内存马、进程注入等技术因其极高的隐蔽性,成为高级攻击者的首选。未来的安全研究,无论是攻是防,都需要更深入地向下探索,深入到运行时(Runtime)、容器(Container)乃至内核(Kernel)层面。AgentInjectTool这样的工具,正是这个趋势下的一个典型产物。

最后,关于工具的使用,我必须再次强调法律与道德的边界。这类工具的双刃剑属性极其明显。在未经授权的情况下对任何系统使用,都是违法行为。它的正确打开方式,应该是在授权的渗透测试、红蓝对抗演练中,用于验证防御体系的有效性,或者用于企业内部安全研究团队的技术储备和演练。通过研究攻击技术,我们才能更好地构建防御,这才是安全技术发展的良性循环。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/26 3:58:38

抑郁症 = 焦虑症?

它的本质是&#xff1a;它们不是同一个病&#xff0c;但它们是 共病率极高 (High Comorbidity) 的“孪生兄弟”。在神经生物学层面&#xff0c;它们共享相似的神经递质失衡&#xff08;如血清素、去甲肾上腺素&#xff09;和脑区异常&#xff08;如杏仁核过度活跃、前额叶功能减…

作者头像 李华
网站建设 2026/4/26 3:54:42

Vector:高性能可观测性数据管道实战指南与性能调优

1. Vector&#xff1a;重新定义可观测性数据管道的全能选手如果你正在为日志、指标数据的收集、处理和路由而头疼&#xff0c;或者对现有方案&#xff08;比如 Fluentd、Logstash、Filebeat&#xff09;的性能、资源消耗或灵活性感到不满&#xff0c;那么 Vector 这个名字你应该…

作者头像 李华
网站建设 2026/4/26 3:52:43

时间序列特征工程:从基础到实战

1. 时间序列数据特征工程基础时间序列分析是数据科学领域的重要分支&#xff0c;广泛应用于金融、气象、工业监测等多个领域。与传统的监督学习不同&#xff0c;时间序列数据具有明显的时序依赖性&#xff0c;这使得我们需要采用特殊的方法来构建特征。关键认知&#xff1a;时间…

作者头像 李华
网站建设 2026/4/26 3:52:31

如何快速下载喜马拉雅VIP音频?完整免费音频下载工具指南

如何快速下载喜马拉雅VIP音频&#xff1f;完整免费音频下载工具指南 【免费下载链接】xmly-downloader-qt5 喜马拉雅FM专辑下载器. 支持VIP与付费专辑. 使用GoQt5编写(Not Qt Binding). 项目地址: https://gitcode.com/gh_mirrors/xm/xmly-downloader-qt5 你是否曾遇到过…

作者头像 李华
网站建设 2026/4/26 3:51:25

记一次由“HTTP重定向”导致的POST请求变GET请求

记一次由“HTTP重定向”导致的POST请求变GET请求 在Web开发中&#xff0c;HTTP重定向是常见的跳转机制&#xff0c;但稍不注意就可能引发意料之外的问题。最近&#xff0c;我在调试一个表单提交功能时&#xff0c;遇到了一个令人困惑的现象&#xff1a;明明前端发送的是POST请…

作者头像 李华