diff --git a/docs/attack-techniques/AWS/aws.lateral-movement.ec2-serial-console-send-ssh-public-key.md b/docs/attack-techniques/AWS/aws.lateral-movement.ec2-serial-console-send-ssh-public-key.md new file mode 100755 index 00000000..c71b8565 --- /dev/null +++ b/docs/attack-techniques/AWS/aws.lateral-movement.ec2-serial-console-send-ssh-public-key.md @@ -0,0 +1,277 @@ +--- +title: Usage of EC2 Serial Console to push SSH public key +--- + +# Usage of EC2 Serial Console to push SSH public key + + slow + idempotent + +Platform: AWS + +## MITRE ATT&CK Tactics + + +- Lateral Movement + +## Description + + +Simulates an attacker using EC2 Instance Connect to push an SSH public key to multiple EC2 instances, using SendSerialConsoleSSHPublicKey. This allows anyone +with the corresponding private key to connect directly to the systems via SSH, assuming they have appropriate network connectivity. + +Warm-up: + +- Create multiple EC2 instances and a VPC (takes a few minutes). + +Detonation: + +- Adds a public SSH key to the EC2 instances using SendSerialConsoleSSHPublicKey. + +References: + +- https://docs.aws.amazon.com/ec2-instance-connect/latest/APIReference/API_SendSerialConsoleSSHPublicKey.html +- https://permiso.io/blog/lucr-3-scattered-spider-getting-saas-y-in-the-cloud +- https://fwdcloudsec.org/assets/presentations/2024/europe/sebastian-walla-cloud-conscious-tactics-techniques-and-procedures-an-overview.pdf +- https://unit42.paloaltonetworks.com/cloud-lateral-movement-techniques/ +- https://unit42.paloaltonetworks.com/cloud-virtual-machine-attack-vectors/ + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate aws.lateral-movement.ec2-serial-console-send-ssh-public-key +``` +## Detection + + +Identify, through CloudTrail's SendSerialConsoleSSHPublicKey event, when a user is adding an SSH key to EC2 instances. + + + +## Detonation logs new! + +The following CloudTrail events are generated when this technique is detonated[^1]: + + +- `ec2-instance-connect:SendSerialConsoleSSHPublicKey` + +- `ec2:EnableSerialConsoleAccess` + + +??? "View raw detonation logs" + + ```json hl_lines="6 57 109 161" + + [ + { + "awsRegion": "cniso-east-3r", + "eventCategory": "Management", + "eventID": "37ba412b-f943-44f2-ae48-4527f6e789d9", + "eventName": "EnableSerialConsoleAccess", + "eventSource": "ec2.amazonaws.com", + "eventTime": "2024-11-26T15:35:22Z", + "eventType": "AwsApiCall", + "eventVersion": "1.10", + "managementEvent": true, + "readOnly": false, + "recipientAccountId": "844015365555", + "requestID": "e110338f-cc06-4284-bf16-6528a7df1561", + "requestParameters": { + "EnableSerialConsoleAccessRequest": "" + }, + "responseElements": { + "EnableSerialConsoleAccessResponse": { + "requestId": "e110338f-cc06-4284-bf16-6528a7df1561", + "serialConsoleAccessEnabled": true, + "xmlns": "http://ec2.amazonaws.com/doc/2016-11-15/" + } + }, + "sourceIPAddress": "201.252.42.03", + "tlsDetails": { + "cipherSuite": "TLS_AES_128_GCM_SHA256", + "clientProvidedHostHeader": "ec2.cniso-east-3r.amazonaws.com", + "tlsVersion": "TLSv1.3" + }, + "userAgent": "stratus-red-team_b0fedc91-bd4a-4ba1-a776-80e707fef2a0", + "userIdentity": { + "accessKeyId": "ASIA2HJRQF0DHNYEE9N1", + "accountId": "844015365555", + "arn": "arn:aws:sts::844015365555:assumed-role/AWSReservedSSOrandoml3I7nL6f7BmB@gmail.com", + "principalId": "AROAEMHZD694LU95MUYOP:randomca0L529zwNAY@gmail.com", + "sessionContext": { + "attributes": { + "creationDate": "2024-11-26T15:14:58Z", + "mfaAuthenticated": "false" + }, + "sessionIssuer": { + "accountId": "844015365555", + "arn": "arn:aws:iam::844015365555:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_account-admin_599c9e90e350d2ff", + "principalId": "AROAEMHZD694LU95MUYOP", + "type": "Role", + "userName": "AWSReservedSSO_account-admin_599c9e90e350d2ff" + } + }, + "type": "AssumedRole" + } + }, + { + "awsRegion": "cniso-east-3r", + "eventCategory": "Management", + "eventID": "787b2464-f27b-4d4c-91bc-6396f2297d0e", + "eventName": "SendSerialConsoleSSHPublicKey", + "eventSource": "ec2-instance-connect.amazonaws.com", + "eventTime": "2024-11-26T15:35:23Z", + "eventType": "AwsApiCall", + "eventVersion": "1.08", + "managementEvent": true, + "readOnly": false, + "recipientAccountId": "844015365555", + "requestID": "c74b1e77-bc91-4174-b297-d06a71c89abf", + "requestParameters": { + "instanceId": "i-EFCb4e480CAbc4CF9", + "monitorMode": false, + "sSHPublicKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtAlK45MAEWZ7MUY2QEmi3M6W+peGL3VCrc0qH54xRu", + "serialPort": 0 + }, + "responseElements": { + "requestId": "c74b1e77-bc91-4174-b297-d06a71c89abf", + "success": true + }, + "sourceIPAddress": "201.252.42.03", + "tlsDetails": { + "cipherSuite": "TLS_AES_128_GCM_SHA256", + "clientProvidedHostHeader": "ec2-instance-connect.cniso-east-3r.amazonaws.com", + "tlsVersion": "TLSv1.3" + }, + "userAgent": "stratus-red-team_b0fedc91-bd4a-4ba1-a776-80e707fef2a0", + "userIdentity": { + "accessKeyId": "ASIA2HJRQF0DHNYEE9N1", + "accountId": "844015365555", + "arn": "arn:aws:sts::844015365555:assumed-role/AWSReservedSSOrandoml3I7nL6f7BmB@gmail.com", + "principalId": "AROAEMHZD694LU95MUYOP:randomca0L529zwNAY@gmail.com", + "sessionContext": { + "attributes": { + "creationDate": "2024-11-26T15:14:58Z", + "mfaAuthenticated": "false" + }, + "sessionIssuer": { + "accountId": "844015365555", + "arn": "arn:aws:iam::844015365555:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_account-admin_599c9e90e350d2ff", + "principalId": "AROAEMHZD694LU95MUYOP", + "type": "Role", + "userName": "AWSReservedSSO_account-admin_599c9e90e350d2ff" + }, + "webIdFederationData": {} + }, + "type": "AssumedRole" + } + }, + { + "awsRegion": "cniso-east-3r", + "eventCategory": "Management", + "eventID": "e49972cb-b394-43e2-aab5-602f1fb56f85", + "eventName": "SendSerialConsoleSSHPublicKey", + "eventSource": "ec2-instance-connect.amazonaws.com", + "eventTime": "2024-11-26T15:35:23Z", + "eventType": "AwsApiCall", + "eventVersion": "1.08", + "managementEvent": true, + "readOnly": false, + "recipientAccountId": "844015365555", + "requestID": "d392c0ca-351f-472f-9ca3-b411beb9df9c", + "requestParameters": { + "instanceId": "i-B2ABDCa5b78E0f1dd", + "monitorMode": false, + "sSHPublicKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtAlK45MAEWZ7MUY2QEmi3M6W+peGL3VCrc0qH54xRu", + "serialPort": 0 + }, + "responseElements": { + "requestId": "d392c0ca-351f-472f-9ca3-b411beb9df9c", + "success": true + }, + "sourceIPAddress": "201.252.42.03", + "tlsDetails": { + "cipherSuite": "TLS_AES_128_GCM_SHA256", + "clientProvidedHostHeader": "ec2-instance-connect.cniso-east-3r.amazonaws.com", + "tlsVersion": "TLSv1.3" + }, + "userAgent": "stratus-red-team_b0fedc91-bd4a-4ba1-a776-80e707fef2a0", + "userIdentity": { + "accessKeyId": "ASIA2HJRQF0DHNYEE9N1", + "accountId": "844015365555", + "arn": "arn:aws:sts::844015365555:assumed-role/AWSReservedSSOrandoml3I7nL6f7BmB@gmail.com", + "principalId": "AROAEMHZD694LU95MUYOP:randomca0L529zwNAY@gmail.com", + "sessionContext": { + "attributes": { + "creationDate": "2024-11-26T15:14:58Z", + "mfaAuthenticated": "false" + }, + "sessionIssuer": { + "accountId": "844015365555", + "arn": "arn:aws:iam::844015365555:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_account-admin_599c9e90e350d2ff", + "principalId": "AROAEMHZD694LU95MUYOP", + "type": "Role", + "userName": "AWSReservedSSO_account-admin_599c9e90e350d2ff" + }, + "webIdFederationData": {} + }, + "type": "AssumedRole" + } + }, + { + "awsRegion": "cniso-east-3r", + "eventCategory": "Management", + "eventID": "f4dc86c9-6b22-4643-a0e8-fcb97fcfae68", + "eventName": "SendSerialConsoleSSHPublicKey", + "eventSource": "ec2-instance-connect.amazonaws.com", + "eventTime": "2024-11-26T15:35:22Z", + "eventType": "AwsApiCall", + "eventVersion": "1.08", + "managementEvent": true, + "readOnly": false, + "recipientAccountId": "844015365555", + "requestID": "88c8e41e-7754-4377-983f-140f8ca5617e", + "requestParameters": { + "instanceId": "i-D46eD8FCdefED5aAE", + "monitorMode": false, + "sSHPublicKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtAlK45MAEWZ7MUY2QEmi3M6W+peGL3VCrc0qH54xRu", + "serialPort": 0 + }, + "responseElements": { + "requestId": "88c8e41e-7754-4377-983f-140f8ca5617e", + "success": true + }, + "sourceIPAddress": "201.252.42.03", + "tlsDetails": { + "cipherSuite": "TLS_AES_128_GCM_SHA256", + "clientProvidedHostHeader": "ec2-instance-connect.cniso-east-3r.amazonaws.com", + "tlsVersion": "TLSv1.3" + }, + "userAgent": "stratus-red-team_b0fedc91-bd4a-4ba1-a776-80e707fef2a0", + "userIdentity": { + "accessKeyId": "ASIA2HJRQF0DHNYEE9N1", + "accountId": "844015365555", + "arn": "arn:aws:sts::844015365555:assumed-role/AWSReservedSSOrandoml3I7nL6f7BmB@gmail.com", + "principalId": "AROAEMHZD694LU95MUYOP:randomca0L529zwNAY@gmail.com", + "sessionContext": { + "attributes": { + "creationDate": "2024-11-26T15:14:58Z", + "mfaAuthenticated": "false" + }, + "sessionIssuer": { + "accountId": "844015365555", + "arn": "arn:aws:iam::844015365555:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_account-admin_599c9e90e350d2ff", + "principalId": "AROAEMHZD694LU95MUYOP", + "type": "Role", + "userName": "AWSReservedSSO_account-admin_599c9e90e350d2ff" + }, + "webIdFederationData": {} + }, + "type": "AssumedRole" + } + } + ] + ``` + +[^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 a66dc9d5..5145dee1 100755 --- a/docs/attack-techniques/AWS/index.md +++ b/docs/attack-techniques/AWS/index.md @@ -85,6 +85,8 @@ Note that some Stratus attack techniques may correspond to more than a single AT ## Lateral Movement +- [Usage of EC2 Serial Console to push SSH public key](./aws.lateral-movement.ec2-serial-console-send-ssh-public-key.md) + - [Usage of EC2 Instance Connect on multiple instances](./aws.lateral-movement.ec2-instance-connect.md) diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md index be75c57b..40d1f63e 100755 --- a/docs/attack-techniques/list.md +++ b/docs/attack-techniques/list.md @@ -38,6 +38,7 @@ This page contains the list of all Stratus Attack Techniques. | [S3 Ransomware through client-side encryption](./AWS/aws.impact.s3-ransomware-client-side-encryption.md) | [AWS](./AWS/index.md) | Impact | | [S3 Ransomware through individual file deletion](./AWS/aws.impact.s3-ransomware-individual-deletion.md) | [AWS](./AWS/index.md) | Impact | | [Console Login without MFA](./AWS/aws.initial-access.console-login-without-mfa.md) | [AWS](./AWS/index.md) | Initial Access | +| [Usage of EC2 Serial Console to push SSH public key](./AWS/aws.lateral-movement.ec2-serial-console-send-ssh-public-key.md) | [AWS](./AWS/index.md) | Lateral Movement | | [Usage of EC2 Instance Connect on multiple instances](./AWS/aws.lateral-movement.ec2-instance-connect.md) | [AWS](./AWS/index.md) | Lateral Movement | | [Backdoor an IAM Role](./AWS/aws.persistence.iam-backdoor-role.md) | [AWS](./AWS/index.md) | Persistence | | [Create an Access Key on an IAM User](./AWS/aws.persistence.iam-backdoor-user.md) | [AWS](./AWS/index.md) | Persistence, Privilege Escalation | diff --git a/docs/detonation-logs/aws.lateral-movement.ec2-serial-console-send-ssh-public-key.json b/docs/detonation-logs/aws.lateral-movement.ec2-serial-console-send-ssh-public-key.json new file mode 100644 index 00000000..2fa0ce23 --- /dev/null +++ b/docs/detonation-logs/aws.lateral-movement.ec2-serial-console-send-ssh-public-key.json @@ -0,0 +1,209 @@ +[ + { + "awsRegion": "cniso-east-3r", + "eventCategory": "Management", + "eventID": "37ba412b-f943-44f2-ae48-4527f6e789d9", + "eventName": "EnableSerialConsoleAccess", + "eventSource": "ec2.amazonaws.com", + "eventTime": "2024-11-26T15:35:22Z", + "eventType": "AwsApiCall", + "eventVersion": "1.10", + "managementEvent": true, + "readOnly": false, + "recipientAccountId": "844015365555", + "requestID": "e110338f-cc06-4284-bf16-6528a7df1561", + "requestParameters": { + "EnableSerialConsoleAccessRequest": "" + }, + "responseElements": { + "EnableSerialConsoleAccessResponse": { + "requestId": "e110338f-cc06-4284-bf16-6528a7df1561", + "serialConsoleAccessEnabled": true, + "xmlns": "http://ec2.amazonaws.com/doc/2016-11-15/" + } + }, + "sourceIPAddress": "201.252.42.03", + "tlsDetails": { + "cipherSuite": "TLS_AES_128_GCM_SHA256", + "clientProvidedHostHeader": "ec2.cniso-east-3r.amazonaws.com", + "tlsVersion": "TLSv1.3" + }, + "userAgent": "stratus-red-team_b0fedc91-bd4a-4ba1-a776-80e707fef2a0", + "userIdentity": { + "accessKeyId": "ASIA2HJRQF0DHNYEE9N1", + "accountId": "844015365555", + "arn": "arn:aws:sts::844015365555:assumed-role/AWSReservedSSOrandoml3I7nL6f7BmB@gmail.com", + "principalId": "AROAEMHZD694LU95MUYOP:randomca0L529zwNAY@gmail.com", + "sessionContext": { + "attributes": { + "creationDate": "2024-11-26T15:14:58Z", + "mfaAuthenticated": "false" + }, + "sessionIssuer": { + "accountId": "844015365555", + "arn": "arn:aws:iam::844015365555:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_account-admin_599c9e90e350d2ff", + "principalId": "AROAEMHZD694LU95MUYOP", + "type": "Role", + "userName": "AWSReservedSSO_account-admin_599c9e90e350d2ff" + } + }, + "type": "AssumedRole" + } + }, + { + "awsRegion": "cniso-east-3r", + "eventCategory": "Management", + "eventID": "787b2464-f27b-4d4c-91bc-6396f2297d0e", + "eventName": "SendSerialConsoleSSHPublicKey", + "eventSource": "ec2-instance-connect.amazonaws.com", + "eventTime": "2024-11-26T15:35:23Z", + "eventType": "AwsApiCall", + "eventVersion": "1.08", + "managementEvent": true, + "readOnly": false, + "recipientAccountId": "844015365555", + "requestID": "c74b1e77-bc91-4174-b297-d06a71c89abf", + "requestParameters": { + "instanceId": "i-EFCb4e480CAbc4CF9", + "monitorMode": false, + "sSHPublicKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtAlK45MAEWZ7MUY2QEmi3M6W+peGL3VCrc0qH54xRu", + "serialPort": 0 + }, + "responseElements": { + "requestId": "c74b1e77-bc91-4174-b297-d06a71c89abf", + "success": true + }, + "sourceIPAddress": "201.252.42.03", + "tlsDetails": { + "cipherSuite": "TLS_AES_128_GCM_SHA256", + "clientProvidedHostHeader": "ec2-instance-connect.cniso-east-3r.amazonaws.com", + "tlsVersion": "TLSv1.3" + }, + "userAgent": "stratus-red-team_b0fedc91-bd4a-4ba1-a776-80e707fef2a0", + "userIdentity": { + "accessKeyId": "ASIA2HJRQF0DHNYEE9N1", + "accountId": "844015365555", + "arn": "arn:aws:sts::844015365555:assumed-role/AWSReservedSSOrandoml3I7nL6f7BmB@gmail.com", + "principalId": "AROAEMHZD694LU95MUYOP:randomca0L529zwNAY@gmail.com", + "sessionContext": { + "attributes": { + "creationDate": "2024-11-26T15:14:58Z", + "mfaAuthenticated": "false" + }, + "sessionIssuer": { + "accountId": "844015365555", + "arn": "arn:aws:iam::844015365555:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_account-admin_599c9e90e350d2ff", + "principalId": "AROAEMHZD694LU95MUYOP", + "type": "Role", + "userName": "AWSReservedSSO_account-admin_599c9e90e350d2ff" + }, + "webIdFederationData": {} + }, + "type": "AssumedRole" + } + }, + { + "awsRegion": "cniso-east-3r", + "eventCategory": "Management", + "eventID": "e49972cb-b394-43e2-aab5-602f1fb56f85", + "eventName": "SendSerialConsoleSSHPublicKey", + "eventSource": "ec2-instance-connect.amazonaws.com", + "eventTime": "2024-11-26T15:35:23Z", + "eventType": "AwsApiCall", + "eventVersion": "1.08", + "managementEvent": true, + "readOnly": false, + "recipientAccountId": "844015365555", + "requestID": "d392c0ca-351f-472f-9ca3-b411beb9df9c", + "requestParameters": { + "instanceId": "i-B2ABDCa5b78E0f1dd", + "monitorMode": false, + "sSHPublicKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtAlK45MAEWZ7MUY2QEmi3M6W+peGL3VCrc0qH54xRu", + "serialPort": 0 + }, + "responseElements": { + "requestId": "d392c0ca-351f-472f-9ca3-b411beb9df9c", + "success": true + }, + "sourceIPAddress": "201.252.42.03", + "tlsDetails": { + "cipherSuite": "TLS_AES_128_GCM_SHA256", + "clientProvidedHostHeader": "ec2-instance-connect.cniso-east-3r.amazonaws.com", + "tlsVersion": "TLSv1.3" + }, + "userAgent": "stratus-red-team_b0fedc91-bd4a-4ba1-a776-80e707fef2a0", + "userIdentity": { + "accessKeyId": "ASIA2HJRQF0DHNYEE9N1", + "accountId": "844015365555", + "arn": "arn:aws:sts::844015365555:assumed-role/AWSReservedSSOrandoml3I7nL6f7BmB@gmail.com", + "principalId": "AROAEMHZD694LU95MUYOP:randomca0L529zwNAY@gmail.com", + "sessionContext": { + "attributes": { + "creationDate": "2024-11-26T15:14:58Z", + "mfaAuthenticated": "false" + }, + "sessionIssuer": { + "accountId": "844015365555", + "arn": "arn:aws:iam::844015365555:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_account-admin_599c9e90e350d2ff", + "principalId": "AROAEMHZD694LU95MUYOP", + "type": "Role", + "userName": "AWSReservedSSO_account-admin_599c9e90e350d2ff" + }, + "webIdFederationData": {} + }, + "type": "AssumedRole" + } + }, + { + "awsRegion": "cniso-east-3r", + "eventCategory": "Management", + "eventID": "f4dc86c9-6b22-4643-a0e8-fcb97fcfae68", + "eventName": "SendSerialConsoleSSHPublicKey", + "eventSource": "ec2-instance-connect.amazonaws.com", + "eventTime": "2024-11-26T15:35:22Z", + "eventType": "AwsApiCall", + "eventVersion": "1.08", + "managementEvent": true, + "readOnly": false, + "recipientAccountId": "844015365555", + "requestID": "88c8e41e-7754-4377-983f-140f8ca5617e", + "requestParameters": { + "instanceId": "i-D46eD8FCdefED5aAE", + "monitorMode": false, + "sSHPublicKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtAlK45MAEWZ7MUY2QEmi3M6W+peGL3VCrc0qH54xRu", + "serialPort": 0 + }, + "responseElements": { + "requestId": "88c8e41e-7754-4377-983f-140f8ca5617e", + "success": true + }, + "sourceIPAddress": "201.252.42.03", + "tlsDetails": { + "cipherSuite": "TLS_AES_128_GCM_SHA256", + "clientProvidedHostHeader": "ec2-instance-connect.cniso-east-3r.amazonaws.com", + "tlsVersion": "TLSv1.3" + }, + "userAgent": "stratus-red-team_b0fedc91-bd4a-4ba1-a776-80e707fef2a0", + "userIdentity": { + "accessKeyId": "ASIA2HJRQF0DHNYEE9N1", + "accountId": "844015365555", + "arn": "arn:aws:sts::844015365555:assumed-role/AWSReservedSSOrandoml3I7nL6f7BmB@gmail.com", + "principalId": "AROAEMHZD694LU95MUYOP:randomca0L529zwNAY@gmail.com", + "sessionContext": { + "attributes": { + "creationDate": "2024-11-26T15:14:58Z", + "mfaAuthenticated": "false" + }, + "sessionIssuer": { + "accountId": "844015365555", + "arn": "arn:aws:iam::844015365555:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_account-admin_599c9e90e350d2ff", + "principalId": "AROAEMHZD694LU95MUYOP", + "type": "Role", + "userName": "AWSReservedSSO_account-admin_599c9e90e350d2ff" + }, + "webIdFederationData": {} + }, + "type": "AssumedRole" + } + } +] \ No newline at end of file diff --git a/docs/index.yaml b/docs/index.yaml index e87c594a..5779854d 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -211,6 +211,13 @@ AWS: platform: AWS isIdempotent: true Lateral Movement: + - id: aws.lateral-movement.ec2-serial-console-send-ssh-public-key + name: Usage of EC2 Serial Console to push SSH public key + isSlow: true + mitreAttackTactics: + - Lateral Movement + platform: AWS + isIdempotent: true - id: aws.lateral-movement.ec2-instance-connect name: Usage of EC2 Instance Connect on multiple instances isSlow: true diff --git a/v2/internal/attacktechniques/aws/lateral-movement/ec2-send-serial-console-send-ssh-public-key/main.go b/v2/internal/attacktechniques/aws/lateral-movement/ec2-send-serial-console-send-ssh-public-key/main.go new file mode 100644 index 00000000..91daa0a3 --- /dev/null +++ b/v2/internal/attacktechniques/aws/lateral-movement/ec2-send-serial-console-send-ssh-public-key/main.go @@ -0,0 +1,128 @@ +package aws + +import ( + "context" + _ "embed" + "fmt" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect" + "github.com/datadog/stratus-red-team/v2/pkg/stratus" + "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" + "log" + "strconv" + "strings" + "time" +) + +//go:embed my_key.pub +var publicSSHKey string + +//go:embed main.tf +var tf []byte + +func init() { + stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ + ID: "aws.lateral-movement.ec2-serial-console-send-ssh-public-key", + FriendlyName: "Usage of EC2 Serial Console to push SSH public key", + IsSlow: true, + Description: ` +Simulates an attacker using EC2 Instance Connect to push an SSH public key to multiple EC2 instances, using SendSerialConsoleSSHPublicKey. This allows anyone +with the corresponding private key to connect directly to the systems via SSH, assuming they have appropriate network connectivity. + +Warm-up: + +- Create multiple EC2 instances and a VPC (takes a few minutes). + +Detonation: + +- Adds a public SSH key to the EC2 instances using SendSerialConsoleSSHPublicKey. + +References: + +- https://docs.aws.amazon.com/ec2-instance-connect/latest/APIReference/API_SendSerialConsoleSSHPublicKey.html +- https://permiso.io/blog/lucr-3-scattered-spider-getting-saas-y-in-the-cloud +- https://fwdcloudsec.org/assets/presentations/2024/europe/sebastian-walla-cloud-conscious-tactics-techniques-and-procedures-an-overview.pdf +- https://unit42.paloaltonetworks.com/cloud-lateral-movement-techniques/ +- https://unit42.paloaltonetworks.com/cloud-virtual-machine-attack-vectors/ +`, + Detection: ` +Identify, through CloudTrail's SendSerialConsoleSSHPublicKey event, when a user is adding an SSH key to EC2 instances. +`, + Platform: stratus.AWS, + PrerequisitesTerraformCode: tf, + IsIdempotent: true, + MitreAttackTactics: []mitreattack.Tactic{mitreattack.LateralMovement}, + Detonate: detonate, + Revert: revert, + }) +} + +func detonate(params map[string]string, providers stratus.CloudProviders) error { + ec2Client := ec2.NewFromConfig(providers.AWS().GetConnection()) + ec2instanceconnectClient := ec2instanceconnect.NewFromConfig(providers.AWS().GetConnection()) + instanceIDs := strings.Split(params["instance_ids"], ",") + + // Enable serial console access + log.Println("Enabling serial console access at the region level") + if err := setSerialConsoleEnabled(ec2Client, true); err != nil { + return fmt.Errorf("failed to disable serial console access: %v", err) + } + + log.Println("Sending SSH public key to " + strconv.Itoa(len(instanceIDs)) + " EC2 instances via serial console") + for _, instanceID := range instanceIDs { + cleanInstanceID := strings.Trim(instanceID, " \"\n\r") + err := sendSerialConsoleSSHPublicKey(ec2instanceconnectClient, cleanInstanceID, publicSSHKey) + if err != nil { + if strings.Contains(err.Error(), "SerialConsoleSessionLimitExceededException") { + log.Printf("Serial console session limit exceeded for instance %s. Retrying after waiting 60s...", cleanInstanceID) + time.Sleep(60 * time.Second) + err = sendSerialConsoleSSHPublicKey(ec2instanceconnectClient, cleanInstanceID, publicSSHKey) + } + if err != nil { + return fmt.Errorf("failed to send SSH public key via serial console to instance %s: %v", cleanInstanceID, err) + } + } + + log.Printf("SSH public key successfully added to instance %s via serial console", cleanInstanceID) + } + + return nil +} + +func revert(params map[string]string, providers stratus.CloudProviders) error { + // Serial console access was already enabled before running Stratus Red Team. Nothing to do + if params["serial_console_access_initial_value"] == "true" { + log.Println("Serial console access was already enabled before running Stratus Red Team. Keeping it enabled") + return nil + } + + // Serial console access was disabled before running Stratus Red Team. Since the detonation enabled it, + // and it's a region-wide setting, we now need to revert it back to its original value (false) + ec2Client := ec2.NewFromConfig(providers.AWS().GetConnection()) + log.Println("Serial console access was disabled before running Stratus Red Team. Disabling it again.") + if err := setSerialConsoleEnabled(ec2Client, false); err != nil { + return fmt.Errorf("failed to disable serial console access: %v", err) + } + + return nil +} + +// Utility functions +func sendSerialConsoleSSHPublicKey(ec2instanceconnectClient *ec2instanceconnect.Client, instanceId string, sshPublicKey string) error { + _, err := ec2instanceconnectClient.SendSerialConsoleSSHPublicKey(context.Background(), &ec2instanceconnect.SendSerialConsoleSSHPublicKeyInput{ + InstanceId: &instanceId, + SSHPublicKey: &sshPublicKey, + }) + + return err +} + +func setSerialConsoleEnabled(ec2Client *ec2.Client, enabled bool) error { + if enabled { + _, err := ec2Client.EnableSerialConsoleAccess(context.Background(), &ec2.EnableSerialConsoleAccessInput{}) + return err + } else { + _, err := ec2Client.DisableSerialConsoleAccess(context.Background(), &ec2.DisableSerialConsoleAccessInput{}) + return err + } +} diff --git a/v2/internal/attacktechniques/aws/lateral-movement/ec2-send-serial-console-send-ssh-public-key/main.tf b/v2/internal/attacktechniques/aws/lateral-movement/ec2-send-serial-console-send-ssh-public-key/main.tf new file mode 100644 index 00000000..4a5ea099 --- /dev/null +++ b/v2/internal/attacktechniques/aws/lateral-movement/ec2-send-serial-console-send-ssh-public-key/main.tf @@ -0,0 +1,135 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 4.0" + } + } +} + +provider "aws" { + skip_region_validation = true + skip_credentials_validation = true + default_tags { + tags = { + StratusRedTeam = true + } + } +} + +locals { + resource_prefix = "stratus-red-team-ec2-serialconsole-ssh-lateral-movement" +} + +variable "instance_count" { + description = "Number of instances to create" + default = 3 +} + +data "aws_availability_zones" "available" { + state = "available" +} + +data "aws_ec2_serial_console_access" "current" {} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 3.0" + + name = "${local.resource_prefix}-vpc" + cidr = "10.0.0.0/16" + + azs = [data.aws_availability_zones.available.names[0]] + private_subnets = ["10.0.1.0/24"] + public_subnets = ["10.0.128.0/24"] + + map_public_ip_on_launch = false + enable_nat_gateway = true + + tags = { + StratusRedTeam = true + } +} + +data "aws_ami" "amazon-2" { + most_recent = true + + filter { + name = "name" + values = ["amzn2-ami-hvm-*-x86_64-ebs"] + } + owners = ["amazon"] +} + +resource "aws_network_interface" "iface" { + count = var.instance_count + subnet_id = module.vpc.private_subnets[0] + + private_ips = [format("10.0.1.%d", count.index + 10)] +} + +resource "aws_iam_role" "instance-role" { + name = "${local.resource_prefix}-role" + path = "/" + + assume_role_policy = <