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层面进行类加载拦截和字节码转换的能力。它的核心优势在于无侵入性无需修改目标应用源码无需重启服务。对于需要保持隐蔽性的红队行动或线上问题诊断这是黄金准则。底层权限Agent运行在JVM内部拥有极高的权限可以访问和修改几乎所有已加载和即将加载的类。时机精准通过ClassFileTransformer接口我们可以在类被加载进JVM的瞬间对其字节码进行修改。这意味着我们可以“偷梁换柱”在目标类如Shiro处理登录的类执行关键逻辑时插入我们自己的代码逻辑。项目选择Agent技术正是看中了它在运行时动态修改程序行为的强大能力和隐蔽性。这比依赖特定框架漏洞如某些内存马注入依赖特定版本漏洞更为通用和稳定。2.2 Shiro Key的存储与获取原理Apache Shiro是一个广泛使用的Java安全框架。其核心功能之一是利用一个对称密钥CipherKey来加密和解密RememberMe Cookie。在Shiro 1.2.4及之前默认使用硬编码的密钥kPHbIxk5D2deZiIxcaaaA这也是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版本”。这种注入方式的优势在于高兼容性不依赖特定应用的Filter注册机制直击Tomcat请求处理的核心链路。高隐蔽性没有在StandardContext等容易检查的地方留下明显的Filter映射记录。执行早在应用自定义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命令)在动手之前你需要知道目标应用的进程IDPID。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。操作流程与原理剖析执行命令java -jar AgentInjectTool.jar inject 12345 G:/temp/key.txtAgent附着工具会通过VirtualMachine.attach(PID)接口将我们的Agent动态加载到目标JVM中。字节码转换Agent中的ClassFileTransformer开始工作。它会定位到Shiro处理请求的关键类例如RememberMeManager相关类并修改其某个方法如处理登录的方法在方法入口或出口处插入一段“钩子”代码。触发与捕获这个“钩子”代码被设计成当该方法被调用时会通过反射获取当前实例的cipherKey字段值然后将这个值写入你指定的文件路径。所以你需要去“触发”这个钩子。最直接的方式就是向目标应用发送一个Shiro登录请求无论是成功还是失败。你可以使用任何Shiro漏洞检测工具如ShiroAttack2的“检测当前密钥”功能或者手动构造一个登录的HTTP请求。查看结果触发请求后检查你指定的文件如G:/temp/key.txt。如果一切顺利文件里应该已经写入了Shiro的Base64编码的密钥。实操心得路径与触发我第一次用时在Windows上习惯性地写了反斜杠\结果工具报错。后来看源码才发现工具内部对路径进行了统一处理要求正斜杠。这是个小坑但很关键。另外“触发”这一步容易被忽略。注入成功后工具控制台可能没有明显提示你必须主动发送一个能流经被修改方法的请求才能激活钩子代码。耐心等待几秒再查看输出文件。3. 动态修改Shiro Key (inject pid shirokey)更强大的功能是动态修改运行中Shiro的密钥。命令格式为java -jar AgentInjectTool.jar inject {PID} {新的ShiroKey}{新的ShiroKey}: 一个Base64编码的字符串作为你想要设置的新密钥。例如ES2ZK5q7qgNrkigR4EmGNg。操作流程执行命令java -jar AgentInjectTool.jar inject 12345 ES2ZK5q7qgNrkigR4EmGNg附着与转换同样Agent会附着并修改字节码。但这次插入的“钩子”代码逻辑是设置密钥。它可能会修改AbstractRememberMeManager的setCipherKey方法或者直接在初始化逻辑里将cipherKey字段的值替换为我们指定的新值。验证修改完成后立即使用新密钥去构造RememberMe Cookie进行漏洞利用你会发现可以成功执行命令。而用旧密钥则会失败。这在内网横向移动中非常有用拿到一个Shell后将目标的Shiro密钥改为你知道的一个就等于为自己留下了一个稳定的、只有你知道的后门。4. 注入Tomcat内存马 (inject pid /shell_path)这是针对Tomcat容器的功能。命令格式为java -jar AgentInjectTool.jar inject {PID} {内存马路径}{内存马路径}: 你希望访问内存马的URL路径例如/helloshell。操作流程执行命令java -jar AgentInjectTool.jar inject 12345 /helloshell字节码修改Agent会定位到org.apache.catalina.core.ApplicationFilterChain类修改其internalDoFilter方法。注入的代码会检查当前请求的路径是否匹配/helloshell。如果匹配则直接接管请求执行内置的恶意逻辑如命令执行并返回响应不再走后续的过滤器链。连接内存马注入成功后直接访问http://target:port/helloshell?cmdwhoami即可执行命令。这种内存马存活在Tomcat进程内存中在文件系统上找不到对应的Servlet或Filter类文件重启后消失。3.3 实战案例从获取Key到权限维持假设我们作为红队已经通过Shiro反序列化漏洞在目标服务器PID: 8888上获得了命令执行能力但这是一个临时漏洞点我们需要建立更稳定的后门。第一步信息收集获取当前Keyjava -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?cmdipconfig即可获得命令回显。通过这三步我们实现了从信息窃取Key到主动权限维持改Key再到部署隐蔽后门内存马的完整链条且全程无需重启目标服务隐蔽性极高。4. 高级技巧、排查与防御4.1 使用技巧与注意事项JDK版本兼容性该工具对高版本JDK特别是JDK 9的支持可能存在问题。高版本Java引入了模块化JPMS和更强的安全限制。在实战中优先选择在JDK 8环境下使用。如果目标为高版本JDK可能需要尝试添加JVM启动参数来放宽权限但这在攻击中通常不可控。路径与权限指定输出文件路径时确保工具进程有对该路径的写入权限。在Linux下/tmp目录通常是安全的选择。在Windows下注意避免写入受保护目录。“触发”的艺术获取Key时务必理解“触发”机制。不是注入完就万事大吉。你需要让目标应用执行被修改的代码路径。对于Shiro Key获取发送登录请求是最通用的触发方式。如果应用有特定的登录API最好针对该API进行触发。进程选择使用list命令时仔细辨认进程。在Tomcat服务器上可能有多个Java进程如Tomcat主进程、子应用进程。通常部署Web应用的是Tomcat的主进程。不确定时可以结合进程命令行参数和端口号判断。内存马的隐蔽性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 防御视角如何检测与防护知己知彼百战不殆。从防御者角度了解攻击手法才能有效防护。Agent注入检测启动参数检查检查JVM启动参数中是否有非授权的-javaagent选项。这是最直接的检测方式但攻击者使用的是动态附着Attach不会修改启动参数。运行时检测使用jcmd PID VM.command_line可以查看进程的命令行。动态附着的Agent会在jcmd PID VM.agent_properties中留下痕迹。可以编写定时任务脚本扫描服务器上Java进程的Agent列表。RASP运行时应用自保护部署RASP产品它本身就是一个Agent可以监控和阻止其他恶意Agent的附着和字节码修改行为。Shiro Key修改检测基线比对在应用启动后立即通过管理接口或安全Agent读取一次RememberMeManager的cipherKey并保存为基线。定期或通过钩子检查运行时该Key是否与基线一致。流量监控监控突然出现的、使用新密钥的成功RememberMe Cookie认证请求这可能是攻击者在测试修改后的密钥。内存马检测ApplicationFilterChain类校验定期计算ApplicationFilterChain.class文件的字节码哈希值或内存中该类字节数组的哈希与标准版本进行比对。这是检测此类内存马最有效的方法。行为监控监控是否存在对非常见URL路径如/helloshell,/api/health且带有命令执行参数cmd,exec等的访问请求。使用专业工具部署如Copagent、Java-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这样的工具正是这个趋势下的一个典型产物。最后关于工具的使用我必须再次强调法律与道德的边界。这类工具的双刃剑属性极其明显。在未经授权的情况下对任何系统使用都是违法行为。它的正确打开方式应该是在授权的渗透测试、红蓝对抗演练中用于验证防御体系的有效性或者用于企业内部安全研究团队的技术储备和演练。通过研究攻击技术我们才能更好地构建防御这才是安全技术发展的良性循环。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2554922.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!