diff --git a/docs/attack-techniques/AWS/aws.discovery.ses-enumerate.md b/docs/attack-techniques/AWS/aws.discovery.ses-enumerate.md new file mode 100755 index 000000000..40260c98a --- /dev/null +++ b/docs/attack-techniques/AWS/aws.discovery.ses-enumerate.md @@ -0,0 +1,51 @@ +--- +title: Enumerate SES +--- + +# Enumerate SES + + + idempotent + +Platform: AWS + +## MITRE ATT&CK Tactics + + +- Discovery + +## Description + + +Simulates an attacker enumerating SES. Attackers frequently use this enumeration technique after having compromised an access key, to use it to launch phishing campaigns or further resell stolen credentials. + +Warm-up: None. + +Detonation: + +- Perform ses:GetAccountSendingEnabled to check if SES sending is enabled. +- Perform ses:GetSendQuota to discover the current [email sending quotas](https://docs.aws.amazon.com/ses/latest/APIReference/API_GetSendQuota.html). +- Perform ses:ListIdentities to discover the list of [identities](https://docs.aws.amazon.com/ses/latest/APIReference/API_ListIdentities.html) in the account. +- If identities are found, use ses:GetIdentityVerificationAttributes (only once) to discover [verification status](https://docs.aws.amazon.com/ses/latest/APIReference/API_GetIdentityVerificationAttributes.html) of each identity. + +References: + +- https://securitylabs.datadoghq.com/articles/following-attackers-trail-in-aws-methodology-findings-in-the-wild/#most-common-enumeration-techniques +- https://www.invictus-ir.com/news/ransomware-in-the-cloud +- https://unit42.paloaltonetworks.com/compromised-cloud-compute-credentials/#post-125981-_kdq0vw6banab +- https://permiso.io/blog/s/aws-ses-pionage-detecting-ses-abuse/ +- https://www.invictus-ir.com/news/the-curious-case-of-dangerdev-protonmail-me + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate aws.discovery.ses-enumerate +``` +## Detection + + +Through CloudTrail's GetAccountSendingEnabled, GetSendQuota and ListIdentities events. +These can be considered suspicious especially when performed by a long-lived access key, or when the calls span across multiple regions. + + diff --git a/docs/attack-techniques/AWS/index.md b/docs/attack-techniques/AWS/index.md index 47a7741c0..9d964d3cc 100755 --- a/docs/attack-techniques/AWS/index.md +++ b/docs/attack-techniques/AWS/index.md @@ -40,6 +40,8 @@ Note that some Stratus attack techniques may correspond to more than a single AT - [Download EC2 Instance User Data](./aws.discovery.ec2-download-user-data.md) +- [Enumerate SES](./aws.discovery.ses-enumerate.md) + ## Execution diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md index 8ff5a5ba3..0da70d670 100755 --- a/docs/attack-techniques/list.md +++ b/docs/attack-techniques/list.md @@ -23,6 +23,7 @@ This page contains the list of all Stratus Attack Techniques. | [Remove VPC Flow Logs](./AWS/aws.defense-evasion.vpc-remove-flow-logs.md) | [AWS](./AWS/index.md) | Defense Evasion | | [Execute Discovery Commands on an EC2 Instance](./AWS/aws.discovery.ec2-enumerate-from-instance.md) | [AWS](./AWS/index.md) | Discovery | | [Download EC2 Instance User Data](./AWS/aws.discovery.ec2-download-user-data.md) | [AWS](./AWS/index.md) | Discovery | +| [Enumerate SES](./AWS/aws.discovery.ses-enumerate.md) | [AWS](./AWS/index.md) | Discovery | | [Launch Unusual EC2 instances](./AWS/aws.execution.ec2-launch-unusual-instances.md) | [AWS](./AWS/index.md) | Execution | | [Execute Commands on EC2 Instance via User Data](./AWS/aws.execution.ec2-user-data.md) | [AWS](./AWS/index.md) | Execution, Privilege Escalation | | [Usage of ssm:SendCommand on multiple instances](./AWS/aws.execution.ssm-send-command.md) | [AWS](./AWS/index.md) | Execution | @@ -56,7 +57,6 @@ This page contains the list of all Stratus Attack Techniques. | [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 | | [Invite an External User to a GCP Project](./GCP/gcp.persistence.invite-external-user.md) | [GCP](./GCP/index.md) | Persistence | -| [Impersonate GCP Service Accounts](./GCP/gcp.privilege-escalation.impersonate-service-accounts.md) | [GCP](./GCP/index.md) | Privilege Escalation | | [Dump All Secrets](./kubernetes/k8s.credential-access.dump-secrets.md) | [Kubernetes](./kubernetes/index.md) | Credential Access | | [Steal Pod Service Account Token](./kubernetes/k8s.credential-access.steal-serviceaccount-token.md) | [Kubernetes](./kubernetes/index.md) | Credential Access | | [Create Admin ClusterRole](./kubernetes/k8s.persistence.create-admin-clusterrole.md) | [Kubernetes](./kubernetes/index.md) | Persistence, Privilege Escalation | @@ -65,3 +65,4 @@ This page contains the list of all Stratus Attack Techniques. | [Container breakout via hostPath volume mount](./kubernetes/k8s.privilege-escalation.hostpath-volume.md) | [Kubernetes](./kubernetes/index.md) | Privilege Escalation | | [Privilege escalation through node/proxy permissions](./kubernetes/k8s.privilege-escalation.nodes-proxy.md) | [Kubernetes](./kubernetes/index.md) | Privilege Escalation | | [Run a Privileged Pod](./kubernetes/k8s.privilege-escalation.privileged-pod.md) | [Kubernetes](./kubernetes/index.md) | Privilege Escalation | +| [Impersonate GCP Service Accounts](./GCP/gcp.privilege-escalation.impersonate-service-accounts.md) | [GCP](./GCP/index.md) | Privilege Escalation | diff --git a/docs/index.yaml b/docs/index.yaml index edde566c0..cdb2d5a34 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -100,6 +100,13 @@ AWS: - Discovery platform: AWS isIdempotent: true + - id: aws.discovery.ses-enumerate + name: Enumerate SES + isSlow: false + mitreAttackTactics: + - Discovery + platform: AWS + isIdempotent: true Execution: - id: aws.execution.ec2-launch-unusual-instances name: Launch Unusual EC2 instances diff --git a/v2/go.mod b/v2/go.mod index 1797d3d6a..4097ce91f 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -8,7 +8,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 - github.com/aws/aws-sdk-go-v2 v1.24.1 + github.com/aws/aws-sdk-go-v2 v1.26.1 github.com/aws/aws-sdk-go-v2/config v1.25.11 github.com/aws/aws-sdk-go-v2/credentials v1.16.9 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4 @@ -23,9 +23,11 @@ require ( github.com/aws/aws-sdk-go-v2/service/route53resolver v1.25.0 github.com/aws/aws-sdk-go-v2/service/s3 v1.47.2 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.2 + github.com/aws/aws-sdk-go-v2/service/ses v1.22.4 + github.com/aws/aws-sdk-go-v2/service/sesv2 v1.27.3 github.com/aws/aws-sdk-go-v2/service/ssm v1.44.2 github.com/aws/aws-sdk-go-v2/service/sts v1.26.2 - github.com/aws/smithy-go v1.19.0 + github.com/aws/smithy-go v1.20.2 github.com/cenkalti/backoff/v4 v4.2.1 github.com/fatih/color v1.13.0 github.com/golang-jwt/jwt v3.2.2+incompatible @@ -49,8 +51,8 @@ require ( github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.8 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.3 // indirect diff --git a/v2/go.sum b/v2/go.sum index 99bf75a81..49b86f7e0 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -34,8 +34,8 @@ github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/ github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= -github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= +github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= +github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3 h1:Zx9+31KyB8wQna6SXFWOewlgoY5uGdDAu6PTOEU3OQI= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3/go.mod h1:zxbEJhRdKTH1nqS2qu6UJ7zGe25xaHxZXaC2CvuQFnA= github.com/aws/aws-sdk-go-v2/config v1.25.11 h1:RWzp7jhPRliIcACefGkKp03L0Yofmd2p8M25kbiyvno= @@ -46,10 +46,10 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9 h1:FZVFahMyZle6WcogZCOxo6D github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9/go.mod h1:kjq7REMIkxdtcEC9/4BVXjOsNY5isz6jQbEgk6osRTU= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4 h1:TUCNKBd4/JEefsZDxo5deRmrRRPZHqGyBYiUAeBKOWU= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4/go.mod h1:egDkcl+zsgFqS6VO142bKboip5Pe1sNMwN55Xy38QsM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.8 h1:abKT+RuM1sdCNZIGIfZpLkvxEX3Rpsto019XG/rkYG8= @@ -84,6 +84,10 @@ github.com/aws/aws-sdk-go-v2/service/s3 v1.47.2 h1:DLSAG8zpJV2pYsU+UPkj1IEZghyBn github.com/aws/aws-sdk-go-v2/service/s3 v1.47.2/go.mod h1:thjZng67jGsvMyVZnSxlcqKyLwB0XTG8bHIRZPTJ+Bs= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.2 h1:JKbfiLwEqJp8zaOAOn6AVSMS96gdwP3TjBMvZYsbxqE= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.2/go.mod h1:pbBOMK8UicdDK11zsPSGbpFh9Xwbd1oD3t7pSxXgNxU= +github.com/aws/aws-sdk-go-v2/service/ses v1.22.4 h1:MNU3UWV47ylAAdlU+VxuyItYfuGGp00MvCBxdVAI3kM= +github.com/aws/aws-sdk-go-v2/service/ses v1.22.4/go.mod h1:M/ZQn5uXL4BP1qolIWrlN2SeoUFngJtU/oCwR4WOfZU= +github.com/aws/aws-sdk-go-v2/service/sesv2 v1.27.3 h1:8KP71cUPALMQxs8lhGiWcwdtqv1wsogigS7StDHq0IE= +github.com/aws/aws-sdk-go-v2/service/sesv2 v1.27.3/go.mod h1:WIpmp3q5Iw1AEhotd5OL03OFc0kOUoLPcqKFzcAOImU= github.com/aws/aws-sdk-go-v2/service/ssm v1.44.2 h1:lmdmYCvG1EJKGLEsUsYDNO6MwZyBZROrRg04Vrb5TwA= github.com/aws/aws-sdk-go-v2/service/ssm v1.44.2/go.mod h1:pHJ1md/3F3WkYfZ4JKOllPfXQi4NiWk7NxbeOD53HQc= github.com/aws/aws-sdk-go-v2/service/sso v1.18.2 h1:xJPydhNm0Hiqct5TVKEuHG7weC0+sOs4MUnd7A5n5F4= @@ -92,8 +96,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.2 h1:8dU9zqA77C5egbU6yd4hFLai github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.2/go.mod h1:7Lt5mjQ8x5rVdKqg+sKKDeuwoszDJIIPmkd8BVsEdS0= github.com/aws/aws-sdk-go-v2/service/sts v1.26.2 h1:fFrLsy08wEbAisqW3KDl/cPHrF43GmV79zXB9EwJiZw= github.com/aws/aws-sdk-go-v2/service/sts v1.26.2/go.mod h1:7Ld9eTqocTvJqqJ5K/orbSDwmGcpRdlDiLjz2DO+SL8= -github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= -github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= +github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= +github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/v2/internal/attacktechniques/aws/discovery/ses-enumerate/main.go b/v2/internal/attacktechniques/aws/discovery/ses-enumerate/main.go new file mode 100644 index 000000000..ac1ea9242 --- /dev/null +++ b/v2/internal/attacktechniques/aws/discovery/ses-enumerate/main.go @@ -0,0 +1,94 @@ +package aws + +import ( + "context" + _ "embed" + "fmt" + "github.com/datadog/stratus-red-team/v2/pkg/stratus" + "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" + "log" + + "github.com/aws/aws-sdk-go-v2/service/ses" +) + +func init() { + stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ + ID: "aws.discovery.ses-enumerate", + FriendlyName: "Enumerate SES", + Description: ` +Simulates an attacker enumerating SES. Attackers frequently use this enumeration technique after having compromised an access key, to use it to launch phishing campaigns or further resell stolen credentials. + +Warm-up: None. + +Detonation: + +- Perform ses:GetAccountSendingEnabled to check if SES sending is enabled. +- Perform ses:GetSendQuota to discover the current [email sending quotas](https://docs.aws.amazon.com/ses/latest/APIReference/API_GetSendQuota.html). +- Perform ses:ListIdentities to discover the list of [identities](https://docs.aws.amazon.com/ses/latest/APIReference/API_ListIdentities.html) in the account. +- If identities are found, use ses:GetIdentityVerificationAttributes (only once) to discover [verification status](https://docs.aws.amazon.com/ses/latest/APIReference/API_GetIdentityVerificationAttributes.html) of each identity. + +References: + +- https://securitylabs.datadoghq.com/articles/following-attackers-trail-in-aws-methodology-findings-in-the-wild/#most-common-enumeration-techniques +- https://www.invictus-ir.com/news/ransomware-in-the-cloud +- https://unit42.paloaltonetworks.com/compromised-cloud-compute-credentials/#post-125981-_kdq0vw6banab +- https://permiso.io/blog/s/aws-ses-pionage-detecting-ses-abuse/ +- https://www.invictus-ir.com/news/the-curious-case-of-dangerdev-protonmail-me +`, + Detection: ` +Through CloudTrail's GetAccountSendingEnabled, GetSendQuota and ListIdentities events. +These can be considered suspicious especially when performed by a long-lived access key, or when the calls span across multiple regions. +`, + Platform: stratus.AWS, + IsIdempotent: true, + MitreAttackTactics: []mitreattack.Tactic{mitreattack.Discovery}, + Detonate: detonate, + }) +} + +func detonate(_ map[string]string, providers stratus.CloudProviders) error { + awsConnection := providers.AWS().GetConnection() + sesClient := ses.NewFromConfig(awsConnection) + + log.Println("Checking is SES e-mail sending is enabled in the current region") + result, err := sesClient.GetAccountSendingEnabled(context.Background(), &ses.GetAccountSendingEnabledInput{}) + if err != nil { + return fmt.Errorf("unable to check if SES sending is enabled: %w", err) + } + if result.Enabled { + log.Println("SES e-mail sending is enabled") + } else { + log.Println("SES e-mail sending is disabled") + } + + log.Println("Enumerating verified SES identities using ses:ListIdentities") + identities, err := sesClient.ListIdentities(context.Background(), &ses.ListIdentitiesInput{}) + if err != nil { + return fmt.Errorf("unable to list SES identities: %w", err) + } + + if len(identities.Identities) == 0 { + log.Println("No verified SES identities found") + } else { + log.Printf("Found %d verified SES identities", len(identities.Identities)) + verificationAttributes, err := sesClient.GetIdentityVerificationAttributes(context.Background(), &ses.GetIdentityVerificationAttributesInput{ + Identities: identities.Identities, + }) + if err != nil { + return fmt.Errorf("unable to get identity verification attributes: %w", err) + } + for identity := range verificationAttributes.VerificationAttributes { + log.Printf("- Identity %s has verification status '%s'", identity, verificationAttributes.VerificationAttributes[identity].VerificationStatus) + } + } + + log.Println("Enumerating SES quotas") + quotas, err := sesClient.GetSendQuota(context.Background(), &ses.GetSendQuotaInput{}) + if err != nil { + return fmt.Errorf("unable to get SES quotas: %w", err) + } + + log.Printf("Current quotas: max24hoursend: %d, maxsendrate: %d, sentlast24hours: %d\n", int(quotas.Max24HourSend), int(quotas.MaxSendRate), int(quotas.SentLast24Hours)) + + return nil +} diff --git a/v2/internal/attacktechniques/main.go b/v2/internal/attacktechniques/main.go index 0e3a5c21d..ff5a64246 100644 --- a/v2/internal/attacktechniques/main.go +++ b/v2/internal/attacktechniques/main.go @@ -15,6 +15,7 @@ import ( _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/defense-evasion/vpc-remove-flow-logs" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/discovery/ec2-enumerate-from-instance" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/discovery/ec2-get-user-data" + _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/discovery/ses-enumerate" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/execution/ec2-launch-unusual-instances" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/execution/ec2-user-data" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/execution/ssm-send-command"