Skip to content

GCP_UNLINK_BILLING

Copy page

Summary

Forestall ACL Alias GCP_UNLINK_BILLING
GCP Alias Guardrail Bypass & Destruction
Affected Object Types Projects
Exploitation Certainty Certain
Granting Roles roles/billing.admin, roles/billing.projectManager

Description

GCP_UNLINK_BILLING indicates that an identity can remove the billing account association from a GCP project. When a billing account is unlinked, all billable GCP services in the project are immediately disabled — compute instances stop, Cloud Run services become unavailable, GKE control planes are disabled, and billable API calls fail. The project’s resources are not deleted, but they are inaccessible until billing is re-linked.

This edge represents an availability attack surface. An attacker who cannot exfiltrate or destroy data (due to VPC-SC or encryption) can still cause a service outage by unlinking billing. In organizations with strict billing separation, an identity with roles/billing.admin at the billing account level may be able to unlink billing from any project associated with that account.

Key abuse scenarios:

  • Unlink billing from production projects → all compute and managed services stop immediately.
  • Unlink billing from security or logging projects → blind defenders before a larger attack.
  • Cloud extortion: unlink billing from business-critical projects and demand payment before data is lost to resource deletion (note: resources are not deleted immediately, but the pressure is real).

Identification

gcloud CLI

# Find who has billing admin or project billing manager at org or billing account level
ORG_ID=$(gcloud organizations list --format="value(name)" | head -1)
gcloud organizations get-iam-policy $ORG_ID --format=json | \
jq '.bindings[] | select(.role | test("billing.admin|billing.projectManager")) | {role: .role, members: .members}'
# Check the billing account linked to a specific project
PROJECT_ID="my-project"
gcloud billing projects describe $PROJECT_ID \
--format="table(billingAccountName, billingEnabled)"
# List all projects linked to a billing account
BILLING_ACCOUNT=$(gcloud billing accounts list --format="value(name)" | head -1)
gcloud billing projects list --billing-account=$BILLING_ACCOUNT \
--format="table(projectId, billingEnabled)"

Exploitation

gcloud CLI

# Unlink billing from a single project — all billable services stop immediately
PROJECT_ID="target-project"
gcloud billing projects unlink $PROJECT_ID
# Mass unlink: remove billing from all projects the identity has rights on
for PROJECT in $(gcloud projects list --format="value(projectId)"); do
echo "Unlinking billing from: $PROJECT"
gcloud billing projects unlink $PROJECT
done

Services that stop immediately after billing is unlinked:

  • Compute Engine VMs (instances stop running)
  • Cloud Run services (requests begin failing)
  • GKE clusters (control plane is disabled)
  • Cloud SQL instances (connections are refused)
  • BigQuery (queries fail with quota errors)

Free-tier and always-free resources continue operating within their limits.

Mitigation

  1. Restrict roles/billing.admin — assign to at most a dedicated finance or ops account; never to developer identities.
  2. Restrict roles/billing.projectManagerr to approved automation accounts with narrow scope.
  3. Enable budget alerts — a sudden drop to zero spend can indicate billing was unlinked before the operational impact is noticed.
  4. Maintain runbooks for re-linking billing under incident conditions, including the identity and approvals required.

Detection

Log Type Method Key Fields
Admin Activity UpdateProjectBillingInfo resource.type=project, billingAccountName set to empty
ORG_ID=$(gcloud organizations list --format="value(name)" | head -1)
gcloud logging read \
'protoPayload.methodName="UpdateProjectBillingInfo"' \
--organization=$ORG_ID \
--format="table(timestamp, protoPayload.authenticationInfo.principalEmail, resource.labels.project_id, protoPayload.request.projectBillingInfo.billingAccountName)"

Alert on:

  • Any UpdateProjectBillingInfo call where billingAccountName is set to an empty value (unlinking).
  • Multiple billing unlinks within a short time window (mass disruption pattern).
  • Billing changes outside of business hours or from identities not in the approved billing admin list.

References

Was this page helpful?

Thank you for your feedback.