diff --git a/docs/attack-techniques/AWS/aws.credential-access.ec2-steal-instance-credentials.md b/docs/attack-techniques/AWS/aws.credential-access.ec2-steal-instance-credentials.md index b39812a7..4949d7e7 100755 --- a/docs/attack-techniques/AWS/aws.credential-access.ec2-steal-instance-credentials.md +++ b/docs/attack-techniques/AWS/aws.credential-access.ec2-steal-instance-credentials.md @@ -54,15 +54,15 @@ See also: [Known detection bypasses](https://hackingthe.cloud/aws/avoiding-detec The following CloudTrail events are generated when this technique is detonated[^1]: -- `ssm:SendCommand` +- `ec2:DescribeInstances` - `ssm:DescribeInstanceInformation` -- `sts:GetCallerIdentity` +- `ssm:GetCommandInvocation` -- `ec2:DescribeInstances` +- `ssm:SendCommand` -- `ssm:GetCommandInvocation` +- `sts:GetCallerIdentity` ??? "View raw detonation logs" diff --git a/docs/attack-techniques/AWS/aws.defense-evasion.organizations-leave.md b/docs/attack-techniques/AWS/aws.defense-evasion.organizations-leave.md index 70f9a3e2..52c937d5 100755 --- a/docs/attack-techniques/AWS/aws.defense-evasion.organizations-leave.md +++ b/docs/attack-techniques/AWS/aws.defense-evasion.organizations-leave.md @@ -49,10 +49,10 @@ Use the CloudTrail event LeaveOrganization. The following CloudTrail events are generated when this technique is detonated[^1]: -- `sts:AssumeRole` - - `organizations:LeaveOrganization` +- `sts:AssumeRole` + ??? "View raw detonation logs" diff --git a/docs/attack-techniques/AWS/aws.execution.ec2-user-data.md b/docs/attack-techniques/AWS/aws.execution.ec2-user-data.md index 65a5c112..3154b1d7 100755 --- a/docs/attack-techniques/AWS/aws.execution.ec2-user-data.md +++ b/docs/attack-techniques/AWS/aws.execution.ec2-user-data.md @@ -61,14 +61,14 @@ provisioned before instantiation. The following CloudTrail events are generated when this technique is detonated[^1]: -- `ec2:ModifyInstanceAttribute` - -- `ec2:StopInstances` - - `ec2:DescribeInstances` +- `ec2:ModifyInstanceAttribute` + - `ec2:StartInstances` +- `ec2:StopInstances` + ??? "View raw detonation logs" diff --git a/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md b/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md index 7e319695..95dd589d 100755 --- a/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md +++ b/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md @@ -66,11 +66,11 @@ Identify, through CloudTrail's StartSession event, when a user is s The following CloudTrail events are generated when this technique is detonated[^1]: -- `ssm:TerminateSession` +- `ssm:DescribeInstanceInformation` - `ssm:StartSession` -- `ssm:DescribeInstanceInformation` +- `ssm:TerminateSession` ??? "View raw detonation logs" diff --git a/docs/attack-techniques/AWS/aws.persistence.iam-create-backdoor-role.md b/docs/attack-techniques/AWS/aws.persistence.iam-create-backdoor-role.md index 4f88f78e..50772f16 100755 --- a/docs/attack-techniques/AWS/aws.persistence.iam-create-backdoor-role.md +++ b/docs/attack-techniques/AWS/aws.persistence.iam-create-backdoor-role.md @@ -84,10 +84,10 @@ which generates a finding when a role can be assumed from a new AWS account or p The following CloudTrail events are generated when this technique is detonated[^1]: -- `iam:CreateRole` - - `iam:AttachRolePolicy` +- `iam:CreateRole` + ??? "View raw detonation logs" diff --git a/docs/attack-techniques/AWS/aws.persistence.iam-create-user-login-profile.md b/docs/attack-techniques/AWS/aws.persistence.iam-create-user-login-profile.md index 3ad9df11..b2a96201 100755 --- a/docs/attack-techniques/AWS/aws.persistence.iam-create-user-login-profile.md +++ b/docs/attack-techniques/AWS/aws.persistence.iam-create-user-login-profile.md @@ -58,11 +58,11 @@ In particular, it's suspicious when these events occur on IAM users intended to The following CloudTrail events are generated when this technique is detonated[^1]: -- `sts:GetCallerIdentity` +- `iam:CreateLoginProfile` - `iam:DeleteLoginProfile` -- `iam:CreateLoginProfile` +- `sts:GetCallerIdentity` ??? "View raw detonation logs" diff --git a/docs/attack-techniques/AWS/aws.persistence.rolesanywhere-create-trust-anchor.md b/docs/attack-techniques/AWS/aws.persistence.rolesanywhere-create-trust-anchor.md index 82f86315..fef2e1e8 100755 --- a/docs/attack-techniques/AWS/aws.persistence.rolesanywhere-create-trust-anchor.md +++ b/docs/attack-techniques/AWS/aws.persistence.rolesanywhere-create-trust-anchor.md @@ -58,10 +58,10 @@ Identify when a trust anchor is created, through CloudTrail's CreateTrustA The following CloudTrail events are generated when this technique is detonated[^1]: -- `rolesanywhere:CreateTrustAnchor` - - `rolesanywhere:CreateProfile` +- `rolesanywhere:CreateTrustAnchor` + ??? "View raw detonation logs" diff --git a/docs/attack-techniques/AWS/aws.privilege-escalation.iam-update-user-login-profile.md b/docs/attack-techniques/AWS/aws.privilege-escalation.iam-update-user-login-profile.md new file mode 100755 index 00000000..9b2f089a --- /dev/null +++ b/docs/attack-techniques/AWS/aws.privilege-escalation.iam-update-user-login-profile.md @@ -0,0 +1,99 @@ +--- +title: Change IAM user password +--- + +# Change IAM user password + + + idempotent + +Platform: AWS + +## MITRE ATT&CK Tactics + + +- Privilege Escalation + +## Description + + +Establishes persistence by updating a Login Profile on an existing IAM user to change its password. This allows an attacker to hijack +an IAM user with an existing login profile. + +Warm-up: + +- Create an IAM user with a login profile + +Detonation: + +- Update the user's login profile to change its password + +References: + +- https://www.invictus-ir.com/news/the-curious-case-of-dangerdev-protonmail-me +- https://expel.com/blog/incident-report-from-cli-to-console-chasing-an-attacker-in-aws/ +- https://permiso.io/blog/lucr-3-scattered-spider-getting-saas-y-in-the-cloud + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate aws.privilege-escalation.iam-update-user-login-profile +``` +## Detection + + +Through CloudTrail's UpdateLoginProfile events. + + + +## Detonation logs new! + +The following CloudTrail events are generated when this technique is detonated[^1]: + + +- `iam:UpdateLoginProfile` + + +??? "View raw detonation logs" + + ```json hl_lines="6" + + [ + { + "awsRegion": "megov-southcentral-3r", + "eventCategory": "Management", + "eventID": "a46a1a42-9ef1-48d4-9c61-507eb6d4019f", + "eventName": "UpdateLoginProfile", + "eventSource": "iam.amazonaws.com", + "eventTime": "2024-08-28T09:54:40Z", + "eventType": "AwsApiCall", + "eventVersion": "1.09", + "managementEvent": true, + "readOnly": false, + "recipientAccountId": "763751499319", + "requestID": "bd8967e5-b80d-48cd-b8b5-45c9905a4a7f", + "requestParameters": { + "userName": "stratus-red-team-update-login-profile-user" + }, + "responseElements": null, + "sourceIPAddress": "212.3.253.233", + "tlsDetails": { + "cipherSuite": "TLS_AES_128_GCM_SHA256", + "clientProvidedHostHeader": "iam.amazonaws.com", + "tlsVersion": "TLSv1.3" + }, + "userAgent": "stratus-red-team_33d1bcd6-0716-4e7f-a145-8a75625cf180", + "userIdentity": { + "accessKeyId": "AKIAV1MIS7NGMDMR83FC", + "accountId": "763751499319", + "arn": "arn:aws:iam::763751499319:user/christophe", + "principalId": "AIDAXYBG3LDVX65FGD9O", + "type": "IAMUser", + "userName": "christophe" + } + } + ] + ``` + +[^1]: These logs have been gathered from a real detonation of this technique in a test environment using [Grimoire](https://github.com/DataDog/grimoire), and anonymized using [LogLicker](https://github.com/Permiso-io-tools/LogLicker). diff --git a/docs/attack-techniques/AWS/index.md b/docs/attack-techniques/AWS/index.md index 9d964d3c..f21092f5 100755 --- a/docs/attack-techniques/AWS/index.md +++ b/docs/attack-techniques/AWS/index.md @@ -121,3 +121,5 @@ Note that some Stratus attack techniques may correspond to more than a single AT - [Create an IAM Roles Anywhere trust anchor](./aws.persistence.rolesanywhere-create-trust-anchor.md) +- [Change IAM user password](./aws.privilege-escalation.iam-update-user-login-profile.md) + diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md index aeb9f500..376463fe 100755 --- a/docs/attack-techniques/list.md +++ b/docs/attack-techniques/list.md @@ -47,6 +47,7 @@ This page contains the list of all Stratus Attack Techniques. | [Add a Malicious Lambda Extension](./AWS/aws.persistence.lambda-layer-extension.md) | [AWS](./AWS/index.md) | Persistence, Privilege Escalation | | [Overwrite Lambda Function Code](./AWS/aws.persistence.lambda-overwrite-code.md) | [AWS](./AWS/index.md) | Persistence | | [Create an IAM Roles Anywhere trust anchor](./AWS/aws.persistence.rolesanywhere-create-trust-anchor.md) | [AWS](./AWS/index.md) | Persistence, Privilege Escalation | +| [Change IAM user password](./AWS/aws.privilege-escalation.iam-update-user-login-profile.md) | [AWS](./AWS/index.md) | Privilege Escalation | | [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 | diff --git a/docs/contributing.md b/docs/contributing.md index cce618e8..271429ea 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -6,6 +6,40 @@ We welcome pull requests, contributions and feedback! For any bug report or feed Stratus Red Team is opinionated in the attack techniques it packages - see [Philosophy](./attack-techniques/philosophy.md). Feel free to open an issue to discuss ideas about new attack techniques. You can see the current backlog using the GitHub issue label [`new-technique`](https://github.com/DataDog/stratus-red-team/issues?q=is%3Aissue+is%3Aopen+label%3Anew-technique). +To create a new attack technique: +1. Create a new folder under `v2/internal/attacktechniques/your-cloud/your-mitre-attack-tactic/your-attack-name` +2. Create a `main.go` file that contains the detonation (and optionally, the revert) behavior. See for example [cloudtrail-stop/main.go](https://github.com/DataDog/stratus-red-team/blob/main/v2/internal/attacktechniques/aws/defense-evasion/cloudtrail-stop/main.go) +3. If your attack technique contains pre-requisites, create a `main.tf` file +4. Add your attack technique to the imports of `v2/internal/attacktechniques/main.go` + +To generate the logs dataset using [Grimoire](https://github.com/DataDog/grimoire): +1. Install Grimoire +2. Run the following to detonate the attack and retrieve CloudTrail logs: + +```bash +# Build your local Stratus Red Team version +make + +# Generate cloud audit logs +./bin/stratus warmup your-attack +grimoire shell --command 'export STRATUS_RED_TEAM_DETONATION_ID=$GRIMOIRE_DETONATION_ID; ./bin/stratus detonate your-attack' -o /tmp/your-attack.json +# Press Ctrl+C once you see the expected events +./bin/stratus cleanup your-attack +``` + +3. Anonymize the logs using [LogLicker](https://github.com/Permiso-io-tools/LogLicker): + +```bash +# Note: see https://github.com/Permiso-io-tools/LogLicker/issues/5 for a currently necessary patch +../LogLicker/venv/bin/python ../LogLicker/RunLogLicker.py rawtext -ifp /tmp/your-attack.json -ofp ./docs/detonation-logs/your-attack.json +``` + +4. Generate the docs: + +```bash +make docs +``` + ## Contributing to the core of Stratus Red Team When contributing to the core of Stratus Red Team (i.e. anything that is not a new attack technique), include unit tests if applicable. \ No newline at end of file diff --git a/docs/detonation-logs/aws.privilege-escalation.iam-update-user-login-profile.json b/docs/detonation-logs/aws.privilege-escalation.iam-update-user-login-profile.json new file mode 100644 index 00000000..dd9d3e58 --- /dev/null +++ b/docs/detonation-logs/aws.privilege-escalation.iam-update-user-login-profile.json @@ -0,0 +1,35 @@ +[ + { + "awsRegion": "megov-southcentral-3r", + "eventCategory": "Management", + "eventID": "a46a1a42-9ef1-48d4-9c61-507eb6d4019f", + "eventName": "UpdateLoginProfile", + "eventSource": "iam.amazonaws.com", + "eventTime": "2024-08-28T09:54:40Z", + "eventType": "AwsApiCall", + "eventVersion": "1.09", + "managementEvent": true, + "readOnly": false, + "recipientAccountId": "763751499319", + "requestID": "bd8967e5-b80d-48cd-b8b5-45c9905a4a7f", + "requestParameters": { + "userName": "stratus-red-team-update-login-profile-user" + }, + "responseElements": null, + "sourceIPAddress": "212.3.253.233", + "tlsDetails": { + "cipherSuite": "TLS_AES_128_GCM_SHA256", + "clientProvidedHostHeader": "iam.amazonaws.com", + "tlsVersion": "TLSv1.3" + }, + "userAgent": "stratus-red-team_33d1bcd6-0716-4e7f-a145-8a75625cf180", + "userIdentity": { + "accessKeyId": "AKIAV1MIS7NGMDMR83FC", + "accountId": "763751499319", + "arn": "arn:aws:iam::763751499319:user/christophe", + "principalId": "AIDAXYBG3LDVX65FGD9O", + "type": "IAMUser", + "userName": "christophe" + } + } +] \ No newline at end of file diff --git a/docs/index.yaml b/docs/index.yaml index c4c21c32..58b93948 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -329,6 +329,13 @@ AWS: - Privilege Escalation platform: AWS isIdempotent: false + - id: aws.privilege-escalation.iam-update-user-login-profile + name: Change IAM user password + isSlow: false + mitreAttackTactics: + - Privilege Escalation + platform: AWS + isIdempotent: true EKS: Lateral Movement: - id: eks.lateral-movement.create-access-entry diff --git a/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password/main.go b/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password/main.go new file mode 100644 index 00000000..c7ffdf5d --- /dev/null +++ b/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password/main.go @@ -0,0 +1,72 @@ +package aws + +import ( + "context" + _ "embed" + "errors" + "github.com/aws/aws-sdk-go-v2/service/iam" + "github.com/datadog/stratus-red-team/v2/internal/utils" + "github.com/datadog/stratus-red-team/v2/pkg/stratus" + "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" + "log" +) + +//go:embed main.tf +var tf []byte + +func init() { + stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ + ID: "aws.privilege-escalation.iam-update-user-login-profile", + FriendlyName: "Change IAM user password", + Description: ` +Establishes persistence by updating a Login Profile on an existing IAM user to change its password. This allows an attacker to hijack +an IAM user with an existing login profile. + +Warm-up: + +- Create an IAM user with a login profile + +Detonation: + +- Update the user's login profile to change its password + +References: + +- https://www.invictus-ir.com/news/the-curious-case-of-dangerdev-protonmail-me +- https://expel.com/blog/incident-report-from-cli-to-console-chasing-an-attacker-in-aws/ +- https://permiso.io/blog/lucr-3-scattered-spider-getting-saas-y-in-the-cloud +`, + Detection: ` +Through CloudTrail's UpdateLoginProfile events. +`, + Platform: stratus.AWS, + IsIdempotent: true, + MitreAttackTactics: []mitreattack.Tactic{mitreattack.PrivilegeEscalation}, + PrerequisitesTerraformCode: tf, + Detonate: detonate, + }) +} + +func detonate(params map[string]string, providers stratus.CloudProviders) error { + iamClient := iam.NewFromConfig(providers.AWS().GetConnection()) + userName := params["user_name"] + newPassword := utils.RandomString(16) + ".#1Aa" // extra characters to ensure we meet password requirements, no matter the password policy + + log.Println("Changing console password for IAM user " + userName) + _, err := iamClient.UpdateLoginProfile(context.Background(), &iam.UpdateLoginProfileInput{ + UserName: &userName, + Password: &newPassword, + }) + if err != nil { + return errors.New("unable to update IAM login profile: " + err.Error()) + } + + accountId, _ := utils.GetCurrentAccountId(providers.AWS().GetConnection()) + log.Println("Updated console password for user") + loginUrl := "https://" + accountId + ".signin.aws.amazon.com/console" + log.Println("You can log in at: " + loginUrl) + log.Println("User name: " + userName) + log.Println("Password: " + newPassword) + + return nil +} diff --git a/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password/main.tf b/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password/main.tf new file mode 100644 index 00000000..8ac5a5ee --- /dev/null +++ b/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password/main.tf @@ -0,0 +1,43 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} +provider "aws" { + skip_region_validation = true + skip_credentials_validation = true + skip_get_ec2_platforms = true + default_tags { + tags = { + StratusRedTeam = true + } + } +} + +locals { + resource_prefix = "stratus-red-team-update-login-profile" +} + +resource "aws_iam_user" "legit-user" { + name = "${local.resource_prefix}-user" + force_destroy = true +} + +resource "aws_iam_user_login_profile" "example" { + user = aws_iam_user.legit-user.name + password_length = 20 + password_reset_required = false + pgp_key = "keybase:christophetd" // NOTE: this field is required, but we don't even output/use the password +} + + +output "user_name" { + value = aws_iam_user.legit-user.name +} + +output "display" { + value = format("IAM user %s ready", aws_iam_user.legit-user.name) +} \ No newline at end of file diff --git a/v2/internal/attacktechniques/main.go b/v2/internal/attacktechniques/main.go index 147af3f4..10faafc0 100644 --- a/v2/internal/attacktechniques/main.go +++ b/v2/internal/attacktechniques/main.go @@ -39,6 +39,7 @@ import ( _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/persistence/lambda-layer-extension" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/persistence/lambda-overwrite-code" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/persistence/rolesanywhere-create-trust-anchor" + _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-custom-script-extension" _ "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" diff --git a/v2/pkg/stratus/runner/runner.go b/v2/pkg/stratus/runner/runner.go index af232561..7ab68257 100644 --- a/v2/pkg/stratus/runner/runner.go +++ b/v2/pkg/stratus/runner/runner.go @@ -8,6 +8,7 @@ import ( "github.com/datadog/stratus-red-team/v2/pkg/stratus/useragent" "github.com/google/uuid" "log" + "os" "path/filepath" "strings" ) @@ -15,6 +16,8 @@ import ( const StratusRunnerForce = true const StratusRunnerNoForce = false +const EnvVarStratusRedTeamDetonationId = "STRATUS_RED_TEAM_DETONATION_ID" + type runnerImpl struct { Technique *stratus.AttackTechnique TechniqueState stratus.AttackTechniqueState @@ -44,14 +47,25 @@ func NewRunner(technique *stratus.AttackTechnique, force bool) Runner { func NewRunnerWithContext(ctx context.Context, technique *stratus.AttackTechnique, force bool) Runner { stateManager := state.NewFileSystemStateManager(technique) - uuid := uuid.New() + + var correlationId = uuid.New() + var err error + if grimoireDetonationId := os.Getenv("STRATUS_RED_TEAM_DETONATION_ID"); grimoireDetonationId != "" { + log.Println("STRATUS_RED_TEAM_DETONATION_ID is set, using it as the correlation ID") + correlationId, err = uuid.Parse(grimoireDetonationId) + if err != nil { + log.Println("STRATUS_RED_TEAM_DETONATION_ID is not a valid UUID, falling back to a randomly-generated one: " + err.Error()) + correlationId = uuid.New() + } + } + runner := &runnerImpl{ Technique: technique, ShouldForce: force, StateManager: stateManager, - UniqueCorrelationID: uuid, + UniqueCorrelationID: correlationId, TerraformManager: NewTerraformManagerWithContext( - ctx, filepath.Join(stateManager.GetRootDirectory(), "terraform"), useragent.GetStratusUserAgentForUUID(uuid), + ctx, filepath.Join(stateManager.GetRootDirectory(), "terraform"), useragent.GetStratusUserAgentForUUID(correlationId), ), Context: ctx, } diff --git a/v2/tools/generate-techniques-documentation.go b/v2/tools/generate-techniques-documentation.go index b41744d2..fc6c5bd7 100644 --- a/v2/tools/generate-techniques-documentation.go +++ b/v2/tools/generate-techniques-documentation.go @@ -8,6 +8,7 @@ import ( "log" "os" "path/filepath" + "sort" "strings" "text/template" @@ -143,6 +144,7 @@ func findDetonationLogs(technique *stratus.AttackTechnique) *DetonationLogs { for k := range eventNamesSet { eventNames = append(eventNames, k) } + sort.Strings(eventNames) rawLogs := strings.ReplaceAll(string(data), "\n", "\n\t") // indent for markdown var eventNameLines []int