Analytic rule catalog
AWSCloudTrail - Suspicious command sent to EC2
Back
| Id | 21702832-aff3-4bd6-a8e1-663b6818503d |
| Rulename | AWSCloudTrail - Suspicious command sent to EC2 |
| 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. |
| Severity | High |
| Tactics | Execution |
| Techniques | T1651 |
| Required data connectors | AWS |
| Kind | Scheduled |
| Query frequency | 1d |
| Query period | 1d |
| Trigger threshold | 0 |
| Trigger operator | gt |
| Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Amazon%20Web%20Services/Analytic%20Rules/AWS_SuspiciousCommandEC2.yaml |
| Version | 1.0.2 |
| Arm template | 21702832-aff3-4bd6-a8e1-663b6818503d.json |
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]), "")
name: AWSCloudTrail - Suspicious command sent to EC2
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]), "")
queryFrequency: 1d
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.
id: 21702832-aff3-4bd6-a8e1-663b6818503d
triggerThreshold: 0
queryPeriod: 1d
version: 1.0.2
kind: Scheduled
customDetails:
AWSRegion: AWSRegion
S3Bucket: s3bucket
ExecutionId: executionId
status: Available
severity: High
requiredDataConnectors:
- connectorId: AWS
dataTypes:
- AWSCloudTrail
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Amazon%20Web%20Services/Analytic%20Rules/AWS_SuspiciousCommandEC2.yaml
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
tactics:
- Execution
entityMappings:
- fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- identifier: CloudAppAccountId
columnName: RecipientAccountId
entityType: Account
- fieldMappings:
- identifier: Address
columnName: SourceIpAddress
entityType: IP
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"apiVersion": "2024-01-01-preview",
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/21702832-aff3-4bd6-a8e1-663b6818503d')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/21702832-aff3-4bd6-a8e1-663b6818503d')]",
"properties": {
"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}}"
},
"alertRuleTemplateName": "21702832-aff3-4bd6-a8e1-663b6818503d",
"customDetails": {
"AWSRegion": "AWSRegion",
"ExecutionId": "executionId",
"S3Bucket": "s3bucket"
},
"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.\n",
"displayName": "AWSCloudTrail - Suspicious command sent to EC2",
"enabled": true,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "AccountName",
"identifier": "Name"
},
{
"columnName": "AccountUPNSuffix",
"identifier": "UPNSuffix"
},
{
"columnName": "RecipientAccountId",
"identifier": "CloudAppAccountId"
}
]
},
{
"entityType": "IP",
"fieldMappings": [
{
"columnName": "SourceIpAddress",
"identifier": "Address"
}
]
}
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Amazon%20Web%20Services/Analytic%20Rules/AWS_SuspiciousCommandEC2.yaml",
"query": "let command_executed = AWSCloudTrail\n| where EventName in (\"SendCommand\",\"CreateAssociation\") and isempty(ErrorCode) and isempty(ErrorMessage)\n| extend params = tostring(parse_json(RequestParameters).parameters)\n| extend s3bucketCommand = tostring(parse_json(RequestParameters).outputS3BucketName)\n| extend s3bucketAssociation = tostring(parse_json(RequestParameters).outputLocation.s3Location.outputS3BucketName)\n| where isnotempty(params)\n| extend commandId = tostring(parse_json(ResponseElements).command.commandId)\n| extend associationId = tostring(parse_json(ResponseElements).associationDescription.associationId)\n| extend executionId = iff(isnotempty(commandId), commandId, associationId)\n| extend s3bucket = iff(isnotempty(s3bucketCommand), s3bucketCommand, s3bucketAssociation)\n| extend UserIdentityUserName = iff(isnotempty(UserIdentityUserName), UserIdentityUserName, tostring(split(UserIdentityArn,'/')[-1]));\nAWSCloudTrail\n| where EventName == \"PutObject\" and isempty(ErrorCode) and isempty(ErrorMessage)\n| extend s3bucket = tostring(parse_json(RequestParameters).bucketName)\n| mv-expand todynamic(Resources)\n| extend accountId=tostring(todynamic(Resources.['accountId']))\n| where Resources contains \"accountId\" and accountId <> RecipientAccountId\n| join command_executed on s3bucket\n| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)\n| extend UserName = tostring(split(UserIdentityArn, '/')[-1])\n| extend AccountName = case( UserIdentityPrincipalid == \"Anonymous\", \"Anonymous\", isempty(UserIdentityUserName), UserName, UserIdentityUserName)\n| extend AccountName = iif(AccountName contains \"@\", tostring(split(AccountName, '@', 0)[0]), AccountName),\n AccountUPNSuffix = iif(AccountName contains \"@\", tostring(split(AccountName, '@', 1)[0]), \"\")\n",
"queryFrequency": "P1D",
"queryPeriod": "P1D",
"severity": "High",
"status": "Available",
"subTechniques": [],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"Execution"
],
"techniques": [
"T1651"
],
"templateVersion": "1.0.2",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}