let command_executed = AWSCloudTrail
| where EventName in ("SendCommand","CreateAssociation") and isempty(ErrorCode) and isempty(ErrorMessage)
| extend params = tostring(parse_json(RequestParameters).parameters)
| extend s3bucketCommand = tostring(parse_json(RequestParameters).outputS3BucketName)
| extend s3bucketAssociation = tostring(parse_json(RequestParameters).outputLocation.s3Location.outputS3BucketName)
| where isnotempty(params)
| extend commandId = tostring(parse_json(ResponseElements).command.commandId)
| extend associationId = tostring(parse_json(ResponseElements).associationDescription.associationId)
| extend executionId = iff(isnotempty(commandId), commandId, associationId)
| extend s3bucket = iff(isnotempty(s3bucketCommand), s3bucketCommand, s3bucketAssociation)
| extend UserIdentityUserName = iff(isnotempty(UserIdentityUserName), UserIdentityUserName, tostring(split(UserIdentityArn,'/')[-1]));
AWSCloudTrail
| where EventName == "PutObject" and isempty(ErrorCode) and isempty(ErrorMessage)
| extend s3bucket = tostring(parse_json(RequestParameters).bucketName)
| mv-expand todynamic(Resources)
| extend accountId=tostring(todynamic(Resources.['accountId']))
| where Resources contains "accountId" and accountId <> RecipientAccountId
| join command_executed on s3bucket
| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
status: Available
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
query: |
let command_executed = AWSCloudTrail
| where EventName in ("SendCommand","CreateAssociation") and isempty(ErrorCode) and isempty(ErrorMessage)
| extend params = tostring(parse_json(RequestParameters).parameters)
| extend s3bucketCommand = tostring(parse_json(RequestParameters).outputS3BucketName)
| extend s3bucketAssociation = tostring(parse_json(RequestParameters).outputLocation.s3Location.outputS3BucketName)
| where isnotempty(params)
| extend commandId = tostring(parse_json(ResponseElements).command.commandId)
| extend associationId = tostring(parse_json(ResponseElements).associationDescription.associationId)
| extend executionId = iff(isnotempty(commandId), commandId, associationId)
| extend s3bucket = iff(isnotempty(s3bucketCommand), s3bucketCommand, s3bucketAssociation)
| extend UserIdentityUserName = iff(isnotempty(UserIdentityUserName), UserIdentityUserName, tostring(split(UserIdentityArn,'/')[-1]));
AWSCloudTrail
| where EventName == "PutObject" and isempty(ErrorCode) and isempty(ErrorMessage)
| extend s3bucket = tostring(parse_json(RequestParameters).bucketName)
| mv-expand todynamic(Resources)
| extend accountId=tostring(todynamic(Resources.['accountId']))
| where Resources contains "accountId" and accountId <> RecipientAccountId
| join command_executed on s3bucket
| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Amazon Web Services/Analytic Rules/AWS_SuspiciousCommandEC2.yaml
tactics:
- Execution
triggerThreshold: 0
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- identifier: CloudAppAccountId
columnName: RecipientAccountId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIpAddress
requiredDataConnectors:
- connectorId: AWS
dataTypes:
- AWSCloudTrail
alertDetailsOverride:
alertDescriptionFormat: User {{AccountName}} sent a remote command to EC2 (execution ID {{executionId}}) with output directed to external S3 bucket {{s3bucket}}.
alertDisplayNameFormat: Suspicious EC2 command execution by {{AccountName}} with output to S3 {{s3bucket}}
relevantTechniques:
- T1651
customDetails:
S3Bucket: s3bucket
AWSRegion: AWSRegion
ExecutionId: executionId
description: |
An attacker with the necessary AWS permissions could be executing code remotely on an EC2 instance via SSM and saving the output to their own S3 bucket. Verify this action with the user identity and confirm it was authorized.
name: AWSCloudTrail - Suspicious command sent to EC2
version: 1.0.2
kind: Scheduled
id: 21702832-aff3-4bd6-a8e1-663b6818503d
severity: High