滥用计算实例IAM错误配置在GCP中获取权限
在上一篇文章中,我们学习了如何从配置错误的云函数中窃取访问令牌。本文将进一步探讨如何通过滥用新服务账户,利用计算实例IAM错误配置在GCP环境中获取额外权限。
演示步骤
我们将使用CyberWarFare Labs的Google Cloud红队专家(CGRTS)培训进行演示。这是一个很棒的培训项目,涵盖了许多真实场景。
目录
- 理解计算实例
- 计算实例的IAM策略变更
- IAM更新
- OAuth范围
- 所需工具
- 利用步骤
- 缓解策略
- 参考资料
- GCP渗透测试系列
理解计算实例
实例是托管在Google基础设施上的虚拟机(VM)。可以使用密码、SSH或RDP访问实例。公共SSH密钥可以通过实例元数据在实例级别管理,也可以通过项目元数据在项目级别管理。
另一种访问实例的方式是通过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角色。
图1:旧IAM策略
然而,GCP增加了新的安全措施,以防止在账户受损时直接使用此角色。
IAM更新
- 新项目中的默认服务账户不再默认授予roles/editor,而是授予更严格的角色,如roles/compute.serviceAgent,仅限访问必要的计算引擎操作。
- 如果默认服务账户具有Editor角色,新更新要求用户或其他服务账户具有模拟权限(iam.serviceAccounts.actAs)才能代表默认服务账户操作并利用Editor角色。
- 即使服务账户具有Editor角色,OAuth范围也决定了允许的API访问,进一步限制其权限。这意味着即使服务账户具有提升的(Editor)角色,如果分配给它的OAuth范围过于严格,它可能无法执行某些API操作。
图2:显示新策略更新
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命令:
|
|
在这种情况下,新账户在项目级别没有任何角色或权限分配。
图3:无角色分配
2. 枚举计算实例
由于我们针对计算实例,我们将使用compute instances list命令检查项目中的可用实例:
|
|
图4中的输出显示我们有一个(1)名为process-instance的可用实例。
图4:列出可用计算实例
下一步是获取此实例的元数据以收集更多信息,例如其IP地址、区域、状态和附加磁盘。我们将使用describe命令:
|
|
图5:Process-Instance元数据详细信息
查看元数据详细信息,我们发现实例附加了一个默认服务账户,如图6所示。
图6:默认服务账户
要检查默认服务账户302960820041-compute@developer.gserviceaccount.com的分配角色,运行以下命令:
|
|
图7:具有Editor角色的默认服务账户
尽管默认服务账户在项目级别具有Editor角色,但新更新限制了直接使用此角色,而没有模拟权限和OAuth范围限制。
我们还发现实例元数据中有一个私有SSH密钥。这是一个人为设计的场景,旨在演示元数据错误配置,在真实环境中并不总是如此。
无论如何,我们将私钥保存在一边,并继续我们的枚举。
图8:元数据中的私有SSH密钥
3. 枚举计算实例IAM策略
接下来,我们使用get-iam-policy命令枚举实例本身的IAM策略:
|
|
在图9中,我们看到一些有趣的东西:我们的账户instance-mgmt-srv-acc在实例上具有管理员权限,具有roles/compute.instanceAdmin.v1角色。
图9:IAM策略
您可能认为既然我们在实例上具有管理员权限,我们应该能够修改其元数据—例如添加我们自己的SSH公钥以进行访问。毕竟,预定义的compute.instanceAdmin.v1角色包括compute.instances.setMetadata权限。
然而,情况并非如此。尽管instance-mgmt-srv-acc具有roles/compute.instanceAdmin.v1,但我们仍然无法直接修改元数据,而没有模拟权限来充当附加到它的服务账户。
4. 枚举项目元数据
由于我们还没有匹配的公钥存储在实例上,仅私钥不足以进行身份验证。因此,我们继续枚举更多信息,以帮助我们访问实例。
我们应该始终检查的一个关键事项是项目范围的元数据,它可能包含授予项目范围内实例访问权限的SSH密钥。为此,我们运行以下命令:
|
|
查看项目元数据,我们发现与用户developer关联的公共SSH密钥。由于我们已经有一个附加到实例的私钥,现在又有这个公钥,值得尝试以developer身份SSH到实例。
图10:项目元数据上设置的SSH公钥
5. 使用SSH访问实例
创建一个文件并使用以下格式粘贴SSH私钥,确保没有额外的空格或换行符。
|
|
将文件权限设置为600(只有所有者可以读写文件),然后SSH到实例。
|
|
似乎无法SSH访问实例。由于连接挂起,一个可能的原因是防火墙规则阻止了SSH流量。
图11:SSH连接被拒绝
5. 枚举防火墙规则
使用以下命令检查防火墙规则:
|
|
如图12所示,除两个(2)自定义规则—fw-allow-internal-staging-vpc和fw-allow-public-staging-vpc外,所有规则都是默认的。
图12:防火墙规则
要获取这些规则的更多详细信息,我们以JSON格式检索防火墙规则以进行结构化输出:
|
|
图13:详细防火墙规则
查看自定义规则fw-allow-public-staging-vpc,我们看到它允许从任何源(0.0.0.0/0)到具有网络标签ssh的实例的SSH流量。
在GCP中,防火墙规则不直接针对实例。相反,它们应用于具有特定网络标签的实例,允许进行细粒度访问控制。因此,在我们的案例中,向VM添加ssh网络标签将允许其接收SSH流量。
图14:网络标签-SSH
6. 向实例添加标签
由于我们已将计算实例管理员(roles/compute.instanceAdmin.v1)角色分配给instant-mgmt-srv-acc账户,我们可以向实例添加网络标签,以查看是否允许我们通过SSH访问它。
在继续之前,我们需要获取新的访问令牌,如上一篇文章所述—访问令牌在一(1)小时后过期。一旦我们获得新令牌,我们将运行以下命令添加标签:
|
|
图15:添加网络标签命令
要验证标签是否成功添加,我们可以再次运行describe命令以检查实例详细信息:
|
|
如图16所示,SSH标签已成功添加到实例。
图16:新标签(SSH)添加到实例元数据
7. 使用SSH访问实例
再次尝试使用私钥SSH,这次连接没有挂起。我能够使用实例元数据中设置的私钥成功访问process-instance。
|
|
图17:SSH访问实例
在之前的步骤中,我们看到默认服务账户(302960820041-compute@developer.gserviceaccount.com)附加到实例。要确认这一点,我们可以运行以下命令:
|
|
图18:默认服务账户
到目前为止,我们知道默认服务账户在附加到实例时也受OAuth范围限制。我们需要检查OAuth范围以确定我们对实例具有哪些API权限。
为此,我们将向实例元数据服务器发送请求以检索分配的OAuth范围。
|
|
如图19所示,OAuth范围包括devstorage.read_only权限,允许从项目中的存储桶读取和下载内容。
图19:OAuth范围
使用默认服务账户,我们能够将权限从instance-mgmt-srv-acc(无分配角色)提升到读取存储桶。
我们还可以通过元数据检索账户令牌,使用以下请求:
|
|
图20:默认服务账户令牌
8. 读取存储桶
从这里,我们可以检查存储桶,看看是否能找到任何有趣的文件或数据,例如API密钥、源代码中的硬编码密码、敏感配置文件或数据库凭据。
要与存储桶交互,我们可以在实例上使用gsutil命令列出所有可用桶。
|
|
图21:列出可用桶
我们可以使用以下命令查看和下载特定桶的内容。
|
|
图22:查看和下载存储桶内容
缓解策略
- 禁用项目和实例元数据向任何用户公开。确保只有授权用户可以查看元数据信息。
- 切勿在实例元数据中存储私钥。相反,使用Secret Manager安全存储敏感凭据。
- 通过移除compute.instances.setTags权限进一步限制访问,以防止用户(包括具有管理员角色的用户)添加网络标签。相反,仅将此权限授予需要它的特定用户。
在本文中,我们演示了攻击者如何通过利用计算实例IAM错误配置、元数据暴露和网络标签操作在GCP中提升权限。通过利用最初受损的服务账户,我们从无分配角色的账户逐步升级到访问和下载项目存储桶中的数据。
今天就到这里,感谢阅读!
参考资料
- Google Cloud红队专家[CGRTS]
- IAM基本和预定义角色参考
- HackTricks Cloud — GCP渗透测试
- GCP渗透测试系列
- 使用错误配置的IAM策略窃取云函数访问令牌[GCP]
- 滥用计算实例IAM错误配置在GCP中获取权限
- GCP权限提升:滥用Function Admin角色进行完整项目接管
- CloudHacking
- 渗透测试
- Infosec