AWS EC2 LOLBin Execution via SSM SendCommand

Last updated 16 days ago on 2025-11-23
Created 16 days ago on 2025-11-23

About

Identifies the execution of Living Off the Land Binaries (LOLBins) or GTFOBins on EC2 instances via AWS Systems Manager (SSM) `SendCommand` API. This detection correlates AWS CloudTrail `SendCommand` events with endpoint process execution by matching SSM command IDs. While AWS redacts command parameters in CloudTrail logs, this correlation technique reveals the actual commands executed on EC2 instances. Adversaries may abuse SSM to execute malicious commands remotely without requiring SSH or RDP access, using legitimate system utilities for data exfiltration, establishing reverse shells, or lateral movement.
Tags
Domain: CloudDomain: EndpointOS: LinuxUse Case: Threat DetectionTactic: ExecutionTactic: Command and ControlData Source: AWSData Source: Amazon Web ServicesData Source: AWS CloudTrailData Source: AWS EC2Data Source: AWS SSMData Source: AWS Systems ManagerData Source: Elastic DefendLanguage: esql
Severity
medium
Risk Score
47
MITRE ATT&CK™

Execution (TA0002)(opens in a new tab or window)

Command and Control (TA0011)(opens in a new tab or window)

False Positive Examples
Legitimate administrative tasks using SSM to run system utilities may trigger this rule. Review the command context, user identity, and timing to determine if the activity is authorized. Automated configuration management or monitoring scripts that use LOLBins via SSM for legitimate purposes. Consider excluding known automation accounts or specific command patterns.
License
Elastic License v2(opens in a new tab or window)

Definition

Integration Pack
Prebuilt Security Detection Rules
Related Integrations

aws(opens in a new tab or window)

endpoint(opens in a new tab or window)

Query
FROM logs-aws.cloudtrail*, logs-endpoint.events.process-* METADATA _id, _version, _index
| WHERE
  // CloudTrail SSM SendCommand with AWS-RunShellScript
  (
    event.dataset == "aws.cloudtrail"
    AND event.action == "SendCommand"
    AND aws.cloudtrail.request_parameters LIKE "*documentName=AWS-RunShellScript*"
  )
  // Linux endpoint process events, prefiltered to SSM shell runner OR LOLBins/GTFOBins
  OR
  (
    event.dataset == "endpoint.events.process"
    AND host.os.type == "linux"
    AND (
      // SSM shell (_script.sh) runner
      process.command_line LIKE "%/document/orchestration/%/awsrunShellScript/%/_script.sh"
      // LOLBins / GTFOBins
      OR process.name IN (
        "base64",
        "curl",
        "wget",
        "openssl",
        "nc", "ncat", "netcat",
        "socat",
        "python", "python3",
        "perl",
        "php",
        "ruby",
        "ssh",
        "scp",
        "sftp",
        "rsync"
      )
    )
  )

// Endpoint leg: extract SSM command ID from parent command line
| DISSECT process.parent.command_line
    "%{}/document/orchestration/%{Esql.process_parent_command_line_ssm_command_id}/%{}"

// CloudTrail leg: extract SSM command ID from response_elements
| DISSECT aws.cloudtrail.response_elements
    "%{}commandId=%{Esql.aws_cloudtrail_response_elements_ssm_command_id},%{}"

// Coalesce SSM command ID from both data sources
| EVAL Esql.aws_ssm_command_id = COALESCE(
    Esql.aws_cloudtrail_response_elements_ssm_command_id,
    Esql.process_parent_command_line_ssm_command_id
)
| WHERE Esql.aws_ssm_command_id IS NOT NULL

// Role flags
| EVAL Esql.is_cloud_event    = event.dataset == "aws.cloudtrail"
| EVAL Esql.is_endpoint_event = event.dataset == "endpoint.events.process"

// Identify the SSM shell processes (the _script.sh runners)
| EVAL Esql.is_ssm_shell_process =
    Esql.is_endpoint_event
    AND process.command_line LIKE "%/document/orchestration/%/awsrunShellScript/%/_script.sh"

// LOLBins / GTFOBins on Linux
| EVAL Esql.is_lolbin_process =
    Esql.is_endpoint_event AND NOT Esql.is_ssm_shell_process

// Aggregate per SSM command ID
| STATS
    // Core correlation counts & timing
    Esql.aws_cloudtrail_event_count                 = SUM(CASE(Esql.is_cloud_event, 1, 0)),
    Esql.endpoint_events_process_lolbin_count       = SUM(CASE(Esql.is_lolbin_process, 1, 0)),
    Esql.endpoint_events_process_ssm_shell_count    = SUM(CASE(Esql.is_ssm_shell_process, 1, 0)),
    Esql.aws_cloudtrail_first_event_ts              = MIN(CASE(Esql.is_cloud_event, @timestamp, null)),
    Esql.endpoint_events_process_first_lolbin_ts    = MIN(CASE(Esql.is_lolbin_process, @timestamp, null)),

    // AWS / CloudTrail identity & request context
    Esql_priv.aws_cloudtrail_user_identity_arn_values          =
      VALUES(CASE(Esql.is_cloud_event, aws.cloudtrail.user_identity.arn, null)),
    Esql_priv.aws_cloudtrail_user_identity_access_key_id_values =
      VALUES(CASE(Esql.is_cloud_event, aws.cloudtrail.user_identity.access_key_id, null)),
    Esql_priv.user_name_values                                 =
      VALUES(CASE(Esql.is_cloud_event, user.name, null)),

    // AWS environment / request metadata
    Esql.cloud_region_values     = VALUES(CASE(Esql.is_cloud_event, cloud.region, null)),
    Esql.source_ip_values        = VALUES(CASE(Esql.is_cloud_event, source.ip, null)),
    Esql.user_agent_original_values =
      VALUES(CASE(Esql.is_cloud_event, user_agent.original, null)),

    // Endpoint host & user context
    Esql.host_name_values        = VALUES(CASE(Esql.is_endpoint_event, host.name, null)),
    Esql_priv.endpoint_user_name_values =
      VALUES(CASE(Esql.is_endpoint_event, user.name, null)),

    // SSM shell processes on endpoint
    Esql.process_command_line_ssm_shell_values =
      VALUES(CASE(Esql.is_ssm_shell_process, process.command_line, null)),
    Esql.process_pid_ssm_shell_values =
      VALUES(CASE(Esql.is_ssm_shell_process, process.pid, null)),

    // LOLBin processes on endpoint
    Esql.process_name_lolbin_values =
      VALUES(CASE(Esql.is_lolbin_process, process.name, null)),
    Esql.process_executable_lolbin_values =
      VALUES(CASE(Esql.is_lolbin_process, process.executable, null)),
    Esql.process_command_line_lolbin_values =
      VALUES(CASE(Esql.is_lolbin_process, process.command_line, null)),
    Esql.process_pid_lolbin_values =
      VALUES(CASE(Esql.is_lolbin_process, process.pid, null)),
    Esql.process_parent_command_line_lolbin_values =
      VALUES(CASE(Esql.is_lolbin_process, process.parent.command_line, null)),

    Esql.data_stream_namespace_values = VALUES(data_stream.namespace)
  BY Esql.aws_ssm_command_id

// Detection condition: SSM SendCommand + AWS-RunShellScript + LOLBin on endpoint
| WHERE Esql.aws_cloudtrail_event_count > 0
  AND Esql.endpoint_events_process_lolbin_count > 0
  AND DATE_DIFF(
        "minutes",
        Esql.endpoint_events_process_first_lolbin_ts,
        Esql.aws_cloudtrail_first_event_ts
      ) <= 5
| SORT Esql.aws_cloudtrail_first_event_ts ASC
| KEEP Esql.*, Esql_priv.*

Install detection rules in Elastic Security

Detect AWS EC2 LOLBin Execution via SSM SendCommand in the Elastic Security detection engine by installing this rule into your Elastic Stack.

To setup this rule, check out the installation guide for Prebuilt Security Detection Rules(opens in a new tab or window).