[security] Non-admin broker leases can override privileged GCP project, network, image, tags, and service account fields
Summary
Authenticated non-admin broker users can create normal GCP leases, but the
coordinator currently lets those requests supply several GCP resource selectors
that are consumed by the broker's privileged GCP service account:
gcpProject, gcpImage, gcpNetwork, gcpSubnet, gcpTags, and
gcpServiceAccount.
This crosses Crabbox's supported coordinator boundary when a broker service
account is intentionally broad enough to manage more than one project, network,
or attachable service account. A non-admin user can ask the coordinator to
create a Compute Engine instance in a caller-selected project/network, use a
caller-selected boot image, attach caller-selected target tags, and attach a
caller-selected service account. That is materially different from a trusted
lease owner running commands on their own already-authorized lease: it lets the
lease-create request steer where and under which provider identity the
coordinator spends its GCP authority.
Crabbox already treats adjacent provider-resource pinning and native image
sources as admin-only. POST /v1/leases rejects hostId / awsMacHostID and
native snapshot or machine-image sources for non-admin callers, but it does not
apply an equivalent guard to the remaining GCP project, network, image, tag, or
service-account selectors.
Affected Components
- Target commit:
6dba4afd1c5a4be0a780cf035d1b397e8982d478
- Coordinator lease creation:
worker/src/fleet.ts
- Lease request normalization:
worker/src/config.ts
- GCP provider implementation:
worker/src/gcp.ts
- CLI broker request serialization:
internal/cli/coordinator.go
- Existing tests documenting the current permissive behavior:
worker/test/fleet.test.ts
Relevant source evidence:
worker/src/fleet.ts:1654-1671 rejects non-admin native lease sources
(gcpMachineImage / gcpSnapshot, plus AWS/Azure snapshot fields) and
provider host pinning, but does not reject gcpProject, gcpImage,
gcpNetwork, gcpSubnet, gcpTags, or gcpServiceAccount.
worker/src/config.ts:296-306 copies those GCP request fields into
LeaseConfig without constraining them to broker-managed defaults.
worker/src/gcp.ts:171-176 constructs a GCPClient for
config.gcpProject; worker/src/gcp.ts:266-317 uses
config.gcpProject, config.gcpImage, config.gcpNetwork,
config.gcpSubnet, config.gcpTags, and config.gcpServiceAccount when
creating the Compute Engine instance.
worker/src/gcp.ts:481-499 creates the SSH firewall for
config.gcpNetwork and caller-influenced target tags.
internal/cli/coordinator.go:869-907 serializes explicit GCP config fields
into the coordinator lease-create body.
worker/test/fleet.test.ts:11270-11310 currently asserts that a non-admin
request with gcpProject: "request-project" succeeds and records that
project on the lease.
Attack Path
Attacker role: an authenticated non-admin user token, signed user token, or
shared operator token accepted by the broker.
Deployment prerequisites:
- The coordinator has broker-side GCP credentials configured.
- The broker's GCP service account has permissions broader than one strictly
managed project/network/default service account, such as access to multiple
projects or iam.serviceAccounts.actAs on service accounts the broker did
not intend ordinary lease creators to select.
Steps:
-
Send POST /v1/leases as a non-admin caller with provider: "gcp" and a
body containing values such as:
{
"provider": "gcp",
"gcpProject": "other-project",
"gcpNetwork": "projects/other-project/global/networks/privileged-net",
"gcpSubnet": "projects/other-project/regions/us-central1/subnetworks/privileged-subnet",
"gcpImage": "projects/other-project/global/images/custom-image",
"gcpTags": ["privileged-firewall-tag"],
"gcpServiceAccount": "privileged-sa@other-project.iam.gserviceaccount.com",
"sshPublicKey": "ssh-ed25519 AAAA..."
}
-
leaseConfig preserves those values, and createLease only checks whether
the request uses native snapshot or machine-image sources or host pinning.
The non-admin request proceeds.
-
GCPProvider.prepareLeaseConfig preserves a caller-supplied gcpProject
instead of replacing it with the broker's default project.
-
GCPClient.createServer creates a Compute Engine instance with the
caller-selected project, network/subnet, image, tags, and service account.
Result: where the coordinator service account has enough GCP IAM, a non-admin
Crabbox caller can spend broker authority in a project/network or with a
service account that should be controlled by broker administrators.
Impact
The impact is provider-authorization expansion from a non-admin Crabbox identity
to broker-controlled GCP authority:
- A non-admin can launch an instance in a caller-selected GCP project and
network/subnet visible to the coordinator service account.
- A non-admin can attach a caller-selected GCP service account to the instance;
if the broker service account is allowed to act as that identity, code running
on the lease can use instance metadata credentials for that service account.
- A non-admin can attach caller-selected network tags and cause Crabbox to
create or use firewall policy for those tags.
- A non-admin can select a caller-controlled image path, even though the
stronger native GCP machine-image/snapshot lease sources are already
admin-gated.
This is in scope under Crabbox's maintainer-authored security policy because it
can escalate a non-admin coordinator identity into provider capabilities that
are effectively administrative for the broker's GCP estate. It also affects the
policy's provider-resource boundary when Crabbox uses privileged cloud
credentials against resources not strongly constrained to broker-managed
defaults.
This is not a hostile multi-tenant isolation claim and does not require treating
repository configuration as untrusted code. The boundary is the documented
coordinator authentication and admin/non-admin split for brokered provider
actions.
Severity Assessment
CVSS Assessment
| Metric |
v3.1 |
v4.0 |
| Score |
7.1 / 10.0 |
7.1 / 10.0 |
| Severity |
High |
High |
| Vector |
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N |
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:L/VA:N/SC:H/SI:L/SA:N |
| Calculator |
CVSS v3.1 Calculator |
CVSS v4.0 Calculator |
The score assumes exploitation requires a real but deployment-dependent
precondition: the broker's GCP IAM must be broader than a single tightly scoped
managed project/default service account. Confidentiality impact is high because
attaching an unintended service account can expose that identity's cloud data
and APIs to commands running on the created instance. Integrity impact is low
because the attacker can steer resource creation and network tagging, while the
most severe provider-state mutation still depends on the selected service
account's downstream permissions.
Recommended Remediation
Add a coordinator-side allowlist or admin gate for brokered GCP resource
selectors before provider creation:
- Reject non-admin
gcpProject values that differ from the broker-managed
project selected by CRABBOX_GCP_PROJECT / GCP_PROJECT_ID, unless the
deployment explicitly allows per-user projects.
- Reject non-admin
gcpNetwork and gcpSubnet values that differ from
broker-managed defaults or from an administrator-configured allowlist.
- Reject non-admin
gcpServiceAccount values that differ from the configured
default service account or from an administrator-configured allowlist.
- Reject non-admin
gcpImage values unless they are known safe public defaults
or administrator-promoted images.
- Restrict non-admin
gcpTags to broker-managed tags or an
administrator-configured allowlist.
If per-user GCP project selection is an intended broker feature, make it
explicit and safe: bind allowed projects/service accounts to the authenticated
owner or org, record that policy in documentation, and test that a user cannot
select another owner's project or an arbitrary service account.
Add regression tests in worker/test/fleet.test.ts that cover non-admin
POST /v1/leases requests with each GCP selector set to a value outside the
broker-managed defaults or allowlist. The existing
records per-request GCP project without filling Worker-owned defaults test
should be updated to cover the intended allowlist or admin-only behavior.
Validation
Validation performed:
- Read the maintainer-authored scope policy in
crabbox/SECURITY.md and the
operations guide in crabbox/docs/security.md.
- Read the verification context for this run and confirmed no
discovery-to-verification target drift: discovery and verification both use
commit 6dba4afd1c5a4be0a780cf035d1b397e8982d478.
- Reviewed the current lease-create source path and GCP provider sink in the
isolated target copy.
- Recomputed the CVSS scores with the local
cvss Python package:
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N scores 7.1 High and
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:L/VA:N/SC:H/SI:L/SA:N scores
7.1 High.
Counterevidence considered:
docs/providers/gcp.md documents gcp.project, gcp.network,
gcp.tags, and gcp.serviceAccount as configurable provider settings, and
brokered GCP mode uses coordinator-side credentials.
worker/test/fleet.test.ts:11270-11310 intentionally records a non-admin
per-request GCP project today.
- These facts show the current behavior is deliberate enough to have test
coverage, but they do not establish a safe authorization policy for arbitrary
non-admin selection of projects, networks, tags, images, or service accounts
backed by broker credentials. The maintainer security policy still treats
non-admin-to-admin escalation and unsafe provider-resource mutation as
reportable boundaries.
No live GCP exploitation was attempted. The source proof is sufficient for the
authorization flaw; concrete exploitability and blast radius depend on the
broker's GCP IAM scope and should be assessed per deployment.
[security] Non-admin broker leases can override privileged GCP project, network, image, tags, and service account fields
Summary
Authenticated non-admin broker users can create normal GCP leases, but the
coordinator currently lets those requests supply several GCP resource selectors
that are consumed by the broker's privileged GCP service account:
gcpProject,gcpImage,gcpNetwork,gcpSubnet,gcpTags, andgcpServiceAccount.This crosses Crabbox's supported coordinator boundary when a broker service
account is intentionally broad enough to manage more than one project, network,
or attachable service account. A non-admin user can ask the coordinator to
create a Compute Engine instance in a caller-selected project/network, use a
caller-selected boot image, attach caller-selected target tags, and attach a
caller-selected service account. That is materially different from a trusted
lease owner running commands on their own already-authorized lease: it lets the
lease-create request steer where and under which provider identity the
coordinator spends its GCP authority.
Crabbox already treats adjacent provider-resource pinning and native image
sources as admin-only.
POST /v1/leasesrejectshostId/awsMacHostIDandnative snapshot or machine-image sources for non-admin callers, but it does not
apply an equivalent guard to the remaining GCP project, network, image, tag, or
service-account selectors.
Affected Components
6dba4afd1c5a4be0a780cf035d1b397e8982d478worker/src/fleet.tsworker/src/config.tsworker/src/gcp.tsinternal/cli/coordinator.goworker/test/fleet.test.tsRelevant source evidence:
worker/src/fleet.ts:1654-1671rejects non-admin native lease sources(
gcpMachineImage/gcpSnapshot, plus AWS/Azure snapshot fields) andprovider host pinning, but does not reject
gcpProject,gcpImage,gcpNetwork,gcpSubnet,gcpTags, orgcpServiceAccount.worker/src/config.ts:296-306copies those GCP request fields intoLeaseConfigwithout constraining them to broker-managed defaults.worker/src/gcp.ts:171-176constructs aGCPClientforconfig.gcpProject;worker/src/gcp.ts:266-317usesconfig.gcpProject,config.gcpImage,config.gcpNetwork,config.gcpSubnet,config.gcpTags, andconfig.gcpServiceAccountwhencreating the Compute Engine instance.
worker/src/gcp.ts:481-499creates the SSH firewall forconfig.gcpNetworkand caller-influenced target tags.internal/cli/coordinator.go:869-907serializes explicit GCP config fieldsinto the coordinator lease-create body.
worker/test/fleet.test.ts:11270-11310currently asserts that a non-adminrequest with
gcpProject: "request-project"succeeds and records thatproject on the lease.
Attack Path
Attacker role: an authenticated non-admin user token, signed user token, or
shared operator token accepted by the broker.
Deployment prerequisites:
managed project/network/default service account, such as access to multiple
projects or
iam.serviceAccounts.actAson service accounts the broker didnot intend ordinary lease creators to select.
Steps:
Send
POST /v1/leasesas a non-admin caller withprovider: "gcp"and abody containing values such as:
{ "provider": "gcp", "gcpProject": "other-project", "gcpNetwork": "projects/other-project/global/networks/privileged-net", "gcpSubnet": "projects/other-project/regions/us-central1/subnetworks/privileged-subnet", "gcpImage": "projects/other-project/global/images/custom-image", "gcpTags": ["privileged-firewall-tag"], "gcpServiceAccount": "privileged-sa@other-project.iam.gserviceaccount.com", "sshPublicKey": "ssh-ed25519 AAAA..." }leaseConfigpreserves those values, andcreateLeaseonly checks whetherthe request uses native snapshot or machine-image sources or host pinning.
The non-admin request proceeds.
GCPProvider.prepareLeaseConfigpreserves a caller-suppliedgcpProjectinstead of replacing it with the broker's default project.
GCPClient.createServercreates a Compute Engine instance with thecaller-selected project, network/subnet, image, tags, and service account.
Result: where the coordinator service account has enough GCP IAM, a non-admin
Crabbox caller can spend broker authority in a project/network or with a
service account that should be controlled by broker administrators.
Impact
The impact is provider-authorization expansion from a non-admin Crabbox identity
to broker-controlled GCP authority:
network/subnet visible to the coordinator service account.
if the broker service account is allowed to act as that identity, code running
on the lease can use instance metadata credentials for that service account.
create or use firewall policy for those tags.
stronger native GCP machine-image/snapshot lease sources are already
admin-gated.
This is in scope under Crabbox's maintainer-authored security policy because it
can escalate a non-admin coordinator identity into provider capabilities that
are effectively administrative for the broker's GCP estate. It also affects the
policy's provider-resource boundary when Crabbox uses privileged cloud
credentials against resources not strongly constrained to broker-managed
defaults.
This is not a hostile multi-tenant isolation claim and does not require treating
repository configuration as untrusted code. The boundary is the documented
coordinator authentication and admin/non-admin split for brokered provider
actions.
Severity Assessment
CVSS Assessment
The score assumes exploitation requires a real but deployment-dependent
precondition: the broker's GCP IAM must be broader than a single tightly scoped
managed project/default service account. Confidentiality impact is high because
attaching an unintended service account can expose that identity's cloud data
and APIs to commands running on the created instance. Integrity impact is low
because the attacker can steer resource creation and network tagging, while the
most severe provider-state mutation still depends on the selected service
account's downstream permissions.
Recommended Remediation
Add a coordinator-side allowlist or admin gate for brokered GCP resource
selectors before provider creation:
gcpProjectvalues that differ from the broker-managedproject selected by
CRABBOX_GCP_PROJECT/GCP_PROJECT_ID, unless thedeployment explicitly allows per-user projects.
gcpNetworkandgcpSubnetvalues that differ frombroker-managed defaults or from an administrator-configured allowlist.
gcpServiceAccountvalues that differ from the configureddefault service account or from an administrator-configured allowlist.
gcpImagevalues unless they are known safe public defaultsor administrator-promoted images.
gcpTagsto broker-managed tags or anadministrator-configured allowlist.
If per-user GCP project selection is an intended broker feature, make it
explicit and safe: bind allowed projects/service accounts to the authenticated
owner or org, record that policy in documentation, and test that a user cannot
select another owner's project or an arbitrary service account.
Add regression tests in
worker/test/fleet.test.tsthat cover non-adminPOST /v1/leasesrequests with each GCP selector set to a value outside thebroker-managed defaults or allowlist. The existing
records per-request GCP project without filling Worker-owned defaultstestshould be updated to cover the intended allowlist or admin-only behavior.
Validation
Validation performed:
crabbox/SECURITY.mdand theoperations guide in
crabbox/docs/security.md.discovery-to-verification target drift: discovery and verification both use
commit
6dba4afd1c5a4be0a780cf035d1b397e8982d478.isolated target copy.
cvssPython package:CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:Nscores 7.1 High andCVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:L/VA:N/SC:H/SI:L/SA:Nscores7.1 High.
Counterevidence considered:
docs/providers/gcp.mddocumentsgcp.project,gcp.network,gcp.tags, andgcp.serviceAccountas configurable provider settings, andbrokered GCP mode uses coordinator-side credentials.
worker/test/fleet.test.ts:11270-11310intentionally records a non-adminper-request GCP project today.
coverage, but they do not establish a safe authorization policy for arbitrary
non-admin selection of projects, networks, tags, images, or service accounts
backed by broker credentials. The maintainer security policy still treats
non-admin-to-admin escalation and unsafe provider-resource mutation as
reportable boundaries.
No live GCP exploitation was attempted. The source proof is sufficient for the
authorization flaw; concrete exploitability and blast radius depend on the
broker's GCP IAM scope and should be assessed per deployment.