洞察策略风险,用自然语言提问策略问题。 申请演示 →
Published:
在(但愿)不影响业务的前提下遏制失陷的 EC2 凭证
by FireMon
遏制已泄露的实例凭证有多种方法。最简单的方法往往最容易导致业务中断,但也存在一些巧妙的方案,既能将攻击者拒之门外,又不会破坏应用程序。
在过去几年中,我们看到 Amazon 取得了一些重大进展,有助于降低攻击者窃取并滥用分配给 AWS 实例的凭证的风险。诚然,其中许多进展是在那起至今仍被人们反复提及的重大数据泄露事件之后出现的,但我们现在拥有了更完善的工具来防范此类攻击。尽管如此,这类攻击仍然非常普遍,并在各方的云威胁清单中位居前列。
上个月,AWS 发布了一些用于锁定实例凭证的新策略选项,这促成了本文的构思。除此之外,还有云安全社区中一项有趣的发现,我稍后会谈到。尽管这项新功能非常出色,再加上 AWS 大幅改进了 GuardDuty 对凭证外泄的检测能力,但在某个时刻,您仍可能收到来自我们这类工具的告警,并不得不启动事件响应流程:

多年来,我整理并测试了大量遏制方案,其中大部分都有对应的实验环境,收录在我与 Will Bengtson 共同编写的云事件响应培训课程中。要在遏制攻击者的同时不破坏业务,其中的细微差别之多令人意外。在处理实例凭证时,您实际上要与三个组件打交道:负责权限管理的 IAM 服务、负责将凭证传递给实例的实例元数据服务(IMDS),以及在实例内部运行并使用这些凭证的应用程序代码/SDK。请注意,我在此有意作了一些简化,并且未涉及服务控制策略和资源(存储桶)策略。(本文中的所有截图均毫不客气地取自我自己的培训材料,因此请不要向有关部门举报我。)
简要说明一下背景:当您为实例分配 IAM 角色时,该实例会获得自动轮换的凭证,从而拥有该角色的权限。但如果攻击者能够以某种方式访问该实例,例如通过 SSRF 攻击或暴力破解 SSH,他们就可以复制这些凭证,并在 AWS 之外或从其控制的 AWS 账户中使用它们。
作为实验环境,Will 编写了一个小型应用程序,它会尝试与 S3 存储桶建立内部连接,并回报凭证是否有效(放心,图中显示的 IP 地址已不再有效):

方案 1:为角色添加 Deny All 策略
这无疑是最简单、最快速的方式。通过为 IAM 角色添加 Deny All 策略,所有 API 调用都会失败,攻击者也就无法造成更多损害。

这种方式快速、简便,同时也能极快地搞垮您的应用程序,因为它会阻断所有合法的 API 调用:

请记住,我们的目标是在不影响合法运行的应用程序的前提下将攻击者拒之门外。
糟糕。
方案 2:撤销会话
AWS 提供了一项撤销活动会话的实用功能。在控制台中点击一个按钮,即可添加自定义的 Deny All 策略,该策略仅拒绝在您点击按钮所设定时间之前启动的所有会话的访问(这没有简单的 API 调用可用,但自行编写策略实现相同效果并不困难)。

假设您已阻止攻击者侵入新会话,理论上这种做法会使被窃取的凭证失效,从而将攻击者拒之门外,同时让您的应用程序使用新凭证继续运行。但是……

并非如此。第 2 次翻车。原因何在?
IMDS 只有在凭证过期时才会更新凭证。IMDS 是独立于 IAM 的另一项服务,它并不知道您撤销了凭证,也没有任何理由或动机去拉取新凭证并提供给实例。它会一直提供已撤销的凭证,直到会话结束。
方案 3:更换 IAM 角色并拒绝旧角色
现在开始有点讲究了。如果我们创建一个新角色,让实例改用新角色,并阻断旧角色,会怎么样?与之前一样,这只有在您确信攻击者无法直接窃取新角色凭证的情况下才有效。

并非如此。第 3 次翻车。那么这里究竟发生了什么?

大多数代码和 SDK 会在启动时建立 IAM 会话,并从元数据服务拉取凭证。这些凭证有一个会话有效期,类似于生存时间(TTL)。凭证保存在内存中并持续使用,直到接近有效期结束。例如,使用 Python 中的 Boto 库(本次演示所用)时,代码在凭证过期前 15 分钟才会去获取新凭证。
因此,应用程序会持续失败,直到它去查找更新后的凭证为止。根据配置方式的不同,在 EC2 实例中这通常默认为每 6 小时一次。当然,也许您有优秀的开发人员,他们编写了出色的错误处理逻辑,会在 API 调用失败时尝试拉取新凭证,但这不太可能,因为这并非常见的使用场景。
实例中的代码仍在尝试使用旧角色,因为它无从得知情况已经改变。在方案 2 中,业务中断是因为 IMDS 不知道要与 IAM 核对以更新凭证。这一次,则是代码不知道要与 IMDS 通信以获取新凭证。
方案 4:插入 VPC 端点
这个方案是我的一点巧思,也是迄今为止最复杂的一个,但效果非常好。所有实例都位于 VPC(虚拟私有云)中,这是它们在 AWS 中的虚拟网络。通常情况下,所有 API 调用都要经由互联网到达 AWS 的公共端点。事实上,如果您的实例位于没有出站互联网路由的私有子网中,这些 API 调用就会失败。
如果您希望自己的私有实例与自己的私有 S3 存储桶通信,这就成了一个不小的问题。过去我们不得不启用互联网访问,这既产生成本,又以我们安全从业者极力想要避免的方式扩大了暴露面。
Amazon 给出的答案称为服务端点。这是一种内部的、软件定义的路由结构,您可以对其进行配置,使 VPC 中的资源完全在 Amazon 内部网络上与 API 端点(以及其他一些内容,但本文不展开讨论)通信。有趣的是,当您使用 VPC 端点时,AWS 会在网络流量中插入一些额外的上下文信息,因为它现在可以在其私有报头中嵌入数据,而我们可以将这些信息用作 IAM 策略中的条件!
首先,我们在实例所在的子网中为 S3 添加一个 VPC 端点:

然后,我们在 IAM 策略中添加一个条件,拒绝所有并非来自预期 VPC 的流量。以下是我们在培训实验环境中的做法:

如果此方案奏效,来自我们实例的 API 调用仍可正常工作,而这些凭证一旦在该 VPC 之外使用便会失败(所以我们只能寄希望于攻击者无法访问该 VPC 中的其他资源)。

成功了!我们的凭证只能在该 VPC 内部使用,攻击者被彻底挡在门外。这正是我在前文提到的那个服务控制策略的工作原理。SCP 的问题在于,服务端点会带来成本和复杂性,若在未部署好全部所需端点的情况下启用该策略,同样会导致业务中断,只不过这次是在企业规模上。
一种出人意料的行为
如前所述,我们大约在 2 年前搭建了这个实验环境,并已带领数百名学员完成演练。上周,来自 Uptycs 的 Andre Rall 在我们共同参与的一个云安全社区中发布了以下内容(已获授权引用):
有人知道这种情况是否正常吗?我有一个角色附加到某个实例配置文件上,而该实例配置文件又关联到一个实例。当我(通过 CLI)从实例配置文件中移除该角色,但保留实例配置文件与实例的关联时,该实例仍然能够使用该角色的凭证。按我的理解,既然角色已不再附加,实例就不应继续使用它,但也许是我遗漏了什么。
事实证明,我们再次遇到了服务之间的交互问题。在幕后,实例配置文件是 AWS 在元数据服务中将角色与实例关联起来的机制。Andre 从实例配置文件中移除了角色,但该角色仍然存在,实例配置文件也仍然存在。
您可能会认为 IMDS 将无法再提供这些凭证,但它仍然持有这些凭证,并且不知道 IAM 服务那边发生了什么。它继续提供凭证,而角色仍然认可这些凭证,因此它们依然有效。在这种情况下,撤销凭证确实会奏效,因为在实例配置文件与角色解绑后,IMDS 将无法获取新凭证。
遏制 IAM 凭证涉及诸多细微之处,而这只是来自一项服务(EC2)的一组示例。但一旦您理清 IAM 服务与 IMDS、IMDS 与 SDK 之间的交互流程,您就能牢固掌握一些核心原则,并将其运用于其他场景。