Detection rules › Elastic
Elastic rules: 1d
AMSI Bypass via COM Registry Modification
#Identifies attempts to disable the Microsoft Antimalware Scan Interface via registry modification. This may indicate an attempt to tamper with certain Windows native protections.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Rule Body
[rule]
description = """
Identifies attempts to disable the Microsoft Antimalware Scan Interface via registry modification. This may indicate an
attempt to tamper with certain Windows native protections.
"""
id = "0a3152e5-122f-4145-a107-7ec789b9b1a3"
license = "Elastic License v2"
name = "AMSI Bypass via COM Registry Modification"
os_list = ["windows"]
version = "1.0.4"
query = '''
registry where event.action == "modification" and
registry.path : "HKEY_USERS\\*_Classes\\CLSID\\{fdb00e52-a214-4aa1-8fba-4357bb0072ec}\\InProcServer*" and
not registry.data.strings : ("?:\\Windows\\System32\\amsi.dll", "?:\\Windows\\SysWOW64\\amsi.dll")
'''
min_endpoint_version = "7.16.0"
[[actions]]
action = "kill_process"
field = "process.entity_id"
state = 0
[[optional_actions]]
action = "rollback"
field = "process.entity_id"
state = 0
[[threat]]
framework = "MITRE ATT&CK"
[[threat.technique]]
id = "T1562"
name = "Impair Defenses"
reference = "https://attack.mitre.org/techniques/T1562/"
[[threat.technique.subtechnique]]
id = "T1562.001"
name = "Disable or Modify Tools"
reference = "https://attack.mitre.org/techniques/T1562/001/"
[threat.tactic]
id = "TA0005"
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"
[internal]
min_endpoint_version = "7.16.0"
Stages and Predicates
Stage 1: registry
registry where event.action == "modification" and
registry.path : "HKEY_USERS\\*_Classes\\CLSID\\{fdb00e52-a214-4aa1-8fba-4357bb0072ec}\\InProcServer*" and
not registry.data.strings : ("?:\\Windows\\System32\\amsi.dll", "?:\\Windows\\SysWOW64\\amsi.dll")
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.action | eq |
| field:"EventType" kind:eq value:"modification" |
registry.path | wildcard |
| field:"TargetObject" kind:wildcard value:"HKEY_USERS\*_Classes\CLSID\{fdb00e52-a214-4aa1-8fba-4357bb0072ec}\InProcServer*" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
registry.data.strings | eq | ?:\Windows\System32\amsi.dll, ?:\Windows\SysWOW64\amsi.dll | excludes:registry.data.strings field:"registry.data.strings" value:"?:\Windows\System32\amsi.dll" field:"registry.data.strings" value:"?:\Windows\SysWOW64\amsi.dll" |
AWS EC2 Stop, Start, and User Data Modification Correlation
#Identifies a short sequence of EC2 management APIs against the same instance that is consistent with modifying instance user data and forcing it to run on the next boot: ModifyInstanceAttribute with user data, followed by stop and start. Adversaries may update userData and cycle instance state so malicious scripts execute as root on Linux or as the system context on Windows. This rule correlates successful StopInstances, StartInstances, and ModifyInstanceAttribute events that reference userData within a five-minute window, grouped by instance, user.name, account, source IP, and user agent. A hit requires exactly three distinct API names in that bucket.
Known False Positives
- Legitimate automation or administrators may change user data and restart instances during maintenance, image baking, or configuration fixes. Review the caller identity, change tickets, and whether `user_agent.original` and `source.ip` match known tooling and networks (the rule groups on both together with `user.name`).
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Defense Impairment |
Telemetry Coverage
Rules Detecting the Same Action
These rules filter on the same operation.
Rule Body
[metadata]
creation_date = "2026/04/03"
integration = ["aws"]
maturity = "production"
updated_date = "2026/04/03"
[rule]
author = ["Elastic"]
description = """
Identifies a short sequence of EC2 management APIs against the same instance that is consistent with modifying instance
user data and forcing it to run on the next boot: `ModifyInstanceAttribute` with user data, followed by stop and start.
Adversaries may update `userData` and cycle instance state so malicious scripts execute as root on Linux or as the
system context on Windows. This rule correlates successful `StopInstances`, `StartInstances`, and
`ModifyInstanceAttribute` events that reference `userData` within a five-minute window, grouped by instance,
`user.name`, account, source IP, and user agent. A hit requires exactly three distinct API names in that bucket.
"""
false_positives = [
"""
Legitimate automation or administrators may change user data and restart instances during maintenance, image
baking, or configuration fixes. Review the caller identity, change tickets, and whether `user_agent.original` and
`source.ip` match known tooling and networks (the rule groups on both together with `user.name`).
""",
]
from = "now-20m"
interval = "5m"
language = "esql"
license = "Elastic License v2"
name = "AWS EC2 Stop, Start, and User Data Modification Correlation"
note = """## Triage and analysis
### Investigating AWS EC2 Stop, Start, and User Data Modification Correlation
This detection aggregates successful EC2 `StopInstances`, `StartInstances`, and `ModifyInstanceAttribute` (with
`userData` in request parameters) over **five-minute** windows. Rows are keyed by **instance ID** (`Esql.instance_id`
from the grok on `aws.cloudtrail.request_parameters`), **`user.name`**, **`cloud.account.id`**, **`user_agent.original`**,
and **`source.ip`**. The rule fires only when **`Esql.event_action_unique_count` is 3**, meaning all three API names
appear in the same bucket—consistent with changing user data and cycling the instance to run it.
The aggregated result does **not** include raw `request_parameters`; use the alert’s instance, account, user, IP, user
agent, and time bucket to query CloudTrail for the underlying events and payloads.
#### Possible investigation steps
- **Interpret the alert columns**: Review `Esql.event_action_values` to confirm the three actions are present (typically
`ModifyInstanceAttribute`, `StopInstances`, `StartInstances`). Use `Esql.event_action_unique_count` to verify the
rule logic (expect `3`).
- **Confirm the instance**: Use `Esql.instance_id` plus `cloud.account.id` in CMDB or AWS Resource Groups. Ensure the
grok-derived ID matches the instance you expect (multi-instance API calls can affect extraction).
- **Identify the caller**: Tie `user.name` to an IAM user or role session name as shown in CloudTrail; for assumed roles,
pivot in raw logs on `aws.cloudtrail.user_identity.arn` and session context in the same time window.
- **Validate client and origin**: Compare `user_agent.original` and `source.ip` to known admin workstations, bastions,
or CI/CD egress. The rule intentionally groups by these fields so unrelated sessions do not merge into one bucket.
- **Recover user data context**: In CloudTrail (or the integration’s `aws.cloudtrail.request_parameters` on raw events),
inspect the `ModifyInstanceAttribute` record for `userData` and whether values are base64 or placeholders.
- **Hunt for follow-on activity**: After the window, look for IAM changes, role assumption, or data access from the
instance or the same principal.
### False positive analysis
- **Infrastructure as code**: Terraform, Ansible, and Pulumi user agents are excluded, but other automation may still
match. Validate pipeline identity, change tickets, and whether stop/start is part of approved maintenance.
- **Break-glass or support workflows**: Some teams modify user data and restart instances during recovery; confirm with
the workload owner.
- **Shared `user.name` or NAT**: If many callers share one identity or IP, bucketing may still separate sessions when IP
or user agent differs; conversely, identical UA/IP across benign bulk operations can resemble this pattern—confirm
intent.
### Response and remediation
- If unauthorized, isolate the instance, revoke or restrict the principal’s EC2 permissions, and rotate any credentials
that may have been exposed in user data.
- Prefer Secrets Manager or Parameter Store over long-lived secrets in user data.
### Additional information
- [AWS EC2 User Data](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)
- [ModifyInstanceAttribute](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ModifyInstanceAttribute.html)
- [Local EC2 privilege escalation through user data](https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data)
"""
references = [
"https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ModifyInstanceAttribute.html",
"https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data",
]
risk_score = 73
rule_id = "7e5c0e5a-95a5-404e-a5b0-278d35dc3325"
severity = "high"
tags = [
"Domain: Cloud",
"Data Source: AWS",
"Data Source: Amazon Web Services",
"Data Source: AWS EC2",
"Data Source: AWS CloudTrail",
"Use Case: Threat Detection",
"Tactic: Execution",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "esql"
query = '''
FROM logs-aws.cloudtrail-*
| WHERE event.provider == "ec2.amazonaws.com"
and event.outcome == "success"
and aws.cloudtrail.user_identity.type != "AWSService"
and not (
user_agent.original like "*Terraform*"
or user_agent.original like "*Ansible*"
or user_agent.original like "*Pulumi*"
) and not source.address in ("cloudformation.amazonaws.com", "servicecatalog.amazonaws.com")
and
(
event.action in ("StopInstances", "StartInstances") or
(event.action == "ModifyInstanceAttribute" and aws.cloudtrail.request_parameters like "*userData=*")
)
| grok aws.cloudtrail.request_parameters """instanceId=(?<Esql.instance_id>[^,}\]]+)"""
| STATS Esql.event_action_unique_count = COUNT_DISTINCT(event.action),
Esql.event_action_values = VALUES(event.action) by Esql.instance_id, user.name, cloud.account.id, Esql.time_bucket = DATE_TRUNC(5 minute, @timestamp) , user_agent.original, source.ip, source.as.organization.name, source.geo.country_name
| where Esql.event_action_unique_count == 3
| Keep Esql.*, user.name, cloud.account.id, user_agent.original, source.ip, source.as.organization.name, source.geo.country_name
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1059"
name = "Command and Scripting Interpreter"
reference = "https://attack.mitre.org/techniques/T1059/"
[[rule.threat.technique.subtechnique]]
id = "T1059.009"
name = "Cloud API"
reference = "https://attack.mitre.org/techniques/T1059/009/"
[rule.threat.tactic]
id = "TA0002"
name = "Execution"
reference = "https://attack.mitre.org/tactics/TA0002/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1578"
name = "Modify Cloud Compute Infrastructure"
reference = "https://attack.mitre.org/techniques/T1578/"
[rule.threat.tactic]
id = "TA0005"
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"
[rule.investigation_fields]
field_names = [
"Esql.event_action_unique_count",
"Esql.event_action_values",
"Esql.instance_id",
"user.name",
"cloud.account.id",
"user_agent.original",
"source.ip",
"Esql.time_bucket",
]
Stages and Predicates
Stage 1: from
FROM logs-aws.cloudtrail-*
Stage 2: where
| WHERE event.provider == "ec2.amazonaws.com"
and event.outcome == "success"
and aws.cloudtrail.user_identity.type != "AWSService"
and not (
user_agent.original like "*Terraform*"
or user_agent.original like "*Ansible*"
or user_agent.original like "*Pulumi*"
) and not source.address in ("cloudformation.amazonaws.com", "servicecatalog.amazonaws.com")
and
(
event.action in ("StopInstances", "StartInstances") or
(event.action == "ModifyInstanceAttribute" and aws.cloudtrail.request_parameters like "*userData=*")
)
Stage 3: grok
| grok aws.cloudtrail.request_parameters """instanceId=(?<Esql.instance_id>[^,}\]]+)"""
Stage 4: stats
| STATS Esql.event_action_unique_count = COUNT_DISTINCT(event.action),
Esql.event_action_values = VALUES(event.action) by Esql.instance_id, user.name, cloud.account.id, Esql.time_bucket = DATE_TRUNC(5 minute, @timestamp) , user_agent.original, source.ip, source.as.organization.name, source.geo.country_name
Stage 5: where
| where Esql.event_action_unique_count == 3
Stage 6: keep
| Keep Esql.*, user.name, cloud.account.id, user_agent.original, source.ip, source.as.organization.name, source.geo.country_name
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Esql.event_action_unique_count | eq |
| field:"Esql.event_action_unique_count" kind:eq value:"3" |
aws.cloudtrail.request_parameters | wildcard |
| field:"aws::requestParameters" kind:wildcard value:"*userData=*" |
aws.cloudtrail.user_identity.type | ne |
| field:"aws::userIdentity.type" kind:ne value:"AWSService" |
event.action | eq |
| field:"EventType" kind:eq value:"ModifyInstanceAttribute" |
event.action | in |
| field:"EventType" kind:in |
event.outcome | eq |
| field:"event.outcome" kind:eq value:"success" |
event.provider | eq |
| field:"Provider_Name" kind:eq value:"ec2.amazonaws.com" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
user_agent.original | match | Ansible | excludes:user_agent.original field:"user_agent.original" value:"Ansible" |
user_agent.original | match | Pulumi | excludes:user_agent.original field:"user_agent.original" value:"Pulumi" |
user_agent.original | match | Terraform | excludes:user_agent.original field:"user_agent.original" value:"Terraform" |
source.address | in | cloudformation.amazonaws.com, servicecatalog.amazonaws.com | excludes:source.address field:"source.address" value:"cloudformation.amazonaws.com" field:"source.address" value:"servicecatalog.amazonaws.com" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Esql.* | KEEP Esql.* |
user.name | KEEP user.name |
cloud.account.id | KEEP cloud.account.id |
user_agent.original | KEEP user_agent.original |
source.ip | KEEP source.ip |
source.as.organization.name | KEEP source.as.organization.name |
source.geo.country_name | KEEP source.geo.country_name |
AWS SageMaker Execution Role Passed by Unusual Principal
#Identifies the first time an IAM principal passes a given execution role (roleArn) to an Amazon SageMaker resource, via CreateNotebookInstance, CreateTrainingJob, CreateProcessingJob, CreateAutoMLJob, or CreatePipeline. These actions require iam:PassRole and attach an IAM role that the created resource then runs as. An adversary holding both SageMaker create permissions and a broad iam:PassRole grant can pass a more privileged role to a resource they control and execute code as that role, escalating privileges. The rule keys on the combination of the calling principal and the passed roleArn, so it surfaces a principal using an execution role it has not used before in the last 7 days; a role whose account differs from the caller's, or that is more privileged than the caller, is especially suspicious.
Known False Positives
- MLOps pipelines and data science teams routinely create SageMaker resources with execution roles, and new pipelines or team members appear as new principals on first use. Verify the principal in `aws.cloudtrail.user_identity.arn`, the passed roleArn in `aws.cloudtrail.request_parameters`, and whether the role's privileges and the activity are approved. Known automation roles can be excluded after validation.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Telemetry Coverage
Rule Body
[metadata]
creation_date = "2026/07/13"
integration = ["aws"]
maturity = "production"
updated_date = "2026/07/13"
[rule]
author = ["Elastic"]
description = """
Identifies the first time an IAM principal passes a given execution role (`roleArn`) to an Amazon SageMaker resource,
via `CreateNotebookInstance`, `CreateTrainingJob`, `CreateProcessingJob`, `CreateAutoMLJob`, or `CreatePipeline`. These
actions require `iam:PassRole` and attach an IAM role that the created resource then runs as. An adversary holding both
SageMaker create permissions and a broad `iam:PassRole` grant can pass a more privileged role to a resource they control
and execute code as that role, escalating privileges. The rule keys on the combination of the calling principal and the
passed `roleArn`, so it surfaces a principal using an execution role it has not used before in the last 7 days; a role
whose account differs from the caller's, or that is more privileged than the caller, is especially suspicious.
"""
false_positives = [
"""
MLOps pipelines and data science teams routinely create SageMaker resources with execution roles, and new pipelines
or team members appear as new principals on first use. Verify the principal in `aws.cloudtrail.user_identity.arn`,
the passed roleArn in `aws.cloudtrail.request_parameters`, and whether the role's privileges and the activity are
approved. Known automation roles can be excluded after validation.
""",
]
from = "now-7d"
interval = "10m"
language = "esql"
license = "Elastic License v2"
name = "AWS SageMaker Execution Role Passed by Unusual Principal"
note = """## Triage and analysis
### Investigating AWS SageMaker Execution Role Passed by Unusual Principal
SageMaker resource-creation actions accept a `roleArn` execution role and require the caller to hold `iam:PassRole`
for it. The created resource (notebook, training job, processing job, AutoML job, or pipeline) then runs as that
role. This is a known cloud privilege-escalation path: a principal with SageMaker create rights and a broad
`PassRole` permission can attach a more privileged role to a resource it controls and run code as that role. This
rule keys on the principal and the passed `roleArn` together, so it flags the first time a principal uses a given
execution role within the last 7 days, which should then be reviewed for over-privilege or a cross-account owner.
#### Possible investigation steps
- Identify the actor in `aws.cloudtrail.user_identity.arn`, and review `Esql.source_ip_values` and
`Esql.user_agent_original_values` for an unexpected origin.
- Inspect `Esql.aws_cloudtrail_request_parameters_role_arn` and review that role's policies; determine whether it is
more privileged than the caller.
- Determine whether the principal normally creates SageMaker resources and whether this aligns with an approved
pipeline or project.
- Correlate with follow-on activity by the passed role, such as actions outside SageMaker, presigned URL generation,
or lifecycle configuration changes that would provide interactive execution as the role.
### False positive analysis
- Legitimate MLOps creates SageMaker resources with execution roles; new pipelines and users appear as new
principals on first use. Confirm the role and activity are approved and exclude known automation roles on
`aws.cloudtrail.user_identity.arn` after validation.
### Response and remediation
- If unauthorized, stop and delete the created resource, and review any actions taken by the passed role.
- Rotate or restrict credentials for the principal if compromise is suspected, and constrain `iam:PassRole` and
SageMaker create permissions so principals can only pass narrowly scoped, approved execution roles.
"""
references = [
"https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-roles.html",
"https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateNotebookInstance.html",
"https://stratus-red-team.cloud/attack-techniques/AWS/aws.execution.sagemaker-update-lifecycle-config/",
]
risk_score = 73
rule_id = "ca8c2751-5507-44f2-b58d-08958200cde9"
severity = "high"
tags = [
"Domain: Cloud",
"Data Source: AWS",
"Data Source: Amazon Web Services",
"Data Source: AWS SageMaker",
"Use Case: Threat Detection",
"Tactic: Privilege Escalation",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "esql"
query = '''
FROM logs-aws.cloudtrail-*
| WHERE data_stream.dataset == "aws.cloudtrail"
AND event.provider == "sagemaker.amazonaws.com"
AND event.action IN (
"CreateNotebookInstance",
"CreateTrainingJob",
"CreateProcessingJob",
"CreateAutoMLJob",
"CreatePipeline"
)
AND event.outcome == "success"
AND aws.cloudtrail.user_identity.type != "AWSService"
| GROK aws.cloudtrail.request_parameters """.*roleArn=(?<Esql.aws_cloudtrail_request_parameters_role_arn>arn:aws[a-z-]*:iam::[0-9]{12}:role/[^,}]+).*"""
| WHERE Esql.aws_cloudtrail_request_parameters_role_arn IS NOT NULL
| EVAL Esql.principal_arn = COALESCE(
aws.cloudtrail.user_identity.session_context.session_issuer.arn,
aws.cloudtrail.user_identity.arn
)
| STATS
Esql.timestamp_min = MIN(@timestamp),
Esql.timestamp_max = MAX(@timestamp),
Esql.ingested_min = MIN(COALESCE(event.ingested, @timestamp)),
Esql.event_count = COUNT(*),
Esql.event_action_values = VALUES(event.action),
Esql.source_ip_values = VALUES(source.ip),
Esql.user_agent_original_values = VALUES(user_agent.original),
Esql.user_identity_arn_values = VALUES(aws.cloudtrail.user_identity.arn),
Esql.cloud_account_id_values = VALUES(cloud.account.id),
Esql.cloud_region_values = VALUES(cloud.region)
BY Esql.principal_arn,
Esql.aws_cloudtrail_request_parameters_role_arn
| WHERE Esql.ingested_min >= NOW() - 10 minutes
| KEEP Esql.*
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1078"
name = "Valid Accounts"
reference = "https://attack.mitre.org/techniques/T1078/"
[[rule.threat.technique.subtechnique]]
id = "T1078.004"
name = "Cloud Accounts"
reference = "https://attack.mitre.org/techniques/T1078/004/"
[rule.threat.tactic]
id = "TA0004"
name = "Privilege Escalation"
reference = "https://attack.mitre.org/tactics/TA0004/"
[rule.investigation_fields]
field_names = [
"Esql.principal_arn",
"Esql.aws_cloudtrail_request_parameters_role_arn",
"Esql.user_identity_arn_values",
"Esql.timestamp_min",
"Esql.timestamp_max",
"Esql.event_count",
"Esql.event_action_values",
"Esql.source_ip_values",
"Esql.user_agent_original_values",
"Esql.cloud_account_id_values",
"Esql.cloud_region_values",
]
Stages and Predicates
Stage 1: from
FROM logs-aws.cloudtrail-*
Stage 2: where
| WHERE data_stream.dataset == "aws.cloudtrail"
AND event.provider == "sagemaker.amazonaws.com"
AND event.action IN (
"CreateNotebookInstance",
"CreateTrainingJob",
"CreateProcessingJob",
"CreateAutoMLJob",
"CreatePipeline"
)
AND event.outcome == "success"
AND aws.cloudtrail.user_identity.type != "AWSService"
Stage 3: grok
| GROK aws.cloudtrail.request_parameters """.*roleArn=(?<Esql.aws_cloudtrail_request_parameters_role_arn>arn:aws[a-z-]*:iam::[0-9]{12}:role/[^,}]+).*"""
Stage 4: where
| WHERE Esql.aws_cloudtrail_request_parameters_role_arn IS NOT NULL
Stage 5: eval
| EVAL Esql.principal_arn = COALESCE(
aws.cloudtrail.user_identity.session_context.session_issuer.arn,
aws.cloudtrail.user_identity.arn
)
Stage 6: stats
| STATS
Esql.timestamp_min = MIN(@timestamp),
Esql.timestamp_max = MAX(@timestamp),
Esql.ingested_min = MIN(COALESCE(event.ingested, @timestamp)),
Esql.event_count = COUNT(*),
Esql.event_action_values = VALUES(event.action),
Esql.source_ip_values = VALUES(source.ip),
Esql.user_agent_original_values = VALUES(user_agent.original),
Esql.user_identity_arn_values = VALUES(aws.cloudtrail.user_identity.arn),
Esql.cloud_account_id_values = VALUES(cloud.account.id),
Esql.cloud_region_values = VALUES(cloud.region)
BY Esql.principal_arn,
Esql.aws_cloudtrail_request_parameters_role_arn
Stage 7: where
| WHERE Esql.ingested_min >= NOW() - 10 minutes
Stage 8: keep
| KEEP Esql.*
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Esql.aws_cloudtrail_request_parameters_role_arn | is_not_null | field:"Esql.aws_cloudtrail_request_parameters_role_arn" kind:is_not_null | |
aws.cloudtrail.user_identity.type | ne |
| field:"aws::userIdentity.type" kind:ne value:"AWSService" |
data_stream.dataset | eq |
| field:"data_stream.dataset" kind:eq value:"aws.cloudtrail" |
event.action | in |
| field:"EventType" kind:in |
event.outcome | eq |
| field:"event.outcome" kind:eq value:"success" |
event.provider | eq |
| field:"Provider_Name" kind:eq value:"sagemaker.amazonaws.com" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Esql.* | KEEP Esql.* |
AWS SageMaker Notebook Lifecycle Configuration With Suspicious Script Content
#Identifies an Amazon SageMaker notebook lifecycle configuration whose OnStart or OnCreate script, after base64 decoding, contains patterns associated with malicious activity such as reverse shells, EC2 instance metadata (IMDS) credential access, or download-and-execute commands. A lifecycle configuration runs as root on the notebook instance, so a script with these patterns is a strong indicator of an attempt to backdoor the notebook, steal the execution role's credentials, or establish persistent code execution. This rule decodes the script in the request and matches high-signal indicators; it is a higher-fidelity companion to the rule that alerts on any lifecycle configuration change.
Known False Positives
- Legitimate setup scripts may reference metadata endpoints or download tooling. Review the decoded script in "Esql_priv.aws_cloudtrail_lifecycle_script", verify the principal in "aws.cloudtrail.user_identity.arn", and confirm the activity is approved. Note this rule only matches unobfuscated patterns; a clean result does not guarantee a benign script.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Persistence |
Telemetry Coverage
Rule Body
[metadata]
creation_date = "2026/06/29"
integration = ["aws"]
maturity = "production"
updated_date = "2026/06/29"
[rule]
author = ["Elastic"]
description = """
Identifies an Amazon SageMaker notebook lifecycle configuration whose OnStart or OnCreate script, after base64 decoding,
contains patterns associated with malicious activity such as reverse shells, EC2 instance metadata (IMDS) credential
access, or download-and-execute commands. A lifecycle configuration runs as root on the notebook instance, so a script
with these patterns is a strong indicator of an attempt to backdoor the notebook, steal the execution role's
credentials, or establish persistent code execution. This rule decodes the script in the request and matches high-signal
indicators; it is a higher-fidelity companion to the rule that alerts on any lifecycle configuration change.
"""
false_positives = [
"""
Legitimate setup scripts may reference metadata endpoints or download tooling. Review the decoded script in
"Esql_priv.aws_cloudtrail_lifecycle_script", verify the principal in "aws.cloudtrail.user_identity.arn", and confirm
the activity is approved. Note this rule only matches unobfuscated patterns; a clean result does not guarantee a
benign script.
""",
]
from = "now-60m"
interval = "10m"
language = "esql"
license = "Elastic License v2"
name = "AWS SageMaker Notebook Lifecycle Configuration With Suspicious Script Content"
note = """## Triage and analysis
### Investigating AWS SageMaker Notebook Lifecycle Configuration With Suspicious Script Content
A SageMaker notebook lifecycle configuration is a shell script that runs as root on the notebook instance at create or start. This rule base64-decodes the OnStart/OnCreate script from the request (surfaced as "Esql_priv.aws_cloudtrail_lifecycle_script") and flags high-signal indicators across several categories: reverse shells ("/dev/tcp/", "/dev/udp/", "bash -i", "nc -e", "ncat", "socat", "mkfifo", "import socket", "pty.spawn", "perl"/"ruby"/"php" socket idioms), IMDS credential access ("169.254.169.254", "/latest/meta-data/", "/latest/api/token"), download-and-execute and decode-and-execute ("| sh", "| bash", "base64 -d"), cryptominers ("xmrig", "minerd", "stratum+"), and persistence ("authorized_keys", "crontab", "/etc/cron"). A match is a strong indicator of an implant attempt.
### Possible investigation steps
- Review the decoded "Esql_priv.aws_cloudtrail_lifecycle_script" field and the full "aws.cloudtrail.request_parameters" to understand what the script does.
- Identify the actor in "aws.cloudtrail.user_identity.arn" and review "source.ip" and "user_agent.original" for an unexpected origin.
- Determine which notebook instances reference this configuration and whether they have started since the change.
- Correlate with adjacent activity by the same principal, such as notebook creation, presigned URL generation, or use of the execution role's credentials outside SageMaker.
### False positive analysis
- Setup scripts can legitimately reference metadata endpoints or download tooling. Confirm the script is expected and exclude known automation roles after validation. This rule only matches unobfuscated indicators, so it can miss obfuscated scripts (use the broad lifecycle-configuration-change rule for full coverage) and can match benign scripts that contain these strings.
### Response and remediation
- If unauthorized, remove the lifecycle configuration, stop affected notebook instances, and rotate the notebook execution role's credentials.
- Review actions taken by the execution role since the change, and restrict "sagemaker:CreateNotebookInstanceLifecycleConfig" and "sagemaker:UpdateNotebookInstanceLifecycleConfig" to trusted administrators.
"""
references = ["https://docs.aws.amazon.com/sagemaker/latest/dg/notebook-lifecycle-config.html"]
risk_score = 73
rule_id = "0471a12b-c740-4a72-91f0-12bf95014d3c"
setup = "This rule requires AWS CloudTrail logs ingested via the Elastic AWS integration. See https://docs.elastic.co/integrations/aws/cloudtrail for setup details."
severity = "high"
tags = [
"Domain: Cloud",
"Data Source: AWS",
"Data Source: Amazon Web Services",
"Data Source: AWS SageMaker",
"Use Case: Threat Detection",
"Tactic: Persistence",
"Tactic: Execution",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "esql"
query = '''
FROM logs-aws.cloudtrail-* METADATA _id, _version, _index
| WHERE event.provider == "sagemaker.amazonaws.com"
AND event.action IN ("CreateNotebookInstanceLifecycleConfig", "UpdateNotebookInstanceLifecycleConfig")
AND event.outcome == "success"
AND aws.cloudtrail.user_identity.type != "AWSService"
| GROK aws.cloudtrail.request_parameters "[Cc]ontent=(?<script_b64>[A-Za-z0-9+/=]+)"
| EVAL Esql_priv.aws_cloudtrail_lifecycle_script = FROM_BASE64(script_b64)
| WHERE TO_LOWER(Esql_priv.aws_cloudtrail_lifecycle_script) RLIKE """.*(/dev/tcp/|/dev/udp/|bash -i|sh -i|nc -e|ncat |socat |mkfifo|169\.254\.169\.254|/latest/meta-data/|/latest/api/token|\| ?sh|\| ?bash|base64 -d|import socket|pty\.spawn|perl -e|ruby -rsocket|php -r|xmrig|minerd|stratum\+|authorized_keys|/etc/cron|crontab ).*"""
| KEEP _id, _version, _index, @timestamp, aws.*, cloud.*, event.*, source.*, user.*, user_agent.*, Esql_priv.aws_cloudtrail_lifecycle_script
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1546"
name = "Event Triggered Execution"
reference = "https://attack.mitre.org/techniques/T1546/"
[rule.threat.tactic]
id = "TA0003"
name = "Persistence"
reference = "https://attack.mitre.org/tactics/TA0003/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1059"
name = "Command and Scripting Interpreter"
reference = "https://attack.mitre.org/techniques/T1059/"
[[rule.threat.technique.subtechnique]]
id = "T1059.004"
name = "Unix Shell"
reference = "https://attack.mitre.org/techniques/T1059/004/"
[rule.threat.tactic]
id = "TA0002"
name = "Execution"
reference = "https://attack.mitre.org/tactics/TA0002/"
[rule.investigation_fields]
field_names = [
"@timestamp",
"aws.cloudtrail.user_identity.arn",
"aws.cloudtrail.user_identity.type",
"source.ip",
"cloud.account.id",
"event.action",
"Esql_priv.aws_cloudtrail_lifecycle_script",
"aws.cloudtrail.request_parameters",
]
Stages and Predicates
Stage 1: from
FROM logs-aws.cloudtrail-* METADATA _id, _version, _index
Stage 2: where
| WHERE event.provider == "sagemaker.amazonaws.com"
AND event.action IN ("CreateNotebookInstanceLifecycleConfig", "UpdateNotebookInstanceLifecycleConfig")
AND event.outcome == "success"
AND aws.cloudtrail.user_identity.type != "AWSService"
Stage 3: grok
| GROK aws.cloudtrail.request_parameters "[Cc]ontent=(?<script_b64>[A-Za-z0-9+/=]+)"
Stage 4: eval
| EVAL Esql_priv.aws_cloudtrail_lifecycle_script = FROM_BASE64(script_b64)
Stage 5: where
| WHERE TO_LOWER(Esql_priv.aws_cloudtrail_lifecycle_script) RLIKE """.*(/dev/tcp/|/dev/udp/|bash -i|sh -i|nc -e|ncat |socat |mkfifo|169\.254\.169\.254|/latest/meta-data/|/latest/api/token|\| ?sh|\| ?bash|base64 -d|import socket|pty\.spawn|perl -e|ruby -rsocket|php -r|xmrig|minerd|stratum\+|authorized_keys|/etc/cron|crontab ).*"""
Stage 6: keep
| KEEP _id, _version, _index, @timestamp, aws.*, cloud.*, event.*, source.*, user.*, user_agent.*, Esql_priv.aws_cloudtrail_lifecycle_script
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
aws.cloudtrail.user_identity.type | ne |
| field:"aws::userIdentity.type" kind:ne value:"AWSService" |
event.action | in |
| field:"EventType" kind:in |
event.outcome | eq |
| field:"event.outcome" kind:eq value:"success" |
event.provider | eq |
| field:"Provider_Name" kind:eq value:"sagemaker.amazonaws.com" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
_id | KEEP _id |
_version | KEEP _version |
_index | KEEP _index |
@timestamp | KEEP @timestamp |
aws.* | KEEP aws.* |
cloud.* | KEEP cloud.* |
event.* | KEEP event.* |
source.* | KEEP source.* |
user.* | KEEP user.* |
user_agent.* | KEEP user_agent.* |
Esql_priv.aws_cloudtrail_lifecycle_script | KEEP Esql_priv.aws_cloudtrail_lifecycle_script |
Direct Process Execution via Background Utility
#This is a New Terms rule that identifies the first occurrence of setsid or nohup being used to directly execute a process on a host. Attackers may leverage these tools to execute commands in a new session and/or to ignore signals.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Stealth |
Rule Body
[metadata]
creation_date = "2026/07/30"
integration = ["endpoint", "sentinel_one_cloud_funnel", "auditd_manager"]
maturity = "production"
updated_date = "2026/08/03"
[rule]
author = ["Elastic"]
description = """
This is a New Terms rule that identifies the first occurrence of setsid or nohup being used to directly
execute a process on a host. Attackers may leverage these tools to execute commands in a new session
and/or to ignore signals.
"""
from = "now-9m"
index = [
"endgame-*",
"logs-endpoint.events.process*",
"logs-sentinel_one_cloud_funnel.*",
"auditbeat-*",
"logs-auditd_manager.auditd-*",
]
language = "kuery"
license = "Elastic License v2"
name = "Direct Process Execution via Background Utility"
note = """ ## Triage and analysis
> **Disclaimer**:
> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
### Investigating Direct Process Execution via Background Utility
This rule flags the first time a Linux host uses setsid, nohup, or disown to launch another process directly, which often means someone is detaching execution from the current terminal or login session. Attackers commonly use nohup to start a backdoor, reverse shell, or cryptominer after SSH access so the process keeps running after they disconnect and ignores normal hangup signals.
### Possible investigation steps
- Review the child process launched by the utility, including its full command line, executable path, working directory, and any redirected output files, to quickly separate routine administration from suspicious payload execution.
- Trace the full ancestry and session context around the launch, such as an interactive shell, SSH login, sudo elevation, script runner, or service account, to determine whether the execution came from an expected workflow or an unusual entry point.
- Assess the user and host history for similar behavior by checking recent logins, shell history, and prior detached launches on the same asset or by the same account, since a first-seen event from a normally quiet user often increases concern.
- Inspect immediate follow-on activity from the spawned process, especially outbound network connections, file downloads, child process creation, or persistence changes, because detached execution is commonly used to keep malicious tooling running after logout.
- Confirm with the system owner whether the command aligns with approved long-running tasks such as maintenance, backups, or software updates, and if not, preserve the binary and related artifacts for deeper analysis and potential containment.
### False positive analysis
- A Linux administrator may legitimately use nohup or setsid to launch an approved maintenance, backup, or data-processing script from an interactive shell so it continues after logout; verify the child command, executable path, initiating user, and execution time match expected operational activity on that host.
- An engineer troubleshooting or restarting a local application may detach the process with setsid during a remote session to avoid terminal interruption; verify the parent shell and account are authorized and that the spawned binary or script and working directory align with the host's normal application files.
### Response and remediation
- Isolate the affected Linux host from the network, terminate the detached child process started with `nohup`, `setsid`, or `disown` and any descendants, and preserve the executable, shell script, redirected output files, and shell history for evidence.
- Remove attacker persistence by deleting malicious cron jobs, systemd service or timer units, `rc.local` or shell profile modifications, unauthorized `authorized_keys` entries, and any dropped binaries or scripts referenced by the detached command.
- Reset compromised access by disabling or rotating credentials for the initiating account and any accounts used afterward, revoking active SSH sessions and tokens, and reviewing `sudoers`, newly added local users, and group memberships for unauthorized changes.
- Rebuild the host from a known-good image or restore from a trusted backup if the detached process ran a backdoor, reverse shell, downloader, or altered system binaries, and verify only approved packages, services, and startup items remain before returning it to production.
- Escalate to incident response immediately if the detached process contacted an external command-and-control address, executed from a writable temporary or home directory as root, spread to other hosts, or evidence shows credential theft or persistence beyond the original system.
- Harden the environment by restricting interactive use of backgrounding utilities where not required, tightening SSH and `sudo` access, enforcing application allowlisting and least privilege, and adding detections for detached launches from temporary directories, user home directories, and unexpected service accounts.
"""
references = [
"https://cloud.google.com/blog/topics/threat-intelligence/disrupting-gridtide-global-espionage-campaign?hl=en"
]
risk_score = 21
rule_id = "9dc1d59c-fc5f-4a73-8ca6-1473eabdff2a"
setup = """## Setup
This rule requires data coming in from Elastic Defend.
### Elastic Defend Integration Setup
Elastic Defend is integrated into the Elastic Agent using Fleet. Upon configuration, the integration allows the Elastic Agent to monitor events on your host and send data to the Elastic Security app.
#### Prerequisite Requirements:
- Fleet is required for Elastic Defend.
- To configure Fleet Server refer to the [documentation](https://www.elastic.co/guide/en/fleet/current/fleet-server.html).
#### The following steps should be executed in order to add the Elastic Defend integration on a Linux System:
- Go to the Kibana home page and click "Add integrations".
- In the query bar, search for "Elastic Defend" and select the integration to see more details about it.
- Click "Add Elastic Defend".
- Configure the integration name and optionally add a description.
- Select the type of environment you want to protect, either "Traditional Endpoints" or "Cloud Workloads".
- Select a configuration preset. Each preset comes with different default settings for Elastic Agent, you can further customize these later by configuring the Elastic Defend integration policy. [Helper guide](https://www.elastic.co/guide/en/security/current/configure-endpoint-integration-policy.html).
- We suggest selecting "Complete EDR (Endpoint Detection and Response)" as a configuration setting, that provides "All events; all preventions"
- Enter a name for the agent policy in "New agent policy name". If other agent policies already exist, you can click the "Existing hosts" tab and select an existing policy instead.
For more details on Elastic Agent configuration settings, refer to the [helper guide](https://www.elastic.co/guide/en/fleet/8.10/agent-policy.html).
- Click "Save and Continue".
- To complete the integration, select "Add Elastic Agent to your hosts" and continue to the next section to install the Elastic Agent on your hosts.
For more details on Elastic Defend refer to the [helper guide](https://www.elastic.co/guide/en/security/current/install-endpoint.html).
"""
severity = "low"
tags = [
"Domain: Endpoint",
"OS: Linux",
"Use Case: Threat Detection",
"Tactic: Execution",
"Tactic: Defense Evasion",
"Data Source: Elastic Defend",
"Data Source: Elastic Endgame",
"Data Source: SentinelOne",
"Data Source: Auditd Manager",
"Resources: Investigation Guide"
]
timestamp_override = "event.ingested"
type = "new_terms"
query = '''
host.os.type:linux and event.category:process and
event.action:("exec" or "exec_event" or "executed" or "process_started" or "start") and
process.name:("setsid" or "nohup" or "disown") and process.args_count:2
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1059"
name = "Command and Scripting Interpreter"
reference = "https://attack.mitre.org/techniques/T1059/"
[[rule.threat.technique.subtechnique]]
id = "T1059.004"
name = "Unix Shell"
reference = "https://attack.mitre.org/techniques/T1059/004/"
[rule.threat.tactic]
id = "TA0002"
name = "Execution"
reference = "https://attack.mitre.org/tactics/TA0002/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1036"
name = "Masquerading"
reference = "https://attack.mitre.org/techniques/T1036/"
[[rule.threat.technique.subtechnique]]
id = "T1036.009"
name = "Break Process Trees"
reference = "https://attack.mitre.org/techniques/T1036/009/"
[[rule.threat.technique]]
id = "T1564"
name = "Hide Artifacts"
reference = "https://attack.mitre.org/techniques/T1564/"
[rule.threat.tactic]
id = "TA0005"
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"
[rule.new_terms]
field = "new_terms_fields"
value = ["process.parent.executable", "host.id", "process.command_line"]
[[rule.new_terms.history_window_start]]
field = "history_window_start"
value = "now-5d"
Stages and Predicates
Stage 1: new_terms
host.os.type:linux and event.category:process and
event.action:("exec" or "exec_event" or "executed" or "process_started" or "start") and
process.name:("setsid" or "nohup" or "disown") and process.args_count:2
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.action | in |
| field:"EventType" kind:in |
event.category | eq |
| field:"event.category" kind:eq value:"process" |
process.args_count | eq |
| field:"process.args_count" kind:eq value:"2" |
process.name | in |
| field:"process_basename" kind:in |
Dracut Module Creation
#This rule detects the creation of Dracut module files on Linux systems. Dracut is a tool used to generate an initramfs image that is used to boot the system. Dracut modules are scripts that are executed during the initramfs image generation process. Attackers may create malicious Dracut modules to execute arbitrary code at boot time, which can be leveraged to maintain persistence on a Linux system.
MITRE ATT&CK Coverage
Rule Body
[metadata]
creation_date = "2025/01/16"
integration = ["endpoint", "sentinel_one_cloud_funnel"]
maturity = "production"
updated_date = "2026/03/24"
[rule]
author = ["Elastic"]
description = """
This rule detects the creation of Dracut module files on Linux systems. Dracut is a tool used to generate an initramfs
image that is used to boot the system. Dracut modules are scripts that are executed during the initramfs image
generation process. Attackers may create malicious Dracut modules to execute arbitrary code at boot time, which can be
leveraged to maintain persistence on a Linux system.
"""
from = "now-9m"
index = ["logs-endpoint.events.file*", "logs-sentinel_one_cloud_funnel.*", "endgame-*"]
language = "eql"
license = "Elastic License v2"
name = "Dracut Module Creation"
note = """## Triage and analysis
> **Disclaimer**:
> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
### Investigating Dracut Module Creation
Dracut is a utility for generating initramfs images, crucial for booting Linux systems. It uses modules, which are scripts executed during image creation. Adversaries may exploit this by crafting malicious modules to execute code at boot, ensuring persistence. The detection rule identifies unauthorized module creation by monitoring file paths and excluding known legitimate processes, helping to flag potential threats.
### Possible investigation steps
- Review the file path of the created Dracut module to determine if it matches known legitimate paths or if it appears suspicious, focusing on paths like "/lib/dracut/modules.d/*" and "/usr/lib/dracut/modules.d/*".
- Identify the process that created the Dracut module by examining the process.executable field, and verify if it is listed in the known legitimate processes or if it is an unexpected process.
- Check the file extension of the created module to ensure it is not one of the excluded extensions such as "swp", "swpx", "swx", or "dpkg-remove".
- Investigate the history and behavior of the process that created the module, including its parent process and any associated network activity, to assess if it has been involved in other suspicious activities.
- Correlate the alert with other security events or logs from the same host to identify any patterns or additional indicators of compromise that might suggest malicious activity.
- Consult threat intelligence sources to determine if there are any known threats or campaigns associated with the process or file path involved in the alert.
### False positive analysis
- Package managers like dpkg, rpm, and yum may trigger false positives when they update or install packages. To handle this, ensure these processes are included in the exclusion list within the detection rule.
- Automated system management tools such as Puppet, Chef, and Ansible can create or modify Dracut modules as part of their configuration management tasks. Add these tools to the exclusion list to prevent false alerts.
- System updates or maintenance scripts that run as part of regular system operations might be flagged. Review these scripts and add their executables to the exclusion list if they are verified as non-threatening.
- Custom scripts or applications that interact with Dracut modules for legitimate purposes should be reviewed and, if deemed safe, added to the exclusion list to avoid unnecessary alerts.
- Temporary files or backup files with extensions like swp or dpkg-remove may be mistakenly flagged. Ensure these extensions are included in the exclusion criteria to reduce false positives.
### Response and remediation
- Immediately isolate the affected system from the network to prevent further spread or communication with potential command and control servers.
- Conduct a thorough review of the Dracut module files located in the specified directories (/lib/dracut/modules.d/*, /usr/lib/dracut/modules.d/*) to identify and remove any unauthorized or suspicious modules.
- Restore the system from a known good backup if malicious Dracut modules are confirmed, ensuring that the backup predates the unauthorized changes.
- Implement additional monitoring on the affected system to detect any further unauthorized Dracut module creation or other suspicious activities.
- Review and tighten access controls and permissions for the directories and processes involved in Dracut module creation to prevent unauthorized modifications.
- Escalate the incident to the security operations team for further investigation and to determine if additional systems are affected.
- Update and enhance detection capabilities to include alerts for any future unauthorized Dracut module creation attempts, leveraging the specific indicators identified in this incident."""
risk_score = 21
rule_id = "dc765fb2-0c99-4e57-8c11-dafdf1992b66"
setup = """## Setup
This rule requires data coming in from Elastic Defend.
### Elastic Defend Integration Setup
Elastic Defend is integrated into the Elastic Agent using Fleet. Upon configuration, the integration allows the Elastic Agent to monitor events on your host and send data to the Elastic Security app.
#### Prerequisite Requirements:
- Fleet is required for Elastic Defend.
- To configure Fleet Server refer to the [documentation](https://www.elastic.co/guide/en/fleet/current/fleet-server.html).
#### The following steps should be executed in order to add the Elastic Defend integration on a Linux System:
- Go to the Kibana home page and click "Add integrations".
- In the query bar, search for "Elastic Defend" and select the integration to see more details about it.
- Click "Add Elastic Defend".
- Configure the integration name and optionally add a description.
- Select the type of environment you want to protect, either "Traditional Endpoints" or "Cloud Workloads".
- Select a configuration preset. Each preset comes with different default settings for Elastic Agent, you can further customize these later by configuring the Elastic Defend integration policy. [Helper guide](https://www.elastic.co/guide/en/security/current/configure-endpoint-integration-policy.html).
- We suggest selecting "Complete EDR (Endpoint Detection and Response)" as a configuration setting, that provides "All events; all preventions"
- Enter a name for the agent policy in "New agent policy name". If other agent policies already exist, you can click the "Existing hosts" tab and select an existing policy instead.
For more details on Elastic Agent configuration settings, refer to the [helper guide](https://www.elastic.co/guide/en/fleet/8.10/agent-policy.html).
- Click "Save and Continue".
- To complete the integration, select "Add Elastic Agent to your hosts" and continue to the next section to install the Elastic Agent on your hosts.
For more details on Elastic Defend refer to the [helper guide](https://www.elastic.co/guide/en/security/current/install-endpoint.html).
"""
severity = "low"
tags = [
"Domain: Endpoint",
"OS: Linux",
"Use Case: Threat Detection",
"Tactic: Persistence",
"Tactic: Execution",
"Tactic: Defense Evasion",
"Data Source: Elastic Defend",
"Data Source: SentinelOne",
"Data Source: Elastic Endgame",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "eql"
query = '''
file where host.os.type == "linux" and event.type == "creation" and process.executable != null and
file.path like~ ("/lib/dracut/modules.d/*", "/usr/lib/dracut/modules.d/*") and not (
// Too many FPs from Python automation
process.name like ("python*", "platform-python*") or
process.executable in (
"/bin/dpkg", "/usr/bin/dpkg", "/bin/dockerd", "/usr/bin/dockerd", "/usr/sbin/dockerd", "/bin/microdnf",
"/usr/bin/microdnf", "/bin/rpm", "/usr/bin/rpm", "/bin/snapd", "/usr/bin/snapd", "/bin/yum", "/usr/bin/yum",
"/bin/dnf", "/usr/bin/dnf", "/bin/podman", "/usr/bin/podman", "/bin/dnf-automatic", "/usr/bin/dnf-automatic",
"/bin/pacman", "/usr/bin/pacman", "/usr/bin/dpkg-divert", "/bin/dpkg-divert", "/sbin/apk", "/usr/sbin/apk",
"/usr/local/sbin/apk", "/usr/bin/apt", "/usr/sbin/pacman", "/bin/podman", "/usr/bin/podman", "/usr/bin/puppet",
"/bin/puppet", "/opt/puppetlabs/puppet/bin/puppet", "/usr/bin/chef-client", "/bin/chef-client",
"/bin/autossl_check", "/usr/bin/autossl_check", "/proc/self/exe", "/usr/bin/pamac-daemon",
"/bin/pamac-daemon", "/usr/lib/snapd/snapd", "/usr/local/bin/dockerd", "/usr/bin/crio", "/usr/sbin/crond",
"/opt/puppetlabs/puppet/bin/ruby", "/usr/libexec/platform-python", "/kaniko/kaniko-executor",
"/usr/local/bin/dockerd", "/usr/bin/podman", "/bin/install", "/proc/self/exe", "/usr/lib/systemd/systemd",
"/usr/sbin/sshd", "/usr/bin/gitlab-runner", "/opt/gitlab/embedded/bin/ruby", "/usr/sbin/gdm", "/usr/bin/install",
"/usr/local/manageengine/uems_agent/bin/dcregister", "/usr/local/bin/pacman", "/usr/libexec/packagekitd",
"./usr/bin/podman", "/usr/lib/dracut/dracut-install", "/usr/bin/dnf5", "/kaniko/executor", "/usr/bin/buildah",
"/usr/sbin/yum-cron"
) or
process.executable like~ (
"/nix/store/*", "/var/lib/dpkg/*", "/tmp/vmis.*", "/snap/*", "/dev/fd/*", "/usr/lib/virtualbox/*",
"/var/lib/docker/overlay2/*/dockerd"
) or
file.extension in ("swp", "swpx", "swx", "dpkg-remove") or
(process.name == "sed" and file.name : "sed*")
)
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1542"
name = "Pre-OS Boot"
reference = "https://attack.mitre.org/techniques/T1542/"
[[rule.threat.technique]]
id = "T1543"
name = "Create or Modify System Process"
reference = "https://attack.mitre.org/techniques/T1543/"
[[rule.threat.technique]]
id = "T1574"
name = "Hijack Execution Flow"
reference = "https://attack.mitre.org/techniques/T1574/"
[rule.threat.tactic]
id = "TA0003"
name = "Persistence"
reference = "https://attack.mitre.org/tactics/TA0003/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1059"
name = "Command and Scripting Interpreter"
reference = "https://attack.mitre.org/techniques/T1059/"
[[rule.threat.technique.subtechnique]]
id = "T1059.004"
name = "Unix Shell"
reference = "https://attack.mitre.org/techniques/T1059/004/"
[rule.threat.tactic]
id = "TA0002"
name = "Execution"
reference = "https://attack.mitre.org/tactics/TA0002/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1542"
name = "Pre-OS Boot"
reference = "https://attack.mitre.org/techniques/T1542/"
[rule.threat.tactic]
id = "TA0005"
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"
Stages and Predicates
Stage 1: file
file where host.os.type == "linux" and event.type == "creation" and process.executable != null and
file.path like~ ("/lib/dracut/modules.d/*", "/usr/lib/dracut/modules.d/*") and not (
process.name like ("python*", "platform-python*") or
process.executable in (
"/bin/dpkg", "/usr/bin/dpkg", "/bin/dockerd", "/usr/bin/dockerd", "/usr/sbin/dockerd", "/bin/microdnf",
"/usr/bin/microdnf", "/bin/rpm", "/usr/bin/rpm", "/bin/snapd", "/usr/bin/snapd", "/bin/yum", "/usr/bin/yum",
"/bin/dnf", "/usr/bin/dnf", "/bin/podman", "/usr/bin/podman", "/bin/dnf-automatic", "/usr/bin/dnf-automatic",
"/bin/pacman", "/usr/bin/pacman", "/usr/bin/dpkg-divert", "/bin/dpkg-divert", "/sbin/apk", "/usr/sbin/apk",
"/usr/local/sbin/apk", "/usr/bin/apt", "/usr/sbin/pacman", "/bin/podman", "/usr/bin/podman", "/usr/bin/puppet",
"/bin/puppet", "/opt/puppetlabs/puppet/bin/puppet", "/usr/bin/chef-client", "/bin/chef-client",
"/bin/autossl_check", "/usr/bin/autossl_check", "/proc/self/exe", "/usr/bin/pamac-daemon",
"/bin/pamac-daemon", "/usr/lib/snapd/snapd", "/usr/local/bin/dockerd", "/usr/bin/crio", "/usr/sbin/crond",
"/opt/puppetlabs/puppet/bin/ruby", "/usr/libexec/platform-python", "/kaniko/kaniko-executor",
"/usr/local/bin/dockerd", "/usr/bin/podman", "/bin/install", "/proc/self/exe", "/usr/lib/systemd/systemd",
"/usr/sbin/sshd", "/usr/bin/gitlab-runner", "/opt/gitlab/embedded/bin/ruby", "/usr/sbin/gdm", "/usr/bin/install",
"/usr/local/manageengine/uems_agent/bin/dcregister", "/usr/local/bin/pacman", "/usr/libexec/packagekitd",
"./usr/bin/podman", "/usr/lib/dracut/dracut-install", "/usr/bin/dnf5", "/kaniko/executor", "/usr/bin/buildah",
"/usr/sbin/yum-cron"
) or
process.executable like~ (
"/nix/store/*", "/var/lib/dpkg/*", "/tmp/vmis.*", "/snap/*", "/dev/fd/*", "/usr/lib/virtualbox/*",
"/var/lib/docker/overlay2/*/dockerd"
) or
file.extension in ("swp", "swpx", "swx", "dpkg-remove") or
(process.name == "sed" and file.name : "sed*")
)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.type | eq |
| field:"event.type" kind:eq value:"creation" |
file.path | wildcard |
| field:"TargetFilename" kind:wildcard |
process.executable | is_not_null | field:"Image" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
file.name | starts_with | sed | excludes:file.name field:"file.name" value:"sed" |
process.name | eq | sed | excludes:process.name field:"process.name" value:"sed" |
file.extension | in | dpkg-remove, swp, swpx, swx | excludes:file.extension |
process.executable | in | ./usr/bin/podman, /bin/autossl_check, /bin/chef-client, /bin/dnf, /bin/dnf-automatic, /bin/dockerd, /bin/dpkg, /bin/dpkg-divert, /bin/install, /bin/microdnf, /bin/pacman, /bin/pamac-daemon, /bin/podman, /bin/puppet, /bin/rpm, /bin/snapd, /bin/yum, /kaniko/executor, /kaniko/kaniko-executor, /opt/gitlab/embedded/bin/ruby, /opt/puppetlabs/puppet/bin/puppet, /opt/puppetlabs/puppet/bin/ruby, /proc/self/exe, /sbin/apk, /usr/bin/apt, /usr/bin/autossl_check, /usr/bin/buildah, /usr/bin/chef-client, /usr/bin/crio, /usr/bin/dnf, /usr/bin/dnf-automatic, /usr/bin/dnf5, /usr/bin/dockerd, /usr/bin/dpkg, /usr/bin/dpkg-divert, /usr/bin/gitlab-runner, /usr/bin/install, /usr/bin/microdnf, /usr/bin/pacman, /usr/bin/pamac-daemon, /usr/bin/podman, /usr/bin/puppet, /usr/bin/rpm, /usr/bin/snapd, /usr/bin/yum, /usr/lib/dracut/dracut-install, /usr/lib/snapd/snapd, /usr/lib/systemd/systemd, /usr/libexec/packagekitd, /usr/libexec/platform-python, /usr/local/bin/dockerd, /usr/local/bin/pacman, /usr/local/manageengine/uems_agent/bin/dcregister, /usr/local/sbin/apk, /usr/sbin/apk, /usr/sbin/crond, /usr/sbin/dockerd, /usr/sbin/gdm, /usr/sbin/pacman, /usr/sbin/sshd, /usr/sbin/yum-cron | excludes:process.executable |
process.executable | wildcard | /nix/store/*, /var/lib/dpkg/*, /tmp/vmis.*, /snap/*, /dev/fd/*, /usr/lib/virtualbox/*, /var/lib/docker/overlay2/*/dockerd | excludes:process.executable |
process.name | starts_with | python, platform-python | excludes:process.name field:"process.name" value:"python" field:"process.name" value:"platform-python" |
Enumerating Domain Trusts via DSQUERY.EXE
#Identifies the use of dsquery.exe for domain trust discovery purposes. Adversaries may use this command-line utility to enumerate trust relationships that may be used for Lateral Movement opportunities in Windows multi-domain forest environments.
Known False Positives
- Domain administrators may use this command-line utility for legitimate information gathering purposes.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Sysmon | Event ID 1: Process creation |
| Security-Auditing | Event ID 4688: A new process has been created. |
Rule Body
[metadata]
creation_date = "2023/01/27"
integration = ["endpoint", "windows", "system", "m365_defender", "sentinel_one_cloud_funnel", "crowdstrike"]
maturity = "production"
updated_date = "2026/05/04"
[rule]
author = ["Elastic"]
description = """
Identifies the use of dsquery.exe for domain trust discovery purposes. Adversaries may use this command-line utility to
enumerate trust relationships that may be used for Lateral Movement opportunities in Windows multi-domain forest
environments.
"""
false_positives = [
"Domain administrators may use this command-line utility for legitimate information gathering purposes.",
]
from = "now-9m"
index = [
"endgame-*",
"logs-crowdstrike.fdr*",
"logs-endpoint.events.process-*",
"logs-m365_defender.event-*",
"logs-sentinel_one_cloud_funnel.*",
"logs-system.security*",
"logs-windows.forwarded*",
"logs-windows.sysmon_operational-*",
"winlogbeat-*",
]
language = "eql"
license = "Elastic License v2"
name = "Enumerating Domain Trusts via DSQUERY.EXE"
note = """## Triage and analysis
### Investigating Enumerating Domain Trusts via DSQUERY.EXE
Active Directory (AD) domain trusts define relationships between domains within a Windows AD environment. In this setup, a "trusting" domain permits users from a "trusted" domain to access resources. These trust relationships can be configurable as one-way, two-way, transitive, or non-transitive, enabling controlled access and resource sharing across domains.
This rule identifies the usage of the `dsquery.exe` utility to enumerate domain trusts. Attackers can use this information to enable the next actions in a target environment, such as lateral movement.
#### Possible investigation steps
- Investigate the process execution chain (parent process tree) for unknown processes. Examine their executable files for prevalence, whether they are located in expected locations, and if they are signed with valid digital signatures.
- Identify the user account that performed the action and whether it should perform this kind of action.
- Investigate other alerts associated with the user/host during the past 48 hours.
### False positive analysis
- Discovery activities are not inherently malicious if they occur in isolation and are done within the user business context (e.g., an administrator in this context). As long as the analyst did not identify suspicious activity related to the user or host, such alerts can be dismissed.
### Related rules
- Enumerating Domain Trusts via NLTEST.EXE - 84da2554-e12a-11ec-b896-f661ea17fbcd
### Response and remediation
- Initiate the incident response process based on the outcome of the triage.
- Isolate the involved hosts to prevent further post-compromise behavior.
- Restrict PowerShell usage outside of IT and engineering business units using GPOs, AppLocker, Intune, or similar software.
- Determine the initial vector abused by the attacker and take action to prevent reinfection through the same vector.
- Run a full antimalware scan. This may reveal additional artifacts left in the system, persistence mechanisms, and malware components.
- Using the incident response data, update logging and audit policies to improve the mean time to detect (MTTD) and the mean time to respond (MTTR).
"""
setup = """## Setup
This rule is designed for data generated by [Elastic Defend](https://www.elastic.co/security/endpoint-security), which provides native endpoint detection and response, along with event enrichments designed to work with our detection rules.
Setup instructions: https://ela.st/install-elastic-defend
### Additional data sources
This rule also supports the following third-party data sources. For setup instructions, refer to the links below:
- [CrowdStrike](https://ela.st/crowdstrike-integration)
- [Microsoft Defender XDR](https://ela.st/m365-defender)
- [SentinelOne Cloud Funnel](https://ela.st/sentinel-one-cloud-funnel)
- [Sysmon Event ID 1 - Process Creation](https://ela.st/sysmon-event-1-setup)
- [Windows Process Creation Logs](https://ela.st/audit-process-creation)
"""
references = [
"https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/cc732952(v=ws.11)",
"https://posts.specterops.io/a-guide-to-attacking-domain-trusts-971e52cb2944",
]
risk_score = 21
rule_id = "06a7a03c-c735-47a6-a313-51c354aef6c3"
severity = "low"
tags = [
"Domain: Endpoint",
"OS: Windows",
"Use Case: Threat Detection",
"Tactic: Discovery",
"Data Source: Elastic Endgame",
"Resources: Investigation Guide",
"Data Source: Elastic Defend",
"Data Source: Windows Security Event Logs",
"Data Source: Microsoft Defender XDR",
"Data Source: Sysmon",
"Data Source: SentinelOne",
"Data Source: Crowdstrike",
]
timestamp_override = "event.ingested"
type = "eql"
query = '''
process where host.os.type == "windows" and event.type == "start" and
(process.name : "dsquery.exe" or ?process.pe.original_file_name: "dsquery.exe") and
process.args : "*objectClass=trustedDomain*"
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1018"
name = "Remote System Discovery"
reference = "https://attack.mitre.org/techniques/T1018/"
[[rule.threat.technique]]
id = "T1482"
name = "Domain Trust Discovery"
reference = "https://attack.mitre.org/techniques/T1482/"
[rule.threat.tactic]
id = "TA0007"
name = "Discovery"
reference = "https://attack.mitre.org/tactics/TA0007/"
Stages and Predicates
Stage 1: process
process where host.os.type == "windows" and event.type == "start" and
(process.name : "dsquery.exe" or ?process.pe.original_file_name: "dsquery.exe") and
process.args : "*objectClass=trustedDomain*"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.type | eq |
| field:"event.type" kind:eq value:"start" |
process.args | wildcard |
| field:"process.args" kind:wildcard value:"*objectClass=trustedDomain*" |
process.name | wildcard |
| field:"process_basename" kind:wildcard value:"dsquery.exe" |
process.pe.original_file_name | wildcard |
| field:"OriginalFileName" kind:wildcard value:"dsquery.exe" |
File Execution Permission Modification Detected via Defend for Containers
#This rule detects when chmod is used to add the execute permission to a file inside a container. Modifying file permissions to make a file executable could indicate malicious activity, as an attacker may attempt to run unauthorized or malicious code inside the container.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Privilege Escalation | |
| Defense Impairment |
Rule Body
[metadata]
creation_date = "2023/04/26"
integration = ["cloud_defend"]
maturity = "production"
min_stack_comments = "Defend for Containers integration was re-introduced in 9.3.0"
min_stack_version = "9.3.0"
updated_date = "2026/03/30"
[rule]
author = ["Elastic"]
description = """
This rule detects when chmod is used to add the execute permission to a file inside a container. Modifying file
permissions to make a file executable could indicate malicious activity, as an attacker may attempt to run unauthorized
or malicious code inside the container.
"""
from = "now-6m"
index = ["logs-cloud_defend.file*", "logs-cloud_defend.process*"]
interval = "5m"
language = "eql"
license = "Elastic License v2"
name = "File Execution Permission Modification Detected via Defend for Containers"
note = """## Setup
## Triage and analysis
> **Disclaimer**:
> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
### Investigating File Execution Permission Modification Detected via Defend for Containers
Containers provide isolated environments for running applications, often on Linux systems. The `chmod` command is used to change file permissions, including making files executable. Adversaries may exploit this by altering permissions to execute unauthorized scripts or binaries, potentially leading to malicious activity. The detection rule identifies such actions by monitoring for `chmod` usage that grants execute permissions, focusing on specific permission patterns, and excluding benign cases. This helps in identifying potential threats where attackers attempt to execute unauthorized code within containers.
### Possible investigation steps
- Review the container ID associated with the alert to identify the specific container where the `chmod` command was executed.
- Examine the process arguments to determine the exact permissions that were set and identify the file that was made executable.
- Investigate the origin of the `chmod` command by reviewing the process tree to understand which parent process initiated it and whether it aligns with expected behavior.
- Check the user account or service account that executed the `chmod` command to assess if it has legitimate access and reason to modify file permissions.
- Analyze the file that was made executable to determine its contents and origin, checking for any signs of unauthorized or malicious code.
- Correlate this event with other logs or alerts from the same container to identify any patterns or additional suspicious activities that might indicate a broader attack.
### False positive analysis
- Routine maintenance scripts or automated processes may use chmod to set execute permissions on files within containers. To handle these, identify and whitelist specific scripts or processes that are known to be safe and necessary for operations.
- Development environments often involve frequent changes to file permissions as developers test and deploy code. Consider excluding specific container IDs or paths associated with development environments to reduce noise.
- Some container orchestration tools might use chmod as part of their normal operation. Review the processes and arguments associated with these tools and create exceptions for known benign activities.
- System updates or package installations within containers might trigger this rule. Monitor and document regular update schedules and processes, and exclude these from triggering alerts if they are verified as non-threatening.
- If certain users or roles are responsible for legitimate permission changes, consider excluding their activities by user ID or role, ensuring that these exclusions are well-documented and reviewed regularly.
### Response and remediation
- Immediately isolate the affected container to prevent further execution of unauthorized code. This can be done by stopping the container or disconnecting it from the network.
- Conduct a thorough review of the container's file system to identify any unauthorized or suspicious files that have been made executable. Remove or quarantine these files as necessary.
- Analyze the container's logs to trace the source of the `chmod` command and determine if there are any other indicators of compromise or related malicious activities.
- If the unauthorized execution is confirmed, assess the potential impact on the host system and other containers. Implement additional security measures, such as enhanced monitoring or network segmentation, to protect other assets.
- Escalate the incident to the security operations team for further investigation and to determine if the threat is part of a larger attack campaign.
- Review and update container security policies to prevent unauthorized permission changes, such as implementing stricter access controls and using security tools that enforce policy compliance.
- Enhance detection capabilities by configuring alerts for similar suspicious activities, ensuring that any future attempts to modify file permissions within containers are promptly identified and addressed."""
references = [
"https://flare.io/learn/resources/blog/teampcp-cloud-native-ransomware",
]
risk_score = 21
rule_id = "ec604672-bed9-43e1-8871-cf591c052550"
severity = "low"
tags = [
"Data Source: Elastic Defend for Containers",
"Domain: Container",
"OS: Linux",
"Use Case: Threat Detection",
"Tactic: Execution",
"Tactic: Defense Evasion",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "eql"
query = '''
any where event.category in ("file", "process") and event.type in ("change", "creation", "start") and (
process.name == "chmod" or
(
/* account for tools that execute utilities as a subprocess, in this case the target utility name will appear as a process arg */
process.name in ("bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish", "busybox") and
process.args in (
"chmod", "/bin/chmod", "/usr/bin/chmod", "/usr/local/bin/chmod"
) and
/* default exclusion list to not FP on default multi-process commands */
not process.args in (
"which", "/bin/which", "/usr/bin/which", "/usr/local/bin/which",
"man", "/bin/man", "/usr/bin/man", "/usr/local/bin/man"
)
)
) and process.args in ("4755", "755", "0755", "777", "0777", "444", "+x", "a+x", "700", "0700") and
container.id like "?*" and not process.args == "-x"
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1059"
name = "Command and Scripting Interpreter"
reference = "https://attack.mitre.org/techniques/T1059/"
[rule.threat.tactic]
id = "TA0002"
name = "Execution"
reference = "https://attack.mitre.org/tactics/TA0002/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1222"
name = "File and Directory Permissions Modification"
reference = "https://attack.mitre.org/techniques/T1222/"
[[rule.threat.technique.subtechnique]]
id = "T1222.002"
name = "Linux and Mac File and Directory Permissions Modification"
reference = "https://attack.mitre.org/techniques/T1222/002/"
[rule.threat.tactic]
id = "TA0005"
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1548"
name = "Abuse Elevation Control Mechanism"
reference = "https://attack.mitre.org/techniques/T1548/"
[[rule.threat.technique.subtechnique]]
id = "T1548.001"
name = "Setuid and Setgid"
reference = "https://attack.mitre.org/techniques/T1548/001/"
[rule.threat.tactic]
id = "TA0004"
name = "Privilege Escalation"
reference = "https://attack.mitre.org/tactics/TA0004/"
Stages and Predicates
Stage 1: any
any where event.category in ("file", "process") and event.type in ("change", "creation", "start") and (
process.name == "chmod" or
(
process.name in ("bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish", "busybox") and
process.args in (
"chmod", "/bin/chmod", "/usr/bin/chmod", "/usr/local/bin/chmod"
) and
not process.args in (
"which", "/bin/which", "/usr/bin/which", "/usr/local/bin/which",
"man", "/bin/man", "/usr/bin/man", "/usr/local/bin/man"
)
)
) and process.args in ("4755", "755", "0755", "777", "0777", "444", "+x", "a+x", "700", "0700") and
container.id like "?*" and not process.args == "-x"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
container.id | wildcard |
| field:"container.id" kind:wildcard value:"?*" |
event.category | in |
| field:"event.category" kind:in |
event.type | in |
| field:"event.type" kind:in |
process.args | in |
| field:"process.args" kind:in |
process.name | eq |
| field:"process_basename" kind:eq value:"chmod" |
process.name | in |
| field:"process_basename" kind:in |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
process.args | eq | -x | excludes:process.args field:"process.args" value:"-x" |
File Transfer Utility Launched from Unusual Parent
#This rule leverages ESQL to detect the execution of unusual file transfer utilities on Linux systems. Attackers may use these utilities to exfiltrate data from a compromised system. ESQL rules have limited fields available in its alert documents. Make sure to review the original documents to aid in the investigation of this alert.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Exfiltration |
Rule Body
[metadata]
creation_date = "2025/02/21"
integration = ["endpoint"]
maturity = "production"
updated_date = "2026/04/10"
[rule]
author = ["Elastic"]
description = """
This rule leverages ESQL to detect the execution of unusual file transfer utilities on Linux systems. Attackers may use
these utilities to exfiltrate data from a compromised system. ESQL rules have limited fields available in its alert
documents. Make sure to review the original documents to aid in the investigation of this alert.
"""
from = "now-61m"
interval = "1h"
language = "esql"
license = "Elastic License v2"
name = "File Transfer Utility Launched from Unusual Parent"
note = """ ## Triage and analysis
> **Disclaimer**:
> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
### Investigating File Transfer Utility Launched from Unusual Parent
File transfer utilities like scp, ftp, and rsync are essential for data movement in Linux environments. However, adversaries can exploit these tools to exfiltrate sensitive data. The detection rule identifies suspicious executions of these utilities by monitoring process activities, focusing on rare occurrences and unique agent IDs, which may indicate unauthorized data transfers. This helps in early detection of potential data breaches.
### Possible investigation steps
- Review the process.command_line field to understand the exact command executed and assess if it aligns with typical usage patterns or if it appears suspicious.
- Examine the process.parent.executable field to determine the parent process that initiated the file transfer utility, which may provide insights into whether the execution was part of a legitimate workflow or potentially malicious activity.
- Check the agent.id field to identify the specific host involved in the alert and correlate it with other security events or logs from the same host to gather additional context.
- Investigate the @timestamp field to verify the timing of the event and cross-reference with any known scheduled tasks or user activities that could explain the execution.
- Analyze the host.os.type field to confirm the operating system and ensure that the alert pertains to a Linux environment, as expected by the rule.
### False positive analysis
- Routine administrative tasks using file transfer utilities may trigger alerts. Regularly scheduled backups or updates using scp, rsync, or ftp should be documented and excluded from alerts by creating exceptions for known scripts or cron jobs.
- Automated system updates or patches that utilize these utilities can be mistaken for suspicious activity. Identify and whitelist the processes and command lines associated with these updates to prevent false positives.
- Internal data transfers between trusted servers for legitimate business purposes might be flagged. Establish a list of trusted agent IDs and exclude them from the rule to avoid unnecessary alerts.
- Development and testing environments often use these utilities for transferring test data. Ensure that these environments are recognized and excluded by specifying their hostnames or IP addresses in the rule configuration.
- User-initiated file transfers for legitimate reasons, such as data analysis or reporting, can be misinterpreted. Educate users to notify the security team of such activities in advance, allowing for temporary exceptions to be made.
### Response and remediation
- Immediately isolate the affected Linux system from the network to prevent further data exfiltration and unauthorized access.
- Terminate any suspicious file transfer processes identified by the alert, such as scp, ftp, or rsync, to halt ongoing data transfers.
- Conduct a thorough review of the process command lines and parent executables to identify any malicious scripts or unauthorized software that initiated the file transfer.
- Change credentials and access keys associated with the compromised system to prevent further unauthorized access.
- Escalate the incident to the security operations team for a deeper forensic analysis to determine the extent of the breach and identify any additional compromised systems.
- Implement network monitoring to detect any further attempts of unauthorized file transfers or suspicious activities from the affected system.
- Update and enhance endpoint detection and response (EDR) solutions to improve detection capabilities for similar threats in the future.
"""
risk_score = 47
rule_id = "8eeeda11-dca6-4c3e-910f-7089db412d1c"
setup = """## Setup
This rule requires data coming in from one of the following integrations:
- Elastic Defend
### Elastic Defend Integration Setup
Elastic Defend is integrated into the Elastic Agent using Fleet. Upon configuration, the integration allows the Elastic Agent to monitor events on your host and send data to the Elastic Security app.
#### Prerequisite Requirements:
- Fleet is required for Elastic Defend.
- To configure Fleet Server refer to the [documentation](https://www.elastic.co/guide/en/fleet/current/fleet-server.html).
#### The following steps should be executed in order to add the Elastic Defend integration on a Linux System:
- Go to the Kibana home page and click "Add integrations".
- In the query bar, search for "Elastic Defend" and select the integration to see more details about it.
- Click "Add Elastic Defend".
- Configure the integration name and optionally add a description.
- Select the type of environment you want to protect, either "Traditional Endpoints" or "Cloud Workloads".
- Select a configuration preset. Each preset comes with different default settings for Elastic Agent, you can further customize these later by configuring the Elastic Defend integration policy. [Helper guide](https://www.elastic.co/guide/en/security/current/configure-endpoint-integration-policy.html).
- We suggest selecting "Complete EDR (Endpoint Detection and Response)" as a configuration setting, that provides "All events; all preventions"
- Enter a name for the agent policy in "New agent policy name". If other agent policies already exist, you can click the "Existing hosts" tab and select an existing policy instead.
For more details on Elastic Agent configuration settings, refer to the [helper guide](https://www.elastic.co/guide/en/fleet/8.10/agent-policy.html).
- Click "Save and Continue".
- To complete the integration, select "Add Elastic Agent to your hosts" and continue to the next section to install the Elastic Agent on your hosts.
For more details on Elastic Defend refer to the [helper guide](https://www.elastic.co/guide/en/security/current/install-endpoint.html).
"""
severity = "medium"
tags = [
"Domain: Endpoint",
"OS: Linux",
"Use Case: Threat Detection",
"Tactic: Exfiltration",
"Tactic: Execution",
"Data Source: Elastic Defend",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "esql"
query = '''
from logs-endpoint.events.process-* metadata _id, _index, _version
| mv_expand event.action
| where
host.os.type == "linux" and
event.type == "start" and
event.action == "exec" and
process.name in ("scp", "ftp", "sftp", "vsftpd", "sftp-server", "rsync") and (
(process.parent.name in ("bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish")) or
(
process.parent.name like ".*" or
process.parent.name like "*.elf" or
process.parent.name like "*.sh" or
process.parent.name like "*.py" or
process.parent.name like "*.rb" or
process.parent.name like "*.pl" or
process.parent.name like "*.lua*" or
process.parent.name like "*.php*" or
process.parent.name like "*.js"
) or
(
process.parent.executable like "/tmp/*" or
process.parent.executable like "/var/tmp/*" or
process.parent.executable like "/dev/shm/*" or
process.parent.executable like "./*" or
process.parent.executable like "/run/*" or
process.parent.executable like "/var/run/*" or
process.parent.executable like "/boot/*" or
process.parent.executable like "/sys/*" or
process.parent.executable like "/lost+found/*" or
process.parent.executable like "/proc/*" or
process.parent.executable like "/var/mail/*" or
process.parent.executable like "/var/www/*" or
process.parent.executable like "/home/*" or
process.parent.executable like "/root/*"
)
)
| keep
@timestamp,
_id,
_index,
_version,
host.os.type,
event.type,
event.action,
process.name,
process.parent.name,
process.parent.executable,
process.executable,
agent.id,
host.name,
data_stream.dataset,
data_stream.namespace
| stats
Esql.event_count = count(),
Esql.agent_id_count_distinct = count_distinct(agent.id),
Esql.host_name_values = values(host.name),
Esql.agent_id_values = values(agent.id),
Esql.data_stream_dataset_values = values(data_stream.dataset),
Esql.data_stream_namespace_values = values(data_stream.namespace)
by process.executable, process.parent.executable
| where
Esql.agent_id_count_distinct == 1 and
Esql.event_count < 5
| sort Esql.event_count asc
// Extract unique values to ECS fields for alerts exclusion
| eval agent.id = mv_min(Esql.agent_id_values),
host.name = mv_min(Esql.host_name_values)
| keep agent.id, host.name, process.executable, process.parent.executable, Esql.*
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1048"
name = "Exfiltration Over Alternative Protocol"
reference = "https://attack.mitre.org/techniques/T1048/"
[rule.threat.tactic]
id = "TA0010"
name = "Exfiltration"
reference = "https://attack.mitre.org/tactics/TA0010/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1059"
name = "Command and Scripting Interpreter"
reference = "https://attack.mitre.org/techniques/T1059/"
[[rule.threat.technique.subtechnique]]
id = "T1059.004"
name = "Unix Shell"
reference = "https://attack.mitre.org/techniques/T1059/004/"
[rule.threat.tactic]
id = "TA0002"
name = "Execution"
reference = "https://attack.mitre.org/tactics/TA0002/"
Stages and Predicates
Stage 1: from
from logs-endpoint.events.process-* metadata _id, _index, _version
Stage 2: mv_expand
| mv_expand event.action
Stage 3: where
| where
host.os.type == "linux" and
event.type == "start" and
event.action == "exec" and
process.name in ("scp", "ftp", "sftp", "vsftpd", "sftp-server", "rsync") and (
(process.parent.name in ("bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish")) or
(
process.parent.name like ".*" or
process.parent.name like "*.elf" or
process.parent.name like "*.sh" or
process.parent.name like "*.py" or
process.parent.name like "*.rb" or
process.parent.name like "*.pl" or
process.parent.name like "*.lua*" or
process.parent.name like "*.php*" or
process.parent.name like "*.js"
) or
(
process.parent.executable like "/tmp/*" or
process.parent.executable like "/var/tmp/*" or
process.parent.executable like "/dev/shm/*" or
process.parent.executable like "./*" or
process.parent.executable like "/run/*" or
process.parent.executable like "/var/run/*" or
process.parent.executable like "/boot/*" or
process.parent.executable like "/sys/*" or
process.parent.executable like "/lost+found/*" or
process.parent.executable like "/proc/*" or
process.parent.executable like "/var/mail/*" or
process.parent.executable like "/var/www/*" or
process.parent.executable like "/home/*" or
process.parent.executable like "/root/*"
)
)
Stage 4: keep
| keep
@timestamp,
_id,
_index,
_version,
host.os.type,
event.type,
event.action,
process.name,
process.parent.name,
process.parent.executable,
process.executable,
agent.id,
host.name,
data_stream.dataset,
data_stream.namespace
Stage 5: stats
| stats
Esql.event_count = count(),
Esql.agent_id_count_distinct = count_distinct(agent.id),
Esql.host_name_values = values(host.name),
Esql.agent_id_values = values(agent.id),
Esql.data_stream_dataset_values = values(data_stream.dataset),
Esql.data_stream_namespace_values = values(data_stream.namespace)
by process.executable, process.parent.executable
Stage 6: where
| where
Esql.agent_id_count_distinct == 1 and
Esql.event_count < 5
Stage 7: sort
| sort Esql.event_count asc
Stage 8: eval
| eval agent.id = mv_min(Esql.agent_id_values),
host.name = mv_min(Esql.host_name_values)
Stage 9: keep
| keep agent.id, host.name, process.executable, process.parent.executable, Esql.*
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Esql.agent_id_count_distinct | eq |
| field:"Esql.agent_id_count_distinct" kind:eq value:"1" |
Esql.event_count | lt |
| field:"Esql.event_count" kind:lt value:"5" |
event.action | eq |
| field:"EventType" kind:eq value:"exec" |
event.type | eq |
| field:"event.type" kind:eq value:"start" |
process.name | in |
| field:"process_basename" kind:in |
process.parent.executable | wildcard |
| field:"ParentImage" kind:wildcard |
process.parent.name | in |
| field:"parent_process_basename" kind:in |
process.parent.name | wildcard |
| field:"parent_process_basename" kind:wildcard |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
agent.id | KEEP agent.id |
host.name | KEEP host.name |
process.executable | KEEP process.executable |
process.parent.executable | KEEP process.parent.executable |
Esql.* | KEEP Esql.* |
First Occurrence of User Agent For a GitHub Personal Access Token (PAT)
#Detects a new user agent used for a GitHub PAT not previously seen in the last 14 days.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Stealth | |
| Lateral Movement |
Rule Body
[metadata]
bypass_bbr_timing = true
creation_date = "2023/10/11"
integration = ["github"]
maturity = "production"
updated_date = "2026/03/24"
[rule]
author = ["Elastic"]
building_block_type = "default"
description = "Detects a new user agent used for a GitHub PAT not previously seen in the last 14 days.\n"
from = "now-9m"
index = ["logs-github.audit-*"]
language = "kuery"
license = "Elastic License v2"
name = "First Occurrence of User Agent For a GitHub Personal Access Token (PAT)"
risk_score = 21
rule_id = "0e4367a0-a483-439d-ad2e-d90500b925fd"
severity = "low"
tags = [
"Domain: Cloud",
"Use Case: Threat Detection",
"Use Case: UEBA",
"Tactic: Initial Access",
"Rule Type: BBR",
"Data Source: Github",
]
timestamp_override = "event.ingested"
type = "new_terms"
query = '''
event.dataset:"github.audit" and event.category:"configuration" and
github.user_agent:* and github.hashed_token:* and
github.programmatic_access_type:("OAuth access token" or "Fine-grained personal access token")
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1078"
name = "Valid Accounts"
reference = "https://attack.mitre.org/techniques/T1078/"
[[rule.threat.technique.subtechnique]]
id = "T1078.004"
name = "Cloud Accounts"
reference = "https://attack.mitre.org/techniques/T1078/004/"
[rule.threat.tactic]
id = "TA0001"
name = "Initial Access"
reference = "https://attack.mitre.org/tactics/TA0001/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1550"
name = "Use Alternate Authentication Material"
reference = "https://attack.mitre.org/techniques/T1550/"
[[rule.threat.technique.subtechnique]]
id = "T1550.001"
name = "Application Access Token"
reference = "https://attack.mitre.org/techniques/T1550/001/"
[rule.threat.tactic]
id = "TA0005"
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"
[rule.new_terms]
field = "new_terms_fields"
value = ["github.hashed_token", "github.user_agent"]
[[rule.new_terms.history_window_start]]
field = "history_window_start"
value = "now-5d"
Stages and Predicates
Stage 1: new_terms
event.dataset:"github.audit" and event.category:"configuration" and
github.user_agent:* and github.hashed_token:* and
github.programmatic_access_type:("OAuth access token" or "Fine-grained personal access token")
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.category | eq |
| field:"event.category" kind:eq value:"configuration" |
event.dataset | eq |
| field:"event.dataset" kind:eq value:"github.audit" |
github.hashed_token | is_not_null | field:"github.hashed_token" kind:is_not_null | |
github.programmatic_access_type | in |
| field:"github.programmatic_access_type" kind:in |
github.user_agent | is_not_null | field:"github.user_agent" kind:is_not_null |
FortiGate SSL VPN Login Followed by SIEM Alert by User
#Detects when a FortiGate SSL VPN login event is followed by any SIEM detection alert for the same user name within a short time window. This correlation can indicate abuse of VPN access for malicious activity, credential compromise used from a VPN session, or initial access via VPN followed by post-compromise behavior.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
[metadata]
creation_date = "2026/02/20"
integration = ["fortinet_fortigate"]
maturity = "production"
updated_date = "2026/04/10"
[rule]
author = ["Elastic"]
description = """
Detects when a FortiGate SSL VPN login event is followed by any SIEM detection alert for the same user name within a
short time window. This correlation can indicate abuse of VPN access for malicious activity, credential compromise
used from a VPN session, or initial access via VPN followed by post-compromise behavior.
"""
from = "now-9m"
index = ["logs-fortinet_fortigate.log-*", ".alerts-security.*"]
language = "eql"
license = "Elastic License v2"
name = "FortiGate SSL VPN Login Followed by SIEM Alert by User"
references = [
"https://attack.mitre.org/tactics/TA0001/",
"https://www.elastic.co/docs/reference/integrations/fortinet_fortigate",
]
risk_score = 47
rule_id = "a7f2c1b4-5d8e-4f3a-9b0c-2e1d4a6b8f3e"
severity = "medium"
tags = [
"Use Case: Threat Detection",
"Rule Type: Higher-Order Rule",
"Tactic: Initial Access",
"Data Source: Fortinet",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "eql"
query = '''
sequence by user.name with maxspan=10m
[authentication where data_stream.dataset == "fortinet_fortigate.log" and event.action == "login" and event.code in ("0101039426", "0101039427") and
user.name != "root"]
[any where event.kind == "signal" and kibana.alert.rule.name != null and data_stream.dataset != "fortinet_fortigate.log" and
kibana.alert.risk_score > 21 and kibana.alert.rule.rule_id != "a7f2c1b4-5d8e-4f3a-9b0c-2e1d4a6b8f3e" and user.name != null]
'''
note = """## Triage and analysis
### Investigating FortiGate SSL VPN Login Followed by SIEM Alert by User
This rule correlates a FortiGate SSL VPN login with a subsequent security alert for the same user name, highlighting possible abuse of VPN access or activity shortly after remote access.
### Possible investigation steps
- Review the FortiGate login event (source IP, user, time) and the SIEM alert(s) that followed for the same user.
- Determine whether the user is expected to use VPN and whether the subsequent alert is related to legitimate work (e.g. admin tools, updates).
- Check for other alerts or logins for the same user in the same time window to assess scope.
- Correlate with authentication logs to identify impossible travel or credential reuse from the VPN session.
### False positive analysis
- Legitimate VPN users triggering detections (e.g. scripted tasks, admin tooling) after login.
- Security scans or automated jobs that run in the context of a VPN-authenticated user.
### Response and remediation
- If abuse or compromise is suspected, disable or reset the user’s VPN access and credentials.
- Investigate the host and process associated with the SIEM alert.
- Escalate to the security or incident response team if the alert indicates malicious activity.
"""
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1078"
name = "Valid Accounts"
reference = "https://attack.mitre.org/techniques/T1078/"
[rule.threat.tactic]
id = "TA0001"
name = "Initial Access"
reference = "https://attack.mitre.org/tactics/TA0001/"
Stages and Predicates
Ordered sequence: each step below must occur in order within 10m, correlated by user.name.
Stage 1: authentication
[authentication where data_stream.dataset == "fortinet_fortigate.log" and event.action == "login" and event.code in ("0101039426", "0101039427") and
user.name != "root"]
Stage 2: any
[any where event.kind == "signal" and kibana.alert.rule.name != null and data_stream.dataset != "fortinet_fortigate.log" and
kibana.alert.risk_score > 21 and kibana.alert.rule.rule_id != "a7f2c1b4-5d8e-4f3a-9b0c-2e1d4a6b8f3e" and user.name != null]
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
data_stream.dataset | eq |
| field:"data_stream.dataset" kind:eq value:"fortinet_fortigate.log" |
data_stream.dataset | ne |
| field:"data_stream.dataset" kind:ne value:"fortinet_fortigate.log" |
event.action | eq |
| field:"EventType" kind:eq value:"login" |
event.code | in |
| field:"EventID" kind:in |
event.kind | eq |
| field:"event.kind" kind:eq value:"signal" |
kibana.alert.risk_score | gt |
| field:"kibana.alert.risk_score" kind:gt value:"21" |
kibana.alert.rule.name | is_not_null | field:"kibana.alert.rule.name" kind:is_not_null | |
kibana.alert.rule.rule_id | ne |
| field:"kibana.alert.rule.rule_id" kind:ne value:"a7f2c1b4-5d8e-4f3a-9b0c-2e1d4a6b8f3e" |
user.name | is_not_null | field:"user" kind:is_not_null | |
user.name | ne |
| field:"user" kind:ne value:"root" |
GKE Secret Access via Unusual User Agent
#Detects GKE secrets get or list requests from a previously unseen combination of source IP, identity, and user agent, excluding the default Kubernetes client placeholder. Attackers who compromise a pod or steal a kubeconfig often use curl, custom scripts, or atypical clients from a new host to read service-account tokens, registry credentials, or application secrets. Anonymous identities are excluded; use dedicated anonymous-access rules for unauthenticated probing.
Known False Positives
- Administrators or CI jobs using a new workstation, bastion, VPN egress, or nonstandard API client can produce a first-seen alert; confirm against change records and expected automation.
- Workloads or operators that change client libraries after a deploy may introduce a new user agent for an otherwise known identity; baseline after validation.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Kubernetes | get-secrets: get secrets |
| Kubernetes | list-secrets: list secrets |
Rules Detecting the Same Action
These rules filter on the same operation.
- Azure AKS Secret get or list with Suspicious User Agent (Elastic)
- Direct Interactive Kubernetes API Request by Unusual Utilities (Elastic)
- GKE Rapid Secret GET Activity Against Multiple Objects (Elastic)
- GKE Secret Access from Node or Denied Service Account (Elastic)
- GKE Secret get or list with Suspicious User Agent (Elastic)
- GKE Secrets List from Unusual Source AS Organization (Elastic)
- GKE Unusual Service Account Secret Access via New User Agent (Elastic)
- Kubernetes Abuse of Secret by Unusual Location (Splunk)
Rule Body
[metadata]
creation_date = "2026/07/22"
integration = ["gcp"]
maturity = "production"
updated_date = "2026/07/22"
[rule]
author = ["Elastic"]
description = """
Detects GKE secrets get or list requests from a previously unseen combination of source IP, identity, and user agent,
excluding the default Kubernetes client placeholder. Attackers who compromise a pod or steal a kubeconfig often use
curl, custom scripts, or atypical clients from a new host to read service-account tokens, registry credentials, or
application secrets. Anonymous identities are excluded; use dedicated anonymous-access rules for unauthenticated
probing.
"""
false_positives = [
"""
Administrators or CI jobs using a new workstation, bastion, VPN egress, or nonstandard API client can produce a
first-seen alert; confirm against change records and expected automation.
""",
"""
Workloads or operators that change client libraries after a deploy may introduce a new user agent for an otherwise
known identity; baseline after validation.
""",
]
from = "now-6m"
index = ["logs-gcp.audit-*"]
language = "kuery"
license = "Elastic License v2"
name = "GKE Secret Access via Unusual User Agent"
note = """## Triage and analysis
### Investigating GKE Secret Access via Unusual User Agent
This new-terms rule alerts on secrets `get`/`list` when the (`source.ip`, `client.user.email`, `user_agent.original`)
triple is new in the history window.
### Possible investigation steps
- Review `client.user.email`, `source.ip`, `user_agent.original`, and `gcp.audit.resource_name` for the secret and
namespace accessed.
- Determine whether the client fingerprint matches an approved admin path, CI runner, or controller upgrade.
- Pivot on the same identity or IP for secret bursts, pod exec, token creation, or RBAC changes.
### False positive analysis
- New admin workstations, VPN egress IPs, or SDK version bumps can first-seen alert; tune after confirming ownership.
- First enablement surfaces legitimate clients until the history window fills.
### Response and remediation
- If malicious, revoke the credential, rotate exposed secrets, isolate the source host or workload, and tighten who
can read secrets.
"""
setup = "The GCP Fleet integration with GKE audit logs enabled is required to be compatible with this rule."
references = [
"https://attack.mitre.org/techniques/T1552/007/",
]
risk_score = 21
rule_id = "854e89c1-f70c-4180-bc53-65999df9d3c3"
severity = "low"
tags = [
"Domain: Cloud",
"Domain: Kubernetes",
"Data Source: GCP",
"Data Source: Google Cloud Platform",
"Use Case: Threat Detection",
"Tactic: Credential Access",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "new_terms"
query = '''
data_stream.dataset:gcp.audit and service.name:k8s.io and
event.action:("io.k8s.core.v1.secrets.get" or "io.k8s.core.v1.secrets.list") and
user_agent.original:(* and not (*kubernetes/$Format* or kube-probe* or gke-exec-auth-plugin*)) and
source.ip:(* and not (127.0.0.1 or "::1")) and
client.user.email:(* and not (
"system:anonymous" or "system:unauthenticated" or "system:addon-manager" or
"system:serviceaccount:kube-system:namespace-controller"
))
'''
[rule.new_terms]
field = "new_terms_fields"
value = ["source.ip", "client.user.email", "user_agent.original"]
[[rule.new_terms.history_window_start]]
field = "history_window_start"
value = "now-7d"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1552"
name = "Unsecured Credentials"
reference = "https://attack.mitre.org/techniques/T1552/"
[[rule.threat.technique.subtechnique]]
id = "T1552.007"
name = "Container API"
reference = "https://attack.mitre.org/techniques/T1552/007/"
[rule.threat.tactic]
id = "TA0006"
name = "Credential Access"
reference = "https://attack.mitre.org/tactics/TA0006/"
[rule.investigation_fields]
field_names = [
"@timestamp",
"client.user.email",
"source.ip",
"user_agent.original",
"event.action",
"event.outcome",
"gcp.audit.resource_name",
"data_stream.namespace",
]
Stages and Predicates
Stage 1: new_terms
data_stream.dataset:gcp.audit and service.name:k8s.io and
event.action:("io.k8s.core.v1.secrets.get" or "io.k8s.core.v1.secrets.list") and
user_agent.original:(* and not (*kubernetes/$Format* or kube-probe* or gke-exec-auth-plugin*)) and
source.ip:(* and not (127.0.0.1 or "::1")) and
client.user.email:(* and not (
"system:anonymous" or "system:unauthenticated" or "system:addon-manager" or
"system:serviceaccount:kube-system:namespace-controller"
))
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
client.user.email | is_not_null | field:"client.user.email" kind:is_not_null | |
data_stream.dataset | eq |
| field:"data_stream.dataset" kind:eq value:"gcp.audit" |
event.action | in |
| field:"EventType" kind:in |
service.name | eq |
| field:"ServiceName" kind:eq value:"k8s.io" |
source.ip | is_not_null | field:"src_ip" kind:is_not_null | |
user_agent.original | is_not_null | field:"aws::userAgent" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
user_agent.original | match | kubernetes/$Format | excludes:user_agent.original field:"user_agent.original" value:"kubernetes/$Format" |
user_agent.original | starts_with | gke-exec-auth-plugin | excludes:user_agent.original field:"user_agent.original" value:"gke-exec-auth-plugin" |
user_agent.original | starts_with | kube-probe | excludes:user_agent.original field:"user_agent.original" value:"kube-probe" |
client.user.email | in | system:addon-manager, system:anonymous, system:serviceaccount:kube-system:namespace-controller, system:unauthenticated | excludes:client.user.email |
source.ip | in | 127.0.0.1, ::1 | excludes:source.ip field:"source.ip" value:"127.0.0.1" field:"source.ip" value:"::1" |
Kubeconfig File Discovery
#The kubeconfig file is a critical component in Kubernetes environments, containing configuration details for accessing and managing Kubernetes clusters. Attackers may attempt to get access to, create, or modify kubeconfig files to gain unauthorized initial access to Kubernetes clusters or move laterally within the cluster. This rule detects process discovery executions that involve kubeconfig files, particularly those executed from common shell environments or world-writeable directories.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access | |
| Discovery |
Rule Body
[metadata]
creation_date = "2025/06/17"
integration = ["endpoint", "cloud_defend"]
maturity = "production"
min_stack_comments = "Defend for Containers integration was re-introduced in 9.3.0"
min_stack_version = "9.3.0"
updated_date = "2026/03/24"
[rule]
author = ["Elastic"]
description = """
The kubeconfig file is a critical component in Kubernetes environments, containing configuration
details for accessing and managing Kubernetes clusters. Attackers may attempt to get access to,
create, or modify kubeconfig files to gain unauthorized initial access to Kubernetes clusters or
move laterally within the cluster. This rule detects process discovery executions that involve
kubeconfig files, particularly those executed from common shell environments or world-writeable
directories.
"""
from = "now-9m"
index = [
"logs-endpoint.events.process*",
"logs-cloud_defend.process*",
]
language = "eql"
license = "Elastic License v2"
name = "Kubeconfig File Discovery"
note = """ ## Triage and analysis
> **Disclaimer**:
> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
### Investigating Kubeconfig File Discovery
Kubeconfig files are essential in Kubernetes, storing credentials and configurations for cluster access. Adversaries may target these files to gain unauthorized access or move laterally within clusters. The detection rule identifies suspicious processes interacting with kubeconfig files, especially from common shell environments or risky directories, flagging potential misuse by excluding benign commands like 'stat' or 'md5sum'.
### Possible investigation steps
- Review the process details to identify the parent process name and executable path, focusing on those originating from common shell environments or risky directories like /tmp, /var/tmp, or /dev/shm.
- Examine the process arguments to determine if they include references to sensitive kubeconfig files such as admin.conf, kubelet.conf, or any files within /etc/kubernetes or ~/.kube directories.
- Check the working directory of the process to see if it aligns with known Kubernetes configuration paths like /etc/kubernetes or ~/.kube, which may indicate an attempt to access or modify kubeconfig files.
- Investigate the user account associated with the process to assess whether it has legitimate access to Kubernetes configurations or if it might be compromised.
- Correlate the event with other recent activities from the same user or IP address to identify any patterns of suspicious behavior or potential lateral movement within the cluster.
- Review any related alerts or logs for the same host or container to gather additional context on the system's state and any other potential indicators of compromise.
### False positive analysis
- Processes like 'stat' and 'md5sum' are excluded from detection as they are commonly used for legitimate file checks. Ensure these exclusions are correctly configured to prevent unnecessary alerts.
- Scripts located in user directories such as '/home/*/.kube' may trigger alerts if they interact with kubeconfig files. Consider adding exceptions for known scripts or users that regularly access these files for legitimate purposes.
- Processes originating from world-writeable directories like '/tmp' or '/var/tmp' can be flagged. Review these alerts to identify routine operations and whitelist specific processes or directories that are part of regular maintenance tasks.
- Alerts triggered by processes with names matching patterns like '*.sh' may include legitimate scripts. Evaluate these scripts and exclude them if they are part of standard operations or administrative tasks.
- Regular administrative tasks involving kubeconfig files in directories like '/etc/kubernetes' may be flagged. Implement exceptions for known administrative processes to reduce false positives while maintaining security oversight.
### Response and remediation
- Immediately isolate the affected system to prevent further unauthorized access to the Kubernetes cluster.
- Revoke any compromised credentials associated with the kubeconfig files and issue new credentials to authorized users.
- Conduct a thorough review of recent access logs and audit trails for the Kubernetes cluster to identify any unauthorized access or lateral movement attempts.
- Restore any modified or deleted kubeconfig files from a secure backup to ensure the integrity of the cluster configuration.
- Implement stricter access controls and permissions for directories containing kubeconfig files, ensuring only authorized personnel have access.
- Escalate the incident to the security operations team for further investigation and to determine if additional clusters or systems are affected.
- Enhance monitoring and alerting for suspicious activities related to kubeconfig files, leveraging the MITRE ATT&CK framework to identify potential discovery tactics.
"""
references = [
"https://kubernetes-threat-matrix.redguard.ch/initial-access/kubeconfig-file/",
"https://kubenomicon.com/Initial_access/Kubeconfig_file.html",
]
risk_score = 21
rule_id = "9a6f5d74-c7e7-4a8b-945e-462c102daee4"
setup = """## Setup
This rule requires data coming in from Elastic Defend.
### Elastic Defend Integration Setup
Elastic Defend is integrated into the Elastic Agent using Fleet. Upon configuration, the integration allows the Elastic Agent to monitor events on your host and send data to the Elastic Security app.
#### Prerequisite Requirements:
- Fleet is required for Elastic Defend.
- To configure Fleet Server refer to the [documentation](https://www.elastic.co/guide/en/fleet/current/fleet-server.html).
#### The following steps should be executed in order to add the Elastic Defend integration on a Linux System:
- Go to the Kibana home page and click "Add integrations".
- In the query bar, search for "Elastic Defend" and select the integration to see more details about it.
- Click "Add Elastic Defend".
- Configure the integration name and optionally add a description.
- Select the type of environment you want to protect, either "Traditional Endpoints" or "Cloud Workloads".
- Select a configuration preset. Each preset comes with different default settings for Elastic Agent, you can further customize these later by configuring the Elastic Defend integration policy. [Helper guide](https://www.elastic.co/guide/en/security/current/configure-endpoint-integration-policy.html).
- We suggest selecting "Complete EDR (Endpoint Detection and Response)" as a configuration setting, that provides "All events; all preventions"
- Enter a name for the agent policy in "New agent policy name". If other agent policies already exist, you can click the "Existing hosts" tab and select an existing policy instead.
For more details on Elastic Agent configuration settings, refer to the [helper guide](https://www.elastic.co/guide/en/fleet/8.10/agent-policy.html).
- Click "Save and Continue".
- To complete the integration, select "Add Elastic Agent to your hosts" and continue to the next section to install the Elastic Agent on your hosts.
For more details on Elastic Defend refer to the [helper guide](https://www.elastic.co/guide/en/security/current/install-endpoint.html).
"""
severity = "low"
tags = [
"Domain: Endpoint",
"Domain: Container",
"Domain: Kubernetes",
"OS: Linux",
"Use Case: Threat Detection",
"Tactic: Discovery",
"Data Source: Elastic Defend",
"Data Source: Elastic Defend for Containers",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "eql"
query = '''
process where host.os.type == "linux" and event.type == "start" and event.action == "exec" and (
process.parent.name in ("bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish") or
(
process.parent.executable like ("/tmp/*", "/var/tmp/*", "/dev/shm/*", "/root/*", "/home/*") or
process.parent.name like (".*", "*.sh")
)
) and
(
(
process.working_directory like ("/etc/kubernetes", "/root/.kube", "/home/*/.kube") and
process.args in ("kubeconfig", "admin.conf", "super-admin.conf", "kubelet.conf", "controller-manager.conf", "scheduler.conf")
) or
process.args like (
"/etc/kubernetes/admin.conf",
"/etc/kubernetes/super-admin.conf",
"/etc/kubernetes/kubelet.conf",
"/etc/kubernetes/controller-manager.conf",
"/etc/kubernetes/scheduler.conf",
"/home/*/.kube/config",
"/root/.kube/config",
"/var/lib/*/kubeconfig"
)
) and not (
process.name in ("stat", "md5sum", "dirname") or
(process.name like "python*" and process.args == "/usr/bin/yq" and process.parent.name == "fish") or
process.executable in (
"/usr/bin/file", "/usr/bin/readlink", "/bin/readlink", "/bin/file", "/tmp/newroot/bin/readlink",
"/tmp/newroot/bin/file", "/usr/local/bin/helm"
)
)
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1083"
name = "File and Directory Discovery"
reference = "https://attack.mitre.org/techniques/T1083/"
[[rule.threat.technique]]
id = "T1613"
name = "Container and Resource Discovery"
reference = "https://attack.mitre.org/techniques/T1613/"
[rule.threat.tactic]
id = "TA0007"
name = "Discovery"
reference = "https://attack.mitre.org/tactics/TA0007/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1552"
name = "Unsecured Credentials"
reference = "https://attack.mitre.org/techniques/T1552/"
[[rule.threat.technique.subtechnique]]
id = "T1552.001"
name = "Credentials In Files"
reference = "https://attack.mitre.org/techniques/T1552/001/"
[rule.threat.tactic]
id = "TA0006"
name = "Credential Access"
reference = "https://attack.mitre.org/tactics/TA0006/"
Stages and Predicates
Stage 1: process
process where host.os.type == "linux" and event.type == "start" and event.action == "exec" and (
process.parent.name in ("bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish") or
(
process.parent.executable like ("/tmp/*", "/var/tmp/*", "/dev/shm/*", "/root/*", "/home/*") or
process.parent.name like (".*", "*.sh")
)
) and
(
(
process.working_directory like ("/etc/kubernetes", "/root/.kube", "/home/*/.kube") and
process.args in ("kubeconfig", "admin.conf", "super-admin.conf", "kubelet.conf", "controller-manager.conf", "scheduler.conf")
) or
process.args like (
"/etc/kubernetes/admin.conf",
"/etc/kubernetes/super-admin.conf",
"/etc/kubernetes/kubelet.conf",
"/etc/kubernetes/controller-manager.conf",
"/etc/kubernetes/scheduler.conf",
"/home/*/.kube/config",
"/root/.kube/config",
"/var/lib/*/kubeconfig"
)
) and not (
process.name in ("stat", "md5sum", "dirname") or
(process.name like "python*" and process.args == "/usr/bin/yq" and process.parent.name == "fish") or
process.executable in (
"/usr/bin/file", "/usr/bin/readlink", "/bin/readlink", "/bin/file", "/tmp/newroot/bin/readlink",
"/tmp/newroot/bin/file", "/usr/local/bin/helm"
)
)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.action | eq |
| field:"EventType" kind:eq value:"exec" |
event.type | eq |
| field:"event.type" kind:eq value:"start" |
process.args | in |
| field:"process.args" kind:in |
process.args | wildcard |
| field:"process.args" kind:wildcard |
process.parent.executable | wildcard |
| field:"ParentImage" kind:wildcard |
process.parent.name | in |
| field:"parent_process_basename" kind:in |
process.parent.name | wildcard |
| field:"parent_process_basename" kind:wildcard |
process.working_directory | wildcard |
| field:"CurrentDirectory" kind:wildcard |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
process.args | eq | /usr/bin/yq | excludes:process.args field:"process.args" value:"/usr/bin/yq" |
process.name | starts_with | python | excludes:process.name field:"process.name" value:"python" |
process.parent.name | eq | fish | excludes:process.parent.name field:"process.parent.name" value:"fish" |
process.executable | in | /bin/file, /bin/readlink, /tmp/newroot/bin/file, /tmp/newroot/bin/readlink, /usr/bin/file, /usr/bin/readlink, /usr/local/bin/helm | excludes:process.executable |
process.name | in | dirname, md5sum, stat | excludes:process.name field:"process.name" value:"dirname" field:"process.name" value:"md5sum" field:"process.name" value:"stat" |
Library Loaded via a CallBack Function
#Identifies the load of library from via a callback function. This may be the result of an evasion attempt to hide the origin of the LoadLibrary call from the call stack.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth |
Rule Body
[rule]
description = """
Identifies the load of library from via a callback function. This may be the result of an evasion attempt to hide the
origin of the LoadLibrary call from the call stack.
"""
id = "fae9f554-d3bc-4d48-8863-54d0dd68db54"
license = "Elastic License v2"
name = "Library Loaded via a CallBack Function"
os_list = ["windows"]
reference = [
"https://malpedia.caad.fkie.fraunhofer.de/details/win.nighthawk",
"https://www.elastic.co/security-labs/upping-the-ante-detecting-in-memory-threats-with-kernel-call-stacks",
]
version = "1.0.23"
query = '''
sequence by process.entity_id
[process where event.action == "start"]
[library where
(
((dll.Ext.relative_file_creation_time <= 300 or
(dll.Ext.device.product_id : ("Virtual DVD-ROM", "Virtual Disk") and not dll.path : "C:\\*")) and
(dll.code_signature.trusted == false or dll.code_signature.exists == false)) or
dll.name in~ ("vaultcli.dll", "wmiutils.dll", "taskschd.dll", "dnsapi.dll", "dsquery.dll",
"mstask.dll", "mstscax.dll", "sqlite3.dll", "clr.dll", "coreclr.dll", "ws2_32.dll",
"wininet.dll", "dnsapi.dll", "winhttp.dll", "psapi.dll", "bitsproxy.dll", "softokn3.dll",
"System.Management.Automation.dll", "Wldap32.dll")
) and
process.thread.Ext.call_stack_summary in
("ntdll.dll|kernelbase.dll|ntdll.dll|kernel32.dll|ntdll.dll",
"ntdll.dll|wow64.dll|wow64cpu.dll|wow64.dll|ntdll.dll|kernelbase.dll|ntdll.dll|kernel32.dll|ntdll.dll") and
not _arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info like "C:\\Windows\\Sys?????\\ntdll.dll!KiUserApcDispatcher*")
]
until [process where event.action == "end"]
'''
min_endpoint_version = "8.7.0"
[[actions]]
action = "kill_process"
field = "process.entity_id"
state = 0
[[optional_actions]]
action = "rollback"
field = "process.entity_id"
state = 0
[[threat]]
framework = "MITRE ATT&CK"
[[threat.technique]]
id = "T1055"
name = "Process Injection"
reference = "https://attack.mitre.org/techniques/T1055/"
[threat.tactic]
id = "TA0005"
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"
[internal]
min_endpoint_version = "8.7.0"
Stages and Predicates
Ordered sequence: each step below must occur in order, correlated by process.entity_id.
Stage 1: process
[process where event.action == "start"]
Stage 2: library
[library where
(
((dll.Ext.relative_file_creation_time <= 300 or
(dll.Ext.device.product_id : ("Virtual DVD-ROM", "Virtual Disk") and not dll.path : "C:\\*")) and
(dll.code_signature.trusted == false or dll.code_signature.exists == false)) or
dll.name in~ ("vaultcli.dll", "wmiutils.dll", "taskschd.dll", "dnsapi.dll", "dsquery.dll",
"mstask.dll", "mstscax.dll", "sqlite3.dll", "clr.dll", "coreclr.dll", "ws2_32.dll",
"wininet.dll", "dnsapi.dll", "winhttp.dll", "psapi.dll", "bitsproxy.dll", "softokn3.dll",
"System.Management.Automation.dll", "Wldap32.dll")
) and
process.thread.Ext.call_stack_summary in
("ntdll.dll|kernelbase.dll|ntdll.dll|kernel32.dll|ntdll.dll",
"ntdll.dll|wow64.dll|wow64cpu.dll|wow64.dll|ntdll.dll|kernelbase.dll|ntdll.dll|kernel32.dll|ntdll.dll") and
not _arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info like "C:\\Windows\\Sys?????\\ntdll.dll!KiUserApcDispatcher*")
]
Until: process
until [process where event.action == "end"]
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
dll.Ext.device.product_id | wildcard |
| field:"dll.Ext.device.product_id" kind:wildcard |
dll.Ext.relative_file_creation_time | le |
| field:"dll.Ext.relative_file_creation_time" kind:le value:"300" |
dll.code_signature.exists | eq |
| field:"dll.code_signature.exists" kind:eq value:"false" |
dll.code_signature.trusted | eq |
| field:"dll.code_signature.trusted" kind:eq value:"false" |
dll.name | in |
| field:"dll.name" kind:in |
event.action | eq |
| field:"EventType" kind:eq |
process.thread.Ext.call_stack_summary | in |
| field:"process.thread.Ext.call_stack_summary" kind:in |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
process.thread.Ext.call_stack | array_any | excludes:process.thread.Ext.call_stack |
Microsoft Build Engine Started by a System Process
#An instance of MSBuild, the Microsoft Build Engine, was started by Explorer or the WMI (Windows Management Instrumentation) subsystem. This behavior is unusual and is sometimes used by malicious payloads.
Known False Positives
- The Build Engine is commonly used by Windows developers but use by non-engineers is unusual.
MITRE ATT&CK Coverage
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Sysmon | Event ID 1: Process creation |
| Security-Auditing | Event ID 4688: A new process has been created. |
Rule Body
[metadata]
creation_date = "2020/03/25"
integration = ["endpoint", "windows", "system", "m365_defender", "sentinel_one_cloud_funnel", "crowdstrike"]
maturity = "production"
updated_date = "2026/05/04"
[rule]
author = ["Elastic"]
description = """
An instance of MSBuild, the Microsoft Build Engine, was started by Explorer or the WMI (Windows Management
Instrumentation) subsystem. This behavior is unusual and is sometimes used by malicious payloads.
"""
false_positives = ["The Build Engine is commonly used by Windows developers but use by non-engineers is unusual."]
from = "now-9m"
index = [
"endgame-*",
"logs-crowdstrike.fdr*",
"logs-endpoint.events.process-*",
"logs-m365_defender.event-*",
"logs-sentinel_one_cloud_funnel.*",
"logs-system.security*",
"logs-windows.forwarded*",
"logs-windows.sysmon_operational-*",
"winlogbeat-*",
]
language = "eql"
license = "Elastic License v2"
name = "Microsoft Build Engine Started by a System Process"
note = """## Triage and analysis
> **Disclaimer**:
> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
### Investigating Microsoft Build Engine Started by a System Process
The Microsoft Build Engine (MSBuild) is a platform for building applications, typically invoked by developers. However, adversaries exploit it to execute malicious code, leveraging its trusted status to bypass security measures. The detection rule identifies unusual MSBuild activity initiated by system processes like Explorer or WMI, which may indicate an attempt to evade defenses and execute unauthorized actions.
### Possible investigation steps
- Review the process tree to understand the parent-child relationship, focusing on instances where MSBuild.exe is started by explorer.exe or wmiprvse.exe.
- Check the command line arguments used to start MSBuild.exe for any suspicious or unusual parameters that could indicate malicious activity.
- Investigate the user account associated with the process to determine if it aligns with expected behavior or if it might be compromised.
- Examine recent file modifications or creations in directories commonly used by MSBuild to identify any unauthorized or unexpected files.
- Correlate the event with other security alerts or logs from data sources like Microsoft Defender XDR or Sysmon to gather additional context on the activity.
- Assess the network activity of the host during the time of the alert to identify any potential data exfiltration or communication with known malicious IP addresses.
### False positive analysis
- Legitimate software installations or updates may trigger MSBuild.exe to start from Explorer or WMI. Monitor these events and verify if they coincide with known software changes.
- Development environments where MSBuild is frequently used might see this behavior as part of normal operations. Identify and document these environments to create exceptions for known development machines.
- Automated scripts or administrative tools that leverage MSBuild for legitimate tasks can cause false positives. Review and whitelist these scripts or tools if they are verified as non-malicious.
- System maintenance tasks initiated by IT personnel might use MSBuild in a manner that appears suspicious. Coordinate with IT to understand routine maintenance activities and exclude them from alerts.
- Security software or monitoring tools that interact with MSBuild for scanning or analysis purposes should be identified and excluded from triggering alerts.
### Response and remediation
- Immediately isolate the affected system from the network to prevent further malicious activity and lateral movement.
- Terminate the MSBuild.exe process if it is confirmed to be executing unauthorized or malicious code.
- Conduct a thorough scan of the affected system using updated antivirus and anti-malware tools to identify and remove any malicious payloads or associated files.
- Review and analyze the parent processes (explorer.exe or wmiprvse.exe) to determine if they have been compromised or are executing other suspicious activities.
- Restore the system from a known good backup if any critical system files or applications have been altered or corrupted.
- Escalate the incident to the security operations center (SOC) or incident response team for further investigation and to determine if additional systems are affected.
- Implement enhanced monitoring and logging for MSBuild.exe and related processes to detect similar activities in the future, ensuring alerts are configured for rapid response."""
risk_score = 47
rule_id = "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae3"
severity = "medium"
tags = [
"Domain: Endpoint",
"OS: Windows",
"Use Case: Threat Detection",
"Tactic: Defense Evasion",
"Tactic: Execution",
"Data Source: Elastic Endgame",
"Data Source: Elastic Defend",
"Data Source: Windows Security Event Logs",
"Data Source: Microsoft Defender XDR",
"Data Source: Sysmon",
"Data Source: SentinelOne",
"Data Source: Crowdstrike",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "eql"
query = '''
process where host.os.type == "windows" and event.type == "start" and
process.name : "MSBuild.exe" and
process.parent.name : ("explorer.exe", "wmiprvse.exe")
'''
setup = """## Setup
This rule is designed for data generated by [Elastic Defend](https://www.elastic.co/security/endpoint-security), which provides native endpoint detection and response, along with event enrichments designed to work with our detection rules.
Setup instructions: https://ela.st/install-elastic-defend
### Additional data sources
This rule also supports the following third-party data sources. For setup instructions, refer to the links below:
- [CrowdStrike](https://ela.st/crowdstrike-integration)
- [Microsoft Defender XDR](https://ela.st/m365-defender)
- [SentinelOne Cloud Funnel](https://ela.st/sentinel-one-cloud-funnel)
- [Sysmon Event ID 1 - Process Creation](https://ela.st/sysmon-event-1-setup)
- [Windows Process Creation Logs](https://ela.st/audit-process-creation)
"""
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1127"
name = "Trusted Developer Utilities Proxy Execution"
reference = "https://attack.mitre.org/techniques/T1127/"
[[rule.threat.technique.subtechnique]]
id = "T1127.001"
name = "MSBuild"
reference = "https://attack.mitre.org/techniques/T1127/001/"
[rule.threat.tactic]
id = "TA0005"
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1047"
name = "Windows Management Instrumentation"
reference = "https://attack.mitre.org/techniques/T1047/"
[rule.threat.tactic]
id = "TA0002"
name = "Execution"
reference = "https://attack.mitre.org/tactics/TA0002/"
Stages and Predicates
Stage 1: process
process where host.os.type == "windows" and event.type == "start" and
process.name : "MSBuild.exe" and
process.parent.name : ("explorer.exe", "wmiprvse.exe")
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.type | eq |
| field:"event.type" kind:eq value:"start" |
process.name | wildcard |
| field:"process_basename" kind:wildcard value:"MSBuild.exe" |
process.parent.name | wildcard |
| field:"parent_process_basename" kind:wildcard |
Okta ThreatInsight Threat Suspected Promotion
#Okta ThreatInsight is a feature that provides valuable debug data regarding authentication and authorization processes, which is logged in the system. Within this data, there is a specific field called threat_suspected, which represents Okta's internal evaluation of the authentication or authorization workflow. When this field is set to True, it suggests the presence of potential credential access techniques, such as password-spraying, brute-forcing, replay attacks, and other similar threats.
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Okta | System Log event type security.threat.detected: Request from malicious IP detected by ThreatInsight |
Rules Detecting the Same Action
These rules filter on the same operation.
- Device Registration from Malicious IP (Kusto)
- Okta Security Threat Detected (Sigma)
- Okta ThreatInsight Login Failure With High Unknown Users (YARA-L)
- Okta ThreatInsight Security Threat Detected (Panther)
- Okta ThreatInsight Suspected Bruteforce Attack (YARA-L)
- Okta ThreatInsight Suspected Password Spray Attack (YARA-L)
- Okta ThreatInsight Threat Detected (Splunk)
Rule Body
[metadata]
creation_date = "2020/05/21"
integration = ["okta"]
maturity = "production"
promotion = true
updated_date = "2026/04/10"
[rule]
author = ["Elastic"]
description = """
Okta ThreatInsight is a feature that provides valuable debug data regarding authentication and authorization processes,
which is logged in the system. Within this data, there is a specific field called threat_suspected, which represents
Okta's internal evaluation of the authentication or authorization workflow. When this field is set to True, it suggests
the presence of potential credential access techniques, such as password-spraying, brute-forcing, replay attacks, and
other similar threats.
"""
index = ["filebeat-*", "logs-okta*"]
language = "kuery"
license = "Elastic License v2"
name = "Okta ThreatInsight Threat Suspected Promotion"
note = """## Setup
## Triage and analysis
This is a promotion rule for Okta ThreatInsight suspected threat events, which are alertable events per the vendor.
Consult vendor documentation on interpreting specific events.
"""
references = [
"https://developer.okta.com/docs/reference/api/system-log/",
"https://developer.okta.com/docs/reference/api/event-types/",
"https://help.okta.com/en-us/Content/Topics/Security/threat-insight/configure-threatinsight-system-log.html",
"https://www.elastic.co/security-labs/testing-okta-visibility-and-detection-dorothy",
"https://www.elastic.co/security-labs/monitoring-okta-threats-with-elastic-security",
"https://www.elastic.co/security-labs/starter-guide-to-understanding-okta",
]
risk_score = 47
rule_id = "6885d2ae-e008-4762-b98a-e8e1cd3a81e9"
rule_name_override = "okta.display_message"
severity = "medium"
tags = ["Use Case: Identity and Access Audit", "Data Source: Okta", "Resources: Investigation Guide"]
timestamp_override = "event.ingested"
type = "query"
query = '''
data_stream.dataset:okta.system and (event.action:security.threat.detected or okta.debug_context.debug_data.threat_suspected: true)
'''
[[rule.severity_mapping]]
field = "okta.debug_context.debug_data.risk_level"
operator = "equals"
severity = "low"
value = "LOW"
[[rule.severity_mapping]]
field = "okta.debug_context.debug_data.risk_level"
operator = "equals"
severity = "medium"
value = "MEDIUM"
[[rule.severity_mapping]]
field = "okta.debug_context.debug_data.risk_level"
operator = "equals"
severity = "high"
value = "HIGH"
Stages and Predicates
Stage 1: query
data_stream.dataset:okta.system and (event.action:security.threat.detected or okta.debug_context.debug_data.threat_suspected: true)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
data_stream.dataset | eq |
| field:"data_stream.dataset" kind:eq value:"okta.system" |
event.action | eq |
| field:"EventType" kind:eq value:"security.threat.detected" |
okta.debug_context.debug_data.threat_suspected | eq |
| field:"okta.debug_context.debug_data.threat_suspected" kind:eq value:"true" |
Potential Privilege Escalation via a Suspicious UID Change
#Detects a potential privilege escalation sequence via a suspicious UID change sequence. This rule checks for non-root execution of a process executable in a user or world-writable directory followed by a UID change event to 0 (root). This sequence is indicative of a potential local privilege escalation exploit.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Rule Body
[metadata]
creation_date = "2026/07/02"
integration = ["endpoint"]
maturity = "production"
updated_date = "2026/07/02"
[rule]
author = ["Elastic"]
description = """
Detects a potential privilege escalation sequence via a suspicious UID change sequence. This rule checks for
non-root execution of a process executable in a user or world-writable directory followed by a UID change
event to 0 (root). This sequence is indicative of a potential local privilege escalation exploit.
"""
from = "now-6m"
index = ["logs-endpoint.events.process*"]
language = "eql"
license = "Elastic License v2"
name = "Potential Privilege Escalation via a Suspicious UID Change"
note = """## Triage and analysis
> **Disclaimer**:
> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
### Investigating Potential Privilege Escalation via a Suspicious UID Change
This rule catches a Linux process launched by a non-root user from a user- or world-writable location that soon changes to run as root, a strong sign of local privilege escalation. Attackers often stage an exploit or wrapper in /tmp or /dev/shm, execute it as an ordinary user, and use a vulnerable setuid binary or kernel flaw to flip the process to UID 0 and gain full system control.
### Possible investigation steps
- Reconstruct the full process ancestry and execution context around the UID transition to determine whether it originated from a user shell, script interpreter, scheduled task, container runtime, or another trusted administrative workflow.
- Inspect the executed file and nearby artifacts in the writable location for creation and modification times, ownership, permissions, package ownership, hashes, and evidence that the binary or script was recently dropped, replaced, or unpacked.
- Determine how root was obtained by identifying any vulnerable or misconfigured setuid or file-capability helper, writable library or search path abuse, environment variable manipulation, or host exposure to known local privilege escalation vulnerabilities.
- Review the same user’s and host’s surrounding activity for staging behavior such as downloads, compilation, archive extraction, permission changes, and repeated exploit attempts that can distinguish malicious escalation from benign testing or software installation.
- Assess post-escalation behavior by looking for root shells, changes to authentication or sudo configuration, persistence creation, security control tampering, sensitive file access, and new outbound network connections to scope impact and prioritize containment.
### False positive analysis
- Developers or administrators may intentionally execute a custom setuid test binary or troubleshooting script from `/home`, `/tmp`, or `/dev/shm` during maintenance, so verify the file owner, creation time, and parent shell session match an authorized user and approved change window.
- A legitimate install, upgrade, or recovery workflow may unpack files into `/tmp`, `/var/tmp`, or `/run/user` and invoke a helper that transitions to UID 0, so confirm the executable and its parent process are expected for the host and coincide with documented software maintenance activity.
### Response and remediation
- Isolate the affected Linux host from the network while keeping a controlled management channel, then stop the suspicious root process and any child shell or payload launched from writable paths such as `/tmp`, `/dev/shm`, `/var/tmp`, or a user home directory.
- Remove attacker persistence by deleting the dropped binary or script and reversing malicious changes such as new cron entries, systemd services or timers, modified shell startup files, unauthorized `authorized_keys` entries, unexpected setuid binaries, and altered `/etc/sudoers` or `/etc/passwd`.
- Preserve and review the malicious file and recent privileged changes across `/etc/shadow`, `/etc/sudoers`, `/root/.ssh/`, loaded kernel modules, and package contents, and escalate immediately to the incident response team if you find a new privileged account, outbound command-and-control traffic, or signs of access to other hosts.
- Restore the system to a known-good state by reimaging or replacing the host from a trusted baseline, reinstalling only verified software, and applying fixes for the vulnerable kernel, setuid helper, or misconfigured file capability that allowed the UID change.
- Reset credentials and secrets exposed on the host, including the affected user account, local service accounts, SSH keys, API tokens, and any cached credentials, then review adjacent systems for the same dropped files, persistence artifacts, or root-level changes.
- Harden the environment by removing unnecessary setuid programs and file capabilities, mounting writable directories with `nosuid` and `noexec` where feasible, tightening sudo access, and maintaining detections for execution from world-writable paths followed by a transition to UID 0.
"""
risk_score = 73
rule_id = "156f8c1d-bc54-46c0-a870-04e6a75526a1"
severity = "high"
tags = [
"Data Source: Elastic Defend",
"Domain: Endpoint",
"OS: Linux",
"Use Case: Threat Detection",
"Tactic: Privilege Escalation",
"Resources: Investigation Guide",
]
type = "eql"
query = '''
sequence by host.id, process.entity_id with maxspan=30s
[process where host.os.type == "linux" and event.type == "start" and event.action == "exec" and
user.id != "0" and process.parent.user.id != "0" and process.parent.group.id != "0" and
(
process.executable like (".*", "/tmp/*", "/dev/shm/*", "/var/tmp/*", "/home/*/*", "/run/user/*", "/var/run/user/*") or
process.parent.executable like (".*", "/tmp/*", "/dev/shm/*", "/var/tmp/*", "/home/*/*", "/run/user/*", "/var/run/user/*")
)]
[process where host.os.type == "linux" and event.type == "change" and event.action == "uid_change" and
user.id == "0" and process.parent.user.id != "0" and process.parent.group.id != "0" and
not process.executable in ("/usr/bin/sudo", "/bin/sudo")]
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1548"
name = "Abuse Elevation Control Mechanism"
reference = "https://attack.mitre.org/techniques/T1548/"
[[rule.threat.technique.subtechnique]]
id = "T1548.001"
name = "Setuid and Setgid"
reference = "https://attack.mitre.org/techniques/T1548/001/"
[rule.threat.tactic]
id = "TA0004"
name = "Privilege Escalation"
reference = "https://attack.mitre.org/tactics/TA0004/"
Stages and Predicates
Ordered sequence: each step below must occur in order within 30s, correlated by host.id, process.entity_id.
Stage 1: process
[process where host.os.type == "linux" and event.type == "start" and event.action == "exec" and
user.id != "0" and process.parent.user.id != "0" and process.parent.group.id != "0" and
(
process.executable like (".*", "/tmp/*", "/dev/shm/*", "/var/tmp/*", "/home/*/*", "/run/user/*", "/var/run/user/*") or
process.parent.executable like (".*", "/tmp/*", "/dev/shm/*", "/var/tmp/*", "/home/*/*", "/run/user/*", "/var/run/user/*")
)]
Stage 2: process
[process where host.os.type == "linux" and event.type == "change" and event.action == "uid_change" and
user.id == "0" and process.parent.user.id != "0" and process.parent.group.id != "0" and
not process.executable in ("/usr/bin/sudo", "/bin/sudo")]
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.action | eq |
| field:"EventType" kind:eq |
event.type | eq |
| field:"event.type" kind:eq |
process.executable | wildcard |
| field:"Image" kind:wildcard |
process.parent.executable | wildcard |
| field:"ParentImage" kind:wildcard |
process.parent.group.id | ne |
| field:"process.parent.group.id" kind:ne value:"0" |
process.parent.user.id | ne |
| field:"process.parent.user.id" kind:ne value:"0" |
user.id | eq |
| field:"user.id" kind:eq value:"0" |
user.id | ne |
| field:"user.id" kind:ne value:"0" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
process.executable | in | /bin/sudo, /usr/bin/sudo | excludes:process.executable field:"process.executable" value:"/bin/sudo" field:"process.executable" value:"/usr/bin/sudo" |
Potential Privilege Escalation via Token Impersonation
#Identifies the creation of a process running as SYSTEM and impersonating a Windows core binary privileges. Adversaries may create a new process with a different token to escalate privileges and bypass access controls.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Rule Body
[rule]
description = """
Identifies the creation of a process running as SYSTEM and impersonating a Windows core binary privileges. Adversaries
may create a new process with a different token to escalate privileges and bypass access controls.
"""
id = "46de65b8-b873-4ae7-988d-12dcdc6fa605"
license = "Elastic License v2"
name = "Potential Privilege Escalation via Token Impersonation"
os_list = ["windows"]
reference = [
"https://lengjibo.github.io/token/",
"https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithtokenw",
]
version = "1.0.35"
query = '''
process where event.action == "start" and process.parent.executable != null and
/* process creation via seclogon */
process.parent.Ext.real.pid > 0 and
/* CreateProcessWithToken and effective parent is a privileged MS native binary used as a target for token theft */
user.id : "S-1-5-18" and
process.Ext.effective_parent.executable :
("?:\\Windows\\system32\\*.exe",
"?:\\Windows\\SysWOW64\\*.exe",
"?:\\Windows\\Microsoft.Net\\*.exe",
"?:\\Windows\\servicing\\TrustedInstaller.exe",
"?:\\Program Files\\Microsoft\\*.exe",
"?:\\Program Files (x86)\\Microsoft\\*.exe") and
not (process.Ext.effective_parent.executable : "?:\\Windows\\System32\\Utilman.exe" and
process.parent.executable : "?:\\Windows\\System32\\Utilman.exe" and process.parent.args : "/debug") and
not (process.executable : ("?:\\Windows\\System32\\WerFault.exe",
"?:\\Windows\\SysWOW64\\WerFault.exe",
"?:\\Windows\\System32\\WerFaultSecure.exe",
"?:\\Windows\\SysWOW64\\WerFaultSecure.exe") and process.args : "-u" and process.args : "-p") and
not process.parent.executable : "?:\\Windows\\System32\\AtBroker.exe" and
not (process.executable : "?:\\Windows\\SoftwareDistribution\\Download\\Install\\securityhealthsetup.exe" and
process.parent.executable : "?:\\Windows\\SoftwareDistribution\\Download\\Install\\securityhealthsetup.exe") and
not (process.code_signature.subject_name :
("philandro Software GmbH", "Freedom Scientific Inc.", "TeamViewer Germany GmbH",
"Projector.is, Inc.", "TeamViewer GmbH", "Cisco WebEx LLC", "TeamViewer", "PURSLANE",
"Tranquil I.T. Systems", "VOICEFIVE, INC.", "AnyDesk Software GmbH", "Zhou Huabing",
"Remote Utilities LLC", "Bayside Computer Systems Inc") and
process.code_signature.trusted == true) and
not (process.executable : "?:\\windows\\system32\\WerMgr.exe" and process.args : "-datacollectorcreate") and
not process.executable : "?:\\Program Files (x86)\\Dolphin\\SnovaMag*\\dol_logon.exe" and
not (process.executable : "?:\\Windows\\SoftwareDistribution\\Download\\Install\\securityhealthsetup.exe" and
process.Ext.effective_parent.executable : "?:\\Windows\\servicing\\TrustedInstaller.exe" and
process.code_signature.subject_name : "Microsoft Windows Publisher" and process.code_signature.trusted == true) and
/* ansible exec_wrapper */
not (process.name : "powershell.exe" and
process.command_line :
("*AkAGUAeABlAGMAXwB3AHIAYQBwAHAAZQByAF8AcwB0AHIALgBTAHAAbABpAHQAKABAACgAIgBgADAAYAAwAGAAMABgADAAIgApA*",
"*ABzAHAAbABpAHQAXwBwAGEAcgB0AHMAIAA9ACAAJABlAHgAZQBjAF8AdwByAGEAcABwAGUAcgBfAHMAdAByAC4AUwBwAGwAaQB0*",
"*CgAgACAAIAAgAHQAcgBhAHAAIAB7AAoAIAAgACAAIAAgACAAIAAgACQAdwByAGEAcABwAGUAcgBfAHAAYQB0AGgAIAA9ACAAIgA*",
"*JABlAHgAZQBjAF8AdwByAGEAcABwAGUAcgBfAHMAdAByACAAPQAgAFsAUwB5AHMAdABlAG0ALgBDAG8AbgBzAG8AbABlAF0*")) and
not (process.name : "powershell.exe" and
process.parent.executable : "C:\\Windows\\system32\\wsmprovhost.exe" and
process.command_line : "*CQAcwBwAGwAaQB0AF8AcABhAHIAdABzACAAPQAgACQAZQB3AC4AUwBwAGwAaQB0ACgAQAAoACIAYAAwAGAAMABgADAAYAAwACIAKQAsACAAMgAsA*") and
not (process.executable : "?:\\WINDOWS\\system32\\osk.exe" and
process.parent.executable : ("?:\\WINDOWS\\System32\\WinLogon.exe", "?:\\Windows\\System32\\LogonUI.exe")) and
not process.hash.sha256 : ("1e07a7a91451303d5cdac0a1673d581f5f7198fcc08fc79e4332aba6400f55a8",
"f3607f433952cb23d9eb0fc5e31fd0c4079cad3133dc79d149022900d208876c") and
not ((process.executable : "C:\\Windows\\SoftwareDistribution\\Download\\Install\\SecurityHealthSetup.exe" or process.pe.original_file_name == "SecurityHealthSetup.exe") and
process.Ext.effective_parent.executable : "C:\\Windows\\servicing\\TrustedInstaller.exe" and
process.code_signature.subject_name in ("Microsoft Corporation", "Microsoft Windows") and process.code_signature.trusted == true)
'''
min_endpoint_version = "8.4.0"
[[actions]]
action = "kill_process"
field = "process.entity_id"
state = 0
[[optional_actions]]
action = "rollback"
field = "process.entity_id"
state = 0
[[threat]]
framework = "MITRE ATT&CK"
[[threat.technique]]
id = "T1134"
name = "Access Token Manipulation"
reference = "https://attack.mitre.org/techniques/T1134/"
[[threat.technique.subtechnique]]
id = "T1134.001"
name = "Token Impersonation/Theft"
reference = "https://attack.mitre.org/techniques/T1134/001/"
[[threat.technique.subtechnique]]
id = "T1134.002"
name = "Create Process with Token"
reference = "https://attack.mitre.org/techniques/T1134/002/"
[threat.tactic]
id = "TA0004"
name = "Privilege Escalation"
reference = "https://attack.mitre.org/tactics/TA0004/"
[internal]
min_endpoint_version = "8.4.0"
Stages and Predicates
Stage 1: process
process where event.action == "start" and process.parent.executable != null and
process.parent.Ext.real.pid > 0 and
user.id : "S-1-5-18" and
process.Ext.effective_parent.executable :
("?:\\Windows\\system32\\*.exe",
"?:\\Windows\\SysWOW64\\*.exe",
"?:\\Windows\\Microsoft.Net\\*.exe",
"?:\\Windows\\servicing\\TrustedInstaller.exe",
"?:\\Program Files\\Microsoft\\*.exe",
"?:\\Program Files (x86)\\Microsoft\\*.exe") and
not (process.Ext.effective_parent.executable : "?:\\Windows\\System32\\Utilman.exe" and
process.parent.executable : "?:\\Windows\\System32\\Utilman.exe" and process.parent.args : "/debug") and
not (process.executable : ("?:\\Windows\\System32\\WerFault.exe",
"?:\\Windows\\SysWOW64\\WerFault.exe",
"?:\\Windows\\System32\\WerFaultSecure.exe",
"?:\\Windows\\SysWOW64\\WerFaultSecure.exe") and process.args : "-u" and process.args : "-p") and
not process.parent.executable : "?:\\Windows\\System32\\AtBroker.exe" and
not (process.executable : "?:\\Windows\\SoftwareDistribution\\Download\\Install\\securityhealthsetup.exe" and
process.parent.executable : "?:\\Windows\\SoftwareDistribution\\Download\\Install\\securityhealthsetup.exe") and
not (process.code_signature.subject_name :
("philandro Software GmbH", "Freedom Scientific Inc.", "TeamViewer Germany GmbH",
"Projector.is, Inc.", "TeamViewer GmbH", "Cisco WebEx LLC", "TeamViewer", "PURSLANE",
"Tranquil I.T. Systems", "VOICEFIVE, INC.", "AnyDesk Software GmbH", "Zhou Huabing",
"Remote Utilities LLC", "Bayside Computer Systems Inc") and
process.code_signature.trusted == true) and
not (process.executable : "?:\\windows\\system32\\WerMgr.exe" and process.args : "-datacollectorcreate") and
not process.executable : "?:\\Program Files (x86)\\Dolphin\\SnovaMag*\\dol_logon.exe" and
not (process.executable : "?:\\Windows\\SoftwareDistribution\\Download\\Install\\securityhealthsetup.exe" and
process.Ext.effective_parent.executable : "?:\\Windows\\servicing\\TrustedInstaller.exe" and
process.code_signature.subject_name : "Microsoft Windows Publisher" and process.code_signature.trusted == true) and
not (process.name : "powershell.exe" and
process.command_line :
("*AkAGUAeABlAGMAXwB3AHIAYQBwAHAAZQByAF8AcwB0AHIALgBTAHAAbABpAHQAKABAACgAIgBgADAAYAAwAGAAMABgADAAIgApA*",
"*ABzAHAAbABpAHQAXwBwAGEAcgB0AHMAIAA9ACAAJABlAHgAZQBjAF8AdwByAGEAcABwAGUAcgBfAHMAdAByAC4AUwBwAGwAaQB0*",
"*CgAgACAAIAAgAHQAcgBhAHAAIAB7AAoAIAAgACAAIAAgACAAIAAgACQAdwByAGEAcABwAGUAcgBfAHAAYQB0AGgAIAA9ACAAIgA*",
"*JABlAHgAZQBjAF8AdwByAGEAcABwAGUAcgBfAHMAdAByACAAPQAgAFsAUwB5AHMAdABlAG0ALgBDAG8AbgBzAG8AbABlAF0*")) and
not (process.name : "powershell.exe" and
process.parent.executable : "C:\\Windows\\system32\\wsmprovhost.exe" and
process.command_line : "*CQAcwBwAGwAaQB0AF8AcABhAHIAdABzACAAPQAgACQAZQB3AC4AUwBwAGwAaQB0ACgAQAAoACIAYAAwAGAAMABgADAAYAAwACIAKQAsACAAMgAsA*") and
not (process.executable : "?:\\WINDOWS\\system32\\osk.exe" and
process.parent.executable : ("?:\\WINDOWS\\System32\\WinLogon.exe", "?:\\Windows\\System32\\LogonUI.exe")) and
not process.hash.sha256 : ("1e07a7a91451303d5cdac0a1673d581f5f7198fcc08fc79e4332aba6400f55a8",
"f3607f433952cb23d9eb0fc5e31fd0c4079cad3133dc79d149022900d208876c") and
not ((process.executable : "C:\\Windows\\SoftwareDistribution\\Download\\Install\\SecurityHealthSetup.exe" or process.pe.original_file_name == "SecurityHealthSetup.exe") and
process.Ext.effective_parent.executable : "C:\\Windows\\servicing\\TrustedInstaller.exe" and
process.code_signature.subject_name in ("Microsoft Corporation", "Microsoft Windows") and process.code_signature.trusted == true)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.action | eq |
| field:"EventType" kind:eq value:"start" |
process.Ext.effective_parent.executable | wildcard |
| field:"process.Ext.effective_parent.executable" kind:wildcard |
process.parent.Ext.real.pid | gt |
| field:"process.parent.Ext.real.pid" kind:gt value:"0" |
process.parent.executable | is_not_null | field:"ParentImage" kind:is_not_null | |
user.id | wildcard |
| field:"user.id" kind:wildcard value:"S-1-5-18" |
Exclusions
The rule actively suppresses these predicates.
Potential Self-Signed TLS Certificate Recently Issued on External Connection
#Identifies completed outbound TLS connections to external destinations where the server presents a recently issued, likely self-signed certificate whose issuer and subject distinguished names are equal. C2 frameworks frequently use freshly generated self-signed certificates instead of publicly trusted CAs. This behavioral logic complements hash-based C2 certificate rules, such as default Cobalt Strike team-server certificates, by catching rotated or custom infrastructure that does not reuse default tooling certificates. Distinguished-name equality identifies self-issued certificates but does not cryptographically prove that the certificate signed itself. The rule does not cover private-CA signed certificates, where issuer and subject differ, or C2 that uses publicly trusted certificates such as Let's Encrypt.
Known False Positives
- Development servers, lab environments, newly stood-up self-hosted services on public IPs (VPS, homelab, NAS, cameras, and similar IoT), and some vendor appliances may use recently issued self-signed certificates. Exclude known internal development egress or validated vendor destinations after review.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
[metadata]
creation_date = "2026/08/20"
integration = ["network_traffic"]
maturity = "production"
updated_date = "2026/08/20"
[rule]
author = ["Elastic"]
description = """
Identifies completed outbound TLS connections to external destinations where the server presents a recently issued,
likely self-signed certificate whose issuer and subject distinguished names are equal. C2 frameworks frequently use
freshly generated self-signed certificates instead of publicly trusted CAs. This behavioral logic complements hash-based
C2 certificate rules, such as default Cobalt Strike team-server certificates, by catching rotated or custom
infrastructure that does not reuse default tooling certificates. Distinguished-name equality identifies self-issued
certificates but does not cryptographically prove that the certificate signed itself. The rule does not cover private-CA
signed certificates, where issuer and subject differ, or C2 that uses publicly trusted certificates such as Let's
Encrypt.
"""
false_positives = [
"""
Development servers, lab environments, newly stood-up self-hosted services on public IPs (VPS, homelab, NAS,
cameras, and similar IoT), and some vendor appliances may use recently issued self-signed certificates. Exclude
known internal development egress or validated vendor destinations after review.
""",
]
from = "now-9m"
language = "esql"
license = "Elastic License v2"
name = "Potential Self-Signed TLS Certificate Recently Issued on External Connection"
note = """## Triage and analysis
> **Disclaimer**:
> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
### Investigating Potential Self-Signed TLS Certificate Recently Issued on External Connection
Malware and post-exploitation C2 often ships with ephemeral, self-signed TLS certificates rather than CA-issued
credentials. This rule matches external, successfully established TLS sessions where the server certificate issuer
matches the subject (self-issued and commonly self-signed) and the `not_before` date falls between 30 days ago and the
current time. Matching sessions are aggregated by source IP, destination IP, subject DN, and certificate `not_before`
so repeated full handshakes in the same detection window collapse into one alert. Distinguished-name equality alone
does not cryptographically verify the certificate signature.
This logic does not detect private-CA signed leaves (issuer DN differs from subject DN) or publicly trusted certificates.
Hash-based default-certificate rules remain complementary coverage for known tooling certs that are often years old.
Resumed TLS sessions typically omit `tls.server.x509.*` fields, so only full handshakes are eligible.
### Possible investigation steps
- Review `source.ip`, `destination.ip`, `Esql.destination_port_values`, `tls.server.x509.subject.distinguished_name`,
`Esql.tls_server_x509_serial_number_values`, `tls.server.x509.not_before`, and `Esql.tls_client_server_name_values`.
Common names can be empty on self-signed certificates; prefer the distinguished name and serial.
- Compare SNI (`Esql.tls_client_server_name_values`) with the certificate subject. A mismatch is a useful pivot, not
proof of malice.
- Pivot on destination IP and certificate serial or available SHA-1/SHA-256 fingerprints across other internal sources:
```esql
FROM logs-network_traffic.tls-*
| WHERE tls.established == true
AND tls.server.x509.issuer.distinguished_name == tls.server.x509.subject.distinguished_name
| STATS event_count = COUNT(*), hosts = MV_SLICE(VALUES(source.ip), 0, 99)
BY destination.ip, tls.server.x509.serial_number, tls.server.hash.sha1, tls.server.hash.sha256
| SORT event_count DESC
```
- Correlate with endpoint alerts, DNS anomalies, or prior commodity C2 detections on the source host, including the
Default Cobalt Strike Team Server Certificate rule.
- Compare certificate age and validity window against expected vendor or ACME renewal patterns. ACME-issued public
certificates should not match this rule because they are not self-signed.
### False positive analysis
- Internal developers testing against staging servers with self-signed certs may appear if traffic hairpins through
external IPs or if staging is hosted outside RFC1918 / ULA space.
- Newly published self-hosted services (Proxmox, NAS, cameras, small-business appliances) often generate a self-signed
certificate on first boot and will match for 30 days. Exclude by destination after validation.
- Some appliance vendors ship with short-lived factory self-signed certificates; exclude by destination after validation.
- Repeated alerts for the same source, destination, and certificate across intervals are expected while the certificate
remains inside the 30-day `not_before` window. Add a destination or serial exception after the first review if the
traffic is authorized.
### Response and remediation
- Isolate the source host if the destination is unknown and no authorized workflow explains the session.
- Block the destination IP or domain at the perimeter pending investigation.
- Preserve the available certificate hashes, `Esql.tls_server_x509_serial_number_values`, and the subject DN for threat
intel sharing. Collect a PCAP or full TLS metadata sample when available.
"""
references = [
"https://www.elastic.co/docs/reference/beats/packetbeat/configuration-tls",
"https://www.elastic.co/docs/reference/ecs/ecs-x509",
"https://www.elastic.co/security-labs/collecting-cobalt-strike-beacons-with-the-elastic-stack",
]
risk_score = 47
rule_id = "869fe008-5dd4-4f07-9c2e-aa90c3926fd9"
setup = """## Setup
This rule requires TLS certificate metadata from the Elastic network_traffic integration
(`logs-network_traffic.tls-*`) with `send_certificates` enabled (the Packetbeat TLS default) so ECS fields under
`tls.server.x509.*` are populated, including `issuer.distinguished_name`, `subject.distinguished_name`, and
`not_before`.
Packetbeat calculates SHA-1 certificate fingerprints by default. To populate `tls.server.hash.sha256`, add `sha256` to
the TLS protocol analyzer's `fingerprints` setting.
Resumed TLS sessions typically do not include certificate fields and will not match. Legacy `packetbeat-*` indices are
intentionally not queried: this rule uses CIDR-based internal-to-external directionality that should be validated per
source mapping before claiming Packetbeat coverage.
"""
severity = "medium"
tags = [
"Domain: Network",
"Use Case: Network Security Monitoring",
"Use Case: Threat Detection",
"Tactic: Command and Control",
"Rule Type: ESQL",
"Data Source: Network Packet Capture",
"Data Source: Network Traffic",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "esql"
query = '''
from logs-network_traffic.tls-*
| where
network.protocol == "tls"
and network.transport == "tcp"
and tls.established == true
and source.ip is not null
and destination.ip is not null
and tls.server.x509.not_before is not null
and tls.server.x509.issuer.distinguished_name is not null
and tls.server.x509.subject.distinguished_name is not null
and tls.server.x509.issuer.distinguished_name == tls.server.x509.subject.distinguished_name
and tls.server.x509.not_before >= now() - 30 days
and tls.server.x509.not_before <= now()
and CIDR_MATCH(
source.ip,
"10.0.0.0/8",
"100.64.0.0/10",
"172.16.0.0/12",
"192.168.0.0/16",
"fc00::/7"
)
and not CIDR_MATCH(
destination.ip,
"0.0.0.0/8",
"10.0.0.0/8",
"100.64.0.0/10",
"127.0.0.0/8",
"169.254.0.0/16",
"172.16.0.0/12",
"192.0.0.0/24",
"192.0.2.0/24",
"192.168.0.0/16",
"192.175.48.0/24",
"192.31.196.0/24",
"192.52.193.0/24",
"192.88.99.0/24",
"198.18.0.0/15",
"198.51.100.0/24",
"203.0.113.0/24",
"224.0.0.0/4",
"240.0.0.0/4",
"::/128",
"::1/128",
"2001:db8::/32",
"fc00::/7",
"fe80::/10",
"ff00::/8"
)
| stats
Esql.event_count = COUNT(*),
Esql.first_seen = MIN(@timestamp),
Esql.last_seen = MAX(@timestamp),
Esql.destination_port_values = MV_SLICE(VALUES(destination.port), 0, 9),
Esql.tls_client_server_name_values = MV_SLICE(VALUES(tls.client.server_name), 0, 9),
Esql.tls_server_x509_subject_common_name_values = MV_SLICE(VALUES(tls.server.x509.subject.common_name), 0, 9),
Esql.tls_server_x509_serial_number_values = MV_SLICE(VALUES(tls.server.x509.serial_number), 0, 4),
Esql.tls_server_hash_sha1_values = MV_SLICE(VALUES(tls.server.hash.sha1), 0, 4),
Esql.tls_server_hash_sha256_values = MV_SLICE(VALUES(tls.server.hash.sha256), 0, 4),
Esql.tls_server_x509_not_after_values = MV_SLICE(VALUES(tls.server.x509.not_after), 0, 4),
Esql.network_community_id_values = MV_SLICE(VALUES(network.community_id), 0, 9),
Esql.host_name_values = MV_SLICE(VALUES(host.name), 0, 9),
Esql.observer_name_values = MV_SLICE(VALUES(observer.name), 0, 19)
by
source.ip,
destination.ip,
tls.server.x509.subject.distinguished_name,
tls.server.x509.not_before
| keep
source.ip,
destination.ip,
tls.server.x509.subject.distinguished_name,
tls.server.x509.not_before,
Esql.event_count,
Esql.first_seen,
Esql.last_seen,
Esql.destination_port_values,
Esql.tls_client_server_name_values,
Esql.tls_server_x509_subject_common_name_values,
Esql.tls_server_x509_serial_number_values,
Esql.tls_server_hash_sha1_values,
Esql.tls_server_hash_sha256_values,
Esql.tls_server_x509_not_after_values,
Esql.network_community_id_values,
Esql.host_name_values,
Esql.observer_name_values
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1071"
name = "Application Layer Protocol"
reference = "https://attack.mitre.org/techniques/T1071/"
[[rule.threat.technique]]
id = "T1573"
name = "Encrypted Channel"
reference = "https://attack.mitre.org/techniques/T1573/"
[[rule.threat.technique.subtechnique]]
id = "T1573.002"
name = "Asymmetric Cryptography"
reference = "https://attack.mitre.org/techniques/T1573/002/"
[rule.threat.tactic]
id = "TA0011"
name = "Command and Control"
reference = "https://attack.mitre.org/tactics/TA0011/"
[rule.investigation_fields]
field_names = [
"source.ip",
"destination.ip",
"tls.server.x509.subject.distinguished_name",
"tls.server.x509.not_before",
"Esql.event_count",
"Esql.first_seen",
"Esql.last_seen",
"Esql.destination_port_values",
"Esql.tls_client_server_name_values",
"Esql.tls_server_x509_subject_common_name_values",
"Esql.tls_server_x509_serial_number_values",
"Esql.tls_server_hash_sha1_values",
"Esql.tls_server_hash_sha256_values",
"Esql.tls_server_x509_not_after_values",
"Esql.network_community_id_values",
"Esql.host_name_values",
"Esql.observer_name_values",
]
Stages and Predicates
Stage 1: from
from logs-network_traffic.tls-*
Stage 2: where
| where
network.protocol == "tls"
and network.transport == "tcp"
and tls.established == true
and source.ip is not null
and destination.ip is not null
and tls.server.x509.not_before is not null
and tls.server.x509.issuer.distinguished_name is not null
and tls.server.x509.subject.distinguished_name is not null
and tls.server.x509.issuer.distinguished_name == tls.server.x509.subject.distinguished_name
and tls.server.x509.not_before >= now() - 30 days
and tls.server.x509.not_before <= now()
and CIDR_MATCH(
source.ip,
"10.0.0.0/8",
"100.64.0.0/10",
"172.16.0.0/12",
"192.168.0.0/16",
"fc00::/7"
)
and not CIDR_MATCH(
destination.ip,
"0.0.0.0/8",
"10.0.0.0/8",
"100.64.0.0/10",
"127.0.0.0/8",
"169.254.0.0/16",
"172.16.0.0/12",
"192.0.0.0/24",
"192.0.2.0/24",
"192.168.0.0/16",
"192.175.48.0/24",
"192.31.196.0/24",
"192.52.193.0/24",
"192.88.99.0/24",
"198.18.0.0/15",
"198.51.100.0/24",
"203.0.113.0/24",
"224.0.0.0/4",
"240.0.0.0/4",
"::/128",
"::1/128",
"2001:db8::/32",
"fc00::/7",
"fe80::/10",
"ff00::/8"
)
Stage 3: stats
| stats
Esql.event_count = COUNT(*),
Esql.first_seen = MIN(@timestamp),
Esql.last_seen = MAX(@timestamp),
Esql.destination_port_values = MV_SLICE(VALUES(destination.port), 0, 9),
Esql.tls_client_server_name_values = MV_SLICE(VALUES(tls.client.server_name), 0, 9),
Esql.tls_server_x509_subject_common_name_values = MV_SLICE(VALUES(tls.server.x509.subject.common_name), 0, 9),
Esql.tls_server_x509_serial_number_values = MV_SLICE(VALUES(tls.server.x509.serial_number), 0, 4),
Esql.tls_server_hash_sha1_values = MV_SLICE(VALUES(tls.server.hash.sha1), 0, 4),
Esql.tls_server_hash_sha256_values = MV_SLICE(VALUES(tls.server.hash.sha256), 0, 4),
Esql.tls_server_x509_not_after_values = MV_SLICE(VALUES(tls.server.x509.not_after), 0, 4),
Esql.network_community_id_values = MV_SLICE(VALUES(network.community_id), 0, 9),
Esql.host_name_values = MV_SLICE(VALUES(host.name), 0, 9),
Esql.observer_name_values = MV_SLICE(VALUES(observer.name), 0, 19)
by
source.ip,
destination.ip,
tls.server.x509.subject.distinguished_name,
tls.server.x509.not_before
Stage 4: keep
| keep
source.ip,
destination.ip,
tls.server.x509.subject.distinguished_name,
tls.server.x509.not_before,
Esql.event_count,
Esql.first_seen,
Esql.last_seen,
Esql.destination_port_values,
Esql.tls_client_server_name_values,
Esql.tls_server_x509_subject_common_name_values,
Esql.tls_server_x509_serial_number_values,
Esql.tls_server_hash_sha1_values,
Esql.tls_server_hash_sha256_values,
Esql.tls_server_x509_not_after_values,
Esql.network_community_id_values,
Esql.host_name_values,
Esql.observer_name_values
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
destination.ip | is_not_null | field:"dest_ip" kind:is_not_null | |
network.protocol | eq |
| field:"DestinationPortName" kind:eq value:"tls" |
network.transport | eq |
| field:"Protocol" kind:eq value:"tcp" |
source.ip | cidr_match |
| field:"src_ip" kind:cidr_match |
source.ip | is_not_null | field:"src_ip" kind:is_not_null | |
tls.established | eq |
| field:"tls.established" kind:eq value:"true" |
tls.server.x509.issuer.distinguished_name | eq |
| field:"tls.server.x509.issuer.distinguished_name" kind:eq value:"tls.server.x509.subject.distinguished_name" |
tls.server.x509.issuer.distinguished_name | is_not_null | field:"tls.server.x509.issuer.distinguished_name" kind:is_not_null | |
tls.server.x509.not_before | is_not_null | field:"tls.server.x509.not_before" kind:is_not_null | |
tls.server.x509.subject.distinguished_name | is_not_null | field:"tls.server.x509.subject.distinguished_name" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
destination.ip | cidr_match | 0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.168.0.0/16, 192.175.48.0/24, 192.31.196.0/24, 192.52.193.0/24, 192.88.99.0/24, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/4, 240.0.0.0/4, ::/128, ::1/128, 2001:db8::/32, fc00::/7, fe80::/10, ff00::/8 | excludes:destination.ip |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
source.ip | KEEP source.ip |
destination.ip | KEEP destination.ip |
tls.server.x509.subject.distinguished_name | KEEP tls.server.x509.subject.distinguished_name |
tls.server.x509.not_before | KEEP tls.server.x509.not_before |
Esql.event_count | KEEP Esql.event_count |
Esql.first_seen | KEEP Esql.first_seen |
Esql.last_seen | KEEP Esql.last_seen |
Esql.destination_port_values | KEEP Esql.destination_port_values |
Esql.tls_client_server_name_values | KEEP Esql.tls_client_server_name_values |
Esql.tls_server_x509_subject_common_name_values | KEEP Esql.tls_server_x509_subject_common_name_values |
Esql.tls_server_x509_serial_number_values | KEEP Esql.tls_server_x509_serial_number_values |
Esql.tls_server_hash_sha1_values | KEEP Esql.tls_server_hash_sha1_values |
Esql.tls_server_hash_sha256_values | KEEP Esql.tls_server_hash_sha256_values |
Esql.tls_server_x509_not_after_values | KEEP Esql.tls_server_x509_not_after_values |
Esql.network_community_id_values | KEEP Esql.network_community_id_values |
Esql.host_name_values | KEEP Esql.host_name_values |
Esql.observer_name_values | KEEP Esql.observer_name_values |
Potential Shadow Credentials added to AD Object
#Identify the modification of the msDS-KeyCredentialLink attribute in an Active Directory Computer or User Object. Attackers can abuse control over the object and create a key pair, append to raw public key in the attribute, and obtain persistent and stealthy access to the target user or computer object.
Known False Positives
- Azure AD Connect or ADFS provisioning can legitimately modify msDS-KeyCredentialLink when the writer account, source, object class, target DN, bounded change set, and post-change authentication all match an expected workflow.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Credential Access |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 5136: A directory service object was modified. |
Rule Body
[metadata]
creation_date = "2022/01/26"
integration = ["system", "windows"]
maturity = "production"
updated_date = "2026/04/27"
[rule]
author = ["Elastic"]
description = """
Identify the modification of the msDS-KeyCredentialLink attribute in an Active Directory Computer or User Object.
Attackers can abuse control over the object and create a key pair, append to raw public key in the attribute, and obtain
persistent and stealthy access to the target user or computer object.
"""
false_positives = [
"""
Azure AD Connect or ADFS provisioning can legitimately modify msDS-KeyCredentialLink when the writer account,
source, object class, target DN, bounded change set, and post-change authentication all match an expected workflow.
""",
]
from = "now-9m"
index = ["winlogbeat-*", "logs-system.security*", "logs-windows.forwarded*"]
language = "kuery"
license = "Elastic License v2"
name = "Potential Shadow Credentials added to AD Object"
references = [
"https://posts.specterops.io/shadow-credentials-abusing-key-trust-account-mapping-for-takeover-8ee1a53566ab",
"https://www.thehacker.recipes/ad/movement/kerberos/shadow-credentials",
"https://github.com/OTRF/Set-AuditRule",
"https://cyberstoph.org/posts/2022/03/detecting-shadow-credentials/",
]
risk_score = 73
rule_id = "79f97b31-480e-4e63-a7f4-ede42bf2c6de"
severity = "high"
tags = [
"Domain: Endpoint",
"OS: Windows",
"Use Case: Threat Detection",
"Tactic: Credential Access",
"Data Source: Active Directory",
"Resources: Investigation Guide",
"Use Case: Active Directory Monitoring",
"Data Source: Windows Security Event Logs",
]
timestamp_override = "event.ingested"
type = "query"
query = '''
event.code:"5136" and host.os.type:"windows" and winlog.event_data.AttributeLDAPDisplayName:"msDS-KeyCredentialLink" and
winlog.event_data.AttributeValue :B\:828* and
not winlog.event_data.SubjectUserName: MSOL_* and
not winlog.event_data.ObjectClass: "msDS-Device"
'''
note = """## Triage and analysis
### Investigating Potential Shadow Credentials added to AD Object
#### Possible investigation steps
- What object received the key-trust change?
- Focus: `winlog.event_data.ObjectDN`, `winlog.event_data.ObjectGUID`, `winlog.event_data.ObjectClass`, `winlog.event_data.OperationType`, and `winlog.event_data.AttributeValue`.
- Implication: escalate when a sensitive user or computer receives an added or replaced key-trust value outside recognized enrollment; lower suspicion only when the object class, DN, and operation fit the same identity-registration workflow.
- Which account and logon session wrote the value?
- Focus: `winlog.event_data.SubjectUserSid`, `winlog.event_data.SubjectUserName`, `winlog.event_data.SubjectLogonId`, recovered `source.ip`, and `winlog.logon.type`. $investigate_0
- Hint: match alert `winlog.event_data.SubjectLogonId` to same `host.id` authentication events with `winlog.event_data.TargetLogonId`; if no 4624 matches, keep the session unresolved.
- Implication: escalate for an unexpected user, admin, service, or machine writer, or source/logon type outside the object's enrollment path; lower suspicion when writer and session match recognized provisioning or device registration.
- Does the writer/object pair fit a recognized ADFS or Azure AD Connect-style path?
- Why: the abuse path writes authentication material, so service-looking writers still need source and change-set validation.
- Focus: compare `winlog.event_data.SubjectUserSid`, `winlog.event_data.SubjectUserName`, `winlog.event_data.ObjectClass`, `winlog.event_data.ObjectDN`, and `source.ip` to the expected service account, object type, and enrollment source.
- Implication: lower suspicion when recognized provisioning updates the expected object from the expected source; escalate when the writer is ad hoc, interactive, non-provisioning, object-class mismatched, or unexplained by source.
- Was the logical change limited to this key credential?
- Focus: use same-operation 5136 events grouped by `winlog.event_data.OpCorrelationID`; compare `winlog.event_data.ObjectGUID`, `winlog.event_data.AttributeLDAPDisplayName`, `winlog.event_data.OperationType`, and `winlog.event_data.AttributeValue`. $investigate_1
- Implication: escalate when the operation touches unrelated objects, adds other authentication or delegation material, or removes cleanup evidence; lower suspicion when bounded to the expected object and enrollment attributes.
- Did the modified identity authenticate after the change?
- Why: post-change authentication shows whether the new key material may already be in use.
- Focus: derive the principal from `winlog.event_data.ObjectDN`; review authentication events for `winlog.event_data.TargetUserName`, `winlog.event_data.TargetDomainName`, `source.ip`, `winlog.logon.type`, and `winlog.event_data.AuthenticationPackageName`.
- Hint: search after `@timestamp` for Target-side fields matching the derived principal; if `source.ip` is empty, lower origin confidence instead of treating absence as benign.
- Implication: escalate when the identity authenticates from a new source, unexpected logon type, or authentication path after the change; absence of follow-on use reduces urgency only when earlier evidence proves recognized provisioning.
- Do related alerts change the scope beyond this object?
- Focus: recent alerts for the modifying account using `user.id` or `winlog.event_data.SubjectUserSid`. $investigate_2
- Hint: compare with alerts scoped to the modified object's `winlog.event_data.ObjectGUID`. $investigate_3
- Implication: broaden response when either scope shows privilege abuse, directory tampering, relay activity, or lateral movement; keep local when related alerts are quiet and local evidence resolves to one recognized workflow.
- Escalate on the key-trust change plus any suspicious or unresolved object, writer session, `winlog.event_data.OpCorrelationID` scope, post-change authentication, or related-alert finding; close only when all evidence binds to one recognized provisioning workflow; preserve and escalate when evidence is mixed, incomplete, or uncorroborated.
### False positive analysis
- AutoPilot or WHfB device enrollment can cause a computer to write its own key credential. Confirm `winlog.event_data.SubjectUserName` matches the CN in `winlog.event_data.ObjectDN`, `winlog.event_data.ObjectClass` is "computer", `winlog.event_data.OpCorrelationID` is bounded, and no unexpected follow-on authentication occurs.
- ADFS or Azure AD Connect provisioning can update key credentials on user or computer objects. Confirm `winlog.event_data.SubjectUserSid`, `winlog.event_data.ObjectDN`, recovered `source.ip`, bounded `winlog.event_data.OpCorrelationID`, and post-change authentication align with one named workflow. Keep open when ownership is unresolved.
- Build exceptions from stable writer SID, object class or GUID, `host.id`, recovered source, and enrollment path across prior alerts. Avoid exceptions on "msDS-KeyCredentialLink", `user.name`, or host alone.
### Response and remediation
- Preserve a case export of the triggering 5136, recovered writer-session authentication events, `winlog.event_data.AttributeValue`, `winlog.event_data.ObjectGUID`, and `winlog.event_data.OpCorrelationID` before containment, reversal, or cleanup.
- If confirmed benign, reverse temporary containment and document the exact workflow evidence: writer SID, object GUID/class, domain naming context, recovered source, bounded change set, and post-change authentication pattern. Keep any exception narrow and only for the recurring workflow.
- If suspicious but unconfirmed, apply reversible controls to the writer first, such as heightened monitoring or temporary access review; restrict the modified identity only when object sensitivity or follow-on authentication shows active risk.
- If confirmed malicious, contain the writer account or source system using `winlog.event_data.SubjectLogonId`, `source.ip`, `host.id`, and follow-on authentication evidence. Disable the writer first when its session performed unauthorized changes; disable or rotate the modified identity only when post-change authentication or object sensitivity shows active risk.
- After containment, remove only the unauthorized key-trust value and verify rollback. Reset or rotate the modified identity according to `winlog.event_data.ObjectClass`: reset user passwords, rotate service credentials, or re-establish the expected computer trust path. Review the same `winlog.event_data.OpCorrelationID` or session for additional unauthorized changes.
- Post-incident hardening: restrict write access to "msDS-KeyCredentialLink" to dedicated identity-management accounts, retain 5136 auditing on domain controllers, and record the confirmed provisioning workflow or abuse pattern for future triage.
"""
setup = """## Setup
Audit Directory Service Changes must be enabled to generate the events used by this rule.
Setup instructions: https://ela.st/audit-directory-service-changes
"""
[rule.investigation_fields]
field_names = [
"@timestamp",
"user.id",
"winlog.event_data.SubjectUserSid",
"winlog.event_data.SubjectUserName",
"winlog.event_data.SubjectDomainName",
"winlog.event_data.SubjectLogonId",
"winlog.event_data.ObjectDN",
"winlog.event_data.ObjectGUID",
"winlog.event_data.ObjectClass",
"winlog.event_data.AttributeLDAPDisplayName",
"winlog.event_data.AttributeValue",
"winlog.event_data.OperationType",
"winlog.event_data.OpCorrelationID",
"host.name",
"host.id",
]
[transform]
[[transform.investigate]]
label = "Successful logon for the modifying session"
description = ""
providers = [
[
{ excluded = false, field = "event.code", queryType = "phrase", value = "4624", valueType = "string" },
{ excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
{ excluded = false, field = "winlog.event_data.TargetLogonId", queryType = "phrase", value = "{{winlog.event_data.SubjectLogonId}}", valueType = "string" }
]
]
relativeFrom = "now-48h/h"
relativeTo = "now"
[[transform.investigate]]
label = "Directory changes in the same operation"
description = ""
providers = [
[
{ excluded = false, field = "event.code", queryType = "phrase", value = "5136", valueType = "string" },
{ excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
{ excluded = false, field = "winlog.event_data.OpCorrelationID", queryType = "phrase", value = "{{winlog.event_data.OpCorrelationID}}", valueType = "string" }
]
]
relativeFrom = "now-48h/h"
relativeTo = "now"
[[transform.investigate]]
label = "Alerts associated with the modifying account"
description = ""
providers = [
[
{ excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
{ excluded = false, field = "user.id", queryType = "phrase", value = "{{user.id}}", valueType = "string" }
]
]
relativeFrom = "now-48h/h"
relativeTo = "now"
[[transform.investigate]]
label = "Alerts associated with the modified object"
description = ""
providers = [
[
{ excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
{ excluded = false, field = "winlog.event_data.ObjectGUID", queryType = "phrase", value = "{{winlog.event_data.ObjectGUID}}", valueType = "string" }
]
]
relativeFrom = "now-48h/h"
relativeTo = "now"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1556"
name = "Modify Authentication Process"
reference = "https://attack.mitre.org/techniques/T1556/"
[rule.threat.tactic]
id = "TA0006"
name = "Credential Access"
reference = "https://attack.mitre.org/tactics/TA0006/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1098"
name = "Account Manipulation"
reference = "https://attack.mitre.org/techniques/T1098/"
[rule.threat.tactic]
id = "TA0003"
name = "Persistence"
reference = "https://attack.mitre.org/tactics/TA0003/"
Stages and Predicates
Stage 1: query
event.code:"5136" and host.os.type:"windows" and winlog.event_data.AttributeLDAPDisplayName:"msDS-KeyCredentialLink" and
winlog.event_data.AttributeValue :B\:828* and
not winlog.event_data.SubjectUserName: MSOL_* and
not winlog.event_data.ObjectClass: "msDS-Device"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
winlog.event_data.AttributeLDAPDisplayName | eq |
| field:"AttributeLDAPDisplayName" kind:eq value:"msDS-KeyCredentialLink" |
winlog.event_data.AttributeValue | wildcard |
| field:"AttributeValue" kind:wildcard value:"B:828*" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
winlog.event_data.ObjectClass | eq | msDS-Device | excludes:winlog.event_data.ObjectClass field:"winlog.event_data.ObjectClass" value:"msDS-Device" |
winlog.event_data.SubjectUserName | starts_with | MSOL_ | excludes:winlog.event_data.SubjectUserName field:"winlog.event_data.SubjectUserName" value:"MSOL_" |
Service Communication via Mail Protocol
#Identifies DNS queries to Mail domain names by the Windows Shared Services Host process svchost. Adversaries may communicate using application layer protocols associated with electronic mail delivery to avoid detection/network filtering by blending in with existing traffic.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Command & Control |
Rule Body
[rule]
description = """
Identifies DNS queries to Mail domain names by the Windows Shared Services Host process svchost. Adversaries may
communicate using application layer protocols associated with electronic mail delivery to avoid detection/network
filtering by blending in with existing traffic.
"""
id = "7f1872c5-0231-4fa7-a1e7-6400964d7cf3"
license = "Elastic License v2"
name = "Service Communication via Mail Protocol"
os_list = ["windows"]
version = "1.0.8"
query = '''
sequence by process.entity_id with maxspan=3m
[library where process.name : "svchost.exe" and dll.Ext.relative_file_creation_time <= 900 and
not dll.code_signature.status : ("trusted", "errorExpired", "errorCode_endpoint*")]
[dns where dns.question.name : ("smtp.*", "mail.*") and process.name : "svchost.exe"]
'''
min_endpoint_version = "8.4.0"
[[actions]]
action = "kill_process"
field = "process.entity_id"
state = 0
[[optional_actions]]
action = "rollback"
field = "process.entity_id"
state = 0
[[threat]]
framework = "MITRE ATT&CK"
[[threat.technique]]
id = "T1543"
name = "Create or Modify System Process"
reference = "https://attack.mitre.org/techniques/T1543/"
[[threat.technique.subtechnique]]
id = "T1543.003"
name = "Windows Service"
reference = "https://attack.mitre.org/techniques/T1543/003/"
[threat.tactic]
id = "TA0003"
name = "Persistence"
reference = "https://attack.mitre.org/tactics/TA0003/"
[[threat]]
framework = "MITRE ATT&CK"
[[threat.technique]]
id = "T1071"
name = "Application Layer Protocol"
reference = "https://attack.mitre.org/techniques/T1071/"
[[threat.technique.subtechnique]]
id = "T1071.003"
name = "Mail Protocols"
reference = "https://attack.mitre.org/techniques/T1071/003/"
[threat.tactic]
id = "TA0011"
name = "Command and Control"
reference = "https://attack.mitre.org/tactics/TA0011/"
[internal]
min_endpoint_version = "8.4.0"
Stages and Predicates
Ordered sequence: each step below must occur in order within 3m, correlated by process.entity_id.
Stage 1: library
[library where process.name : "svchost.exe" and dll.Ext.relative_file_creation_time <= 900 and
not dll.code_signature.status : ("trusted", "errorExpired", "errorCode_endpoint*")]
Stage 2: dns
[dns where dns.question.name : ("smtp.*", "mail.*") and process.name : "svchost.exe"]
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
dll.Ext.relative_file_creation_time | le |
| field:"dll.Ext.relative_file_creation_time" kind:le value:"900" |
dns.question.name | wildcard |
| field:"QueryName" kind:wildcard |
process.name | wildcard |
| field:"process_basename" kind:wildcard value:"svchost.exe" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
dll.code_signature.status | wildcard | trusted, errorExpired, errorCode_endpoint* | excludes:dll.code_signature.status field:"dll.code_signature.status" value:"trusted" field:"dll.code_signature.status" value:"errorExpired" field:"dll.code_signature.status" value:"errorCode_endpoint*" |
Suspicious Descendant Process Execution via Windows Run
#Identifies the execution of Windows commands via the Windows Run window. Adversaries may use phishing to instruct the victim to copy and paste malicious commands for execution via the Windows Run menu.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution |
Rule Body
[rule]
description = """
Identifies the execution of Windows commands via the Windows Run window. Adversaries may use phishing to instruct the
victim to copy and paste malicious commands for execution via the Windows Run menu.
"""
id = "9afd4fdc-15bc-4725-9164-dfcc2f1cb5ee"
license = "Elastic License v2"
name = "Suspicious Descendant Process Execution via Windows Run"
os_list = ["windows"]
reference = ["https://blog.sekoia.io/clickfake-interview-campaign-by-lazarus/"]
version = "1.0.4"
query = '''
process where event.action == "start" and
process.name : ("powershell.exe", "curl.exe", "msiexec.exe", "mshta.exe", "wscript.exe", "rundll32.exe", "certutil.exe", "certreq.exe") and process.args_count >= 2 and
descendant of [process where event.action == "start" and process.name : ("cmd.exe", "powershell.exe") and
process.parent.name : "explorer.exe" and
process.parent.thread.Ext.call_stack_summary like "*|user32.dll|shell32.dll|explorer.exe|shcore.dll|*" and
_arraysearch(process.parent.thread.Ext.call_stack, $entry,
$entry.symbol_info like ("C:\\Windows\\System32\\user32.dll!DialogBoxParamW*",
"C:\\Windows\\System32\\user32.dll!DialogBoxIndirectParamW*",
"C:\\Windows\\System32\\shell32.dll!GetFileNameFromBrowse*"))] and
not (process.name : "powershell.exe" and
process.args :("-NoProfile", "-Command", "-ExecutionPolicy", "-noexit", "-WindowsStyle", "ls", "*-UseBasicParsing*", "-file", "--version", "*Get-WmiObjec*", "Port", "Test-Path", "Get-*")) and
not (process.name : "curl.exe" and process.parent.name : ("cmd.exe", "bash.exe")) and
not (process.name : "curl.exe" and process.args : ("-L", "-F", "POST")) and
not (process.name : "certutil.exe" and not process.args : ("-urlcache", "-decode")) and
not (process.name : "rundll32.exe" and process.parent.executable : ("C:\\Windows\\System32\\control.exe", "C:\\$WINDOWS.~BT\\Sources\\SetupHost.exe")) and
not (process.name : "rundll32.exe" and
process.command_line :("rundll32 printui.dll PrintUIEntry /im",
"rundll32.exe \"C:\\ProgramData\\FastTrack Software\\Admin By Request\\ShellHelper32.dll\",#1",
"rundll32.exe \"C:\\ProgramData\\FastTrack Software\\Admin By Request\\ShellHelper64.dll\",#1",
"rundll32 printui.dll PrintYUEntry /im")) and
not (process.name : "rundll32.exe" and
process.args : ("C:\\Windows\\system32\\inetcpl.cpl,ClearMyTracksByProcess", "url.dll,FileProtocolHandler", "ndfapi.dll,NdfRunDllDiagnoseWithAnswerFile", "inetcpl.cpl",
"C:\\Program Files\\Notepad++\\contextmenu\\NppShell.dll,CleanupDll", "user32.dll,*", "printui.dll", "keymgr.dll,*", "url,OpenURL")) and
not (process.name : "wscript.exe" and process.parent.executable : "C:\\Program Files (x86)\\Beyond Compare ?\\BCompare.exe") and
not (process.name : "wscript.exe" and process.args : ("*\\Program Files\\*", "C:\\Windows\\system32\\slmgr.vbs", "\\\\*")) and
not (process.name : "msiexec.exe" and process.args : ("/log", "/passive", "/L*V", "WAZUH_AGENT_GROUP=default", "/x", "D:\\*.msi", "/a", "/uninstall", "/unregister", "/regserver", "USERDOMAIN*", "C:\\Windows\\Temp\\*", "/I{*", "TOKEN=*")) and
not process.working_directory : ("?:\\", "?:\\Program Files*", "C:\\Users\\*\\AppData\\Local\\Programs\\Microsoft VS Code\\") and
not process.executable : ("C:\\Program Files\\*\\curl.exe", "\\Device\\Mup\\*.exe") and
not process.parent.executable : ("\\Device\\Mup\\*.exe", "D:\\*.exe")
'''
min_endpoint_version = "8.10.0"
optional_actions = []
[[actions]]
action = "kill_process"
field = "process.entity_id"
state = 0
[[threat]]
framework = "MITRE ATT&CK"
[[threat.technique]]
id = "T1566"
name = "Phishing"
reference = "https://attack.mitre.org/techniques/T1566/"
[threat.tactic]
id = "TA0001"
name = "Initial Access"
reference = "https://attack.mitre.org/tactics/TA0001/"
[[threat]]
framework = "MITRE ATT&CK"
[[threat.technique]]
id = "T1059"
name = "Command and Scripting Interpreter"
reference = "https://attack.mitre.org/techniques/T1059/"
[[threat.technique.subtechnique]]
id = "T1059.001"
name = "PowerShell"
reference = "https://attack.mitre.org/techniques/T1059/001/"
[[threat.technique.subtechnique]]
id = "T1059.003"
name = "Windows Command Shell"
reference = "https://attack.mitre.org/techniques/T1059/003/"
[[threat.technique]]
id = "T1204"
name = "User Execution"
reference = "https://attack.mitre.org/techniques/T1204/"
[threat.tactic]
id = "TA0002"
name = "Execution"
reference = "https://attack.mitre.org/tactics/TA0002/"
[internal]
min_endpoint_version = "8.10.0"
Stages and Predicates
Stage 1: process
process where event.action == "start" and
process.name : ("powershell.exe", "curl.exe", "msiexec.exe", "mshta.exe", "wscript.exe", "rundll32.exe", "certutil.exe", "certreq.exe") and process.args_count >= 2 and
descendant of [process where event.action == "start" and process.name : ("cmd.exe", "powershell.exe") and
process.parent.name : "explorer.exe" and
process.parent.thread.Ext.call_stack_summary like "*|user32.dll|shell32.dll|explorer.exe|shcore.dll|*" and
_arraysearch(process.parent.thread.Ext.call_stack, $entry,
$entry.symbol_info like ("C:\\Windows\\System32\\user32.dll!DialogBoxParamW*",
"C:\\Windows\\System32\\user32.dll!DialogBoxIndirectParamW*",
"C:\\Windows\\System32\\shell32.dll!GetFileNameFromBrowse*"))] and
not (process.name : "powershell.exe" and
process.args :("-NoProfile", "-Command", "-ExecutionPolicy", "-noexit", "-WindowsStyle", "ls", "*-UseBasicParsing*", "-file", "--version", "*Get-WmiObjec*", "Port", "Test-Path", "Get-*")) and
not (process.name : "curl.exe" and process.parent.name : ("cmd.exe", "bash.exe")) and
not (process.name : "curl.exe" and process.args : ("-L", "-F", "POST")) and
not (process.name : "certutil.exe" and not process.args : ("-urlcache", "-decode")) and
not (process.name : "rundll32.exe" and process.parent.executable : ("C:\\Windows\\System32\\control.exe", "C:\\$WINDOWS.~BT\\Sources\\SetupHost.exe")) and
not (process.name : "rundll32.exe" and
process.command_line :("rundll32 printui.dll PrintUIEntry /im",
"rundll32.exe \"C:\\ProgramData\\FastTrack Software\\Admin By Request\\ShellHelper32.dll\",#1",
"rundll32.exe \"C:\\ProgramData\\FastTrack Software\\Admin By Request\\ShellHelper64.dll\",#1",
"rundll32 printui.dll PrintYUEntry /im")) and
not (process.name : "rundll32.exe" and
process.args : ("C:\\Windows\\system32\\inetcpl.cpl,ClearMyTracksByProcess", "url.dll,FileProtocolHandler", "ndfapi.dll,NdfRunDllDiagnoseWithAnswerFile", "inetcpl.cpl",
"C:\\Program Files\\Notepad++\\contextmenu\\NppShell.dll,CleanupDll", "user32.dll,*", "printui.dll", "keymgr.dll,*", "url,OpenURL")) and
not (process.name : "wscript.exe" and process.parent.executable : "C:\\Program Files (x86)\\Beyond Compare ?\\BCompare.exe") and
not (process.name : "wscript.exe" and process.args : ("*\\Program Files\\*", "C:\\Windows\\system32\\slmgr.vbs", "\\\\*")) and
not (process.name : "msiexec.exe" and process.args : ("/log", "/passive", "/L*V", "WAZUH_AGENT_GROUP=default", "/x", "D:\\*.msi", "/a", "/uninstall", "/unregister", "/regserver", "USERDOMAIN*", "C:\\Windows\\Temp\\*", "/I{*", "TOKEN=*")) and
not process.working_directory : ("?:\\", "?:\\Program Files*", "C:\\Users\\*\\AppData\\Local\\Programs\\Microsoft VS Code\\") and
not process.executable : ("C:\\Program Files\\*\\curl.exe", "\\Device\\Mup\\*.exe") and
not process.parent.executable : ("\\Device\\Mup\\*.exe", "D:\\*.exe")
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.action | eq |
| field:"EventType" kind:eq value:"start" |
process.args | wildcard |
| field:"process.args" kind:wildcard |
process.args_count | ge |
| field:"process.args_count" kind:ge value:"2" |
process.name | wildcard |
| field:"process_basename" kind:wildcard |
Exclusions
The rule actively suppresses these predicates.
Suspicious RDP ActiveX Client Loaded
#Identifies suspicious Image Loading of the Remote Desktop Services ActiveX Client (mstscax), this may indicate the presence of RDP lateral movement capability.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Lateral Movement |
Rule Body
[metadata]
creation_date = "2020/11/19"
integration = ["endpoint", "windows"]
maturity = "production"
updated_date = "2026/05/04"
[rule]
author = ["Elastic"]
description = """
Identifies suspicious Image Loading of the Remote Desktop Services ActiveX Client (mstscax), this may indicate the
presence of RDP lateral movement capability.
"""
from = "now-9m"
index = ["logs-endpoint.events.library-*", "winlogbeat-*", "logs-windows.sysmon_operational-*", "endgame-*"]
language = "eql"
license = "Elastic License v2"
name = "Suspicious RDP ActiveX Client Loaded"
note = """## Triage and analysis
> **Disclaimer**:
> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
### Investigating Suspicious RDP ActiveX Client Loaded
The Remote Desktop Services ActiveX Client, mstscax.dll, facilitates remote desktop connections, enabling users to access and control other systems. Adversaries may exploit this by loading the DLL in unauthorized contexts to move laterally within a network. The detection rule identifies unusual loading of mstscax.dll outside typical system paths, flagging potential misuse indicative of lateral movement attempts.
### Possible investigation steps
- Review the process executable path to determine if mstscax.dll was loaded from an unusual or unauthorized location, as specified in the query.
- Check the associated process and user context to identify who initiated the process and whether it aligns with expected behavior or known user activity.
- Investigate the network connections associated with the process to identify any suspicious remote connections or lateral movement attempts.
- Examine recent login events and RDP session logs for the involved user account to detect any unauthorized access or anomalies.
- Correlate the alert with other security events or logs to identify potential patterns or related suspicious activities within the network.
### False positive analysis
- Legitimate administrative tools or scripts that load mstscax.dll from non-standard paths may trigger false positives. To mitigate this, identify and document these tools, then add their paths to the exclusion list in the detection rule.
- Software updates or installations that temporarily load mstscax.dll from unusual locations can cause false alerts. Monitor and log these activities, and consider excluding these paths if they are consistently flagged during known update periods.
- Virtualization software or sandbox environments that use mstscax.dll for legitimate purposes might be flagged. Verify the use of such software and exclude their executable paths from the rule to prevent unnecessary alerts.
- Custom user scripts or automation tasks that involve remote desktop functionalities may load mstscax.dll in unexpected ways. Review these scripts and, if deemed safe, add their execution paths to the exclusion list to reduce noise.
- Network drive mappings or shared folders that involve remote desktop components could lead to false positives. Ensure these are part of regular operations and exclude their paths if they are frequently flagged without malicious intent.
### Response and remediation
- Isolate the affected system from the network immediately to prevent further lateral movement by the adversary.
- Terminate any suspicious processes associated with the unauthorized loading of mstscax.dll to halt potential malicious activities.
- Conduct a thorough scan of the affected system using updated antivirus or endpoint detection and response (EDR) tools to identify and remove any malware or unauthorized software.
- Review and analyze the system and network logs to identify any other systems that may have been accessed or compromised by the adversary.
- Reset credentials for any accounts that were accessed or potentially compromised during the incident to prevent unauthorized access.
- Implement network segmentation to limit the ability of adversaries to move laterally within the network in the future.
- Escalate the incident to the security operations center (SOC) or incident response team for further investigation and to determine if additional systems or data have been affected."""
references = [
"https://posts.specterops.io/revisiting-remote-desktop-lateral-movement-8fb905cb46c3",
"https://www.elastic.co/security-labs/hunting-for-lateral-movement-using-event-query-language",
]
risk_score = 47
rule_id = "71c5cb27-eca5-4151-bb47-64bc3f883270"
setup = """## Setup
This rule is designed for data generated by [Elastic Defend](https://www.elastic.co/security/endpoint-security), which provides native endpoint detection and response, along with event enrichments designed to work with our detection rules.
Setup instructions: https://ela.st/install-elastic-defend
### Additional data sources
This rule also supports the following third-party data sources. For setup instructions, refer to the links below:
- [Sysmon Event ID 7 - Image Loaded](https://ela.st/sysmon-event-7-setup)
"""
severity = "medium"
tags = [
"Domain: Endpoint",
"OS: Windows",
"Use Case: Threat Detection",
"Tactic: Lateral Movement",
"Data Source: Elastic Endgame",
"Data Source: Elastic Defend",
"Data Source: Sysmon",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "eql"
query = '''
any where host.os.type == "windows" and
(event.category : ("library", "driver") or (event.category == "process" and event.action : "Image loaded*")) and
(?dll.name : "mstscax.dll" or file.name : "mstscax.dll") and
/* depending on noise in your env add here extra paths */
process.executable : (
"C:\\Windows\\*",
"C:\\Users\\Public\\*",
"C:\\Users\\Default\\*",
"C:\\Intel\\*",
"C:\\PerfLogs\\*",
"C:\\ProgramData\\*",
"\\Device\\Mup\\*",
"\\\\*"
) and
/* add here FPs */
not process.executable : (
"?:\\Windows\\System32\\mstsc.exe",
"?:\\Windows\\SysWOW64\\mstsc.exe",
"?:\\Windows\\System32\\vmconnect.exe",
"?:\\Windows\\System32\\WindowsSandboxClient.exe",
"?:\\Windows\\System32\\hvsirdpclient.exe"
)
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1021"
name = "Remote Services"
reference = "https://attack.mitre.org/techniques/T1021/"
[[rule.threat.technique.subtechnique]]
id = "T1021.001"
name = "Remote Desktop Protocol"
reference = "https://attack.mitre.org/techniques/T1021/001/"
[rule.threat.tactic]
id = "TA0008"
name = "Lateral Movement"
reference = "https://attack.mitre.org/tactics/TA0008/"
Stages and Predicates
Stage 1: any
any where host.os.type == "windows" and
(event.category : ("library", "driver") or (event.category == "process" and event.action : "Image loaded*")) and
(?dll.name : "mstscax.dll" or file.name : "mstscax.dll") and
process.executable : (
"C:\\Windows\\*",
"C:\\Users\\Public\\*",
"C:\\Users\\Default\\*",
"C:\\Intel\\*",
"C:\\PerfLogs\\*",
"C:\\ProgramData\\*",
"\\Device\\Mup\\*",
"\\\\*"
) and
not process.executable : (
"?:\\Windows\\System32\\mstsc.exe",
"?:\\Windows\\SysWOW64\\mstsc.exe",
"?:\\Windows\\System32\\vmconnect.exe",
"?:\\Windows\\System32\\WindowsSandboxClient.exe",
"?:\\Windows\\System32\\hvsirdpclient.exe"
)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
dll.name | wildcard |
| field:"dll.name" kind:wildcard value:"mstscax.dll" |
event.action | wildcard |
| field:"EventType" kind:wildcard value:"Image loaded*" |
event.category | eq |
| field:"event.category" kind:eq value:"process" |
event.category | wildcard |
| field:"event.category" kind:wildcard |
file.name | wildcard |
| field:"file.name" kind:wildcard value:"mstscax.dll" |
process.executable | wildcard |
| field:"Image" kind:wildcard |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
process.executable | eq | ?:\Windows\System32\mstsc.exe, ?:\Windows\SysWOW64\mstsc.exe, ?:\Windows\System32\vmconnect.exe, ?:\Windows\System32\WindowsSandboxClient.exe, ?:\Windows\System32\hvsirdpclient.exe | excludes:process.executable |