Rundeck 实战:Java Agent 插件后门植入全流程
主要讲述拥有rundeck管理员权限编写并安装rundeck的JAVA插件后门以及Demo
Begin
最近碰到一个rundeck的环境,拥有管理员权限,只开放了4430端口,由于rundeck是通过java -jar 启动的,常规的写文件并没有用处,于是想到通过自编插件的方式用java agent劫持路由的方式来注入webshell以及其他的功能。本文章代码同步在Github上Rundeck-backdoor-plugin
Principle
使用Java agent注入测试,看是否能劫持到路由
根据观察,发现rundeck的插件功能和rundeck服务功能执行的用户权限都是rundeck用户权限,这就具备了前置注入条件了,省的权限不一导致我们的java agent没有权限读到rundeck web服务的JVM从而导致注入失败!
劫持哪个路由?需要做什么功能?
Rundeck 是一个基于 Java 开发的运维自动化与作业调度平台,采用 Grails 框架(基于 Groovy 和 Spring)构建 Web 层,并运行在内嵌的 Jetty 容器中。
所以不同于tomcat,我们要对Grails进行注入并且劫持路由。从功能完整性来看,我们需要找到不用认证就能访问的路由。从Rundeck的代码可以看到,不需要经过认证的路由如下路由配置,由SpringSecurity做的安全访问.
我们主要关注如下几个无需认证的路由:
1 | /static/** |
劫持这几个路由即可实现无需登录的访问入口,计划实现的功能有三个:哥斯拉/冰蝎 WEBSHELL、正向代理(suo5)、劫持登录路由记录明文账号密码,这三项已完全满足实战需求。
完整功能实现
1. 测试 Java Agent 注入 JVM
AttachAgent.java
1 | package com.test.agent; |
HelloAgent.java
1 | package com.test.agent; |
2. 使用 Java Agent 注入 WebShell Servlet
由于Grails框架跟spring稍微不一样,获取到ServletContext多了一个步骤,所以需要调试Grails框架的代码获取ServletContext,调用链如下:
1 | Holders.getGrailsApplication() // → GrailsApplication(Grails 应用核心对象) |
拿到ContextHandler之后,手法就跟我们常规的一样了,直接动态代理注册Servlet就好了。
1 | package com.test.agent; |
3. 添加完整功能(suo5,Godzilla),确保功能正常
1. 添加Godzila注入
Godzila.java
1 | package com.test.agent; |
HelloAgent.java
1 | public static void agentmain(String args,Instrumentation inst){ |
2. 添加suo5正向代理
suo5也是常规的套路,使用defineClass字节码动态加载,把suo5.class先base64再调用defineClass加载即可,这里不贴代码,成品在github仓库中。
编写Rundeck 插件 && 正式环境注入插件
根据Rundeck官网的开发者文档我们可以知道实现插件的要素
| 项目类型 | 普通 JAR | Rundeck Plugin JAR |
|---|---|---|
| 入口点 | main() 方法 |
executeStep() 方法 |
| 必需接口 | 无 | StepPlugin 接口 |
| 必需注解 | 无 | @Plugin @PluginDescription |
| MANIFEST.MF | 普通清单 | 必需 Rundeck-Plugin-* 条目 |
| 依赖范围 | compile |
provided |
| 生命周期 | JVM 启动执行 | Rundeck 启动加载,作业执行时调用 |
| ClassLoader | AppClassLoader | Rundeck Plugin ClassLoader |
必需的 3 个要素
- 实现接口:
implements StepPlugin - 添加注解:
@Plugin@PluginDescription - 配置 MANIFEST:
Rundeck-Plugin-Classnames
编译好插件之后,上传插件
激活插件
- 创建一个新的job
- 在workflow选择安装好的插件
- 激活
节点选择 “Excute locally”或者选择Dispatch to Nodes,把rundeck server给包含进来即可激活
验证插件运行正常
Ending
整个后门植入链路:Rundeck 插件 → Java Agent 注入 JVM → 动态注册 Servlet → 无需登录访问 WebShell / 代理,在拥有 Rundeck 管理员权限的场景下完整走通,三项功能(RCE WebShell、哥斯拉、suo5 代理)均验证正常。
再而把Java Agent转换成适用于rundeck的插件jar包,即可搞定了。
需要注意的是,/static/ 路由是 Rundeck 的静态资源路径,注入后的 Servlet 挂在该路径下不会触发 SpringSecurity 认证拦截,天然绕过了登录校验。整个过程的关键难点在于理解 Grails + Jetty 的 ServletContext 获取链路,与常规 Tomcat 场景有所不同,需要通过 Holders.getGrailsApplication() 一路向下拿到 ContextHandler。