Techniques
Sample rules
AWS EC2 LOLBin Execution via SSM SendCommand
- source: elastic
- technicques:
- T1105
- T1651
Description
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.
Detection logic
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.*