jenkins 无限制 rce 分析
字数 1275 2025-08-29 08:31:47
Jenkins 无限制 RCE 漏洞分析
漏洞概述
本文详细分析了 Jenkins 中一个无限制远程代码执行漏洞的利用链,该漏洞结合了 CVE-2018-1000861(动态路由越权调用)和 CVE-2019-1003000(Groovy 沙箱绕过)两个漏洞,最终实现了无需任何权限的远程代码执行。
漏洞背景
- Jenkins 在 2018-12-05 公布了一个动态路由越权调用漏洞(CVE-2018-1000861)
- 2019-01-08 公布了 Pipeline 插件中 Groovy 沙箱绕过漏洞(CVE-2019-1003000)
- 结合这两个漏洞可以实现无限制的 RCE
技术分析
1. Stapler 动态路由机制
Jenkins 使用 Stapler 框架处理 HTTP 请求,其核心路由机制如下:
- 根据 URL 路径逐级解析路由节点
- 对每个节点,在继承家族树中查找匹配 11 种规则的函数
- 匹配规则包括:getXxx、doXxx、jsXxx 等开头的函数
- 通过反射调用匹配的函数,并将返回值作为下一个节点
2. 路由访问限制绕过
Jenkins 实现了 StaplerProxy 接口,会检查 READ 权限:
public Object getTarget() {
if (!hasPermission(READ)) {
throw new AccessDeniedException("需要整体/读取权限");
}
return this;
}
但存在白名单路径 /securityRealm 可以绕过权限检查:
private static final Set<String> ALWAYS_READABLE_PATHS = ImmutableSet.of(
"/login", "/logout", "/accessDenied", "/adjuncts/", "/error", "/oops",
"/signup", "/tcpSlaveAgentListener", "/federatedLoginService/",
"/securityRealm", "/instance-identity"
);
3. 利用链构建
完整的利用链如下:
- 访问
/securityRealm(白名单路径) - 返回
HudsonPrivateSecurityRealm类 - 调用
getUser方法,返回User类 User类实现DescriptorByNameOwner接口- 调用
getDescriptorByName方法,返回Jenkins类 - 最终获取任意
Descriptor实例
4. 寻找 RCE 点
通过分析官方补丁,发现 CpsFlowDefinition$DescriptorImpl 是关键点:
public class CpsFlowDefinition extends FlowDefinition {
public static class DescriptorImpl extends FlowDefinitionDescriptor {
public FormValidation doCheckScript(@QueryParameter String value) {
// 处理 Groovy 脚本
}
}
}
5. Groovy 沙箱绕过
虽然 Pipeline 插件使用了 Groovy 沙箱,但存在多种绕过方式:
- 使用 Grab 注解:
@Grab('org.apache.commons:commons-collections4:4.0')
import org.apache.commons.collections4.functors.*
- 使用 ASTTest 注解:
def x = 1
@ASTTest(phase=INSTRUCTION_SELECTION, value={
assert System.exit(0); null
})
def y = 2
漏洞复现步骤
- 通过
/securityRealm/user/[任意用户名]绕过权限检查 - 调用
getDescriptorByName获取CpsFlowDefinition$DescriptorImpl - 调用
doCheckScript方法并传入恶意 Groovy 脚本 - 使用 Grab 或 ASTTest 注解绕过沙箱限制
- 实现任意代码执行
防御措施
- 升级 Jenkins 到最新版本
- 限制匿名用户权限
- 禁用不必要的插件
- 监控异常 Groovy 脚本执行