利用GCP计算实例IAM配置错误实现权限提升

本文详细介绍了如何通过滥用GCP计算实例的IAM配置错误来获取额外权限。从初始服务账户入手,通过枚举实例元数据、操作网络标签和利用OAuth范围限制,逐步实现从无权限账户到读取存储桶数据的完整攻击链。

滥用计算实例IAM配置错误在GCP中获取权限

理解计算实例

实例是托管在Google基础设施上的虚拟机(VM)。可以通过密码、SSH或RDP访问实例。公共SSH密钥可以通过实例元数据在实例级别管理,也可以通过项目元数据在项目级别管理。

另一种访问实例的方式是通过OS Login功能。启用后,OS Login允许具有(roles/compute.osLogin或roles/compute.osAdminLogin)角色的用户直接登录,无需SSH密钥或密码。相反,身份验证通过IAM策略处理。

以下是分配给与计算实例交互的账户的常见IAM角色:

角色

  • 对项目中所有计算引擎实例的完全访问权限:roles/compute.admin
  • 对单个实例的完全访问权限:roles/compute.instanceAdmin.v1
  • 对实例的只读访问权限:roles/compute.viewer
  • 使用OS Login SSH进入实例:roles/compute.osLogin
  • 使用OS Login以root权限SSH进入VM:roles/compute.osAdminLogin

计算实例的IAM策略变更

以前,在GCP中创建新实例时,通常会分配遵循([PROJECT_NUMBER]-compute@developer.gserviceaccount.com)格式的默认服务账户。此默认服务账户在项目级别具有Editor角色。

然而,GCP增加了新的安全措施,以防止在账户受损时直接使用此角色。

IAM更新

  • 新项目中的默认服务账户不再默认授予roles/editor,而是授予更严格的角色,如roles/compute.serviceAgent,仅限访问必要的计算引擎操作。
  • 如果默认服务账户具有Editor角色,新更新要求用户或其他服务账户具有模拟权限(iam.serviceAccounts.actAs)才能代表默认服务账户操作并利用Editor角色。
  • 即使服务账户具有Editor角色,OAuth范围也决定了允许的API访问,进一步限制其权限。这意味着即使服务账户具有提升的(Editor)角色,如果分配给它的OAuth范围过于严格,它可能无法执行某些API操作。

OAuth范围

创建新实例时,常见的OAuth范围包括:

  • 对所有启用的云服务的完全访问—https://www.googleapis.com/auth/cloud-platform
  • 对计算引擎资源的完全访问—https://www.googleapis.com/auth/compute
  • 对存储桶的只读访问—https://www.googleapis.com/auth/devstorage.read_only

所需工具

对于本实验,我们需要在测试机器上安装gcloud CLI。您可以直接从Google Cloud安装它,或设置RedCloud-OS,这是由CWL组装的VMware机器,预装了云渗透测试评估(GCP、Azure、AWS)所需的所有必要工具。

安装选项

  • 安装gcloud CLI | Google Cloud CLI文档
  • RedCloud-OS | GitHub

利用步骤

在本实验场景中,我们基于上一篇文章(通过错误配置的IAM策略窃取云函数访问令牌[GCP])。我们获得了一个名为instance-mgmt-srv-acc的新服务账户的访问令牌。此账户在项目级别未分配任何角色或权限,但我们将使用它在GCP环境中获取更多权限。

在接下来的步骤中,我们将展示如何通过实例级别的IAM配置错误获取更多权限。

1. 检查新访问账户的IAM策略

与往常一样,每当您获得新账户的访问权限时,您需要检查其IAM策略以查看在项目级别分配了哪些角色和权限。为此,运行以下gcloud get-iam-policy命令:

1
gcloud projects get-iam-policy [PROECT NAME] --flatten="bindings[].members" --filter="bindings.members=serviceaccount:SERVICE ACCOUNT" --format="value(bindings.role)"

在这种情况下,新账户在项目级别未分配任何角色或权限。

2. 枚举计算实例

由于我们针对计算实例,我们将使用compute instances list命令检查项目中的可用实例:

1
gcloud compute instances list

图4中的输出显示我们有一个(1)可用的实例名为process-instance。

下一步是获取此实例的元数据以收集更多信息,例如其IP地址、区域、状态和附加磁盘。我们将使用describe命令:

1
gcloud compute instances describe [INSTANCE_NAME] --zone=[ZONE]

查看元数据详情,我们发现实例附加了一个默认服务账户,如图6所示。

要检查默认服务账户302960820041-compute@developer.gserviceaccount.com的分配角色,运行以下命令:

1
gcloud projects get-iam-policy [PROECT NAME] --flatten="bindings[].members" --filter="bindings.members=serviceaccount:SERVICE ACCOUNT" --format="value(bindings.role)"

即使默认服务账户在项目级别具有Editor角色,新更新限制了在没有模拟权限和OAuth范围限制的情况下直接使用此角色。

另一个有趣的事情是我们在实例元数据中发现了一个私有SSH密钥。这是一个人为设计的场景,旨在演示元数据配置错误,在现实环境中并不总是如此。

无论如何,我们将私钥保存在一边,并继续我们的枚举。

3. 枚举计算实例IAM策略

接下来,我们使用get-iam-policy命令枚举实例本身的IAM策略:

1
gcloud compute instances get-iam-policy [INSTANCE NAME] --zone [ZONE NAME]

在图9中,我们看到一些有趣的东西:我们的账户instance-mgmt-srv-acc在实例上具有admin权限,拥有roles/compute.instanceAdmin.v1角色。

您可能认为既然我们在实例上具有admin权限,我们应该能够修改其元数据—例如添加我们自己的SSH公钥以进行访问。毕竟,预定义的compute.instanceAdmin.v1角色包括compute.instances.setMetadata权限。

然而,情况并非如此。即使instance-mgmt-srv-acc具有roles/compute.instanceAdmin.v1,我们仍然无法直接修改元数据,除非具有模拟权限以作为附加到它的服务账户操作。

4. 枚举项目元数据

由于我们还没有匹配的公钥存储在实例上,仅私钥不足以进行身份验证。因此,我们继续枚举更多可能帮助我们访问实例的信息。

我们应该始终检查的一件事是项目范围的元数据,它可能包含授予跨项目实例访问权限的SSH密钥。为此,我们运行以下命令:

1
gcloud compute project-info describe --project [PROJECT NAME]

查看项目元数据,我们发现了一个与用户developer关联的公共SSH密钥。由于我们已经有一个附加到实例的私钥和现在的这个公钥,值得尝试以developer身份SSH进入实例。

5. 使用SSH访问实例

创建一个文件并使用以下格式粘贴SSH私钥,确保没有额外的空格或换行。

1
2
3
-----BEGIN OPENSSH PRIVATE KEY-----
(base64-encoded data)
-----END OPENSSH PRIVATE KEY-----

将文件权限设置为600(只有所有者可以读写文件),然后SSH到实例。

1
2
chmod 600 [private-ssh-key]
ssh -i private-ssh-key developer@IP_ADDRESS

似乎无法SSH访问实例。由于连接挂起,一个可能的原因可能是防火墙规则阻止了SSH流量。

5. 枚举防火墙规则

使用以下命令检查防火墙规则:

1
cloud compute firewall-rules list

如图12所示,除两个(2)自定义规则—fw-allow-internal-staging-vpc和fw-allow-public-staging-vpc外,所有规则都是默认的。

为了获取这些规则的更多详情,我们以JSON格式检索防火墙规则以获取结构化输出:

1
gcloud compute firewall-rules list --format=json

查看自定义规则fw-allow-public-staging-vpc,我们看到它允许从任何源(0.0.0.0/0)到带有网络标签ssh的实例的SSH流量。

在GCP中,防火墙规则不直接针对实例。相反,它们应用于具有特定网络标签的实例,允许细粒度访问控制。所以在我们的案例中,将ssh网络标签添加到VM将允许它接收SSH流量。

6. 向实例添加标签

由于我们已将计算实例admin(roles/compute.instanceAdmin.v1)角色分配给instant-mgmt-srv-acc账户,我们可以向实例添加网络标签,看看是否允许我们通过SSH访问它。

在继续之前,我们需要获取一个新的访问令牌,如上一篇文章中提到的—访问令牌在一(1)小时后过期。一旦我们获得新令牌,我们将运行以下命令添加标签:

1
gcloud compute instances add-tags [INSTANCE NAME] --zone [ZONE NAME] --tags=[TAG NAME] --access-token-file=[FILE NAME]

为了验证标签是否成功添加,我们可以再次运行describe命令检查实例详情:

1
gcloud compute instances describe [INSTANCE NAME]

如图16所示,SSH标签已成功添加到实例。

7. 使用SSH访问实例

再次尝试使用私钥SSH,这次连接没有挂起。我能够成功使用实例元数据中设置的私钥访问process-instance。

1
ssh -i private-ssh-key developer@IP_ADDRESS

在之前的步骤中,我们看到默认服务账户(302960820041-compute@developer.gserviceaccount.com)附加到实例。为了确认这一点,我们可以运行以下命令:

1
gcloud auth list

到目前为止,我们知道默认服务账户在附加到实例时也受OAuth范围限制。我们需要检查OAuth范围以确定我们对实例具有哪些API权限。

为此,我们将向实例元数据服务器发送请求以检索分配的OAuth范围。

1
curl -H "Metadata-Flavor:Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/[SERVICE ACCOUNT]/scopes

如图19所示,OAuth范围包括devstorage.read_only权限,允许从项目中的存储桶读取和下载内容。

使用默认服务账户,我们能够将权限从instance-mgmt-srv-acc(未分配任何角色)提升到读取存储桶。

我们还可以通过元数据检索账户令牌,使用以下请求:

1
curl -H "Metadata-Flavor:Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/[SERVICE ACCOUNT]/token

8. 读取存储桶

从这里我们可以检查存储桶,看看是否能找到任何有趣的文件或数据,例如API密钥、源代码中的硬编码密码、敏感配置文件或数据库凭据。

要与存储桶交互,我们可以使用实例上的gsutil命令列出所有可用的存储桶。

1
gsutil ls

我们可以使用以下命令查看和下载特定存储桶的内容。

1
2
gsutil ls gs://[BUCKET NAME]
gsutil cp gs://[BUCKET NAME]/[FILE NAME] [LOCAL_PATH]

缓解策略

  • 禁用项目和实例元数据向任何用户公开。确保只有授权用户可以查看元数据信息。
  • 切勿在实例元数据中存储私钥。相反,使用Secret Manager安全存储敏感凭据。
  • 通过移除compute.instances.setTags权限进一步限制访问,以防止用户(包括具有admin角色的用户)添加网络标签。相反,仅将此权限授予需要它的特定用户。

在本文中,我们演示了攻击者如何通过利用计算实例IAM配置错误、元数据暴露和网络标签操作在GCP中提升权限。通过利用最初受损的服务账户,我们从未分配任何角色的账户升级到获得访问权限并从项目存储桶下载数据。

今天就到这里,感谢阅读!

参考文献

  • Google Cloud Red Team Specialist [CGRTS]
  • IAM基本和预定义角色参考
  • HackTricks Cloud — GCP渗透测试
  • GCP渗透测试系列
  • 通过错误配置的IAM策略窃取云函数访问令牌[GCP]
  • 滥用计算实例IAM配置错误在GCP中获取权限
  • GCP权限提升:滥用Function Admin角色实现完全项目接管
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计