Detection rules › Panther

GCP serviceusage.apiKeys.create Privilege Escalation

Severity
high
Log types
GCP.AuditLog
Reference
https://rhinosecuritylabs.com/cloud-security/privilege-escalation-google-cloud-platform-part-2/
Source
github.com/panther-labs/panther-analysis

Detects serviceusage.apiKeys.create method for privilege escalation in GCP. By default, API Keys are created with no restrictions, which means they have access to the entire GCP project they were created in. We can capitalize on that fact by creating a new API key that may have more privileges than our own user.

MITRE ATT&CK coverage

TacticTechniques
Privilege EscalationT1548 Abuse Elevation Control Mechanism

Rule body yaml

AnalysisType: rule
LogTypes:
  - GCP.AuditLog
Description:
  Detects serviceusage.apiKeys.create method for privilege escalation in GCP. By default, API Keys are
  created with no restrictions, which means they have access to the entire GCP project they were created in. We can
  capitalize on that fact by creating a new API key that may have more privileges than our own user.
DisplayName: "GCP serviceusage.apiKeys.create Privilege Escalation"
RuleID: "GCP.serviceusage.apiKeys.create.Privilege.Escalation"
Enabled: true
Filename: gcp_serviceusage_apikeys_create_privilege_escalation.py
Reference: https://rhinosecuritylabs.com/cloud-security/privilege-escalation-google-cloud-platform-part-2/
Runbook:
  Confirm this was authorized and necessary behavior. This is not a vulnerability in GCP, it is a vulnerability
  in how GCP environment is configured, so it is necessary to be aware of these attack vectors and to defend against
  them. It’s also important to remember that privilege escalation does not necessarily need to pass through the
  IAM service to be effective. Make sure to follow the principle of least-privilege in your environments to help
  mitigate these security risks.
Reports:
  MITRE ATT&CK:
    - TA0004:T1548 # Abuse Elevation Control Mechanism
Severity: High
DedupPeriodMinutes: 60
Threshold: 1
Tests:
  - Name: GCP API Key Created
    ExpectedResult: true
    Log:
      {
        "logName": "projects/some-project/logs/cloudaudit.googleapis.com%2Factivity",
        "operation":
          {
            "id": "operations/akmf.p7-1028347275902-fe0c0688-44a7-4dca-bc06-8456068e5673",
            "last": true,
            "producer": "apikeys.googleapis.com",
          },
        "protoPayload":
          {
            "at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog",
            "authenticationInfo":
              {
                "principalEmail": "some.user@some-project.com",
                "principalSubject": "serviceAccount:some-team@some-project.com",
                "serviceAccountKeyName": "//iam.googleapis.com/projects/some-project/serviceAccounts/some-team@some-project.gserviceaccount.com/keys/dc5344c246064589v76ec76f66bafc92b093ed41",
              },
            "authorizationInfo":
              [
                {
                  "granted": true,
                  "permission": "serviceusage.apiKeys.create",
                  "resource": "projectnumbers/1028347245602",
                  "resourceAttributes": {},
                },
              ],
            "methodName": "google.api.apikeys.v2.ApiKeys.CreateKey",
            "request":
              {
                "@type": "type.googleapis.com/google.api.apikeys.v2.CreateKeyRequest",
                "parent": "projects/some-project/locations/global",
              },
            "requestMetadata":
              {
                "callerIP": "189.163.74.177",
                "callerSuppliedUserAgent": "(gzip),gzip(gfe)",
                "destinationAttributes": {},
                "requestAttributes": {},
              },
            "resourceName": "projects/1028347245602",
            "response":
              {
                "@type": "type.googleapis.com/google.api.apikeys.v2.Key",
                "createTime": "1970-01-01T00:00:00Z",
                "etag": 'W/"DSLGu9UKHwqq2ICm7YPE7g=="',
                "name": "projects/1028347245602/locations/global/keys/bf67db25-d748-4335-ae08-7f0e65fnfy02",
                "updateTime": "1970-01-01T00:00:00Z",
              },
            "serviceName": "apikeys.googleapis.com",
            "status": {},
          },
        "receiveTimestamp": "2024-01-25 13:28:18.961519813",
        "resource":
          {
            "labels":
              {
                "method": "google.api.apikeys.v2.ApiKeys.CreateKey",
                "project_id": "some-project",
                "service": "apikeys.googleapis.com",
              },
            "type": "audited_resource",
          },
        "severity": "NOTICE",
        "timestamp": "2024-01-25 13:28:18.961519813",
      }
  - Name: GCP API Key Not Created
    ExpectedResult: false
    Log:
      {
        "logName": "projects/some-project/logs/cloudaudit.googleapis.com%2Factivity",
        "operation":
          {
            "id": "operations/akmf.p7-1028347275902-fe0c0688-44a7-4dca-bc06-8456068e5673",
            "last": true,
            "producer": "apikeys.googleapis.com",
          },
        "protoPayload":
          {
            "at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog",
            "authenticationInfo":
              {
                "principalEmail": "some.user@some-project.com",
                "principalSubject": "serviceAccount:some-team@some-project.com",
                "serviceAccountKeyName": "//iam.googleapis.com/projects/some-project/serviceAccounts/some-team@some-project.gserviceaccount.com/keys/dc5344c246064589v76ec76f66bafc92b093ed41",
              },
            "authorizationInfo":
              [
                {
                  "granted": false,
                  "permission": "serviceusage.apiKeys.create",
                  "resource": "projectnumbers/1028347245602",
                  "resourceAttributes": {},
                },
              ],
            "methodName": "google.api.apikeys.v2.ApiKeys.CreateKey",
            "request":
              {
                "@type": "type.googleapis.com/google.api.apikeys.v2.CreateKeyRequest",
                "parent": "projects/some-project/locations/global",
              },
            "requestMetadata":
              {
                "callerIP": "189.163.74.177",
                "callerSuppliedUserAgent": "(gzip),gzip(gfe)",
                "destinationAttributes": {},
                "requestAttributes": {},
              },
            "resourceName": "projects/1028347245602",
            "response":
              {
                "@type": "type.googleapis.com/google.api.apikeys.v2.Key",
                "createTime": "1970-01-01T00:00:00Z",
                "etag": 'W/"DSLGu9UKHwqq2ICm7YPE7g=="',
                "name": "projects/1028347245602/locations/global/keys/bf67db25-d748-4335-ae08-7f0e65fnfy02",
                "updateTime": "1970-01-01T00:00:00Z",
              },
            "serviceName": "apikeys.googleapis.com",
            "status": {},
          },
        "receiveTimestamp": "2024-01-25 13:28:18.961519813",
        "resource":
          {
            "labels":
              {
                "method": "google.api.apikeys.v2.ApiKeys.CreateKey",
                "project_id": "some-project",
                "service": "apikeys.googleapis.com",
              },
            "type": "audited_resource",
          },
        "severity": "NOTICE",
        "timestamp": "2024-01-25 13:28:18.961519813",
      }
  - Name: Log Without authorizationInfo
    ExpectedResult: false
    Log:
      {
        "logName": "projects/some-project/logs/cloudaudit.googleapis.com%2Factivity",
        "operation":
          {
            "id": "operations/akmf.p7-1028347275902-fe0c0688-44a7-4dca-bc06-8456068e5673",
            "last": true,
            "producer": "apikeys.googleapis.com",
          },
        "protoPayload":
          {
            "at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog",
            "authenticationInfo":
              {
                "principalEmail": "some.user@some-project.com",
                "principalSubject": "serviceAccount:some-team@some-project.com",
                "serviceAccountKeyName": "//iam.googleapis.com/projects/some-project/serviceAccounts/some-team@some-project.gserviceaccount.com/keys/dc5344c246064589v76ec76f66bafc92b093ed41",
              },
            "methodName": "google.api.apikeys.v2.ApiKeys.CreateKey",
            "request":
              {
                "@type": "type.googleapis.com/google.api.apikeys.v2.CreateKeyRequest",
                "parent": "projects/some-project/locations/global",
              },
            "requestMetadata":
              {
                "callerIP": "189.163.74.177",
                "callerSuppliedUserAgent": "(gzip),gzip(gfe)",
                "destinationAttributes": {},
                "requestAttributes": {},
              },
            "resourceName": "projects/1028347245602",
            "response":
              {
                "@type": "type.googleapis.com/google.api.apikeys.v2.Key",
                "createTime": "1970-01-01T00:00:00Z",
                "etag": 'W/"DSLGu9UKHwqq2ICm7YPE7g=="',
                "name": "projects/1028347245602/locations/global/keys/bf67db25-d748-4335-ae08-7f0e65fnfy02",
                "updateTime": "1970-01-01T00:00:00Z",
              },
            "serviceName": "apikeys.googleapis.com",
            "status": {},
          },
        "receiveTimestamp": "2024-01-25 13:28:18.961519813",
        "resource": {},
        "severity": "NOTICE",
        "timestamp": "2024-01-25 13:28:18.961519813",
      }

Detection logic

Condition

protoPayload.methodName ends_with "ApiKeys.CreateKey"
protoPayload.authorizationInfo is_not_null
protoPayload.authorizationInfo array_any

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.

FieldKindValues
protoPayload.authorizationInfois_not_null
  • (no value, null check)
protoPayload.methodNameends_with
  • ApiKeys.CreateKey

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.

FieldSource
projectresource.labels.project_id
principalprotoPayload.authenticationInfo.principalEmail
caller_ipprotoPayload.requestMetadata.callerIP
methodNameprotoPayload.methodName
resourceNameprotoPayload.resourceName
serviceNameprotoPayload.serviceName