洞察策略风险,用自然语言提问策略问题。 申请演示 →

Published:

防护 AWS ExternalID 与跨账户 AssumeRole 访问的高级技术

by FireMon

上个月,Praetorian Security 的 Kesten Broughton 发布了一项出色的研究,针对使用 Amazon 推荐的跨账户连接技术的第三方云安全产品 — 在众多头部厂商中发现的 AWS IAM Assume Role 漏洞。开篇段落对这项研究作了扎实的概述:

在这个跨账户信任系列的第一篇博客中,我们将展示对 90 家厂商的调查结果:其中 37% 未正确实施 ExternalId 以防范混淆代理(confused-deputy)攻击。另有 15% 的厂商虽然在 UI 中正确实现了 AWS 账户集成,但后端未对 ExternalId 参数进行妥善验证,导致这些站点同样存在风险。最后,我们将讨论 AWS 跨账户 assume-role 信任所暴露的新攻击面。我们的结论是,厂商和客户应当审慎评估角色信任是否是其多租户 SaaS 解决方案的最佳信任机制。

我读到这项研究时的第一反应是:“怎么会有人做出如此糟糕的决定”,但现实情况是,“云安全专家”这一概念本身还相对较新;虽然 AWS 对混淆代理问题有不错的阐述,但并未涵盖某些实际落地中的问题 — 这些问题在产品真正对接客户之前,往往不会被想到。使用 AssumeRole 的跨账户连接有着直截了当的工程解决方案,但若缺乏适当的威胁建模,就极易犯下 Kesten 研究中记录的那些错误。

在 DisruptOps,我们早期基于初始威胁建模做出了一些明智的决策,因而得以规避风险。不过,我们也从这项研究中汲取了一些要点,并正在进一步加固。此外,我们还有一个秘密研发项目,可在很大程度上消除该问题,同时在无需对客户环境拥有直接写入权限的情况下实现大规模自动化。

不过,我对这篇文章有一处重大分歧。相较于使用静态凭证加保险库(vaulting)的建议,我任何时候都更倾向于自动轮换的凭证。对于其他云服务商,我们仍不得不使用静态凭证,并且一直在寻找在我们环境中任何位置都不保留静态凭证的方法。

问题所在

如今,各类应用程序都需要直接连接到云 API。可能简单到访问一个 S3 存储桶,也可能复杂到一个全自动的云检测与响应平台(当然,这只是随便举个例子)。这些请求需要某种凭证,而凭证要么是静态的(如用户名和密码,或 IAM 访问密钥和私有密钥),要么是动态的。动态凭证包含令牌或其他具有时效性的临时属性。

允许直接访问您的云管理平面的静态应用凭证……很糟糕。对于用户,我们可以通过 MFA 解决这一问题,但这对自动化应用程序(例如我们那个并非纯属理论的云检测与响应平台)毫无帮助。不久前,Amazon Web Services 以 IAM 角色的概念应对了这一问题。AWS 中的角色本质上是一个权限容器,包含两项策略:该容器可以执行什么操作,以及谁(或什么)可以担任该角色。角色是基于会话的,因此当获得授权的实体担任该角色时,会获得一组凭证(访问密钥、私有密钥和会话令牌),可在会话期限内(1 至 24 小时)使用。

AWS 中的角色可通过外部 SAML 连接(面向用户)、内部“受信任”连接(来自其他 AWS 账户)或 AWS 服务(如 EC2 实例或 Lambda 函数)来担任。因此,您可以做一些很棒的事情,比如在实例中运行代码,而该实例从不存储静态凭证。这确实省去了大量麻烦。

允许来自您无法控制的账户的连接则有所不同;尤其当该账户是一个服务于多个客户的平台时。此类平台(好吧,就是我们)需要访问成百上千个其他 AWS 账户。设想一下,如果攻击者能够诱使平台在错误的账户中执行某项操作,例如对当前用户并不拥有的账户执行配置评估。这就是混淆代理问题的简要版本 — 代理受到多个账户的信任,而用户诱使代理向其授予对当前用户本不应触及的账户的访问权限。

最具现实可行性的利用方式是:平台允许用户输入一个自己并不控制的账户 ID,将其添加到个人配置中,然后滥用平台所获得的信任。

AWS 提供了一种防御机制,称为 AWS ExternalID。它是平台提供商与客户通过带外方式交换的任意共享密钥。该 ID 会在任何担任客户账户中角色的请求里作为属性传递,而该账户中的角色信任策略设有条件要求,用于检查该共享密钥是否正确。正确实施后,这意味着任何人都无法把自己并不控制的账户添加到代理中,因为攻击者无法在客户账户中设定或得知该共享密钥。

除非……

您已经猜到接下来会怎样。Praetorian 发现大量提供商使用默认的 AWS ExternalId,或允许客户为整个账户设置非唯一的 ExternalId 值。他们还发现有些产品根本不验证客户是否在其角色信任策略中强制使用了 External ID。Praetorian 找到了可被实际利用的平台,由于跨账户角色的安全措施薄弱,这些平台容易遭受混淆代理攻击。

加固跨账户连接

这些都属于我们在 DisruptOps 进行威胁建模时考虑过的内容,因此我们的状况良好,不过我们也获得了一两个可以进一步改进的思路。让我们逐一梳理 Praetorian 指出的每个问题,并看看最佳的安全加固方案:

  • AWS ExternalID 使用默认设置: 我们使用随机的 ExternalID。
  • AWS ExternalID 在多个客户账户之间共用:我们按账户而非按客户使用随机的 ExternalID。
  • AWS ExternalID 可被枚举或猜测: 我们使用完全随机且足够长的 AWS ExternalID。得益于 API 速率限制,我并不担心我们的 PRNG 选择(这句是说给密码学爱好者听的)。
  • 客户可以自行设置 ExternalID,可能重复使用或使用强度不足的 ExternalID: 我们不支持客户自行设置 ExternalID,尽管确实收到过此类需求。我认为其他一些提供商正是在这一点上出了问题。客户希望具备这项能力,以便自行实现产品的自动化预配;但要安全地做到这一点,我们需要验证 ExternalID 是否满足长度和随机性要求。只要验证得当,这大概是可以安全实现的。
  • 平台允许同一个 AWS 账户被多次预配: 我们不允许这样做。
  • IAM 角色未限定为提供商账户中的单一实体,而是对该账户中的任何角色开放: 本文尚未讨论这一点,但您既可以允许该账户中的任何角色访问客户账户中的“工作”角色,也可以只授予单个资源或角色访问权限。我们将访问权限限定在专门用于跨账户访问的特定角色上。
  • IAM 角色名称在所有客户账户中都相同: 这里的风险实质上是用户名(角色名)可被猜测。除非还存在其他非常基础性的错误,否则我完全不认为这是一项风险。举个例子:如果攻击者知道角色名称,并且获得了对客户账户的访问权限,他们有可能将自己添加到角色信任策略中,进而使用这些权限(我在事件响应培训课程中就讲授过类似内容)。这是一种潜在风险,但我们对其评级相当低。如果攻击者已具备那种级别的访问权限,他们很可能可以接管该账户中的任何角色。
  • 提供商未验证 ExternalID 是否被设置为访问条件: 我们为客户提供用于预配的 CloudFormation 模板,确保该项被正确设置。我们还将增加对其是否持续存在的验证支持,尤其是在我们不断新增其他(非 CloudFormation)预配方式以满足客户需求的情况下。

Kesten 似乎更倾向于静态凭证模式,并在提供商一侧使用良好的保险库机制来保护连接。就我个人而言,我不认同这一点;我认为只要遵循基本的防范措施,AWS 的模式从一开始就更为安全。

在 Praetorian 的建议之外,我们目前还采取了两项额外的防范措施:

  • 我们在 CloudFormation 中对 ExternalID 进行掩码处理: 我们在 CloudFormation 模板中将 ExternalID 设为参数,并启用 NoEcho 选项。这会在控制台、命令行工具或 API 中对其进行掩码处理,从而降低其暴露给有权运行 CloudFormation 但不具备 IAM 权限者的风险。
  • 我们将 CloudFormation 模板的访问权限仅限于目标账户: 这降低了他人在预配过程中趁机窃取 ExternalID 的风险。

即便 ExternalID 被泄露也不应造成影响 — 您的平台应确保目标账户只能注册一次,并且只能使用随机的 ExternalID。即使攻击者知道角色名称和 ExternalID,也无法加以利用,因为平台中没有任何地方可以输入这些信息,而且 AWS 自身会强制要求跨账户连接必须源自受信任的账户。这是无法伪造的。

希望了解我们的处理方式,能为您在自有工具中的做法提供一些思路。我建议采用账户注册与随机 ExternalID 的要求,即使您使用 AWS Organizations 也是如此,因为仅添加条件将访问限制在自己的组织内,仍可能使您面临从低安全级别账户向高安全级别账户发起的攻击。

敬请关注那个全新的秘密研发项目!我们应该会在几个月内谈及它,它对此类问题而言真正具有颠覆性。

防护 AWS ExternalID 的高级技术 | FireMon