GCP IAM serviceAccounts.signJwt Privilege Escalation


Description

Detects iam.serviceAccounts.signJwt method for privilege escalation in GCP. This method works by signing well-formed JSON web tokens (JWTs). The script for this method will sign a well-formed JWT and request a new access token belonging to the Service Account with it.

Query · python

from panther_gcp_helpers import gcp_alert_context


def rule(event):
    if event.deep_get("protoPayload", "methodName") != "SignJwt":
        return False

    authorization_info = event.deep_walk("protoPayload", "authorizationInfo")
    if not authorization_info:
        return False

    for auth in authorization_info:
        if auth.get("permission") == "iam.serviceAccounts.signJwt" and auth.get("granted") is True:
            return True
    return False


def title(event):
    actor = event.deep_get(
        "protoPayload", "authenticationInfo", "principalEmail", default="<ACTOR_NOT_FOUND>"
    )
    operation = event.deep_get("protoPayload", "methodName", default="<OPERATION_NOT_FOUND>")
    project_id = event.deep_get("resource", "labels", "project_id", default="<PROJECT_NOT_FOUND>")

    return f"[GCP]: [{actor}] performed [{operation}] on project [{project_id}]"


def alert_context(event):
    context = gcp_alert_context(event)
    context["serviceAccountKeyName"] = event.deep_get(
        "protoPayload", "authenticationInfo", "serviceAccountKeyName"
    )
    return context

Analyst notes

These is not a vulnerability in GCP, this is a vulnerability in how you have configured your GCP environment, so it is your responsibility to be aware of these attack vectors and to defend against them. Make sure to follow the principle of least-privilege in your environments to help mitigate these security risks.

Raw source GCP IAM serviceAccounts.signJwt Privilege Escalation · Panther Python
Esc
Published by panther-labs/panther-analysis ↗, licensed under Apache 2.0 ↗. Reproduced here unmodified.
AnalysisType: rule
Filename: gcp_iam_serviceaccounts_signjwt.py
RuleID: "GCP.IAM.serviceAccounts.signJwt.Privilege.Escalation"
DisplayName: "GCP IAM serviceAccounts.signJwt Privilege Escalation"
Enabled: true
LogTypes:
  - GCP.AuditLog
Reports:
  MITRE ATT&CK:
    - TA0004:T1548
Severity: High
Description:
  Detects iam.serviceAccounts.signJwt method for privilege escalation in GCP. This method works
  by signing well-formed JSON web tokens (JWTs). The script for this method will sign a well-formed JWT and
  request a new access token belonging to the Service Account with it.
Runbook:
  These is not a vulnerability in GCP, this is a vulnerability in how you have configured your GCP environment,
  so it is your responsibility to be aware of these attack vectors and to defend against them. Make sure
  to follow the principle of least-privilege in your environments to help mitigate these security risks.
Reference: https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/
Tests:
  - Name: JWT Signed
    ExpectedResult: true
    Log:
      {
        "protoPayload":
          {
            "@type": "type.googleapis.com/google.cloud.audit.AuditLog",
            "status": {},
            "authenticationInfo":
              {
                "principalEmail": "some-project@company.iam.gserviceaccount.com",
                "serviceAccountKeyName": "//iam.googleapis.com/projects/some-project/serviceAccounts/some-project@company.iam.gserviceaccount.com/keys/a378358365ff3d22e9c1a72fecf4605ddff76b47",
                "principalSubject": "serviceAccount:some-project@company.iam.gserviceaccount.com",
              },
            "requestMetadata":
              {
                "callerIp": "1.2.3.4",
                "requestAttributes":
                  { "time": "2024-02-26T17:15:16.327542536Z", "auth": {} },
                "destinationAttributes": {},
              },
            "serviceName": "iamcredentials.googleapis.com",
            "methodName": "SignJwt",
            "authorizationInfo":
              [
                {
                  "permission": "iam.serviceAccounts.signJwt",
                  "granted": true,
                  "resourceAttributes": {},
                },
              ],
            "resourceName": "projects/-/serviceAccounts/114885146936855121342",
            "request":
              {
                "name": "projects/-/serviceAccounts/some-project@company.iam.gserviceaccount.com",
                "@type": "type.googleapis.com/google.iam.credentials.v1.SignJwtRequest",
              },
          },
        "insertId": "1hu88qbef4d2o",
        "resource":
          {
            "type": "service_account",
            "labels":
              {
                "project_id": "some-project",
                "unique_id": "114885146936855121342",
                "email_id": "some-project@company.iam.gserviceaccount.com",
              },
          },
        "timestamp": "2024-02-26T17:15:16.314854637Z",
        "severity": "INFO",
        "logName": "projects/some-project/logs/cloudaudit.googleapis.com%2Fdata_access",
        "receiveTimestamp": "2024-02-26T17:15:17.100020459Z",
      }
  - Name: JWT Not Signed
    ExpectedResult: false
    Log:
      {
        "protoPayload":
          {
            "@type": "type.googleapis.com/google.cloud.audit.AuditLog",
            "status": {},
            "authenticationInfo":
              {
                "principalEmail": "some-project@company.iam.gserviceaccount.com",
                "serviceAccountKeyName": "//iam.googleapis.com/projects/some-project/serviceAccounts/some-project@company.iam.gserviceaccount.com/keys/a378358365ff3d22e9c1a72fecf4605ddff76b47",
                "principalSubject": "serviceAccount:some-project@company.iam.gserviceaccount.com",
              },
            "requestMetadata":
              {
                "callerIp": "1.2.3.4",
                "requestAttributes":
                  { "time": "2024-02-26T17:15:16.327542536Z", "auth": {} },
                "destinationAttributes": {},
              },
            "serviceName": "iamcredentials.googleapis.com",
            "methodName": "SignJwt",
            "authorizationInfo":
              [
                {
                  "permission": "iam.serviceAccounts.signJwt",
                  "granted": false,
                  "resourceAttributes": {},
                },
              ],
            "resourceName": "projects/-/serviceAccounts/114885146936855121342",
            "request":
              {
                "name": "projects/-/serviceAccounts/some-project@company.iam.gserviceaccount.com",
                "@type": "type.googleapis.com/google.iam.credentials.v1.SignJwtRequest",
              },
          },
        "insertId": "1hu88qbef4d2o",
        "resource":
          {
            "type": "service_account",
            "labels":
              {
                "project_id": "some-project",
                "unique_id": "114885146936855121342",
                "email_id": "some-project@company.iam.gserviceaccount.com",
              },
          },
        "timestamp": "2024-02-26T17:15:16.314854637Z",
        "severity": "INFO",
        "logName": "projects/some-project/logs/cloudaudit.googleapis.com%2Fdata_access",
        "receiveTimestamp": "2024-02-26T17:15:17.100020459Z",
      }


# ------ paired body: gcp_iam_serviceaccounts_signjwt.py ------

from panther_gcp_helpers import gcp_alert_context


def rule(event):
    if event.deep_get("protoPayload", "methodName") != "SignJwt":
        return False

    authorization_info = event.deep_walk("protoPayload", "authorizationInfo")
    if not authorization_info:
        return False

    for auth in authorization_info:
        if auth.get("permission") == "iam.serviceAccounts.signJwt" and auth.get("granted") is True:
            return True
    return False


def title(event):
    actor = event.deep_get(
        "protoPayload", "authenticationInfo", "principalEmail", default="<ACTOR_NOT_FOUND>"
    )
    operation = event.deep_get("protoPayload", "methodName", default="<OPERATION_NOT_FOUND>")
    project_id = event.deep_get("resource", "labels", "project_id", default="<PROJECT_NOT_FOUND>")

    return f"[GCP]: [{actor}] performed [{operation}] on project [{project_id}]"


def alert_context(event):
    context = gcp_alert_context(event)
    context["serviceAccountKeyName"] = event.deep_get(
        "protoPayload", "authenticationInfo", "serviceAccountKeyName"
    )
    return context

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.