Detection rules › Panther
GCP CloudBuild Potential Privilege Escalation
Detects privilege escalation attacks designed to gain access to the Cloud Build Service Account. A user with permissions to start a new build with Cloud Build can gain access to the Cloud Build Service Account and abuse it for more access to the environment.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation | T1548 Abuse Elevation Control Mechanism |
Rule body yaml
AnalysisType: rule
LogTypes:
- GCP.AuditLog
Description:
Detects privilege escalation attacks designed to gain access to the Cloud Build Service Account.
A user with permissions to start a new build with Cloud Build can gain access to the Cloud Build Service Account
and abuse it for more access to the environment.
DisplayName: "GCP CloudBuild Potential Privilege Escalation"
RuleID: "GCP.CloudBuild.Potential.Privilege.Escalation"
Enabled: true
Filename: gcp_cloudbuild_potential_privilege_escalation.py
Reference: https://rhinosecuritylabs.com/gcp/iam-privilege-escalation-gcp-cloudbuild/
Runbook:
Confirm this was authorized and necessary behavior. To defend against this privilege escalation attack,
it is necessary to restrict the permissions granted to the Cloud Build Service Account and to be careful granting
the cloudbuild.builds.create permission to any users in your Organization. Most importantly, you need to know that
any user who is granted cloudbuild.builds.create, is also indirectly granted all the permissions granted to the
Cloud Build Service Account. If that’s alright with you, then you may not need to worry about this attack vector,
but it is still highly recommended to modify the default permissions granted to the Cloud Build Service Account.
Reports:
MITRE ATT&CK:
- TA0004:T1548 # Abuse Elevation Control Mechanism
Severity: High
DedupPeriodMinutes: 60
Threshold: 1
Tests:
- Name: GCP CloudBuild - Build with Potentially Privileged Access
ExpectedResult: true
Log:
{
"logName": "projects/some-project/logs/cloudaudit.googleapis.com%2Factivity",
"operation":
{
"first": true,
"id": "operations/build/some-project/YzNhZWI0YWYtNjAwNi00YzM5LTgxYmUtMjhmMjc1YzJkOGEz",
"producer": "cloudbuild.googleapis.com",
},
"protoPayload":
{
"at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo":
{
"principalEmail": "whodoneit@some-project.iam.gserviceaccount.com",
"principalSubject": "serviceAccount:whodoneit@some-project.iam.gserviceaccount.com",
"serviceAccountKeyName": "//iam.googleapis.com/projects/some-project/serviceAccounts/whodoneit@some-project.iam.gserviceaccount.com/keys/123er456788",
},
"authorizationInfo":
[
{
"granted": true,
"permission": "cloudbuild.builds.create",
"resource": "projects/some-project",
"resourceAttributes": {},
},
],
"methodName": "google.devtools.cloudbuild.v1.CloudBuild.CreateBuild",
"request":
{
"@type": "type.googleapis.com/google.devtools.cloudbuild.v1.CreateBuildRequest",
"build": {},
"projectId": "some-project",
},
"requestMetadata":
{
"callerIP": "189.163.74.177",
"callerSuppliedUserAgent": "(gzip),gzip(gfe),gzip(gfe)",
"destinationAttributes": {},
"requestAttributes":
{ "auth": {}, "time": "2024-01-25T11:55:09.740095Z" },
},
"resourceLocation": { "currentLocations": ["global"] },
"resourceName": "projects/some-project/builds",
"serviceName": "cloudbuild.googleapis.com",
},
"receiveTimestamp": "2024-01-25 11:55:09.854909113",
"resource":
{
"labels":
{
"build_id": "c3aeb4ap-6006-4c39-81be-28f275c2d8a3",
"build_trigger_id": "",
"project_id": "some-project",
},
"type": "build",
},
"severity": "NOTICE",
"timestamp": "2024-01-25 11:55:08.919358000",
}
- Name: GCP CreateBrand - No Privileged Access
ExpectedResult: false
Log:
{
"logName": "projects/some-project/logs/cloudaudit.googleapis.com%2Factivity",
"protoPayload":
{
"at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo":
{ "principalEmail": "john.doe@some-project.com" },
"authorizationInfo":
[
{
"granted": true,
"permission": "clientauthconfig.brands.create",
"resource": "brands/1028347248702",
"resourceAttributes": {},
},
],
"methodName": "CreateBrand",
"request":
{
"brand":
{
"displayName": "NewBrand",
"projectNumbers": ["1028345275902"],
"supportEmail": "john.doe@some-project.com",
},
"visibility": "INTERNAL",
},
"requestMetadata":
{
"callerIP": "189.163.74.177",
"destinationAttributes": {},
"requestAttributes":
{ "auth": {}, "time": "2024-01-24T14:30:05.891336Z" },
},
"resourceName": "brands/1028347275902",
"response":
{
"brandId": "1028347248702",
"creationTime": "2024-01-24T14:30:05.400Z",
"displayName": "NewBrand test",
"projectNumbers": ["1028347248702"],
"supportEmail": "some.user@company.com",
"updateTime": "2024-01-24T14:30:05.866002Z",
},
"serviceName": "clientauthconfig.googleapis.com",
"status": {},
},
"receiveTimestamp": "2024-01-24 14:30:06.741210353",
"resource":
{
"labels":
{ "brand_id": "1028347248702", "project_id": "some-project" },
"type": "client_auth_config_brand",
},
"severity": "NOTICE",
"timestamp": "2024-01-24 14:30:05.207884000",
}
Detection logic
Condition
protoPayload.methodName ends_with "CloudBuild.CreateBuild"
protoPayload.authorizationInfo is_not_null
protoPayload.authenticationInfo.principalEmail not ends_with "@gcf-admin-robot.iam.gserviceaccount.com"
protoPayload.authorizationInfo array_any
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
protoPayload.authenticationInfo.principalEmail | ends_with | @gcf-admin-robot.iam.gserviceaccount.com |
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
protoPayload.authorizationInfo | is_not_null | |
protoPayload.methodName | ends_with |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
project | resource.labels.project_id |
principal | protoPayload.authenticationInfo.principalEmail |
caller_ip | protoPayload.requestMetadata.callerIP |
methodName | protoPayload.methodName |
resourceName | protoPayload.resourceName |
serviceName | protoPayload.serviceName |