Back
Id21702832-aff3-4bd6-a8e1-663b6818503d
RulenameAWSCloudTrail - Suspicious command sent to EC2
DescriptionAn 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.
SeverityHigh
TacticsExecution
TechniquesT1651
Required data connectorsAWS
KindScheduled
Query frequency1d
Query period1d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Amazon%20Web%20Services/Analytic%20Rules/AWS_SuspiciousCommandEC2.yaml
Version1.0.2
Arm template21702832-aff3-4bd6-a8e1-663b6818503d.json
Deploy To Azure
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]), "")
entityMappings:
- entityType: Account
  fieldMappings:
  - columnName: AccountName
    identifier: Name
  - columnName: AccountUPNSuffix
    identifier: UPNSuffix
  - columnName: RecipientAccountId
    identifier: CloudAppAccountId
- entityType: IP
  fieldMappings:
  - columnName: SourceIpAddress
    identifier: Address
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]), "")
id: 21702832-aff3-4bd6-a8e1-663b6818503d
queryFrequency: 1d
alertDetailsOverride:
  alertDisplayNameFormat: Suspicious EC2 command execution by {{AccountName}} with output to S3 {{s3bucket}}
  alertDescriptionFormat: User {{AccountName}} sent a remote command to EC2 (execution ID {{executionId}}) with output directed to external S3 bucket {{s3bucket}}.
status: Available
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Amazon%20Web%20Services/Analytic%20Rules/AWS_SuspiciousCommandEC2.yaml
version: 1.0.2
severity: High
relevantTechniques:
- T1651
name: AWSCloudTrail - Suspicious command sent to EC2
kind: Scheduled
tactics:
- Execution
requiredDataConnectors:
- dataTypes:
  - AWSCloudTrail
  connectorId: AWS
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.
customDetails:
  S3Bucket: s3bucket
  ExecutionId: executionId
  AWSRegion: AWSRegion
triggerOperator: gt
triggerThreshold: 0
queryPeriod: 1d
{
  "$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"
    }
  ]
}