对于那些不熟悉的人来说,GCP是一个云平台,为任何规模的企业提供各种云计算解决方案。大多数人都会把它放在可用的“三大”云提供商中,即亚马逊网络服务(AWS)、Microsoft Azure和GCP。

云安全是一个极其重要的研究领域,对于这些云平台的用户来说,理解和接受它变得越来越重要。与竞争对手AWS相比,GCP安全是一个似乎未触及的研究领域。这可能是出于许多不同的原因,主要原因可能是他们每个人控制的市场份额。根据最近在ZDNet.com上发布的一篇文章,AWS拥有最大的市场份额,其次是Microsoft Azure,其次是GCP,因此AWS安全性(和Azure安全性在小得多的情况下)将远远领先于GCP是有道理的。这并不意味着GCP完全是“不安全的”,而是更意味着对GCP安全性的外部第三方研究更少。

谷歌存储桶安全

Google Storage是通过GCP提供的服务,在被称为“桶”的资源中提供静态文件托管。如果熟悉AWS,Google Storage是GCP版本的AWS简单存储服务(S3),S3存储桶相当于两个云的Google存储桶。

GCP Bucket 提权策略与枚举

不过,谷歌存储桶权限策略可能会变得非常细粒度。根据设计,他们可以接触到各种来源(其他帐户、组织、用户等),包括向公共互联网或所有经过身份验证的GCP用户开放。

GCPBucketBrute

GCPBucketBrute目前在我们的GitHub上可用:https://github.com/RhinoSecurityLabs/GCPBucketBrute

该工具是用Python 3编写的,只需要几个不同的库,因此安装起来很简单。

为此,请按照以下步骤操作:

  1. git clone https://github.com/RhinoSecurityLabs/GCPBucketBrute.git
  2. cd GCPBucketBrute && pip3 安装 -r requirements.txt

安装后,你可以向下移动到此帖子的下一部分开始,也可以运行以下命令自行查看

帮助输出:

python3 gcpbucketbrute.py –help

工作方式

与其使用“gsutil”(谷歌存储实用程序)CLI程序来执行枚举,不如点击我们正在寻找的每个存储桶的HTTP端点来检查是否存在要快得多,因为在这种情况下,“gsutil”CLI没有开销。它还使用子进程而不是线程进行设计并发执行。

当脚本开始时,它将根据提供给“-k/–keyword”参数的关键字生成一个排列列表。然后,它将通过向Google

API发送HTTP请求来开始粗暴执行存储桶,并将根据HTTP响应代码确定存储桶的存在。通过提出HTTP HEAD请求而不是HTTP

GET请求,我们可以确保HTTP响应不包含正文,同时仍然获得有效的响应代码。虽然差异可能可以忽略不计,但理论上,较小的响应(即HEAD请求中没有主体的响应)到达并解析的速度比较大的响应(即来自GET请求的正文的响应)到达和解析要快。使用HEAD请求还可以让谷歌的服务器在尝试处理我们的请求时工作更少,这在大规模上很有帮助。

每个HTTP HEAD请求都将发送到以下URL:“https://www.googleapis.com/storage/v1/b/BUCKET_NAME”,其中“BUCKET_NAME”被当前猜测所取代。如果HTTP响应代码为“404”或“400”,则存储桶不存在。根据测试期间发现的情况,我们遇到的任何其他HTTP响应代码都表明存储桶存在。

对于发现的任何存储桶,将使用Google Storage TestIamPermissions

API来确定我们对目标存储桶的访问级别(如果有的话)。如果凭据传递到脚本中,则经过身份验证的TestIamPermissions和未经身份验证的TestIamPermissions调用的结果都将输出到屏幕上进行比较,因此你可以看到授予allAuthenticatedUsers和allUsers的访问权限之间的区别。如果没有传递凭据,则仅进行未经身份验证的检查并输出(如果传递到“-o/–out-file”参数,则输出到屏幕和外部文件)。

如果发现用户对存储桶有任何权限(已身份验证与否),则将输出所有权限。在此之前,该工具将检查一些常见的配置错误,并将输出一条单独的行,使事情更清晰一点。例如,授予“allUsers”“storage.objects.list”权限的存储桶将在输出所有权限之前输出消息“未经身份验证的LISTABLE(storage.objects.list)”。这只是为了在目标桶中出现配置错误时更清楚地说明问题。

还将检查权限列表,看看用户是否有权限通过修改存储桶策略来升级其在存储桶上的权限。

快速入门

注意:如果不传递任何与身份验证相关的参数,脚本将提示执行你要做的事情(服务帐户、访问令牌、默认凭据、未经身份验证)。

使用“netflix”作为关键字,使用5个并发子进程(默认)扫描存储桶,提示身份验证类型以检查任何找到的存储桶(默认)上的身份验证列表权限:

python3 gcpbucketbrute.py -k netflix

使用“谷歌”作为关键字,使用10个并发子进程扫描存储桶,同时保持未经身份验证:

python3 gcpbucketbrute.py -k google -s 10 -u

使用“apple”作为关键字,使用5个并发子进程(默认)扫描存储桶,提示身份验证类型以检查任何找到的存储桶(默认)上的身份验证列表权限,并将结果输出到“out.txt”:

python3 gcpbucketbrute.py -k apple -o out.txt

使用“android”作为关键字,使用5个并发子进程(默认)扫描存储桶,同时使用凭据存储在sa.pem中的GCP服务帐户进行身份验证:

python3 gcpbucketbrute.py -k android -f sa.pem

使用“samsung”作为关键字,使用8个并发子进程扫描存储桶,同时使用凭据存储在service-account.pem中的GCP服务帐户进行身份验证:

python3 gcpbucketbrute.py -k samsung -s 8 -f service-account.pem

存储桶提权

正如AWS S3存储桶可能容易因配置错误的桶ACL而易受提权(此处深入讨论))一样,谷歌存储桶也容易受到同类攻击。

与GCPBucketBrute如何通过直接HTTP请求“https://www.googleapis.com/storage/v1/b/BUCKET_NAME/o”来检查打开的谷歌存储桶一样,我们可以通过向“https://www.googleapis.com/storage/v1/b/BUCKET_NAME/iam”发出直接HTTP请求来检查存储桶的策略,或者我们可以使用“gsutil”CLI工具运行“gsutil

iam get

gs://BUCKET_NAME”。

如果允许“allUsers”或“allAuthenticatedUsers”阅读存储桶策略,我们将在拉动存储桶策略时收到有效的响应,否则我们将被拒绝访问。

桶策略很有帮助,但这要求我们拥有“storage.buckets.getIamPolicy”权限,而我们可能没有。如果有一种方法可以确定我们被授予的权限,而无需查看存储桶策略,该怎么办?

等等,有!Google

Storage“TestIamPermissions”API允许我们提供存储桶名称和Google存储权限列表,它将响应我们(提出API请求的用户)在该存储桶上的权限。这完全绕过了查看存储桶策略的要求,甚至可以为我们提供更好的信息(在使用自定义角色的情况下)。

为了确定我们在存储桶上拥有哪些权限,我们可以向类似于“https://www.googleapis.com/storage/v1/b/BUCKET_NAME/iam/testPermissions”的URL提出请求。permissions=storage.objects.list”,它将响应并告诉我们我们是否在存储桶“BUCKET_NAME”上有“storage.objects.list”权限。权限参数可以传递多次以同时检查多个权限,谷歌存储Python库支持test_iam_permissions

API(即使“gsutil”不支持)。

GCPBucketBrute将使用当前凭据(如果运行未经身份验证的扫描,则使用无凭据/匿名凭据)来确定我们被授予每个发现的存储桶哪些特权。如上所述,对于我们无法访问的桶,不会输出任何东西。对于我们有权访问的存储桶,它将输出我们拥有的权限列表。对于我们有足够的权限升级并成为完整存储桶管理员的存储桶,它将输出权限和一条消息,表明它容易受到提权的影响。

以下屏幕截图显示了在查找具有一些特权的存储桶和容易受到提权影响的存储桶时输出的内容。

对于报告容易受到提权影响的存储桶,这本质上意味着存储桶策略允许“allUsers”或“allAuthenticatedUsers”写入他们的存储桶策略(storage.buckets.setIamPolicy权限)。这使我们能够写信给“所有用户”/“所有身份验证用户”是存储桶所有者的提权策略,从而授予我们对存储桶的完全访问权限。

对于被发现容易受到公共提权影响的桶,我们向拥有它们的公司报告了这一发现(在那里我们可以识别这样的事情,其余的直接报告给谷歌)。

为了执行提权,我们按照以下步骤操作:

  1. 根据我们提供的关键字,扫描现有存储桶
  2. 对于找到的任何存储桶,请检查通过使用TestIamPermissions
  3. API作为经过身份验证和未经身份验证的用户,检查授予了哪些权限给“allUsers”或“allAuthenticatedUsers”。如果发现他们有权写入存储桶策略(storage.buckets.setIamPolicy),我们将有提权。脆弱的桶提权策略可能看起来像这样:
{"bindings":[{"members":["allAuthenticatedUsers","projectEditor:my-test-project","projectOwner:my-test-project"],"role":"roles\/storage.legacyBucketOwner"},{"members":["projectViewer:my-test-project"],"role":"roles\/storage.legacyBucketReader"},{"members":["projectEditor:my-test-project","projectOwner:my-test-project"],"role":"roles\/storage.legacyObjectOwner"},{"members":["projectViewer:my-test-project"],"role":"roles\/storage

忽略了本提权策略中定义的大部分内容,我们可以看到“allAuthenticatedUsers”组是“roles/storage.legacyBucketOwner”角色的成员。如果我们看看该角色被授予了什么权限,我们会看到以下内容:

  • storage.buckets.get
  • storage.buckets.getIam策略
  • storage.buckets.setIam策略
  • storage.buckets.update
  • storage.objects.create
  • storage.objects.delete
  • storage.objects.list

这意味着我们可以读取(storage.buckets.getIamPolicy)和写入(storage.buckets.setIamPolicy)到存储桶策略,并且我们可以在存储桶中创建、删除和列出对象。

请注意,我们通过访问以下URL看到相同的信息:(请注意,省略了“storage.objects.getIamPolicy”和“storage.objects.setIamPolicy”权限,因为它们会在设置为禁用对象级权限的任何存储桶上抛出错误。对于启用对象级权限的存储桶,可以包含这些值)。

https://www.googleapis.com/storage/v1/b/BUCKET_NAME/iam/testPermissions?permissions=storage.buckets.delete&permissions=storage.buckets.get&permissions=storage.buckets.getIamPolicy&permissions=storage.buckets.setIamPolicy&permissions=storage.buckets.update&permissions=storage.objects.create&permissions=storage.objects.delete&permissions=storage.objects.get&permissions=storage.objects.list&permissions=storage.objects.update

如果我们查看存储管理员角色授予的权限,我们可以看到它授予了这些特权:

  • firebase.projects.get
  • resourcemanager.projects.get
  • resourcemanager.projects.list
  • storage.buckets.create
  • storage.buckets.delete
  • storage.buckets.get
  • storage.buckets.getIam策略
  • storage.buckets.list
  • storage.buckets.setIam策略
  • storage.buckets.update
  • storage.objects.create
  • storage.objects.delete
  • storage.objects.get
  • storage.objects.getIamPolicy
  • storage.objects.list
  • storage.objects.setIam策略
  • storage.objects.update

授予此角色的特权比“allAuthenticatedUsers”的角色成员还多,所以我们为什么不改变呢?

使用“gsutil”Google Storage CLI程序,我们可以运行以下命令,授予“allAuthenticatedUsers”对“存储管理员”角色的访问权限,从而将我们授予的权限升级到存储桶中:

gsutil iam ch group:allAuthenticatedUsers:admin gs://BUCKET_NAME

现在,如果我们再次查看存储桶策略,我们可以看到添加了以下内容(因为“ch”命令附加到策略中,而不是覆盖):

{"members":["group:allAuthenticatedUsers"],"role":"roles\/storage.admin"}

就这样,我们已经将我们的特权从存储遗产桶所有者升级到我们甚至不拥有的存储桶上的存储管理员!

从LegacyBucketOwner升级到Storage Admin的主要吸引力之一是能够使用“storage.buckets.delete”特权。理论上,你可以在升级权限后删除存储桶,然后你可以在自己的帐户中创建存储桶来窃取名称。

现在,如果我们再次查看TestIamPermissions

API授予的特权,我们就会看到从我们使用的新角色中添加了一些额外内容。请注意,在使用TestIamPermissions

API(如resourcemanager.projects.list)时,并非该角色允许的所有特权都会列出,因为并非所有权限都是Google

Storage特定的权限,并且API不支持。

注意:“gsutil iam

ch”命令需要读取目标存储桶策略的权限,因为它首先读取策略,然后添加你的添加,然后编写新策略。即使你拥有SetBucketPolicy权限,你可能也不总是拥有此权限。在这些情况下,你需要覆盖现有策略,并有可能在其环境中造成错误,例如,如果不小心撤销了需要访问的内容。

免责声明:提权实际上没有在任何易受攻击的桶上执行,而是只确认存在漏洞。

总结

尽管存储桶是默认私有创建的,但我们一次又一次地看到用户错误配置其资产的权限,并将其暴露给公共中的恶意行为者,而Google Storage TestIamPermissions等简单API只会让它变得更容易。