对于针对持续集成和持续交付/持续部署(CI/CD)通道的网络攻击,攻击者和防御者都越来越明白,构建通道是具有重大攻击面的高权限目标。
但是CI/CD通道的潜在攻击面是什么呢?这种类型的攻击实际是什么样的呢?NCC 组织通过不同的安全评估发现了许多攻击路径,这些路径可能导致大型和小型企业的CI/CD通道遭到攻击。
在这篇文章中,我们通过展示可能的被利用通道上的许多不同类型的攻击,强调保护软件供应链的重要性。
Jenkins潜藏多个攻击面
从S3桶开始发起攻击
S3桶中常见的错误配置经常会导致整个DevOps环境的攻击。最初的攻击途径是通过一个web应用程序。该攻击方案的攻击流程包括:
Web应用-> S3桶上的目录列表->脚本文件中的硬编码Git凭据-> Git访问->使用相同的硬编码Git凭据访问Jenkins -> Dump凭据从Jenkins ->横向移动->游戏结束->事件->内部调查
NCC组织对一个面向网络的web应用程序进行了匿名访问的黑盒web应用程序评估。在测试开始时,在一个网站地图文件夹中发现了一个网站地图文件。sitemap文件夹是一个S3桶,启用了目录列表。在查看S3存储桶中的文件时,发现了一个bash shell脚本。经过仔细检查,我们发现了一个带有证书的硬编码的git命令。该证书允许NCC集团顾问作为受限用户访问Jenkins Master web登录界面,该界面只能在内部访问,而不能从互联网访问。在点击几下鼠标并在其中四处查看之后,他们能够切换到一个管理员帐户。凭借管理员权限,该顾问在脚本控制台中使用了 Groovy 单行代码,并转储了大约 200 个不同的凭据,例如 AWS 访问令牌、SAST/DAST 令牌、EC2 SSH 证书、Jenkins 用户和其他 Jenkins 凭据。评估结束时,客户与顾问密切合作进行事件响应以进行补救。
NCC 为客户提供了一份详细的报告,其中包含修复和强化步骤,其中一些推荐步骤如下:
1.删除S3的目录列表;
2.删除shell脚本文件和硬编码凭据;
3.删除允许任何拥有 GitHub 访问权限的人都可以访问 Jenkins 的连接;
4.安装和审查审计跟踪和作业配置历史插件;
5.在我们现场测试的情况下,不应该通过互联网访问Jenkins;
6.更改和降低Jenkins帐户拥有的权限;
7.为管理员帐户部署和使用MFA;
强化环境中的权限升级
顾问在不同评估中发现的另一条权限升级路径的步骤如下:
使用 SSO 凭据登录 -> 测试分离、锁定和记录的角色 -> 一个具有构建/重播代码执行的角色 -> 凭据转储
评估的目的是审查一个新实现的强化Jenkins环境,该环境包含使用最小权限原则创建的记录用户角色。Jenkins在非root用户下运行,最新版本的核心和插件,具有SSL认证和SSO,使用MFA进行登录。NCC 组织顾问每天访问一个具有特定角色的用户,并测试是否存在任何权限升级路径。
构建器角色也具有构建/重播权限,这允许使用修改过的脚本或额外的Groovy代码重播Pipeline构建,而不仅仅是构建作业的构建权限。这允许NCC 组织顾问运行Groovy代码并转储Jenkins用户的凭据和其他秘密。
插件中令人困惑的语言表达
最后一个途径是在Jenkins插件中发现的一个令人困惑的选项,它导致了完全开放的访问:
GitHub 授权 -> 具有读取权限的经过身份验证的 Git 用户 -> 使用 Gmail 帐户的 Jenkins 访问权限
GitHub OAuth 插件部署在 Jenkins 中,提供身份验证和授权。勾选了“向所有经过身份验证的用户授予读取权限”和“使用 GitHub 存储库权限”选项,允许任何拥有 GitHub 帐户的人(甚至是外部用户)访问 Jenkins Web 登录 UI。 NCC 能够注册并使用他们自己的托管电子邮件帐户来访问他们的项目。
GitLab CI/CD通道攻击
NCC集团已经做了很多工作,研究了另一个众所周知的被使用的工具GitLab。因此,NCC 组织顾问发现了一些有趣的攻击路径。
利用受保护的分支
在一项特定的工作中,GitLab runner的设置存在多个主要漏洞。第一个主要漏洞是,runner使用了权限容器,这意味着它们被配置为使用“-privileged”标志,这将允许它们启动其他可以轻易逃逸到主机的特权容器。这是一个非常简单的攻击媒介,可以让你到达主机。但有趣的是,这些GitLab runner也共享runner,而不是孤立的。一位只应该将代码推送到某个存储库的开发人员也可以访问机密和高权限存储库。此外,这些共享运行程序使用的是存储高度敏感秘密(如身份验证令牌和密码)的普通环境变量。对存储库具有有限的推送访问权限的用户可以获得高度权限的机密。
受保护的分支是可以由GitLab中具有维护者角色的人维护的分支,只有这些人具有推送这些源代码存储库或分支的权限,并且有一个与之关联的更改请求 (CR) 链。这些受保护的分支可以与受保护的运行程序相关联。你可以将其锁定,因此开发人员必须获得 CR 批准才能推送代码。但在这种情况下,没有实施和执行 CR 和受保护的分支。任何人都可以推送到未受保护的分支,然后链接以前的漏洞利用。这 4-5 个漏洞的链接提供了所有访问权限。
也有很多不同的路径。即使使用了“-privileged”标志,也有另一条路径可以访问特权容器。操作人员需要能够运行docker命令。主机的docker守护进程与GitLab共享Runner共享。这导致了对主机的访问和在容器之间的切换。
顾问要求客户了解并帮助纠正这些问题,但很想了解导致作出这些选择的根本原因。他们为什么做出这些配置选择,他们考虑了哪些权衡?除了这些选择,还有什么更安全的选择呢?他们知道这些选项吗?如果知道,为什么不选择他们?
公司想要权限容器的原因是对被推送的代码进行静态分析,顾问解释说,他们应该使用隔离的runner,而不是使用共享的runner,并且应该有进一步的访问控制限制。要注意的是,可以运行权限容器,但仍然可以在一定程度上限制暴露的敏感信息的数量。
GitLab执行作业的许多CI/CD安全机制依赖于这样一个前提,即受保护的分支只包含受信任的构建作业和由项目维护者管理的内容。项目维护者权限级别或更高级别的用户可以委托其他用户管理和推送到特定的受保护分支。这些有权限的用户是表示项目中什么是可信的,什么是不可信的网关。进行这种区分对于缓解对不可信构建作业的权限暴露非常重要。
使用权限容器的GitLab runner
在另一个操作中,GitLab runner被配置为使用Docker的“—privileged”标志执行CI/CD作业。这个标志将否定Docker提供的任何安全隔离,以保护主机免受潜在的不安全容器的攻击。通过禁用这些安全功能,容器进程可以通过各种功能将其权限升级到主机上的root权限。一些工具被打包成Docker映像,为了支持这一点,客户端在Docker (DIND)中使用一个权限容器中的Docker来执行嵌套的容器。
如果有权限的CI/CD作业是必要的,那么相应的runner应该配置为只在已知合法的受保护的项目分支上执行。这将防止任意攻击人员提交可能导致主机被攻击的未经审查的脚本。维护者角色管理项目受保护分支的能力意味着他们控制由相关受保护的Runner提供的任何权限。
高权限共享Runner可能会请求与敏感环境变量和权限Kubernetes环境相关的工作
权限运行程序不应配置为共享运行程序或广泛作用域组,相反,它们应该根据特定项目或组的需要进行配置,这些项目或组被认为在维护者级别或更高级别的用户之间具有相同的权限级别。
Runner将秘密暴露给不可信的CI/CD作业
运行程序调用API终端,这些终端使用各种令牌和密码进行身份验证。因为这些是共享Runner,任何有权限向Gitlab提交源代码的用户都可以轻松地访问身份验证令牌和密码。Runner被配置为通过环境变量公开秘密。秘密管理,特别是CI/CD通道的秘密管理是一个难以解决的问题。
要缓解这些类型的风险,请确保由runner在所有构建作业中配置的环境变量不包含任何权限凭据。可以使用适当作用域的GitLab变量作为替换。环境变量应该只包含信息配置值,应该认为相关项目和组中的任何被利用人员都可以访问这些配置值。
如果 Runner 必须通过环境变量或挂载的卷为其作业提供凭据,那么Runner应该限制它们所暴露的工作负载。为此,此类 Runner应仅与最具体的可能项目/组相关联。此外,它们应该被标记为“受保护的”,以便它们只能处理受保护的分支上的作业。
主机Docker守护进程暴露给共享的GitLab Runner
在一次操作中,Gitlab共享Runner在运行时将主机的Docker套接字挂载到CI/CD作业容器中。虽然这允许合法的开发人员为了构建目的在主机上运行任意Docker命令,但它也允许构建作业在主机上部署权限容器,以逃避它们的约束。这也为攻击者提供了一个窗口,通过这个窗口他们可以攻击在主机上运行的其他构建作业。本质上,这否定了Docker所提供的所有隔离,阻止被包含的进程访问其他容器和主机。在这种情况下,建议采取以下缓解措施:
1.不允许开发人员直接与不受其控制的主机上的 Docker 守护进程交互。考虑使用支持无根 Docker 构建的工具(例如 kaniko)运行 Docker 构建作业。
2.开发一个进程,在源代码存储库上运行一组静态 Docker 命令来构建它们。此过程不应在 CI/CD 作业本身内执行,因为作业脚本是用户定义的,并且可以覆盖命令。
3.如果这必须通过 CI/CD 作业来实现,那么在这些 Runner上执行的构建作业应该被视为特权,因此应该限制 Docker Runner接受提交了保护和已知的安全存储库以确保任何用户定义的CI / CD工作已经通过了正式的审批程序。
Kubernetes
运行特定功能的pod有时会使用不同的pod身份验证机制,这些机制可以连接到各种服务,AWS凭据就是一个例子。很多时候,人们使用插件,并不限制插件周围的API路径。例如,Kube2IAM是一个经常看到的插件,如果你没有从pod中正确地配置它,你可以获得权限容器,它可以导致具有特权API凭据,让你看到底层主机正在做什么。
Kube2IAM
Kube2IAM 使用 pod 注释。它拦截来自容器 pod 对 AWS API (169254) 的调用。 NCC 组织的一位顾问发现了一个有趣的情况,每个开发人员都可以对 pod 进行注释。在 Kube2IAM 使用的 AWS 角色中,有一个使用“sts assume-role *”行配置的设置。这使得任何能够创建/注释pod的被利用人员都继承了AWS的管理员角色。这意味着任何可以创建任意pod 并指定注释的人都可以获得一家银行的AWS主工具帐户的管理权限。此帐户配置了 VPC 对等互连,可以查看任何 pod 和非 pod 环境。你可以使用该访问权限到达任何地方。这是一个构建 pod 的通道,攻击者所要做的就是在最后输出一些东西的注释中添加一个注释。
NCC组织的一位顾问还进行了另一项类似的工作,在这种情况下,他们无法对 pod 进行注释。相反,在 Kube2IAM 中有标记“白名单路由正则表达式”,你可以提及 AWS API 路径。你可以指定你想去或不去的路径,DevOps管理员已经将其配置为一个白色字符,允许某人访问权限路径,从而导致底层节点凭据。
利用笔记本电脑发起攻击
NCC 组织顾问进行了基于场景的评估——利用开发人员的笔记本电脑。
顾问所能做的就是将代码提交到使用 Maven 项目的单个 Java 库中。他们将一个前置需求文件设置为一个任意文件,该文件将提供来自构建环境的 shell。他们将其更改为反向 Meterpreter shell 有效负载。他们发现 pod 有一个 SSH 密钥位于磁盘上,该密钥进入 Jenkins 主节点,然后从 Jenkins 中转储了所有变量。然后他们发现这是一个真正的部署通道,它拥有写入权限和集群管理到Kubernetes工作负载中。因此,他们现在可以访问完整的生产环境。
在另一项操作中,NCC 组织顾问成功攻击了一个用户帐户,并访问了经过验证的通往被利用团队的通道。在通道中运行自定义代码是不可能的,但是他们可以告诉通道构建一个不同的分支,即使它不存在。通道崩溃并转储环境变量。其中一个环境变量是Windows域管理员帐户。在该攻击场景中,他们能够对基础设施进行端口扫描,最终形成一个构建通道。他们发现了许多功能未知的应用程序。其中一个应用程序容易受到服务器端请求伪造(SSRF)的攻击,它们运行在AWS EC2实例上。AWS节点能够编辑配置映射,允许AWS用户帐户和集群内角色之间的映射。结果,这并没有检查集群和帐户是否在同一个用户帐户中。因此,顾问可以指定另一个AWS帐户来控制集群,并在Elastic Kubernetes服务集群(EKS)上拥有管理权限。
本文翻译自:https://research.nccgroup.com/2022/01/13/10-real-world-stories-of-how-weve-compromised-ci-cd-pipelines/如若转载,请注明原文地址。