-
Notifications
You must be signed in to change notification settings - Fork 200
/
main.go
121 lines (104 loc) · 4.18 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package gcp
import (
_ "embed"
"fmt"
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"
)
const AttackTechniqueId = "gcp.persistence.invite-external-user"
func init() {
const codeBlock = "```"
stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{
ID: AttackTechniqueId,
FriendlyName: "Invite an External User to a GCP Project",
Description: `
Persists in the GCP project by inviting an external (fictitious) user to the project. The attacker could then use the external user to access the project.
Warm-up: None
Detonation:
- Updates the project IAM policy to grant the attacker account the role <code>roles/editor</code>
!!! 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: `
The Google Cloud Admin logs event <code>SetIamPolicy</code> is generated when a principal is granted non-owner permissions at the project level.
` + codeBlock + `javascript hl_lines="5 11 12 13"
{
"protoPayload": {
"@type": "type.googleapis.com/google.cloud.audit.AuditLog",
"serviceName": "cloudresourcemanager.googleapis.com",
"methodName": "SetIamPolicy",
"serviceData": {
"@type": "type.googleapis.com/google.iam.v1.logging.AuditData",
"policyDelta": {
"bindingDeltas": [
{
"action": "ADD",
"role": "roles/editor",
"member": "user:stratusredteam@gmail.com"
}
]
}
},
"request": {
"resource": "target-project",
"policy": {
// ...
},
"@type": "type.googleapis.com/google.iam.v1.SetIamPolicyRequest"
}
}
}
` + codeBlock + `
Although this attack technique does not simulate it, an attacker can also
<a href="https://support.google.com/googleapi/answer/6158846?hl=en">use the GCP console to invite an external user as owner</a> of a GCP project,
which cannot be done through the SetIamPolicy API call. In that case, an <code>InsertProjectOwnershipInvite</code> event is generated:
` + codeBlock + `json hl_lines="5 8"
{
"protoPayload": {
"@type": "type.googleapis.com/google.cloud.audit.AuditLog",
"serviceName": "cloudresourcemanager.googleapis.com",
"methodName": "InsertProjectOwnershipInvite",
"resourceName": "projects/target-project",
"request": {
"member": "user:attacker@gmail.com",
"projectId": "target-project",
"@type": "type.googleapis.com/google.internal.cloud.resourcemanager.InsertProjectOwnershipInviteRequest"
},
"response": {
"@type": "type.googleapis.com/google.internal.cloud.resourcemanager.InsertProjectOwnershipInviteResponse"
}
}
}
` + codeBlock + `
`,
Platform: stratus.GCP,
IsIdempotent: true,
MitreAttackTactics: []mitreattack.Tactic{mitreattack.Persistence},
Detonate: detonate,
Revert: revert,
})
}
const RoleToGrant = "roles/editor"
func detonate(_ map[string]string, providers stratus.CloudProviders) error {
attackerPrincipal := gcp_utils.GetAttackerPrincipal()
err := gcp_utils.GCPAssignProjectRole(providers.GCP(), attackerPrincipal, RoleToGrant)
if err != nil {
return fmt.Errorf("unable to assign %s to %s: %w", attackerPrincipal, RoleToGrant, err)
}
return nil
}
func revert(_ map[string]string, providers stratus.CloudProviders) error {
attackerPrincipal := gcp_utils.GetAttackerPrincipal()
err := gcp_utils.GCPUnassignProjectRole(providers.GCP(), attackerPrincipal, RoleToGrant)
if err != nil {
return fmt.Errorf("unable to assign %s to %s: %w", attackerPrincipal, RoleToGrant, err)
}
return nil
}