Java Agent内存马注入工具Agenst原理与实战指南
1. 项目概述与核心思路最近在整理一些内部安全演练的工具链发现一个挺有意思的Java Agent内存马注入工具叫Agenst。这工具本质上是一个“维权工具”或者说是一个在特定授权测试场景下用于在目标Java Web服务器上快速植入Webshell内存马的轻量级Agent。它把冰蝎、Neo-reg这类经典Webshell的Agent部分打包成了一个可独立运行的Jar包通过Java的Attach机制注入到目标JVM进程里从而在内存中动态注册Filter或Servlet实现无文件落地、重启即失效的持久化访问能力。对于做红队评估或者内部攻防演练的兄弟来说这种工具的价值在于“快”和“隐蔽”。传统Webshell需要写文件到磁盘动静大容易被各类安全防护设备HIDS、WAF、RASP检测到。而基于Java Agent的内存马技术其所有恶意代码都驻留在目标JVM的运行时内存中不落盘从文件系统层面看是“干净”的。Agenst把这个过程进一步简化了你不需要再去手动编写复杂的Transformer字节码增强逻辑也不用去纠结如何构造一个有效的Jar包并正确附着到目标进程。它提供了一个开箱即用的解决方案把agenst.jar扔到服务器上一条java -jar命令几个预设的Web路径就自动生效了你可以立刻用冰蝎客户端去连接。当然工具自带的默认配置比如路径/agenstR密码rebeyond在真实环境中无异于“裸奔”所以Agenst也支持自定义校验头和注入路径这算是基本的安全操作意识。接下来我会结合自己的使用经验和理解把这个工具从原理到实操再到一些关键的避坑细节系统地拆解一遍。无论你是想了解Java Agent内存马的技术原理还是需要在授权测试中实际应用这篇文章应该都能给你提供清晰的参考。2. 核心原理Java Agent与内存马技术解析要玩转Agenst首先得搞清楚它底层依赖的两大核心技术Java Agent的Attach机制以及基于Servlet/Filter API的内存Webshell实现。理解了这些你才能明白为什么它这么“强大”以及在使用时需要注意哪些潜在的“雷区”。2.1 Java Agent与Attach机制Java Agent是一种特殊的Java程序它并非独立运行而是用来“监测”或“修改”其他Java应用程序即目标JVM的运行状态。它主要通过两种方式加载静态加载Premain在目标JVM启动时通过-javaagent:agent.jar参数指定。这种方式侵入性强需要控制启动参数在实战中较难实现。动态加载Attach在目标JVM运行时通过com.sun.tools.attach.VirtualMachine类提供的API将一个Agent的Jar包动态“附着”并加载到目标进程中。这正是Agenst所采用的方式也是其“一键注入”能力的基石。动态Attach的核心在于一个名为attach.dllWindows或libattach.soLinux的本地库文件。这个库是JVM工具接口JVMTI的一部分提供了进程间通信和代码注入的能力。当你执行java -jar agenst.jar时Agenst程序本身会枚举当前系统上的所有JVM进程通常是jps命令看到的结果。通过Attach API连接到目标Tomcat、Spring Boot等Web容器的JVM进程。将agenst.jar作为Agent加载到目标JVM中。目标JVM会调用Agent Jar中MANIFEST.MF文件指定的Agent-Class的agentmain方法。关键点很多兄弟第一次运行时报错问题就出在找不到这个本地库。Agenst的lib文件夹下已经提供了编译好的attach.dll和libattach.so你必须确保它们位于Java的库加载路径下通常是把它们和agenst.jar放在同一个目录或者通过-Djava.library.path参数指定。2.2 内存马In-Memory Webshell的实现Agent被加载后它的agentmain方法就开始执行了。这里就是内存马“魔法”发生的地方。Agenst的核心功能是通过字节码增强技术向目标Web容器的运行时动态添加恶意组件。以最常用的Filter型内存马为例冰蝎、Neo-reg的后端基本都是Filter获取上下文Agent代码会通过线程上下文类加载器Thread Context ClassLoader或遍历已加载的类等方式找到当前Web容器的ServletContext对于Tomcat就是ApplicationContext。动态注册Filter利用ServletContext的addFilter方法动态创建一个Filter并注册到指定的URL路径如/agenstR。这个Filter的类字节码是由Agent直接定义并加载的它并不来源于WEB-INF/classes或WEB-INF/lib下的任何文件。恶意逻辑注册的Filter会包含完整的Webshell逻辑。对于冰蝎它会解密请求中的特定参数如密码rebeyond执行客户端传来的加密指令并将结果加密返回。对于Neo-reg它则扮演一个HTTP代理转发流量。内存马的优势与劣势优势无文件落地、隐蔽性高、存活于Web容器生命周期内重启则失效。劣势技术门槛相对高依赖目标JVM的Attach功能可能被禁用注入动作本身可能被RASP运行时应用自保护拦截一旦JVM重启内存马即消失需要重新注入。Agenst的价值就在于它把上述复杂的技术流程封装成了一个简单的Jar包和几条命令极大地降低了使用门槛。但作为使用者你必须清楚你正在对目标JVM进行运行时字节码修改这是一个风险极高的操作务必在完全可控的测试环境进行。3. 环境准备与工具部署实操理论清楚了我们来看手把手怎么把它跑起来。这里我会分成Windows和Linux两个环境来讲并把可能遇到的坑提前标出来。3.1 前置条件检查无论什么系统第一步永远是检查基础环境。Java环境目标服务器上必须安装有JDK而不仅仅是JRE。因为Attach机制相关的工具类com.sun.tools.attach.VirtualMachine通常只包含在JDK中。用java -version可以查看但更可靠的是检查是否存在$JAVA_HOME/lib/tools.jarOracle JDK或对应的模块OpenJDK 9。权限执行注入的用户需要有权限向目标JVM进程发送信号并进行内存操作。在Linux上通常需要与目标进程如tomcat用户相同的用户权限或者是root用户。在Windows上需要有相应的进程操作权限。网络可达虽然注入是本地操作但后续需要通过Web访问内存马。确保你用于连接的客户端能访问到目标服务器的Web端口如8080, 8443。3.2 Windows环境部署步骤假设我们把工具部署在C:\tools\agenst目录下。文件准备将agenst.jar复制到目录。从Agenst项目的lib-windows文件夹中找到attach.dll也复制到同一目录。目录结构应如下C:\tools\agenst\ ├── agenst.jar └── attach.dll基础启动与测试 打开命令行CMD或PowerShell切换到该目录执行最基础的命令cd C:\tools\agenst java -jar agenst.jar如果一切正常控制台会输出一些日志表明正在尝试查找并附加到JVM进程成功后会提示注入的路径信息。验证注入效果 保持命令行窗口运行这是Agent进程本身。打开浏览器访问http://目标服务器IP:端口/agenstR。如果返回一个空白页面或者特定的错误而不是404说明Filter可能已经注册成功。更准确的验证是使用冰蝎4.1客户端连接地址填上述URL密码填rebeyond。3.3 Linux环境部署步骤假设部署在/opt/agenst目录使用tomcat用户运行。文件准备与权限sudo mkdir -p /opt/agenst sudo cp agenst.jar /opt/agenst/ sudo cp lib-linux/libattach.so /opt/agenst/ # 从项目lib-linux目录获取 sudo chown -R tomcat:tomcat /opt/agenst # 根据你的Tomcat运行用户修改 sudo chmod x /opt/agenst/libattach.so # 确保so文件有执行权限处理库文件路径问题关键坑点 这是Linux下最容易出错的地方。Java在查找本地库.so文件时会按照java.library.path系统属性指定的路径来寻找。默认通常不包含当前目录。推荐做法在启动命令中明确指定库路径。cd /opt/agenst sudo -u tomcat java -Djava.library.path/opt/agenst -jar agenst.jar通过-Djava.library.path/opt/agenst我们告诉JVM在这个目录下寻找libattach.so。后台运行 由于这是一个需要持续运行的Agent进程我们通常希望它在后台运行。cd /opt/agenst sudo -u tomcat nohup java -Djava.library.path/opt/agenst -jar agenst.jar agenst.log 21 这样进程会在后台运行日志输出到agenst.log文件。可以通过tail -f agenst.log查看实时日志。3.4 目标进程选择与注入确认Agenst默认会尝试附加到它找到的第一个合适的JVM进程通常是第一个非自身的Java进程。但在生产环境可能同时运行着多个Java应用。查看Java进程 在注入前先用jps -l在JDK的bin目录下命令查看所有Java进程及其主类。找到你的Web应用进程记下其PID。# 示例输出 12345 org.apache.catalina.startup.Bootstrap 67890 com.sun.tools.attach.VirtualMachine这里PID为12345的Tomcat进程就是我们的目标。高级指定目标PID 虽然Agenst的文档没有明确说明但查看其源码或类似工具可知很多此类工具支持通过参数指定PID。如果Agenst不支持你可能需要确保在运行Agenst时目标进程是唯一的候选者或者修改其源码中的进程选择逻辑。如何确认注入成功日志观察Agenst自身的输出日志成功通常会提示“Injected to PID: xxx”或“Servlet/Filter registered at /xxx”。Web请求访问注入的路径如/agenstR。如果返回404则注入可能失败。如果返回400、500或其他非404错误可能是路径存在但校验未通过如冰蝎密码错误这反而说明Filter可能注册成功了。进程内存检查可以通过jmap -histo PID | grep -i filter或一些内存扫描工具如Scanner来查看目标JVM中是否加载了相关的类但这属于进阶排查手段。4. 功能详解与自定义配置实战Agenst提供了三个核心功能冰蝎、自定义内存马、Neo-reg和两个关键的自定义选项校验头、注入路径。我们来逐一拆解并给出具体的配置示例。4.1 三大支持功能解析4.1.1 冰蝎4.1内存马 (/agenstR)这是最常用的功能。注入后会在目标Web应用中注册一个Filter映射到/agenstR或你自定义的路径。这个Filter实现了冰蝎4.1的服务端通信协议。默认密码rebeyond。强烈建议在实战中修改虽然需要通过自定义校验头来增强安全性但修改默认密码是基本操作需要你自行编译修改源码中的密码常量。通信流程冰蝎客户端发送的请求中密码和指令都经过AES加密并放在特定的HTTP头如Cookie或参数中。服务端Filter解密后执行指令可以是命令执行、文件管理、数据库连接等再将结果加密返回。使用在冰蝎4.1客户端中URL填写http://target:port/agenstR密码填写rebeyond连接类型选择“JSP”即可连接。4.1.2 自定义内存马加载器 (/agenstE)这是一个非常灵活的功能。它提供了一个“通用”的加载端点允许你通过HTTP POST请求动态地上传并执行一个内存马。工作原理/agenstE路径对应的Servlet或Filter会读取POST请求Body中的数据将其作为Base64编码的字符串进行解码。解码后的字节码通过自定义的类加载器通常是DefineClass的方式加载到JVM中并实例化执行。内存马生成作者提到“用JMG生成就行”。JMGJava Memoryshell Generator是一个开源的内存马生成工具可以生成各种类型的Filter, Servlet, Listener, Controller等、支持多种加密方式的冰蝎、哥斯拉等Webshell的字节码。你需要使用JMG生成一个二进制的.class文件或包含它的Jar包然后将其Base64编码。使用示例# 1. 使用JMG生成一个内存马class文件比如叫shell.class # 2. 将shell.class进行base64编码 base64 -w 0 shell.class shell.b64 # 3. 使用curl或其他工具POST这个base64字符串到/agenstE curl -X POST http://target:port/agenstE --data-binary shell.b64如果成功这个新上传的内存马就会被加载并生效通常它会自己注册一个新的访问路径。这个功能风险极高因为它允许远程加载任意字节码务必在绝对可控的环境测试。4.1.3 Neo-reg代理 (/agenstN)Neo-reg是另一个流行的HTTP代理型Webshell常用于内网穿透。Agenst集成了它的服务端。默认密码donttouch。使用方式在Neo-reg客户端中配置连接地址为http://target:port/agenstN密钥-k参数为donttouch。之后Neo-reg客户端就会将所有代理流量通过这个路径转发到目标服务器服务器端的Filter负责将流量转发到内网的其他机器。与冰蝎的区别冰蝎是交互式命令执行/文件管理Neo-reg是流量转发代理。用途不同。4.2 安全增强自定义校验机制使用默认路径和密码在稍有防护的环境里瞬间就会被封。Agenst提供了请求头校验机制来增加一点访问门槛。启动参数-aHeaderName:HeaderValue作用启动此参数后所有通过Agenst注入的内存马路由/agenstR,/agenstE,/agenstN及其自定义变体都会增加一层校验。只有当HTTP请求中包含指定的请求头且值完全匹配时请求才会被处理否则返回404或其他错误。示例java -jar agenst.jar -areferer:https://www.baidu.com启动后你必须这样访问冰蝎curl -H Referer: https://www.baidu.com http://target:port/agenstR或者在冰蝎客户端中想办法添加这个自定义请求头一些客户端支持修改请求模板。注意事项大小写根据文档请求头和值请使用小写。但HTTP头本身不区分大小写这里可能是工具内部做了规范化处理遵循文档即可。空格参数值HeaderName:HeaderValue中间不要有空格。局限性这只是一个简单的静态校验无法防御流量审计或WAF的深度行为分析。它的主要作用是防止路径被偶然扫描或简单的爬虫发现。4.3 路径混淆自定义注入路径修改默认路径是另一个基本的隐蔽手段。Agenst提供了两种粒度的路径自定义。4.3.1 全局路径替换 (-u)启动参数-uCustomPrefix作用将三个内存马路径中的公共前缀agenst替换为你指定的字符串。示例java -jar agenst.jar -ustatic注入后路径将变为冰蝎/staticR自定义加载器/staticENeo-reg/staticN逻辑工具内部只是做了字符串替换。将代码中硬编码的agenst前缀换成了你输入的CustomPrefix。4.3.2 独立路径定义 (-uR,-uE,-uN)启动参数-uRPathForRebeyond,-uEPathForExploit,-uNPathForNeoreg作用分别独立地定义冰蝎、自定义加载器、Neo-reg的完整访问路径。这个优先级高于-u参数。示例java -jar agenst.jar -uRapi/v1/user -uEupload/img -uNproxy/gateway注入后路径将变为冰蝎/api/v1/user自定义加载器/upload/imgNeo-reg/proxy/gateway注意事项路径不要以/开头工具内部会拼接。定义的路径就是完整的Servlet/Filter映射路径。你可以将其伪装成正常的API接口增加隐蔽性。重要文档提到“注入的filter等不沿用此规则”意思是使用-u或-uX参数修改的只是访问路径而内存马内部注册的Filter/Servlet名称等可能还是原来的但这不影响使用。4.4 组合使用示例一个相对隐蔽的启动命令可能如下java -jar agenst.jar -aauthorization:bearer internal-token-2024 -uRactuator/health -uNproxy/socks5这条命令做了以下事情设置了请求头校验必须带有Authorization: bearer internal-token-2024头。将冰蝎路径伪装成Spring Boot Actuator的健康检查端点/actuator/health。将Neo-reg路径设置为/proxy/socks5。没有指定-uE因此自定义内存马加载器将使用默认路径/agenstE因为没使用-u全局替换。这样即使有人扫描到/actuator/health没有正确的Authorization头也无法利用而Neo-reg的路径也看起来像是一个内部代理服务。5. 编译与定制化进阶如果你不满足于默认的密码和功能或者需要适配特定环境就需要自己动手编译和修改Agenst的源码。5.1 项目结构与编译环境获取源码从开源仓库如Github克隆Agenst项目。项目结构通常是一个Gradle项目。核心代码在src/main/java下构建配置在build.gradle。编译环境你需要安装JDK 8或以上版本以及Gradle。5.2 关键定制点修改默认密码 在源码中全局搜索rebeyond和donttouch。这些字符串常量通常定义在某个工具类或Filter类中。例如冰蝎的密码可能在处理请求的Filter类里用于解密客户端数据。将其修改为你自己的复杂密码。切记修改后需要同时更新客户端连接时的密码。修改默认路径前缀 搜索字符串agenstR,agenstE,agenstN。这些是硬编码的路径常量。你可以在这里修改默认值这样即使不通过启动参数-u也能使用你自定义的默认路径。增强校验逻辑 默认的-a参数是简单的字符串匹配。你可以修改校验逻辑使其支持更复杂的机制例如多个请求头组合校验。请求头值的哈希校验如MD5、SHA256。基于时间的动态Token但需要客户端同步生成比较麻烦。添加新类型的内存马 如果你想集成哥斯拉Godzilla、蚁剑AntSword等其他Webshell的内存马需要理解该Webshell的服务端通信协议。编写对应的Filter或Servlet类。在Agent的主入口agentmain方法中添加注册该新组件的逻辑。在启动参数解析部分增加对应的路径配置选项。5.3 交叉编译与构建Agenst项目已经考虑了跨平台其build.gradle文件中应该包含了为不同操作系统构建的配置。查看build.gradle找到类似task buildWindows或task buildLinux的任务或者注释中关于切换attach.dll和libattach.so的说明。编译Windows版本通常需要确保在Windows系统上或者配置Gradle使用Windows的编译工具链。运行对应的Gradle任务如gradlew buildWindows或gradlew.bat build。编译Linux版本在Linux系统上运行./gradlew buildLinux或./gradlew build。输出编译完成后在build/libs/目录下会生成agenst.jar。同时需要将对应平台的本地库文件来自lib-windows或lib-linux与Jar包一起分发。实操心得有时候直接使用项目预编译的lib-attach库可能因为glibc版本等问题在目标服务器上无法加载。如果遇到UnsatisfiedLinkError最稳妥的方法是在与目标服务器相同或兼容的系统环境如相同版本的CentOS上从该服务器的JDK中直接提取libattach.so文件位于$JAVA_HOME/jre/lib/amd64/或类似路径并用它替换工具自带的库文件。6. 常见问题排查与防御视角6.1 问题排查清单问题现象可能原因排查步骤运行java -jar agenst.jar后立即退出或无日志1. 未找到可附加的JVM进程。2.attach.dll/libattach.so未找到或加载失败。1. 使用jps -l确认有目标Java进程运行。2. 检查库文件是否存在并通过-Djava.library.path指定正确路径。3. 在Linux下使用strace或查看系统日志dmesg,/var/log/messages看是否有库加载错误。控制台日志显示附加成功但访问路径返回4041. 路径错误自定义路径未生效。2. 请求头校验未通过。3. 目标Web应用上下文路径Context Path问题。1. 仔细检查启动命令中的-u或-uX参数。2. 使用Burp Suite或curl手动构造请求确保包含正确的校验头。3. 如果目标应用部署在http://ip:port/myapp下那么内存马完整路径是/myapp/agenstR。Agenst注入的是根上下文需注意这点。冰蝎客户端连接失败连接中断、解密错误1. 密码不匹配。2. 版本不兼容Agenst仅支持冰蝎4.1。3. 网络问题或WAF拦截。1. 确认使用的密码是默认的rebeyond或你修改后的密码。2. 确保使用冰蝎4.1客户端。3. 尝试在简单网络环境下测试排除网络设备干扰。注入后导致目标Web应用崩溃或功能异常1. Agent代码存在Bug与目标应用类冲突。2. 字节码转换影响了关键类。3. 内存马逻辑有误抛出未处理异常。1. 立即停止Agent进程。2. 重启目标Web应用。3. 在测试环境充分验证后再用于重要环境。这是一个高风险操作务必谨慎。自定义内存马(/agenstE)POST数据后无反应1. 上传的Base64数据格式错误或解码失败。2. 生成的字节码不兼容当前JVM版本。3. 类加载或实例化过程抛出异常。1. 检查Base64编码是否正确是否包含换行符。2. 确认JMG生成内存马时选择的JVM版本与目标一致。3. 查看Agenst进程的输出日志或目标应用日志寻找异常堆栈。6.2 从防御角度看Agenst的检测了解攻击工具才能更好地防御。作为蓝队或安全运维可以从以下维度检测此类内存马异常进程检查服务器上是否存在陌生的、长期运行的java -jar进程其Jar包名称可疑。异常网络连接虽然内存马无文件但网络连接是实在的。监控Web进程如java/tomcat是否存在与异常外网IP的长期HTTP连接。JVM Attach审计在JVM启动参数中加入-XX:DisableAttachMechanism可以禁用Attach功能但可能影响监控工具。监控系统日志查找关于VirtualMachine.attach的调用记录需要审计JVM安全事件。运行时内存检测使用命令jcmd PID VM.classloader_stats可以查看由自定义类加载器加载的类。寻找可疑的类名如包含Filter,Shell,Agent等关键词。使用工具阿里巴巴开源的Arthas、Scanner等工具可以扫描JVM中已加载的类特别是动态生成的类查找可疑的Filter、Servlet或Controller。流量特征检测冰蝎早期版本有固定的HTTP头如Accept: text/html, image/gif, image/jpeg, *; q.2, */*; q.2和加密参数特征。虽然4.x版本可定制但加密流量的随机性、请求/响应时间关联性仍有别于正常业务流量。Neo-reg通常有固定的URL路径和密码参数-k。代理流量虽然加密但连接模式长时间连接、双向流量可能与普通HTTP请求不同。自定义校验头如果攻击者使用了自定义校验头在WAF或流量日志中寻找那些带有不常见请求头如Referer值固定为某个特定字符串却访问类似/actuator/health这种看似正常路径的请求是一个检测思路。RASP运行时应用自保护部署RASP agent可以实时监控并拦截危险的运行时行为例如动态添加Filter/Servlet。defineClass等自定义类加载操作。执行系统命令、反射调用危险方法等。6.3 使用中的安全与合规警告最后必须强调Agenst及同类工具是双刃剑。仅用于授权测试绝对只能在你自己拥有完全所有权和控制权的环境如自己的测试服务器、明确获得书面授权的渗透测试目标中使用。遵守法律法规未经授权对他人系统使用此类工具是明确的违法行为将面临严重的法律后果。风险自知该工具会修改目标JVM内存可能导致应用不稳定、崩溃或数据损坏。在生产环境使用前必须在隔离的测试环境进行充分验证。及时清理测试完成后务必重启目标Web应用服务器以彻底清除内存中的恶意代码。因为内存马在JVM重启后会自动消失这是最彻底的清理方式。