diff --git a/hunting/generate_markdown.py b/hunting/generate_markdown.py index f71faec58..fa5dad3b3 100644 --- a/hunting/generate_markdown.py +++ b/hunting/generate_markdown.py @@ -28,12 +28,13 @@ class Hunt: """Dataclass to represent a hunt.""" author: str + description: str integration: list[str] uuid: str name: str language: str license: str - query: str + query: list[str] notes: Optional[List[str]] = field(default_factory=list) mitre: Optional[List[str]] = field(default_factory=list) references: Optional[List[str]] = field(default_factory=list) @@ -81,11 +82,13 @@ def convert_toml_to_markdown(hunt_config: Hunt, file_path: Path) -> str: markdown = f"# {hunt_config.name}\n\n---\n\n" markdown += "## Metadata\n\n" markdown += f"- **Author:** {hunt_config.author}\n" + markdown += f"- **Description:** {hunt_config.description}\n" markdown += f"- **UUID:** `{hunt_config.uuid}`\n" markdown += f"- **Integration:** {", ".join(generate_integration_links(hunt_config.integration))}\n" markdown += f"- **Language:** `{hunt_config.language}`\n\n" markdown += "## Query\n\n" - markdown += f"```sql\n{hunt_config.query}```\n\n" + for query in hunt_config.query: + markdown += f"```sql\n{query}```\n\n" if hunt_config.notes: markdown += "## Notes\n\n" + "\n".join(f"- {note}" for note in hunt_config.notes) diff --git a/hunting/index.md b/hunting/index.md index eb6207e43..33bb3d17c 100644 --- a/hunting/index.md +++ b/hunting/index.md @@ -3,9 +3,9 @@ Here are the queries currently available: ## llm -- [Denial of Service or Resource Exhaustion Attacks Detection](./llm/docs/llm_dos_resource_exhaustion_detection.md) (ES|QL) -- [Monitoring for Latency Anomalies](./llm/docs/llm_latency_anomalies_detection.md) (ES|QL) -- [Sensitive Content Refusal Detection](./llm/docs/llm_sensitive_content_refusal_detection.md) (ES|QL) +- [AWS Bedrock LLM Denial-of-Service or Resource Exhaustion](./llm/docs/aws_bedrock_dos_resource_exhaustion_detection.md) (ES|QL) +- [AWS Bedrock LLM Latency Anomalies](./llm/docs/aws_bedrock_latency_anomalies_detection.md) (ES|QL) +- [AWS Bedrock LLM Sensitive Content Refusals](./llm/docs/aws_bedrock_sensitive_content_refusal_detection.md) (ES|QL) ## macos @@ -13,55 +13,36 @@ Here are the queries currently available: ## windows -- [CreateRemoteThread by source process with low occurrence](./windows/docs/createremotethread_by_source_process_with_low_occurrence.md) (ES|QL) -- [Detect DLL Hijack via Masquerading as Microsoft Native Libraries - Elastic Defend](./windows/docs/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_elastic_defend.md) (ES|QL) -- [Detect DLL Hijack via Masquerading as Microsoft Native Libraries - Sysmon](./windows/docs/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_sysmon.md) (ES|QL) -- [Detect masquerading attempts as native Windows binaries](./windows/docs/detect_masquerading_attempts_as_native_windows_binaries.md) (ES|QL) -- [Detect Rare DLL SideLoad by Occurrence - Elastic Defend](./windows/docs/detect_rare_dll_sideload_by_occurrence_elastic_defend.md) (ES|QL) -- [Detect Rare DLL SideLoad by Occurrence - Sysmon](./windows/docs/detect_rare_dll_sideload_by_occurrence_sysmon.md) (ES|QL) -- [Detect Rare LSASS Process Access Attempts - Elastic Defend](./windows/docs/detect_rare_lsass_process_access_attempts_elastic_defend.md) (ES|QL) -- [Detect Rare LSASS Process Access Attempts - Sysmon](./windows/docs/detect_rare_lsass_process_access_attempts_sysmon.md) (ES|QL) -- [Doamin Names queries via Lolbins and with low occurence frequency](./windows/docs/doamin_names_queries_via_lolbins_and_with_low_occurence_frequency.md) (ES|QL) -- [Drivers Load with low occurrence frequency - Elastic Defend](./windows/docs/drivers_load_with_low_occurrence_frequency_elastic_defend.md) (ES|QL) -- [Drivers Load with low occurrence frequency - Sysmon](./windows/docs/drivers_load_with_low_occurrence_frequency_sysmon.md) (ES|QL) -- [Drivers Load with low occurrence frequency - Windows 7045](./windows/docs/drivers_load_with_low_occurrence_frequency_windows_7045.md) (ES|QL) -- [Excessive RDP Network Activity by Source Host and User- Elastic Defend - Sysmon](./windows/docs/excessive_rdp_network_activity_by_source_host_and_user-_elastic_defend_sysmon.md) (ES|QL) -- [Excessive RDP Network Activity by Source Host - Elastic Defend - Sysmon](./windows/docs/excessive_rdp_network_activity_by_source_host_elastic_defend_sysmon.md) (ES|QL) -- [Excessive SMB Network Activity by process Id](./windows/docs/excessive_smb_network_activity_by_process_id.md) (ES|QL) -- [Executable File creation by an Unusual Microsoft Binary - Elastic Defend](./windows/docs/executable_file_creation_by_an_unusual_microsoft_binary_elastic_defend.md) (ES|QL) -- [Executable File creation by an Unusual Microsoft Binary - Sysmon](./windows/docs/executable_file_creation_by_an_unusual_microsoft_binary_sysmon.md) (ES|QL) -- [Execution via Network Logon by occurrence frequency](./windows/docs/execution_via_network_logon_by_occurrence_frequency.md) (ES|QL) -- [Execution via Network Logon by occurrence frequency by top Source IP](./windows/docs/execution_via_network_logon_by_occurrence_frequency_by_top_source_ip.md) (ES|QL) +- [Low Occurrence Rate of CreateRemoteThread by Source Process](./windows/docs/createremotethread_by_source_process_with_low_occurrence.md) (ES|QL) +- [DLL Hijack via Masquerading as Microsoft Native Libraries](./windows/docs/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries.md) (ES|QL) +- [Masquerading Attempts as Native Windows Binaries](./windows/docs/detect_masquerading_attempts_as_native_windows_binaries.md) (ES|QL) +- [Rare DLL Side-Loading by Occurrence](./windows/docs/detect_rare_dll_sideload_by_occurrence.md) (ES|QL) +- [Rare LSASS Process Access Attempts](./windows/docs/detect_rare_lsass_process_access_attempts.md) (ES|QL) +- [DNS Queries via LOLBins with Low Occurence Frequency](./windows/docs/domain_names_queried_via_lolbins_and_with_low_occurence_frequency.md) (ES|QL) +- [Low Occurrence of Drivers Loaded on Unique Hosts](./windows/docs/drivers_load_with_low_occurrence_frequency.md) (ES|QL) +- [Excessive RDP Network Activity by Host and User](./windows/docs/excessive_rdp_network_activity_by_source_host_and_user.md) (ES|QL) +- [Excessive SMB Network Activity by Process ID](./windows/docs/excessive_smb_network_activity_by_process_id.md) (ES|QL) +- [Executable File Creation by an Unusual Microsoft Binary](./windows/docs/executable_file_creation_by_an_unusual_microsoft_binary.md) (ES|QL) +- [Frequency of Process Execution via Network Logon by Source Address](./windows/docs/execution_via_network_logon_by_occurrence_frequency_by_top_source_ip.md) (ES|QL) - [Execution via Remote Services by Client Address](./windows/docs/execution_via_remote_services_by_client_address.md) (ES|QL) -- [Execution via Startup with low occurrence frequency](./windows/docs/execution_via_startup_with_low_occurrence_frequency.md) (ES|QL) -- [Execution via Windows Management Instrumentation by occurrence frequency by Unique Agent - Elastic Defend - Sysmon](./windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent_elastic_defend_sysmon.md) (ES|QL) -- [Execution via Windows Management Instrumentation by occurrence frequency - Elastic Defend - Sysmon](./windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon.md) (ES|QL) -- [Execution via Windows Management Instrumentation by occurrence frequency - Elastic Defend - Sysmon - Windows Security](./windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon_windows_security.md) (ES|QL) -- [Execution via Windows Scheduled Task with low occurrence frequency](./windows/docs/execution_via_windows_scheduled_task_with_low_occurrence_frequency.md) (ES|QL) -- [Execution via Windows Services with low occurrence frequency - Elastic Defend - Sysmon](./windows/docs/execution_via_windows_services_with_low_occurrence_frequency_elastic_defend_sysmon.md) (ES|QL) -- [Execution via Windows Services with low occurrence frequency - Windows Security](./windows/docs/execution_via_windows_services_with_low_occurrence_frequency_windows_security.md) (ES|QL) -- [High count of network connection over extended period by process - Elastic Defend Network](./windows/docs/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network.md) (ES|QL) -- [High count of network connection over extended period by process - Elastic Defend Network - Sysmon](./windows/docs/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network_sysmon.md) (ES|QL) -- [High count of network connection over extended period by process - Elastic Defend - Sysmon](./windows/docs/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_sysmon.md) (ES|QL) -- [Libraries loaded by svchost with low occurrence frequency - Elastic Defend](./windows/docs/libraries_loaded_by_svchost_with_low_occurrence_frequency_elastic_defend.md) (ES|QL) -- [Libraries loaded by svchost with low occurrence frequency - Sysmon](./windows/docs/libraries_loaded_by_svchost_with_low_occurrence_frequency_sysmon.md) (ES|QL) -- [Microsoft Office Child Processes with low occurrence frequency](./windows/docs/microsoft_office_child_processes_with_low_occurrence_frequency.md) (ES|QL) -- [Network Discovery via sensitive ports by unusual process](./windows/docs/network_discovery_via_sensitive_ports_by_unusual_process.md) (ES|QL) -- [PE File Transfer via SMB_Admin Shares by Agent](./windows/docs/pe_file_transfer_via_smb_admin_shares_by_agent.md) (ES|QL) -- [PE File Transfer via SMB_Admin Shares by User](./windows/docs/pe_file_transfer_via_smb_admin_shares_by_user.md) (ES|QL) -- [Persistence via Run Key with low occurrence frequency - Elastic Defend](./windows/docs/persistence_via_run_key_with_low_occurrence_frequency_elastic_defend.md) (ES|QL) -- [Persistence via Run Key with low occurrence frequency - Sysmon](./windows/docs/persistence_via_run_key_with_low_occurrence_frequency_sysmon.md) (ES|QL) -- [Persistence via Startup with low occurrence frequency](./windows/docs/persistence_via_startup_with_low_occurrence_frequency.md) (ES|QL) -- [Persistence via Suspicious Launch Agent or Launch Daemon with low occurrence](./windows/docs/persistence_via_suspicious_launch_agent_or_launch_daemon_with_low_occurrence.md) (ES|QL) -- [Potential Exfiltration by process total egress bytes](./windows/docs/potential_exfiltration_by_process_total_egress_bytes.md) (ES|QL) -- [Rundll32 execution aggregated by cmdline](./windows/docs/rundll32_execution_aggregated_by_cmdline.md) (ES|QL) -- [Scheduled tasks creation by action via registry](./windows/docs/scheduled_task_creation_by_action_via_registry.md) (ES|QL) -- [Scheduled tasks creation with low occurrence frequency](./windows/docs/scheduled_tasks_creation_with_low_occurrence_frequency.md) (ES|QL) -- [Suspicious Base64 Encoded PowerShell Command](./windows/docs/suspicious_base64_encoded_powershell_commands.md) (ES|QL) -- [Suspicious DNS TXT Record lookups by process](./windows/docs/suspicious_dns_txt_record_lookups_by_process.md) (ES|QL) -- [Unique Windows Services Creation by ServiceFileName - Elastic Defend Registry - Sysmon](./windows/docs/unique_windows_services_creation_by_servicefilename_elastic_defend_registry_sysmon.md) (ES|QL) -- [Unique Windows Services Creation by ServiceFileName - Elastic Defend - Sysmon Registry](./windows/docs/unique_windows_services_creation_by_servicefilename_elastic_defend_sysmon_registry.md) (ES|QL) -- [Unique Windows Services Creation by ServiceFileName - Windows Security 4697](./windows/docs/unique_windows_services_creation_by_servicefilename_windows_security_4697.md) (ES|QL) -- [Unique Windows Services Creation by ServiceFileName - Windows Security 7045](./windows/docs/unique_windows_services_creation_by_servicefilename_windows_security_7045.md) (ES|QL) -- [Windows Command and Scripting Interpreter from unusual parent](./windows/docs/windows_command_and_scripting_interpreter_from_unusual_parent.md) (ES|QL) -- [Windows logon activity by source IP](./windows/docs/windows_logon_activity_by_source_ip.md) (ES|QL) +- [Startup Execution with Low Occurrence Frequency by Unique Host](./windows/docs/execution_via_startup_with_low_occurrence_frequency.md) (ES|QL) +- [Low Frequency of Process Execution via WMI by Unique Agent](./windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent.md) (ES|QL) +- [Low Frequency of Process Execution via Windows Scheduled Task by Unique Agent](./windows/docs/execution_via_windows_scheduled_task_with_low_occurrence_frequency.md) (ES|QL) +- [Low Occurence of Process Execution via Windows Services with Unique Agent](./windows/docs/execution_via_windows_services_with_low_occurrence_frequency.md) (ES|QL) +- [High Count of Network Connection Over Extended Period by Process](./windows/docs/high_count_of_network_connection_over_extended_period_by_process.md) (ES|QL) +- [Libraries Loaded by svchost with Low Occurrence Frequency](./windows/docs/libraries_loaded_by_svchost_with_low_occurrence_frequency.md) (ES|QL) +- [Microsoft Office Child Processes with Low Occurrence Frequency by Unique Agent](./windows/docs/microsoft_office_child_processes_with_low_occurrence_frequency.md) (ES|QL) +- [Network Discovery via Sensitive Ports by Unusual Process](./windows/docs/network_discovery_via_sensitive_ports_by_unusual_process.md) (ES|QL) +- [PE File Transfer via SMB_Admin Shares by Agent or User](./windows/docs/pe_file_transfer_via_smb_admin_shares_by_agent.md) (ES|QL) +- [Persistence via Run Key with Low Occurrence Frequency](./windows/docs/persistence_via_run_key_with_low_occurrence_frequency.md) (ES|QL) +- [Persistence via Startup with Low Occurrence Frequency by Unique Host](./windows/docs/persistence_via_startup_with_low_occurrence_frequency.md) (ES|QL) +- [Low Occurrence of Suspicious Launch Agent or Launch Daemon](./windows/docs/persistence_via_suspicious_launch_agent_or_launch_daemon_with_low_occurrence.md) (ES|QL) +- [Egress Network Connections with Total Bytes Greater than Threshold](./windows/docs/potential_exfiltration_by_process_total_egress_bytes.md) (ES|QL) +- [Rundll32 Execution Aggregated by Command Line](./windows/docs/rundll32_execution_aggregated_by_cmdline.md) (ES|QL) +- [Scheduled tasks Creation by Action via Registry](./windows/docs/scheduled_task_creation_by_action_via_registry.md) (ES|QL) +- [Scheduled Tasks Creation for Unique Hosts by Task Command](./windows/docs/scheduled_tasks_creation_for_unique_hosts_by_task_command.md) (ES|QL) +- [Suspicious Base64 Encoded Powershell Command](./windows/docs/suspicious_base64_encoded_powershell_commands.md) (ES|QL) +- [Suspicious DNS TXT Record Lookups by Process](./windows/docs/suspicious_dns_txt_record_lookups_by_process.md) (ES|QL) +- [Unique Windows Services Creation by Service File Name](./windows/docs/unique_windows_services_creation_by_servicefilename.md) (ES|QL) +- [Windows Command and Scripting Interpreter from Unusual Parent Process](./windows/docs/windows_command_and_scripting_interpreter_from_unusual_parent.md) (ES|QL) +- [Windows Logon Activity by Source IP](./windows/docs/windows_logon_activity_by_source_ip.md) (ES|QL) diff --git a/hunting/llm/docs/llm_dos_resource_exhaustion_detection.md b/hunting/llm/docs/aws_bedrock_dos_resource_exhaustion_detection.md similarity index 79% rename from hunting/llm/docs/llm_dos_resource_exhaustion_detection.md rename to hunting/llm/docs/aws_bedrock_dos_resource_exhaustion_detection.md index fd01b2365..0f373c33c 100644 --- a/hunting/llm/docs/llm_dos_resource_exhaustion_detection.md +++ b/hunting/llm/docs/aws_bedrock_dos_resource_exhaustion_detection.md @@ -1,10 +1,12 @@ -# Denial of Service or Resource Exhaustion Attacks Detection +# AWS Bedrock LLM Denial-of-Service or Resource Exhaustion --- ## Metadata - **Author:** Elastic +- **Description:** This hunting query identifies unusual spikes in token usage that may indicate malicious attempts to disrupt services. High token usage can strain system resources and degrade performance, aligning with tactics observed in DoS attacks. + - **UUID:** `dc181967-c32c-46c9-b84b-ec4c8811c6a0` - **Integration:** [aws_bedrock.invocation](https://docs.elastic.co/integrations/aws_bedrock) - **Language:** `ES|QL` @@ -29,7 +31,6 @@ from logs-aws_bedrock.invocation-* ## Notes -- This query identifies unusual spikes in token usage that may indicate malicious attempts to disrupt services. High token usage can strain system resources and degrade performance, aligning with tactics observed in DoS attacks. - Consider reviewing the context of high token requests to differentiate between legitimate heavy usage and potential abuse. Monitor the source of requests and patterns over time for better assessment. - Ensure logging and monitoring are correctly configured to capture detailed metrics on token usage. This will facilitate accurate detection and allow for a quick response to potential threats. - Collect evidence from logs that detail the timestamp, user ID, session information, and token counts for incidents flagged by this analytic. This information will be crucial for forensic analysis in the event of a security incident. @@ -40,7 +41,7 @@ from logs-aws_bedrock.invocation-* - https://www.elastic.co/security-labs/elastic-advances-llm-security - https://owasp.org/www-project-top-10-for-large-language-model-applications/ -- [Denial of Service or Resource Exhaustion Attacks Detection](../queries/llm_dos_resource_exhaustion_detection.toml) +- [AWS Bedrock LLM Denial-of-Service or Resource Exhaustion](../queries/aws_bedrock_dos_resource_exhaustion_detection.toml) ## License diff --git a/hunting/llm/docs/llm_latency_anomalies_detection.md b/hunting/llm/docs/aws_bedrock_latency_anomalies_detection.md similarity index 80% rename from hunting/llm/docs/llm_latency_anomalies_detection.md rename to hunting/llm/docs/aws_bedrock_latency_anomalies_detection.md index 5073e1e30..8c3c9b791 100644 --- a/hunting/llm/docs/llm_latency_anomalies_detection.md +++ b/hunting/llm/docs/aws_bedrock_latency_anomalies_detection.md @@ -1,10 +1,12 @@ -# Monitoring for Latency Anomalies +# AWS Bedrock LLM Latency Anomalies --- ## Metadata - **Author:** Elastic +- **Description:** This analytic helps identify delays in LLM responses that are outside expected performance parameters, possibly due to malicious disruptions like DDoS attacks or from operational inefficiencies. + - **UUID:** `3708787b-811b-43b1-b2e7-c7276b8db48c` - **Integration:** [aws_bedrock.invocation](https://docs.elastic.co/integrations/aws_bedrock) - **Language:** `ES|QL` @@ -24,7 +26,6 @@ from logs-aws_bedrock.invocation-* ## Notes -- This analytic helps identify delays in LLM responses that are outside expected performance parameters, possibly due to malicious disruptions like DDoS attacks or from operational inefficiencies. - Review the incidents flagged by this analytic to understand the context and potential sources of latency. This can include network configurations, resource allocation, or external network pressures. - Effective logging and monitoring setup are essential to capture relevant latency metrics accurately. Ensure system clocks and time syncing are properly configured to avoid false positives. - Gather comprehensive logs that detail the request and response timestamps, user IDs, and session details for thorough investigation and evidence collection in case of security incidents. @@ -35,7 +36,7 @@ from logs-aws_bedrock.invocation-* - https://www.elastic.co/security-labs/elastic-advances-llm-security - https://owasp.org/www-project-top-10-for-large-language-model-applications/ -- [Monitoring for Latency Anomalies](../queries/llm_latency_anomalies_detection.toml) +- [AWS Bedrock LLM Latency Anomalies](../queries/aws_bedrock_latency_anomalies_detection.toml) ## License diff --git a/hunting/llm/docs/llm_sensitive_content_refusal_detection.md b/hunting/llm/docs/aws_bedrock_sensitive_content_refusal_detection.md similarity index 78% rename from hunting/llm/docs/llm_sensitive_content_refusal_detection.md rename to hunting/llm/docs/aws_bedrock_sensitive_content_refusal_detection.md index b828a10a9..4def02452 100644 --- a/hunting/llm/docs/llm_sensitive_content_refusal_detection.md +++ b/hunting/llm/docs/aws_bedrock_sensitive_content_refusal_detection.md @@ -1,10 +1,11 @@ -# Sensitive Content Refusal Detection +# AWS Bedrock LLM Sensitive Content Refusals --- ## Metadata - **Author:** Elastic +- **Description:** This analytic flags multiple instances of LLM refusals to respond to sensitive prompts, helping to maintain ethical guidelines and compliance standards. - **UUID:** `8fabae86-7ed2-4006-9623-5db28164f374` - **Integration:** [aws_bedrock.invocation](https://docs.elastic.co/integrations/aws_bedrock) - **Language:** `ES|QL` @@ -24,7 +25,6 @@ from logs-aws_bedrock.invocation-* ## Notes -- This analytic flags multiple instances of LLM refusals to respond to sensitive prompts, helping to maintain ethical guidelines and compliance standards. - Examine flagged interactions for patterns or anomalies in user requests that may indicate malicious intent or probing of model boundaries. - Regularly review and update the phrases that trigger refusals to adapt to new ethical guidelines and compliance requirements. - Ensure that data logs contain enough detail to provide context around the refusal, which will aid in subsequent investigations by security teams. @@ -35,7 +35,7 @@ from logs-aws_bedrock.invocation-* - https://www.elastic.co/security-labs/elastic-advances-llm-security - https://owasp.org/www-project-top-10-for-large-language-model-applications/ -- [Sensitive Content Refusal Detection](../queries/llm_sensitive_content_refusal_detection.toml) +- [AWS Bedrock LLM Sensitive Content Refusals](../queries/aws_bedrock_sensitive_content_refusal_detection.toml) ## License diff --git a/hunting/llm/queries/llm_dos_resource_exhaustion_detection.toml b/hunting/llm/queries/aws_bedrock_dos_resource_exhaustion_detection.toml similarity index 82% rename from hunting/llm/queries/llm_dos_resource_exhaustion_detection.toml rename to hunting/llm/queries/aws_bedrock_dos_resource_exhaustion_detection.toml index 8e693d42f..1288ca5f0 100644 --- a/hunting/llm/queries/llm_dos_resource_exhaustion_detection.toml +++ b/hunting/llm/queries/aws_bedrock_dos_resource_exhaustion_detection.toml @@ -1,11 +1,15 @@ [hunt] author = "Elastic" +description = """ +This hunting query identifies unusual spikes in token usage that may indicate malicious attempts to disrupt services. High token usage can strain system resources and degrade performance, aligning with tactics observed in DoS attacks. +""" integration = ["aws_bedrock.invocation"] uuid = "dc181967-c32c-46c9-b84b-ec4c8811c6a0" -name = "Denial of Service or Resource Exhaustion Attacks Detection" +name = "AWS Bedrock LLM Denial-of-Service or Resource Exhaustion" language = "ES|QL" license = "Elastic License v2" -query = ''' +query = [ +''' from logs-aws_bedrock.invocation-* | WHERE @timestamp > NOW() - 1 DAY AND ( @@ -20,8 +24,8 @@ from logs-aws_bedrock.invocation-* | WHERE request_count > 1 | SORT max_prompt_tokens, max_request_tokens, max_completion_tokens DESC ''' +] notes = [ - "This query identifies unusual spikes in token usage that may indicate malicious attempts to disrupt services. High token usage can strain system resources and degrade performance, aligning with tactics observed in DoS attacks.", "Consider reviewing the context of high token requests to differentiate between legitimate heavy usage and potential abuse. Monitor the source of requests and patterns over time for better assessment.", "Ensure logging and monitoring are correctly configured to capture detailed metrics on token usage. This will facilitate accurate detection and allow for a quick response to potential threats.", "Collect evidence from logs that detail the timestamp, user ID, session information, and token counts for incidents flagged by this analytic. This information will be crucial for forensic analysis in the event of a security incident." diff --git a/hunting/llm/queries/llm_latency_anomalies_detection.toml b/hunting/llm/queries/aws_bedrock_latency_anomalies_detection.toml similarity index 82% rename from hunting/llm/queries/llm_latency_anomalies_detection.toml rename to hunting/llm/queries/aws_bedrock_latency_anomalies_detection.toml index 96e3253cb..52ef8b356 100644 --- a/hunting/llm/queries/llm_latency_anomalies_detection.toml +++ b/hunting/llm/queries/aws_bedrock_latency_anomalies_detection.toml @@ -1,11 +1,15 @@ [hunt] author = "Elastic" +description = """ +This analytic helps identify delays in LLM responses that are outside expected performance parameters, possibly due to malicious disruptions like DDoS attacks or from operational inefficiencies. +""" integration = ["aws_bedrock.invocation"] uuid = "3708787b-811b-43b1-b2e7-c7276b8db48c" -name = "Monitoring for Latency Anomalies" +name = "AWS Bedrock LLM Latency Anomalies" language = "ES|QL" license = "Elastic License v2" -query = ''' +query = [ +''' from logs-aws_bedrock.invocation-* | WHERE @timestamp > NOW() - 1 DAY | EVAL response_delay_seconds = gen_ai.performance.start_response_time / 1000 @@ -15,8 +19,8 @@ from logs-aws_bedrock.invocation-* | WHERE request_count > 3 | SORT max_response_delay DESC ''' +] notes = [ - "This analytic helps identify delays in LLM responses that are outside expected performance parameters, possibly due to malicious disruptions like DDoS attacks or from operational inefficiencies.", "Review the incidents flagged by this analytic to understand the context and potential sources of latency. This can include network configurations, resource allocation, or external network pressures.", "Effective logging and monitoring setup are essential to capture relevant latency metrics accurately. Ensure system clocks and time syncing are properly configured to avoid false positives.", "Gather comprehensive logs that detail the request and response timestamps, user IDs, and session details for thorough investigation and evidence collection in case of security incidents." diff --git a/hunting/llm/queries/llm_sensitive_content_refusal_detection.toml b/hunting/llm/queries/aws_bedrock_sensitive_content_refusal_detection.toml similarity index 82% rename from hunting/llm/queries/llm_sensitive_content_refusal_detection.toml rename to hunting/llm/queries/aws_bedrock_sensitive_content_refusal_detection.toml index 535f8879c..ed5c16dec 100644 --- a/hunting/llm/queries/llm_sensitive_content_refusal_detection.toml +++ b/hunting/llm/queries/aws_bedrock_sensitive_content_refusal_detection.toml @@ -1,11 +1,13 @@ [hunt] author = "Elastic" +description = "This analytic flags multiple instances of LLM refusals to respond to sensitive prompts, helping to maintain ethical guidelines and compliance standards." integration = ["aws_bedrock.invocation"] uuid = "8fabae86-7ed2-4006-9623-5db28164f374" -name = "Sensitive Content Refusal Detection" +name = "AWS Bedrock LLM Sensitive Content Refusals" language = "ES|QL" license = "Elastic License v2" -query = ''' +query = [ +''' from logs-aws_bedrock.invocation-* | WHERE @timestamp > NOW() - 1 DAY AND ( @@ -15,8 +17,8 @@ from logs-aws_bedrock.invocation-* | STATS user_request_count = count() BY gen_ai.user.id | WHERE user_request_count >= 3 ''' +] notes = [ - "This analytic flags multiple instances of LLM refusals to respond to sensitive prompts, helping to maintain ethical guidelines and compliance standards.", "Examine flagged interactions for patterns or anomalies in user requests that may indicate malicious intent or probing of model boundaries.", "Regularly review and update the phrases that trigger refusals to adapt to new ethical guidelines and compliance requirements.", "Ensure that data logs contain enough detail to provide context around the refusal, which will aid in subsequent investigations by security teams." diff --git a/hunting/macos/docs/suspicious_network_connections_by_unsigned_macho.md b/hunting/macos/docs/suspicious_network_connections_by_unsigned_macho.md index 9d689608a..1296c5558 100644 --- a/hunting/macos/docs/suspicious_network_connections_by_unsigned_macho.md +++ b/hunting/macos/docs/suspicious_network_connections_by_unsigned_macho.md @@ -5,6 +5,8 @@ ## Metadata - **Author:** Elastic +- **Description:** This hunt aggregates by process ID and destination IP by the number of connections per hour over a period of time greater than a defined threshold. This may indicate suspicious network connections by unsigned Mach-O binaries. + - **UUID:** `44aff0e3-e0d7-4dca-a94f-2dd0b96f18bd` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) - **Language:** `ES|QL` @@ -14,8 +16,8 @@ ```sql from logs-endpoint.events.network-* | where @timestamp > now() - 7 day -| where host.os.family == "macos" and event.category == "network" and - (process.code_signature.exists == false or process.code_signature.trusted != true) and +| where host.os.family == "macos" and event.category == "network" and + (process.code_signature.exists == false or process.code_signature.trusted != true) and /* excluding private IP ranges */ not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") | keep source.bytes, destination.address, process.name, process.entity_id, @timestamp @@ -29,8 +31,8 @@ from logs-endpoint.events.network-* ## Notes -- This hunt aggregates by process ID and destination IP the number of connections per hour over a period of time greater than a defined threshold. The process paths are scoped to Microsoft signed binaries often injected or used as a lolbin to masquerade malicious execution. This could be a sign of long term network activity to perform command and control from an injected process. -- Hunt can be extended by adding suspicious process paths or lolbins. +- This hunt returns a list of processes by entity_id and name that have a high number of connections per hour over a period of time greater than a defined threshold. +- Pivoting by `process.entity_id` will allow further investigation (parent process, hash, child processes, other network events etc.). ## MITRE ATT&CK Techniques - [T1071](https://attack.mitre.org/techniques/T1071) diff --git a/hunting/macos/queries/suspicious_network_connections_by_unsigned_macho.toml b/hunting/macos/queries/suspicious_network_connections_by_unsigned_macho.toml index b8987070c..1335175eb 100644 --- a/hunting/macos/queries/suspicious_network_connections_by_unsigned_macho.toml +++ b/hunting/macos/queries/suspicious_network_connections_by_unsigned_macho.toml @@ -1,20 +1,24 @@ [hunt] author = "Elastic" +description = """ +This hunt aggregates by process ID and destination IP by the number of connections per hour over a period of time greater than a defined threshold. This may indicate suspicious network connections by unsigned Mach-O binaries. +""" integration = ["endpoint"] uuid = "44aff0e3-e0d7-4dca-a94f-2dd0b96f18bd" name = "Suspicious Network Connections by Unsigned Mach-O" language = "ES|QL" license = "Elastic License v2" notes = [ - "This hunt aggregates by process ID and destination IP the number of connections per hour over a period of time greater than a defined threshold. The process paths are scoped to Microsoft signed binaries often injected or used as a lolbin to masquerade malicious execution. This could be a sign of long term network activity to perform command and control from an injected process.", "Hunt can be extended by adding suspicious process paths or lolbins.", + "This hunt returns a list of processes by entity_id and name that have a high number of connections per hour over a period of time greater than a defined threshold.", + "Pivoting by `process.entity_id` will allow further investigation (parent process, hash, child processes, other network events etc.).", ] mitre = ["T1071"] - -query = ''' +query = [ +''' from logs-endpoint.events.network-* | where @timestamp > now() - 7 day -| where host.os.family == "macos" and event.category == "network" and - (process.code_signature.exists == false or process.code_signature.trusted != true) and +| where host.os.family == "macos" and event.category == "network" and + (process.code_signature.exists == false or process.code_signature.trusted != true) and /* excluding private IP ranges */ not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") | keep source.bytes, destination.address, process.name, process.entity_id, @timestamp @@ -24,4 +28,5 @@ from logs-endpoint.events.network-* | keep process.entity_id, process.name, duration_hours, destination.address, MB_out, count_connections, number_of_con_per_hour /* threshold is set to 120 connections per minute, you can adjust it to your env/FP rate */ | where duration_hours >= 8 and number_of_con_per_hour >= 120 -''' \ No newline at end of file +''' +] diff --git a/hunting/windows/docs/createremotethread_by_source_process_with_low_occurrence.md b/hunting/windows/docs/createremotethread_by_source_process_with_low_occurrence.md index ed7e810f9..f2972c3a4 100644 --- a/hunting/windows/docs/createremotethread_by_source_process_with_low_occurrence.md +++ b/hunting/windows/docs/createremotethread_by_source_process_with_low_occurrence.md @@ -1,10 +1,11 @@ -# CreateRemoteThread by source process with low occurrence +# Low Occurrence Rate of CreateRemoteThread by Source Process --- ## Metadata - **Author:** Elastic +- **Description:** This hunt attempts to identify remote process injection by aggregating Sysmon `CreateRemoteThread` events by source process and returns the ones that we observed in only one unique host. - **UUID:** `0545f23f-84a7-4b88-9b5b-b8cfcfdc9276` - **Integration:** [windows](https://docs.elastic.co/integrations/windows) - **Language:** `ES|QL` @@ -12,7 +13,7 @@ ## Query ```sql -from logs-windows.sysmon_operational-* +from logs-windows.sysmon_operational-* | where @timestamp > now() - 7 day | where host.os.family == "windows" and event.category == "process" and event.action == "CreateRemoteThread" | eval source_process = replace(process.executable, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") @@ -23,8 +24,7 @@ from logs-windows.sysmon_operational-* ## Notes -- This hunt aggregates Sysmon CreateRemoteThread events by source process and returns the ones that we observed in only one unique host. This may indicate remote process injection. -- Adding winlog.event_data.TargetImage to the group by clause can be beneficial but may introduce more legit hits. +- Adding `winlog.event_data.TargetImage` to the aggregation clause can be beneficial but may introduce more false-positives. ## MITRE ATT&CK Techniques - [T1055](https://attack.mitre.org/techniques/T1055) diff --git a/hunting/windows/docs/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries.md b/hunting/windows/docs/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries.md new file mode 100644 index 000000000..554a41cdb --- /dev/null +++ b/hunting/windows/docs/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries.md @@ -0,0 +1,62 @@ +# DLL Hijack via Masquerading as Microsoft Native Libraries + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** This hunt identifies when a process loads a DLL normally located in `System32` or `SysWOW64` folders from an unusual path. Adversaries may execute their own malicious payloads by side-loading malicious DLLs. The host count also should help exclude false-positives by looking at low occurrences when this abnormal behavior is limited to unique agents. +- **UUID:** `87c97865-fdaa-48b2-bfa6-67bed7cf56ef` +- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) +- **Language:** `ES|QL` + +## Query + +```sql +from logs-endpoint.events.library-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.action == "load" and process.code_signature.status == "trusted" and dll.code_signature.status != "trusted" and + not dll.path rlike """[c-fC-F]:\\(Windows|windows|WINDOWS)\\(System32|SysWOW64|system32|syswow64)\\[a-zA-Z0-9_]+.dll""" +| keep dll.name, dll.path, dll.hash.sha256, process.executable, host.id + /* steps how to create DL enrichment policy https://gist.github.com/Samirbous/9f9c3237a0ada745e71cc2ba3425311c */ +| ENRICH libs-policy-defend + /* if the DLL is normally located is system32 or syswow64 folders, native tag will be equal to yes */ +| where native == "yes" and not starts_with(dll.path, "C:\\Windows\\assembly\\NativeImages") + /* normalize paths by removing random patterns */ +| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), + dll_path = replace(dll.path, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| stats host_count = count_distinct(host.id) by dll.name, dll_path, process_path, dll.hash.sha256 +| sort host_count asc +``` + +```sql +from logs-windows.sysmon_operational-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "process" and event.action == "Image loaded" and file.code_signature.status != "Valid" and + not file.path rlike """[c-fC-F]:\\(Windows|windows|WINDOWS)\\(System32|SysWOW64|system32|syswow64)\\[a-zA-Z0-9_]+.dll""" +| keep file.name, file.path, file.hash.sha256, process.executable, host.id + /* steps to create DL enrichment policy https://gist.github.com/Samirbous/9f9c3237a0ada745e71cc2ba3425311c - just replace dll by file */ +| ENRICH libs-policy-sysmon + /* if the DLL is normally located is system32 or syswow64 folders, native tag will be equal to yes */ +| where native == "yes" and not starts_with(file.path, "C:\\Windows\\assembly\\NativeImages") + /* normalize paths by removing random patterns */ +| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), + dll_path = replace(file.path, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| stats host_count = count_distinct(host.id) by file.name, dll_path, process_path, file.hash.sha256 +| sort host_count asc +``` + +## Notes + +- This hunt has two optional queries, one for Elastic Defend data and another for Sysmon data. +- This hunt requires the creation of an [enrichment policy](https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-enrich-data.html) to use with the ES|QL (ENRICH command). +- The `dll.hash.sha256` field can be used to pivot and further investigate the DLL origin and purpose. +- Paths like `C:\Users\Public and C:\ProgramData\` are often observed in malware employing DLL side-loading. +## MITRE ATT&CK Techniques + +- [T1574](https://attack.mitre.org/techniques/T1574) +- [T1574.001](https://attack.mitre.org/techniques/T1574/001) + +## License + +- `Elastic License v2` diff --git a/hunting/windows/docs/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_elastic_defend.md b/hunting/windows/docs/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_elastic_defend.md deleted file mode 100644 index c145a5cd4..000000000 --- a/hunting/windows/docs/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_elastic_defend.md +++ /dev/null @@ -1,43 +0,0 @@ -# Detect DLL Hijack via Masquerading as Microsoft Native Libraries - Elastic Defend - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `87c97865-fdaa-48b2-bfa6-67bed7cf56ef` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.library-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.action == "load" and process.code_signature.status == "trusted" and dll.code_signature.status != "trusted" and - not dll.path rlike """[c-fC-F]:\\(Windows|windows|WINDOWS)\\(System32|SysWOW64|system32|syswow64)\\[a-zA-Z0-9_]+.dll""" -| keep dll.name, dll.path, dll.hash.sha256, process.executable, host.id - /* steps how to create DL enrichment policy https://gist.github.com/Samirbous/9f9c3237a0ada745e71cc2ba3425311c */ -| ENRICH libs-policy-defend - /* if the DLL is normally located is system32 or syswow64 folders, native tag will be equal to yes */ -| where native == "yes" and not starts_with(dll.path, "C:\\Windows\\assembly\\NativeImages") - /* normalize paths by removing random patterns */ -| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), - dll_path = replace(dll.path, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| stats host_count = count_distinct(host.id) by dll.name, dll_path, process_path, dll.hash.sha256 -| sort host_count asc -``` - -## Notes - -- This hunt require the creation of an enrichment policy to use with the ES|QL (ENRICH command). -- The `dll.hash.sha256` field can be used to pivot and further investigate the DLL origin and purpose. -- Paths like C:\Users\Public and C:\ProgramData\ are often observed in malware employing DLL side-loading. -## MITRE ATT&CK Techniques - -- [T1574](https://attack.mitre.org/techniques/T1574) -- [T1574.001](https://attack.mitre.org/techniques/T1574/001) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_sysmon.md b/hunting/windows/docs/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_sysmon.md deleted file mode 100644 index eea9bcfc3..000000000 --- a/hunting/windows/docs/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_sysmon.md +++ /dev/null @@ -1,44 +0,0 @@ -# Detect DLL Hijack via Masquerading as Microsoft Native Libraries - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `68314691-1460-4ac5-ae0d-6b3514e43254` -- **Integration:** [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-windows.sysmon_operational-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.action == "Image loaded" and file.code_signature.status != "Valid" and - not file.path rlike """[c-fC-F]:\\(Windows|windows|WINDOWS)\\(System32|SysWOW64|system32|syswow64)\\[a-zA-Z0-9_]+.dll""" -| keep file.name, file.path, file.hash.sha256, process.executable, host.id - /* steps to create DL enrichment policy https://gist.github.com/Samirbous/9f9c3237a0ada745e71cc2ba3425311c - just replace dll by file */ -| ENRICH libs-policy-sysmon - /* if the DLL is normally located is system32 or syswow64 folders, native tag will be equal to yes */ -| where native == "yes" and not starts_with(file.path, "C:\\Windows\\assembly\\NativeImages") - /* normalize paths by removing random patterns */ -| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), - dll_path = replace(file.path, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| stats host_count = count_distinct(host.id) by file.name, dll_path, process_path, file.hash.sha256 -| sort host_count asc -``` - -## Notes - -- This hunt require the creation of an enrichment policy to use with the ES|QL (ENRICH command). -- Using dll.hash.sha256 for Elastic Defend or file.hash.sha256 for Sysmon you can pivot to further investigate the DLL origin and purpose. -- Paths like C:\Users\Public and C:\ProgramData\ are often observed in malware employing DLL side-loading. -- Process code signature information is not captured in Sysmon Image Load Events (not present in the ES|QL hunt). -## MITRE ATT&CK Techniques - -- [T1574](https://attack.mitre.org/techniques/T1574) -- [T1574.001](https://attack.mitre.org/techniques/T1574/001) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/detect_masquerading_attempts_as_native_windows_binaries.md b/hunting/windows/docs/detect_masquerading_attempts_as_native_windows_binaries.md index 88ce8654b..006061310 100644 --- a/hunting/windows/docs/detect_masquerading_attempts_as_native_windows_binaries.md +++ b/hunting/windows/docs/detect_masquerading_attempts_as_native_windows_binaries.md @@ -1,10 +1,11 @@ -# Detect masquerading attempts as native Windows binaries +# Masquerading Attempts as Native Windows Binaries --- ## Metadata - **Author:** Elastic +- **Description:** This hunt detects processes named as legit Microsoft native binaries located in the system32 folder. Adversaries may attempt to manipulate features of their artifacts to make them appear legitimate or benign to users and/or security tools. Masquerading occurs when the name or location of an object, legitimate or malicious, is manipulated or abused for the sake of evading defenses and observation. - **UUID:** `93a72542-a1f7-4407-9175-8f066343db60` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) - **Language:** `ES|QL` @@ -27,8 +28,8 @@ from logs-endpoint.events.process-* ## Notes -- Output of the query is the process.name and host.id, you can pivot by host.id and process.name(non Microsoft signed) to find the specific suspicious instances. -- Potential false positives include processes with missing code signature details due to enrichment bugs. +- Output of the query is the `process.name` and `host.id` where you can pivot by `host.id` and `process.name` (non Microsoft signed) to find the specific suspicious instances. +- Potential false-positives include processes with missing code signature details due to enrichment bugs. - The queried index must capture process start events with code signature information (e.g. Windows event 4688 is not supported). ## MITRE ATT&CK Techniques diff --git a/hunting/windows/docs/detect_rare_dll_sideload_by_occurrence.md b/hunting/windows/docs/detect_rare_dll_sideload_by_occurrence.md new file mode 100644 index 000000000..af1dc7fbf --- /dev/null +++ b/hunting/windows/docs/detect_rare_dll_sideload_by_occurrence.md @@ -0,0 +1,58 @@ +# Rare DLL Side-Loading by Occurrence + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** This hunt identifies instances where a signed Windows process attempts to load an unsigned DLL from the same process folder. Matches are limited to a unique host with low library load occurrence. Adversaries may execute their own malicious payloads by side-loading malicious DLLs. +- **UUID:** `bcdb7c29-1312-4974-8f2e-10ddeb09cf5c` +- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) +- **Language:** `ES|QL` + +## Query + +```sql +from logs-endpoint.events.library-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.action == "load" and process.code_signature.status == "trusted" and dll.code_signature.status != "trusted" and dll.Ext.relative_file_creation_time <= 86400 +| eval dll_folder = substring(dll.path, 1, length(dll.path) - (length(dll.name) + 1)) +| eval process_folder = substring(process.executable, 1, length(process.executable) - (length(process.name) + 1)) +| where process_folder is not null and dll_folder is not null and process_folder == dll_folder and process.name != dll.name +| eval dll_folder = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), process_folder = replace(process_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval dll_folder = replace(dll_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\"), process_folder = replace(process_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\") +| stats host_count = count_distinct(host.id), total_count = count(*) by dll_folder, dll.name, process.name, dll.hash.sha256 +/* total_count can be adjusted to higher or lower values depending on env */ +| where host_count == 1 and total_count <= 10 | keep total_count, host_count, dll_folder, dll.name, process.name, dll.hash.sha256 +``` + +```sql +from logs-windows.sysmon_operational-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "process" and event.action == "Image loaded" and file.code_signature.status != "Valid" and + not file.path rlike """[c-fC-F]:\\(Windows|windows|WINDOWS)\\(System32|SysWOW64|system32|syswow64)\\[a-zA-Z0-9_]+.dll""" +| eval dll_folder = substring(file.path, 1, length(file.path) - (length(file.name) + 1)) +| eval process_folder = substring(process.executable, 1, length(process.executable) - (length(process.name) + 1)) +| where process_folder is not null and dll_folder is not null and process_folder == dll_folder and file.name != process.name +/* paths normalization by removing random patterns */ +| eval dll_folder = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), process_folder = replace(process_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), dll_folder = replace(dll_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\"), process_folder = replace(process_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\") +| stats host_count = count_distinct(host.id), total_count = count(*) by dll_folder, file.name, process.name, file.hash.sha256 +/* total_count can be adjusted to higher or lower values depending on env */ +| where host_count == 1 and total_count <= 10 +| keep total_count, host_count, dll_folder, file.name, process.name, file.hash.sha256 +``` + +## Notes + +- This hunt has two optional queries, one for Elastic Defend data and another for Sysmon data. +- Based on the returned results you can further investigate suspicious DLLs by sha256 and library path. +- Paths like `C:\\Users\\Public` and `C:\\ProgramData\\` are often observed in malware employing DLL side-loading. +- Elastic Defned DLL Events include `dll.Ext.relative_file_creation_time` which help us limit the hunt to recently dropped DLLs. +## MITRE ATT&CK Techniques + +- [T1574](https://attack.mitre.org/techniques/T1574) +- [T1574.002](https://attack.mitre.org/techniques/T1574/002) + +## License + +- `Elastic License v2` diff --git a/hunting/windows/docs/detect_rare_dll_sideload_by_occurrence_elastic_defend.md b/hunting/windows/docs/detect_rare_dll_sideload_by_occurrence_elastic_defend.md deleted file mode 100644 index eed842ad6..000000000 --- a/hunting/windows/docs/detect_rare_dll_sideload_by_occurrence_elastic_defend.md +++ /dev/null @@ -1,40 +0,0 @@ -# Detect Rare DLL SideLoad by Occurrence - Elastic Defend - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `bcdb7c29-1312-4974-8f2e-10ddeb09cf5c` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.library-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.action == "load" and process.code_signature.status == "trusted" and dll.code_signature.status != "trusted" and dll.Ext.relative_file_creation_time <= 86400 -| eval dll_folder = substring(dll.path, 1, length(dll.path) - (length(dll.name) + 1)) -| eval process_folder = substring(process.executable, 1, length(process.executable) - (length(process.name) + 1)) -| where process_folder is not null and dll_folder is not null and process_folder == dll_folder and process.name != dll.name -| eval dll_folder = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), process_folder = replace(process_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval dll_folder = replace(dll_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\"), process_folder = replace(process_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\") -| stats host_count = count_distinct(host.id), total_count = count(*) by dll_folder, dll.name, process.name, dll.hash.sha256 -/* total_count can be adjusted to higher or lower values depending on env */ -| where host_count == 1 and total_count <= 10 | keep total_count, host_count, dll_folder, dll.name, process.name, dll.hash.sha256 -``` - -## Notes - -- Based on the returned results you can further investigate suspicious DLLs by sha256 and library path. -- Paths like C:\\Users\\Public and C:\\ProgramData\\ are often observed in malware employing DLL side-loading. -- Elastic Defned DLL Events include dll.Ext.relative_file_creation_time which help us limit the hunt to recently dropped DLLs. -## MITRE ATT&CK Techniques - -- [T1574](https://attack.mitre.org/techniques/T1574) -- [T1574.002](https://attack.mitre.org/techniques/T1574/002) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/detect_rare_dll_sideload_by_occurrence_sysmon.md b/hunting/windows/docs/detect_rare_dll_sideload_by_occurrence_sysmon.md deleted file mode 100644 index 6056a0cc5..000000000 --- a/hunting/windows/docs/detect_rare_dll_sideload_by_occurrence_sysmon.md +++ /dev/null @@ -1,42 +0,0 @@ -# Detect Rare DLL SideLoad by Occurrence - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `0df1e142-7d70-4112-be8d-6c60ac812883` -- **Integration:** [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-windows.sysmon_operational-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.action == "Image loaded" and file.code_signature.status != "Valid" and - not file.path rlike """[c-fC-F]:\\(Windows|windows|WINDOWS)\\(System32|SysWOW64|system32|syswow64)\\[a-zA-Z0-9_]+.dll""" -| eval dll_folder = substring(file.path, 1, length(file.path) - (length(file.name) + 1)) -| eval process_folder = substring(process.executable, 1, length(process.executable) - (length(process.name) + 1)) -| where process_folder is not null and dll_folder is not null and process_folder == dll_folder and file.name != process.name -/* paths normalization by removing random patterns */ -| eval dll_folder = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), process_folder = replace(process_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), dll_folder = replace(dll_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\"), process_folder = replace(process_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\") -| stats host_count = count_distinct(host.id), total_count = count(*) by dll_folder, file.name, process.name, file.hash.sha256 -/* total_count can be adjusted to higher or lower values depending on env */ -| where host_count == 1 and total_count <= 10 -| keep total_count, host_count, dll_folder, file.name, process.name, file.hash.sha256 -``` - -## Notes - -- Based on the returned results you can further investigate suspicious DLLs by sha256 and library path. -- Paths like C:\\Users\\Public and C:\\ProgramData\\ are often observed in malware employing DLL side-loading. -- Elastic Defned DLL Events include dll.Ext.relative_file_creation_time which help us limit the hunt to recently dropped DLLs. -## MITRE ATT&CK Techniques - -- [T1574](https://attack.mitre.org/techniques/T1574) -- [T1574.002](https://attack.mitre.org/techniques/T1574/002) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/detect_rare_lsass_process_access_attempts.md b/hunting/windows/docs/detect_rare_lsass_process_access_attempts.md new file mode 100644 index 000000000..f00621d84 --- /dev/null +++ b/hunting/windows/docs/detect_rare_lsass_process_access_attempts.md @@ -0,0 +1,52 @@ +# Rare LSASS Process Access Attempts + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** This hunt identifies instances where a process attempts to open the Local Security Authority Subsystem Service (LSASS) memory and where the number of occurences is limited to one unique agent and a low number of attempts. This may indicate either a rare legitimate condition or a malicious process attempting to obtain credentials or inject code into the LSASS. + +- **UUID:** `3978e183-0b70-4e1c-8c40-24e367f6db5a` +- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) +- **Language:** `ES|QL` + +## Query + +```sql +from logs-endpoint.events.api* +| where @timestamp > NOW() - 7 day +| where event.category == "api" and host.os.family == "windows" and process.Ext.api.name in ("OpenProcess", "OpenThread", "ReadProcessMemory") and + Target.process.name == "lsass.exe" +| keep process.executable.caseless, host.id + /* normalize process paths to reduce known random patterns in process.executable */ +| eval process = replace(process.executable.caseless, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| stats occurences = count(process), agents = count_distinct(host.id) by process +| where agents == 1 and occurences <= 10 +``` + +```sql +from logs-windows.sysmon_operational-* +| where @timestamp > NOW() - 7 day +| where event.category == "process" and host.os.family == "windows" and event.action == "ProcessAccess" and + winlog.event_data.TargetImage in ("C:\\Windows\\system32\\lsass.exe", "c:\\Windows\\system32\\lsass.exe", "c:\\Windows\\System32\\lsass.exe") +| keep process.executable, host.id + /* normalize process paths to reduce known random patterns in process.executable */ +| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval process_path = replace(process_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") +| stats occurences = count(process_path), agents = count_distinct(host.id) by process_path +| where agents == 1 and occurences <= 10 +``` + +## Notes + +- Based on the process.executable and process.name you can pivot and investigate further for the matching instances. +- Potential false-positives include rare legitimate conditions that may trigger this behavior due to third-party software or LSASS crashing. +## MITRE ATT&CK Techniques + +- [T1003](https://attack.mitre.org/techniques/T1003) +- [T1003.001](https://attack.mitre.org/techniques/T1003/001) + +## License + +- `Elastic License v2` diff --git a/hunting/windows/docs/detect_rare_lsass_process_access_attempts_elastic_defend.md b/hunting/windows/docs/detect_rare_lsass_process_access_attempts_elastic_defend.md deleted file mode 100644 index eb78c28ac..000000000 --- a/hunting/windows/docs/detect_rare_lsass_process_access_attempts_elastic_defend.md +++ /dev/null @@ -1,37 +0,0 @@ -# Detect Rare LSASS Process Access Attempts - Elastic Defend - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `3978e183-0b70-4e1c-8c40-24e367f6db5a` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.api* -| where @timestamp > NOW() - 7 day -| where event.category == "api" and host.os.family == "windows" and process.Ext.api.name in ("OpenProcess", "OpenThread", "ReadProcessMemory") and - Target.process.name == "lsass.exe" -| keep process.executable.caseless, host.id - /* normalize process paths to reduce known random patterns in process.executable */ -| eval process = replace(process.executable.caseless, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| stats occurences = count(process), agents = count_distinct(host.id) by process -| where agents == 1 and occurences <= 10 -``` - -## Notes - -- Based on the process.executable and process.name you can pivot and investigate further the matching instances. -- Potential false positives include rare legit condition that may trigger this behavior due to third party software or Lsass crash. -## MITRE ATT&CK Techniques - -- [T1003](https://attack.mitre.org/techniques/T1003) -- [T1003.001](https://attack.mitre.org/techniques/T1003/001) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/detect_rare_lsass_process_access_attempts_sysmon.md b/hunting/windows/docs/detect_rare_lsass_process_access_attempts_sysmon.md deleted file mode 100644 index 90a1d7097..000000000 --- a/hunting/windows/docs/detect_rare_lsass_process_access_attempts_sysmon.md +++ /dev/null @@ -1,38 +0,0 @@ -# Detect Rare LSASS Process Access Attempts - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `3978e183-0b70-4e1c-8c40-24e367f6db5a` -- **Integration:** [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-windows.sysmon_operational-* -| where @timestamp > NOW() - 7 day -| where event.category == "process" and host.os.family == "windows" and event.action == "ProcessAccess" and - winlog.event_data.TargetImage in ("C:\\Windows\\system32\\lsass.exe", "c:\\Windows\\system32\\lsass.exe", "c:\\Windows\\System32\\lsass.exe") -| keep process.executable, host.id - /* normalize process paths to reduce known random patterns in process.executable */ -| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval process_path = replace(process_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") -| stats occurences = count(process_path), agents = count_distinct(host.id) by process_path -| where agents == 1 and occurences <= 10 -``` - -## Notes - -- Based on the process.executable and process.name you can pivot and investigate further the matching instances. -- Potential false positives include rare legit condition that may trigger this behavior due to third party software or Lsass crash. -## MITRE ATT&CK Techniques - -- [T1003](https://attack.mitre.org/techniques/T1003) -- [T1003.001](https://attack.mitre.org/techniques/T1003/001) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/doamin_names_queries_via_lolbins_and_with_low_occurence_frequency.md b/hunting/windows/docs/domain_names_queried_via_lolbins_and_with_low_occurence_frequency.md similarity index 62% rename from hunting/windows/docs/doamin_names_queries_via_lolbins_and_with_low_occurence_frequency.md rename to hunting/windows/docs/domain_names_queried_via_lolbins_and_with_low_occurence_frequency.md index 4f3953036..9ca5fde90 100644 --- a/hunting/windows/docs/doamin_names_queries_via_lolbins_and_with_low_occurence_frequency.md +++ b/hunting/windows/docs/domain_names_queried_via_lolbins_and_with_low_occurence_frequency.md @@ -1,10 +1,12 @@ -# Doamin Names queries via Lolbins and with low occurence frequency +# DNS Queries via LOLBins with Low Occurence Frequency --- ## Metadata - **Author:** Elastic +- **Description:** This hunt looks for DNS queries performed by commonly abused Microsoft binaries that perform remote file transfer or binary proxy execution. Aggregations for the number of occurrences is limited to one host to reduce the number of potentially legitimate hits. + - **UUID:** `ebf8eb13-c98a-4d2c-8bdb-3f72a3a3961b` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) - **Language:** `ES|QL` @@ -13,9 +15,9 @@ ```sql from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day and host.os.family == "windows" and event.category == "network" and - event.action in ("lookup_requested", "DNSEvent (DNS query)") and - process.name in ("powershell.exe", "rundll32.exe", "certutil.exe", "curl.exe", "wget.exe", "CertReq.exe", "bitsadmin.exe", "mshta.exe", "pwsh.exe", "wmic.exe", "wscript.exe", "cscript.exe", "msbuild.exe", "regsvr32.exe", "MSBuild.exe", "InstallUtil.exe", "RegAsm.exe", "RegSvcs.exe", "msxsl.exe", "CONTROL.EXE", "Microsoft.Workflow.Compiler.exe", "msiexec.exe") and dns.question.name rlike """.+\.[a-z-A-Z]{2,3}""" +| where @timestamp > now() - 7 day and host.os.family == "windows" and event.category == "network" and + event.action in ("lookup_requested", "DNSEvent (DNS query)") and + process.name in ("powershell.exe", "rundll32.exe", "certutil.exe", "curl.exe", "wget.exe", "CertReq.exe", "bitsadmin.exe", "mshta.exe", "pwsh.exe", "wmic.exe", "wscript.exe", "cscript.exe", "msbuild.exe", "regsvr32.exe", "MSBuild.exe", "InstallUtil.exe", "RegAsm.exe", "RegSvcs.exe", "msxsl.exe", "CONTROL.EXE", "Microsoft.Workflow.Compiler.exe", "msiexec.exe") and dns.question.name rlike """.+\.[a-z-A-Z]{2,3}""" | keep process.name, dns.question.name, host.id | stats occurrences = count(*), hosts = count_distinct(host.id) by process.name, dns.question.name | where hosts == 1 @@ -23,8 +25,8 @@ from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* ## Notes -- Utilities like curl and SSL verification web-servvices are noisy, while others are rare like scripting utilities and are worth further investigation. -- Connection to legit domains like github, discord, telegram and many other legit web-services by lolbins is still suspicious and require further investigation. +- Utilities like curl and SSL verification for web services are noisy, while others are rare such as scripting utilities and are worth further investigation. +- Connection to legit domains like Github, Discord, Telegram and many other legit web services by LOLBins is still suspicious and require further investigation. ## MITRE ATT&CK Techniques - [T1071](https://attack.mitre.org/techniques/T1071) diff --git a/hunting/windows/docs/drivers_load_with_low_occurrence_frequency.md b/hunting/windows/docs/drivers_load_with_low_occurrence_frequency.md new file mode 100644 index 000000000..8167a99d2 --- /dev/null +++ b/hunting/windows/docs/drivers_load_with_low_occurrence_frequency.md @@ -0,0 +1,57 @@ +# Low Occurrence of Drivers Loaded on Unique Hosts + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** This hunt helps identify drivers loaded once on a unique host and with a unique hash over a 15 day period of time. Advanced adversaries may leverage legit vulnerable driver to tamper with existing defences or execute code in Kernel mode. + +- **UUID:** `99818ad6-c242-4da7-a41a-df64fe7314d6` +- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows), [system](https://docs.elastic.co/integrations/system) +- **Language:** `ES|QL` + +## Query + +```sql +from logs-endpoint.events.library-* +| where @timestamp > now() - 15 day +| where host.os.family == "windows" and event.category == "driver" and event.action == "load" and dll.Ext.relative_file_creation_time <= 900 +| stats host_count = count_distinct(host.id), total_count = count(*), hash_count = count_distinct(dll.hash.sha256) by dll.name, dll.pe.imphash +| where host_count == 1 and total_count == 1 and hash_count == 1 +``` + +```sql +from logs-windows.sysmon_operational-* +| where @timestamp > now() - 15 day +| where host.os.family == "windows" and event.category == "driver" +| stats host_count = count_distinct(host.id), total_count = count(*), hash_count = count_distinct(file.hash.sha256) by file.name +| where host_count == 1 and total_count == 1 and hash_count == 1 +``` + +```sql +from logs-system.system-* +| where @timestamp > now() - 15day +| where host.os.family == "windows" and event.code == "7045" and + winlog.event_data.ServiceType == "kernel mode driver" +| eval ServiceFileName = replace(winlog.event_data.ImagePath, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") +| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName +| where hosts == 1 and cc == 1 +``` + +## Notes + +- This hunt has three optional queries, one for Elastic Defend data, another for Sysmon data and the last one for Windows 7045 events. +- Further investigation can be done pivoting by `dll.pe.imphash` or `dll.name.` +- `dll.Ext.relative_file_creation_time` is used in the first query to limit the result to recently dropped drivers (populated in Elastic Defend). +- Aggregation can also be done by `dll.hash.sha256` / `file.hash.sha256` but will return more results. +- Bring Your Own Vulnerable Driver (BYOVD) are all signed and not malicious, further investigation should be done to check the surrounding events (service creation, process that dropped the driver etc.). +## MITRE ATT&CK Techniques + +- [T1068](https://attack.mitre.org/techniques/T1068) + +## License + +- `Elastic License v2` diff --git a/hunting/windows/docs/drivers_load_with_low_occurrence_frequency_elastic_defend.md b/hunting/windows/docs/drivers_load_with_low_occurrence_frequency_elastic_defend.md deleted file mode 100644 index 3ea91098b..000000000 --- a/hunting/windows/docs/drivers_load_with_low_occurrence_frequency_elastic_defend.md +++ /dev/null @@ -1,34 +0,0 @@ -# Drivers Load with low occurrence frequency - Elastic Defend - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `99818ad6-c242-4da7-a41a-df64fe7314d6` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.library-* -| where @timestamp > now() - 15 day -| where host.os.family == "windows" and event.category == "driver" and event.action == "load" and dll.Ext.relative_file_creation_time <= 900 -| stats host_count = count_distinct(host.id), total_count = count(*), hash_count = count_distinct(dll.hash.sha256) by dll.name, dll.pe.imphash -| where host_count == 1 and total_count == 1 and hash_count == 1 -``` - -## Notes - -- This hunt helps identify drivers loaded once, on a unique host and with a unique hash over a 15 days period of time. Further investigation can be done pivoting by dll.pe.imphash or dll.name. Advanced adversaries may leverage legit vulnerable driver to tamper with existing defences or execute code in Kernel mode. -- dll.Ext.relative_file_creation_time is used in the first query to limit the result to recently dropped drivers (populated in Elastic Defend). -- aggregation can be done also by dll.hash.sha256 / file.hash.sha256 but will return more results. -- Bring Your Own Vulnerable Driver (BYOVD) are all signed and not malicious, further investigation should be done to check the surrounding events (service creation, process that dropped the driver etc.). -## MITRE ATT&CK Techniques - -- [T1068](https://attack.mitre.org/techniques/T1068) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/drivers_load_with_low_occurrence_frequency_sysmon.md b/hunting/windows/docs/drivers_load_with_low_occurrence_frequency_sysmon.md deleted file mode 100644 index 8fbd8a21b..000000000 --- a/hunting/windows/docs/drivers_load_with_low_occurrence_frequency_sysmon.md +++ /dev/null @@ -1,34 +0,0 @@ -# Drivers Load with low occurrence frequency - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `6bb90aba-af6b-4128-a9b2-160e164a15ff` -- **Integration:** [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-windows.sysmon_operational-* -| where @timestamp > now() - 15 day -| where host.os.family == "windows" and event.category == "driver" -| stats host_count = count_distinct(host.id), total_count = count(*), hash_count = count_distinct(file.hash.sha256) by file.name -| where host_count == 1 and total_count == 1 and hash_count == 1 -``` - -## Notes - -- This hunt helps identify drivers loaded once, on a unique host and with a unique hash over a 15 days period of time. Further investigation can be done pivoting by dll.pe.imphash or dll.name. Advanced adversaries may leverage legit vulnerable driver to tamper with existing defences or execute code in Kernel mode. -- dll.Ext.relative_file_creation_time is used in the first query to limit the result to recently dropped drivers (populated in Elastic Defend). -- aggregation can be done also by dll.hash.sha256 / file.hash.sha256 but will return more results. -- Bring Your Own Vulnerable Driver (BYOVD) are all signed and not malicious, further investigation should be done to check the surrounding events (service creation, process that dropped the driver etc.). -## MITRE ATT&CK Techniques - -- [T1068](https://attack.mitre.org/techniques/T1068) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/drivers_load_with_low_occurrence_frequency_windows_7045.md b/hunting/windows/docs/drivers_load_with_low_occurrence_frequency_windows_7045.md deleted file mode 100644 index 4dbfe25f8..000000000 --- a/hunting/windows/docs/drivers_load_with_low_occurrence_frequency_windows_7045.md +++ /dev/null @@ -1,38 +0,0 @@ -# Drivers Load with low occurrence frequency - Windows 7045 - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `bc4848ce-5323-42b4-a559-3333c11ca938` -- **Integration:** [system](https://docs.elastic.co/integrations/system) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-system.system-* -| where @timestamp > now() - 15day -| where host.os.family == "windows" and event.code == "7045" and - winlog.event_data.ServiceType == "kernel mode driver" -| eval ServiceFileName = replace(winlog.event_data.ImagePath, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") -| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName -| where hosts == 1 and cc == 1 -``` - -## Notes - -- This hunt helps identify drivers loaded once, on a unique host and with a unique hash over a 15 days period of time. Further investigation can be done pivoting by dll.pe.imphash or dll.name. Advanced adversaries may leverage legit vulnerable driver to tamper with existing defences or execute code in Kernel mode. -- dll.Ext.relative_file_creation_time is used in the first query to limit the result to recently dropped drivers (populated in Elastic Defend). -- aggregation can be done also by dll.hash.sha256 / file.hash.sha256 but will return more results. -- Bring Your Own Vulnerable Driver (BYOVD) are all signed and not malicious, further investigation should be done to check the surrounding events (service creation, process that dropped the driver etc.). -## MITRE ATT&CK Techniques - -- [T1068](https://attack.mitre.org/techniques/T1068) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/excessive_rdp_network_activity_by_source_host_and_user-_elastic_defend_sysmon.md b/hunting/windows/docs/excessive_rdp_network_activity_by_source_host_and_user-_elastic_defend_sysmon.md deleted file mode 100644 index e518b168b..000000000 --- a/hunting/windows/docs/excessive_rdp_network_activity_by_source_host_and_user-_elastic_defend_sysmon.md +++ /dev/null @@ -1,37 +0,0 @@ -# Excessive RDP Network Activity by Source Host and User- Elastic Defend - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `fe01a8a5-6367-4c4c-a57b-be513ab80e42` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "network" and network.direction == "egress" and - network.transport == "tcp"and destination.port == 3389 and source.port >= 49152 -| keep destination.ip, host.id, user.name -| stats count_unique_dst = count_distinct(destination.ip) by host.id, user.name - /* threshold set to 10 but can be adjusted to reduce normal baseline in your env */ -| where count_unique_dst >= 10 -``` - -## Notes - -- This hunt looks for high number of Remote Desktop connections from same host and user.name to more than a defined threshold of unique destination Ip addresses. This could be a sign of discovery or lateral movement via the Remote Desktop Protocol. -- Further investigation can done pivoting by host.id and user name. -- Depending on normal SysAdmin RDP activity the 10 threshold can be adjusted to reduce normal noisy activity. -## MITRE ATT&CK Techniques - -- [T1021](https://attack.mitre.org/techniques/T1021) -- [T1021.001](https://attack.mitre.org/techniques/T1021/001) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/excessive_rdp_network_activity_by_source_host_and_user.md b/hunting/windows/docs/excessive_rdp_network_activity_by_source_host_and_user.md new file mode 100644 index 000000000..8fb9f6ab9 --- /dev/null +++ b/hunting/windows/docs/excessive_rdp_network_activity_by_source_host_and_user.md @@ -0,0 +1,48 @@ +# Excessive RDP Network Activity by Host and User + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** This hunt looks for a high occurrence of remote desktop connections from the same host and user. The number of unique destination IP addresses is compared to a defined threshold. This could be a sign of discovery or lateral movement via the Remote Desktop Protocol (RDP). + +- **UUID:** `fe01a8a5-6367-4c4c-a57b-be513ab80e42` +- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) +- **Language:** `ES|QL` + +## Query + +```sql +from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "network" and network.direction == "egress" and + network.transport == "tcp"and destination.port == 3389 and source.port >= 49152 +| keep destination.ip, host.id, user.name +| stats count_unique_dst = count_distinct(destination.ip) by host.id, user.name + /* threshold set to 10 but can be adjusted to reduce normal baseline in your env */ +| where count_unique_dst >= 10 +``` + +```sql +from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "network" and process.name == "svchost.exe" and network.direction == "ingress" and + network.transport == "tcp"and destination.port == 3389 and source.port >= 49152 +| stats agents = count_distinct(host.id) by source.ip +| where agents >= 10 +``` + +## Notes + +- Further investigation can done pivoting by `host.id` and `user.name`. +- Depending on normal SysAdmin RDP activity, the threshold of 10 can be adjusted to reduce normal noisy activity. +- The second query uses Windows Security log event ID 4624 to summarize numbers of RDP connections by `source.ip` and `user.name` and duration. +## MITRE ATT&CK Techniques + +- [T1021](https://attack.mitre.org/techniques/T1021) +- [T1021.001](https://attack.mitre.org/techniques/T1021/001) + +## License + +- `Elastic License v2` diff --git a/hunting/windows/docs/excessive_rdp_network_activity_by_source_host_elastic_defend_sysmon.md b/hunting/windows/docs/excessive_rdp_network_activity_by_source_host_elastic_defend_sysmon.md deleted file mode 100644 index b44024fb7..000000000 --- a/hunting/windows/docs/excessive_rdp_network_activity_by_source_host_elastic_defend_sysmon.md +++ /dev/null @@ -1,35 +0,0 @@ -# Excessive RDP Network Activity by Source Host - Elastic Defend - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `6ff3a518-3bf4-4e7d-9a66-2ef7aaa68cfc` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "network" and process.name == "svchost.exe" and network.direction == "ingress" and - network.transport == "tcp"and destination.port == 3389 and source.port >= 49152 -| stats agents = count_distinct(host.id) by source.ip -| where agents >= 10 -``` - -## Notes - -- This hunt looks for high number of Remote Desktop connections from same host and user.name to more than a defined threshold of unique destination Ip addresses. This could be a sign of discovery or lateral movement via the Remote Desktop Protocol. -- Further investigation can done pivoting by host.id and user name. -- Depending on normal SysAdmin RDP activity the 10 threshold can be adjusted to reduce normal noisy activity. -## MITRE ATT&CK Techniques - -- [T1021](https://attack.mitre.org/techniques/T1021) -- [T1021.001](https://attack.mitre.org/techniques/T1021/001) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/excessive_smb_network_activity_by_process_id.md b/hunting/windows/docs/excessive_smb_network_activity_by_process_id.md index f02bee6dc..27b6f75bf 100644 --- a/hunting/windows/docs/excessive_smb_network_activity_by_process_id.md +++ b/hunting/windows/docs/excessive_smb_network_activity_by_process_id.md @@ -1,10 +1,12 @@ -# Excessive SMB Network Activity by process Id +# Excessive SMB Network Activity by Process ID --- ## Metadata - **Author:** Elastic +- **Description:** This hunt looks for a high occurrence of SMB connections from the same process by unique destination IP addresses. The number of unique destination IP addresses is compared to a defined threshold. This could be a sign of SMB scanning or lateral movement via remote services that depend on the SMB protocol. + - **UUID:** `6949135b-76d7-47a3-ae95-ef482508fb7c` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) - **Language:** `ES|QL` @@ -12,9 +14,9 @@ ## Query ```sql -from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day and - host.os.family == "windows" and event.category == "network" and network.direction == "egress" and +from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day and + host.os.family == "windows" and event.category == "network" and network.direction == "egress" and network.transport == "tcp"and destination.port == 445 and source.port >= 49152 and process.pid == 4 | keep destination.ip, process.entity_id, host.id | stats count_unique_dst = count_distinct(destination.ip) by process.entity_id, host.id @@ -24,9 +26,8 @@ from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* ## Notes -- This hunt looks for high number of SMB connections from same process to more than a defined threshold of unique destination Ip addresses. This could be a sign of SMB scanning or some lateral movement via remote services that depend on SMB protocol. -- Further investigation can done pivoting by process.entity_id and host.id. -- Maximum number of unique destination.ip by process can be adjusted to your environment to reduce normal noisy hosts by Id. +- Further investigation can done pivoting by `process.entity_id` and `host.id.` +- Maximum number of unique `destination.ip` by process can be adjusted to your environment to reduce normal noisy hosts by process ID. ## MITRE ATT&CK Techniques - [T1021](https://attack.mitre.org/techniques/T1021) diff --git a/hunting/windows/docs/executable_file_creation_by_an_unusual_microsoft_binary.md b/hunting/windows/docs/executable_file_creation_by_an_unusual_microsoft_binary.md new file mode 100644 index 000000000..60e64edda --- /dev/null +++ b/hunting/windows/docs/executable_file_creation_by_an_unusual_microsoft_binary.md @@ -0,0 +1,51 @@ +# Executable File Creation by an Unusual Microsoft Binary + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** This hunt identifies executable file creation by an unusual Microsoft native binary. This could be the result of +code injection or some other form of exploitation for defense evasion. + +- **UUID:** `3b2900fe-74d9-4c49-b3df-cbeceb02e841` +- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) +- **Language:** `ES|QL` + +## Query + +```sql +from logs-endpoint.events.file-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "file" and event.action == "creation" and + starts_with(file.Ext.header_bytes, "4d5a") and process.code_signature.status == "trusted" and + starts_with(process.code_signature.subject_name, "Microsoft") and process.executable rlike """[c-fC-F]:\\Windows\\(System32|SysWOW64)\\[a-zA-Z0-9_]+.exe""" +| keep process.executable, host.id +| stats occurences = count(*), agents = count_distinct(host.id) by process.executable +| where agents == 1 and occurences <= 10 +``` + +```sql +from logs-windows.sysmon_operational-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "file" and event.action == "FileCreate" and + file.extension in ("exe", "dll") and process.executable rlike """[c-fC-F]:\\Windows\\(System32|SysWOW64)\\[a-zA-Z0-9_]+.exe""" +| keep process.executable, host.id +| stats occurences = count(*), agents = count_distinct(host.id) by process.executable +| where agents == 1 and occurences <= 10 +``` + +## Notes + +- This hunt includes two optional queries, one for Elastic Defend data and another for Sysmon data. +- Sysmon file events don't populate file header and process code signature information thus we use `file.extension`. +- Some exploits may result in the creation of an executable file by the exploited process. +- Further investigation can be done by pivoting on `process.executable` and filtering for executable file creation. +## MITRE ATT&CK Techniques + +- [T1211](https://attack.mitre.org/techniques/T1211) +- [T1055](https://attack.mitre.org/techniques/T1055) + +## License + +- `Elastic License v2` diff --git a/hunting/windows/docs/executable_file_creation_by_an_unusual_microsoft_binary_elastic_defend.md b/hunting/windows/docs/executable_file_creation_by_an_unusual_microsoft_binary_elastic_defend.md deleted file mode 100644 index 630befd53..000000000 --- a/hunting/windows/docs/executable_file_creation_by_an_unusual_microsoft_binary_elastic_defend.md +++ /dev/null @@ -1,37 +0,0 @@ -# Executable File creation by an Unusual Microsoft Binary - Elastic Defend - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `3b2900fe-74d9-4c49-b3df-cbeceb02e841` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.file-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "file" and event.action == "creation" and - starts_with(file.Ext.header_bytes, "4d5a") and process.code_signature.status == "trusted" and - starts_with(process.code_signature.subject_name, "Microsoft") and process.executable rlike """[c-fC-F]:\\Windows\\(System32|SysWOW64)\\[a-zA-Z0-9_]+.exe""" -| keep process.executable, host.id -| stats occurences = count(*), agents = count_distinct(host.id) by process.executable -| where agents == 1 and occurences <= 10 -``` - -## Notes - -- Sysmon file event don't populate file header and process code signature information thus the use of file.extension. -- Some exploits may result in the creation of an executable file by the exploited process. -- Further investigation can be done pivoting by process.executable and filter for executable file creation. -## MITRE ATT&CK Techniques - -- [T1211](https://attack.mitre.org/techniques/T1211) -- [T1055](https://attack.mitre.org/techniques/T1055) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/executable_file_creation_by_an_unusual_microsoft_binary_sysmon.md b/hunting/windows/docs/executable_file_creation_by_an_unusual_microsoft_binary_sysmon.md deleted file mode 100644 index 2a0ef394a..000000000 --- a/hunting/windows/docs/executable_file_creation_by_an_unusual_microsoft_binary_sysmon.md +++ /dev/null @@ -1,36 +0,0 @@ -# Executable File creation by an Unusual Microsoft Binary - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `36c94354-9d6e-4dc5-b2aa-a7cf578a4169` -- **Integration:** [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-windows.sysmon_operational-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "file" and event.action == "FileCreate" and - file.extension in ("exe", "dll") and process.executable rlike """[c-fC-F]:\\Windows\\(System32|SysWOW64)\\[a-zA-Z0-9_]+.exe""" -| keep process.executable, host.id -| stats occurences = count(*), agents = count_distinct(host.id) by process.executable -| where agents == 1 and occurences <= 10 -``` - -## Notes - -- Sysmon file event don't populate file header and process code signature information thus the use of file.extension. -- Some exploits may result in the creation of an executable file by the exploited process. -- Further investigation can be done pivoting by process.executable and filter for executable file creation. -## MITRE ATT&CK Techniques - -- [T1211](https://attack.mitre.org/techniques/T1211) -- [T1055](https://attack.mitre.org/techniques/T1055) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/execution_via_network_logon_by_occurrence_frequency.md b/hunting/windows/docs/execution_via_network_logon_by_occurrence_frequency.md deleted file mode 100644 index 0626e574e..000000000 --- a/hunting/windows/docs/execution_via_network_logon_by_occurrence_frequency.md +++ /dev/null @@ -1,36 +0,0 @@ -# Execution via Network Logon by occurrence frequency - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `fd3f9982-fd8c-4f0f-bbe6-e589752c34db` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.process-* -| where @timestamp > now() - 7 day and host.os.family == "windows" and - event.category == "process" and event.action == "start" and - /* network logon type and the execution is within 30 seconds of the logon time */ - process.Ext.session_info.logon_type == "Network" and process.Ext.session_info.relative_logon_time <= 30 -| stats total = count(*), hosts = count_distinct(host.id) by process.hash.sha256, process.Ext.session_info.client_address, user.name, process.parent.name - /* unique hash limited to one host and number of execution is 1 */ -| where hosts == 1 and total == 1 -``` - -## Notes - -- process.Ext.session_info.* is populated for Elastic Defend version 8.6 and above. -- Execution via legit Microsoft processes like powershell and cmd need to further investigated via aggregation by process.command_line. -- Aggregation can be also done by process.executable, normalizing process path by removing random patterns using the REPLACE function via regex. -## MITRE ATT&CK Techniques - -- [T1021](https://attack.mitre.org/techniques/T1021) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/execution_via_network_logon_by_occurrence_frequency_by_top_source_ip.md b/hunting/windows/docs/execution_via_network_logon_by_occurrence_frequency_by_top_source_ip.md index fc27d3f52..818f73aea 100644 --- a/hunting/windows/docs/execution_via_network_logon_by_occurrence_frequency_by_top_source_ip.md +++ b/hunting/windows/docs/execution_via_network_logon_by_occurrence_frequency_by_top_source_ip.md @@ -1,10 +1,12 @@ -# Execution via Network Logon by occurrence frequency by top Source IP +# Frequency of Process Execution via Network Logon by Source Address --- ## Metadata - **Author:** Elastic +- **Description:** This hunt aggregates process execution and remote network logons by source address, account name and process hash. It then limits the results by unique host within a 7 day period. This may indicate lateral movement via remote services. + - **UUID:** `ae07c580-290e-4421-add8-d6ca30509b6a` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) - **Language:** `ES|QL` @@ -13,8 +15,8 @@ ```sql from logs-endpoint.events.process-* -| where @timestamp > now() - 7 day and host.os.family == "windows" and - event.category == "process" and event.action == "start" and +| where @timestamp > now() - 7 day and host.os.family == "windows" and + event.category == "process" and event.action == "start" and /* network logon type and the execution is within 30 seconds of the logon time */ process.Ext.session_info.logon_type == "Network" and process.Ext.session_info.relative_logon_time <= 30 | stats total = count(*) by process.Ext.session_info.client_address, user.name @@ -22,11 +24,23 @@ from logs-endpoint.events.process-* | sort total desc ``` +```sql +from logs-endpoint.events.process-* +| where @timestamp > now() - 7 day and host.os.family == "windows" and + event.category == "process" and event.action == "start" and + /* network logon type and the execution is within 30 seconds of the logon time */ + process.Ext.session_info.logon_type == "Network" and process.Ext.session_info.relative_logon_time <= 30 +| stats total = count(*), hosts = count_distinct(host.id) by process.hash.sha256, process.Ext.session_info.client_address, user.name, process.parent.name + /* unique hash limited to one host and number of execution is 1 */ +| where hosts == 1 and total == 1 +``` + ## Notes -- process.Ext.session_info.* is populated for Elastic Defend version 8.6 and above. -- Execution via legit Microsoft processes like powershell and cmd need to further investigated via aggregation by process.command_line. -- Aggregation can be also done by process.executable, normalizing process path by removing random patterns using the REPLACE function via regex. +- The second query highest occurrence of source addresses/accounts performing remote process execution +- `process.Ext.session_info.*` is populated for Elastic Defend versions 8.6.0+ and above. +- Execution via legitimate Microsoft processes for PowerShell and cmd need to be further investigated via aggregation by `process.command_line`. +- Aggregation can be also done by `process.executable`, normalizing process path by removing random patterns using the ES|QL REPLACE function. ## MITRE ATT&CK Techniques - [T1021](https://attack.mitre.org/techniques/T1021) diff --git a/hunting/windows/docs/execution_via_remote_services_by_client_address.md b/hunting/windows/docs/execution_via_remote_services_by_client_address.md index 91f0d2c55..94dc4e1dd 100644 --- a/hunting/windows/docs/execution_via_remote_services_by_client_address.md +++ b/hunting/windows/docs/execution_via_remote_services_by_client_address.md @@ -5,6 +5,8 @@ ## Metadata - **Author:** Elastic +- **Description:** This hunt aggregates process execution via remote network logon by source address, account name and where the parent process is related to remote services such as WMI, WinRM, DCOM and remote PowerShell. This may indicate lateral movement via remote services. + - **UUID:** `e6e54717-2676-4785-a4a6-503577bfb0ea` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) - **Language:** `ES|QL` @@ -13,10 +15,10 @@ ```sql from logs-endpoint.events.process-* -| where @timestamp > now() - 7 day and host.os.family == "windows" and - event.category == "process" and event.action == "start" and +| where @timestamp > now() - 7 day and host.os.family == "windows" and + event.category == "process" and event.action == "start" and /* network logon type */ - process.Ext.session_info.logon_type == "Network" and + process.Ext.session_info.logon_type == "Network" and (process.parent.name .caseless in ("wmiprvse.exe", "wsmprovhost.exe", "winrshost.exe") or (process.parent.name == "svchost.exe" and process.parent.args == "DcomLaunch")) | stats total = count(*), hosts = count_distinct(host.id) by process.Ext.session_info.client_address, user.name, process.parent.name /* sort by top source.ip and account */ @@ -25,7 +27,7 @@ from logs-endpoint.events.process-* ## Notes -- process.Ext.session_info.* is populated for Elastic Defend version 8.6 and above. +- `process.Ext.session_info.*` is populated for Elastic Defend versions 8.6.0+. ## MITRE ATT&CK Techniques - [T1021](https://attack.mitre.org/techniques/T1021) diff --git a/hunting/windows/docs/execution_via_startup_with_low_occurrence_frequency.md b/hunting/windows/docs/execution_via_startup_with_low_occurrence_frequency.md index 6b2c4a68d..845b308f9 100644 --- a/hunting/windows/docs/execution_via_startup_with_low_occurrence_frequency.md +++ b/hunting/windows/docs/execution_via_startup_with_low_occurrence_frequency.md @@ -1,10 +1,12 @@ -# Execution via Startup with low occurrence frequency +# Startup Execution with Low Occurrence Frequency by Unique Host --- ## Metadata - **Author:** Elastic +- **Description:** This hunt identifies programs started shortly after user logon and presence limited to a unique host. Run registry key and Startup folder cause programs to run each time that a user logs on and are often abused by malwares to maintain persistence on an endpoint. + - **UUID:** `a447df80-d3d5-48b3-a175-a864264ec487` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) - **Language:** `ES|QL` @@ -13,10 +15,10 @@ ```sql from logs-endpoint.events.process-* -| where host.os.family == "windows" and event.category == "process" and event.action == "start" and +| where host.os.family == "windows" and event.category == "process" and event.action == "start" and /* programs started shortly after user logon like startup items */ - process.parent.executable.caseless == "c:\\windows\\explorer.exe" and process.Ext.session_info.relative_logon_time <= 100 and - not starts_with(process.executable, "C:\\Program Files") and not starts_with(process.executable, "C:\\Windows\\System32\\DriverStore\\FileRepository\\") and + process.parent.executable.caseless == "c:\\windows\\explorer.exe" and process.Ext.session_info.relative_logon_time <= 100 and + not starts_with(process.executable, "C:\\Program Files") and not starts_with(process.executable, "C:\\Windows\\System32\\DriverStore\\FileRepository\\") and /* this hunt is scoped to unsigned or untrusted code-sig or Microsoft signed binaries to not miss lolbins */ (process.code_signature.exists == false or process.code_signature.trusted == false or starts_with(process.code_signature.subject_name, "Microsoft")) | keep process.executable, host.id, process.hash.sha256 @@ -28,8 +30,8 @@ from logs-endpoint.events.process-* ## Notes -- Items set to persist via Startup like Run key and Startup folder will be executed by Explorer.exe shortly after user logon (process.Ext.session_info.relative_logon_time help us to capture that time difference). -- Pay close attention to unknown hashes, suspicious paths and lolbins. +- Items set to persist via Startup such as Run keys and Startup folder will be executed by `Explorer.exe` shortly after user logon (`process.Ext.session_info.relative_logon_time` helps us to capture that time difference). +- Special attention to unknown hashes, suspicious paths and LOLBins should be given. ## MITRE ATT&CK Techniques - [T1547](https://attack.mitre.org/techniques/T1547) diff --git a/hunting/windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent.md b/hunting/windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent.md new file mode 100644 index 000000000..7df963b1f --- /dev/null +++ b/hunting/windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent.md @@ -0,0 +1,58 @@ +# Low Frequency of Process Execution via WMI by Unique Agent + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** This hunt looks for unique process execution via Windows Management Instrumentation (WMI) by removing random patterns from `process.command_line` and aggregating execution by count of agents with the same command line to limit results to unique ones. + +- **UUID:** `b5efeb92-9b51-45b9-839f-be4cdc054ef4` +- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows), [system](https://docs.elastic.co/integrations/system) +- **Language:** `ES|QL` + +## Query + +```sql +from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day and + host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation") and + to_lower(process.parent.name) == "wmiprvse.exe" and starts_with(process.code_signature.subject_name, "Microsoft") +| keep process.hash.sha256, host.id, process.name +| stats agents = count_distinct(host.id) by process.name +| where agents == 1 +``` + +```sql +from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-system.security-* +| where @timestamp > now() - 7 day and + host.os.family == "windows" and event.category == "process" and + event.action in ("start", "Process creation", "created-process") and + process.parent.name.caseless == "wmiprvse.exe" +| keep process.command_line, host.id +| eval cmdline = replace(process.command_line, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| eval cmdline = replace(cmdline, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| stats agents = count_distinct(host.id) by cmdline +| where agents == 1 +``` + +```sql +from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day and + host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation") and + process.parent.name.caseless == "wmiprvse.exe" and (process.code_signature.exists == false or process.code_signature.trusted == false) +| keep process.hash.sha256, host.id, process.name +| stats agents = count_distinct(host.id) by process.hash.sha256 +| where agents == 1 +``` + +## Notes + +- This hunt contains three queries for Elastic Defend, Sysmon, and Windows Security event 4688. +## MITRE ATT&CK Techniques + +- [T1047](https://attack.mitre.org/techniques/T1047) + +## License + +- `Elastic License v2` diff --git a/hunting/windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent_elastic_defend_sysmon.md b/hunting/windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent_elastic_defend_sysmon.md deleted file mode 100644 index 5244065f6..000000000 --- a/hunting/windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent_elastic_defend_sysmon.md +++ /dev/null @@ -1,34 +0,0 @@ -# Execution via Windows Management Instrumentation by occurrence frequency by Unique Agent - Elastic Defend - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `b5efeb92-9b51-45b9-839f-be4cdc054ef4` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day and - host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation") and - process.parent.name.caseless == "wmiprvse.exe" and starts_with(process.code_signature.subject_name, "Microsoft") -| keep process.hash.sha256, host.id, process.name -| stats agents = count_distinct(host.id) by process.name -| where agents == 1 -``` - -## Notes - -- This hunt looks for unique process execution via Windows Management Instrumentation by removing random patterns from process.command_line and aggregating execution by count of agents with same cmdline to limit result to unique ones. -- This hunt is compatible with Sysmon, Elastic Defend and Windows Security event 4688. -## MITRE ATT&CK Techniques - -- [T1047](https://attack.mitre.org/techniques/T1047) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon.md b/hunting/windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon.md deleted file mode 100644 index 740578c5f..000000000 --- a/hunting/windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon.md +++ /dev/null @@ -1,34 +0,0 @@ -# Execution via Windows Management Instrumentation by occurrence frequency - Elastic Defend - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `958a9027-2c6f-4eb0-a9ca-d1116a3bec76` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day and - host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation") and - process.parent.name.caseless == "wmiprvse.exe" and (process.code_signature.exists == false or process.code_signature.trusted == false) -| keep process.hash.sha256, host.id, process.name -| stats agents = count_distinct(host.id) by process.hash.sha256 -| where agents == 1 -``` - -## Notes - -- This hunt looks for unique process execution via Windows Management Instrumentation by removing random patterns from process.command_line and aggregating execution by count of agents with same cmdline to limit result to unique ones. -- This hunt is compatible with Sysmon, Elastic Defend and Windows Security event 4688. -## MITRE ATT&CK Techniques - -- [T1047](https://attack.mitre.org/techniques/T1047) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon_windows_security.md b/hunting/windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon_windows_security.md deleted file mode 100644 index 601673507..000000000 --- a/hunting/windows/docs/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon_windows_security.md +++ /dev/null @@ -1,37 +0,0 @@ -# Execution via Windows Management Instrumentation by occurrence frequency - Elastic Defend - Sysmon - Windows Security - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `793d5655-d7d9-422a-ba9d-1fa75029265e` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows), [system](https://docs.elastic.co/integrations/system) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-system.security-* -| where @timestamp > now() - 7 day and - host.os.family == "windows" and event.category == "process" and - event.action in ("start", "Process creation", "created-process") and - process.parent.name.caseless == "wmiprvse.exe" -| keep process.command_line, host.id -| eval cmdline = replace(process.command_line, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| eval cmdline = replace(cmdline, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| stats agents = count_distinct(host.id) by cmdline -| where agents == 1 -``` - -## Notes - -- This hunt looks for unique process execution via Windows Management Instrumentation by removing random patterns from process.command_line and aggregating execution by count of agents with same cmdline to limit result to unique ones. -- This hunt is compatible with Sysmon, Elastic Defend and Windows Security event 4688. -## MITRE ATT&CK Techniques - -- [T1047](https://attack.mitre.org/techniques/T1047) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/execution_via_windows_scheduled_task_with_low_occurrence_frequency.md b/hunting/windows/docs/execution_via_windows_scheduled_task_with_low_occurrence_frequency.md index 04a951fb1..88d568359 100644 --- a/hunting/windows/docs/execution_via_windows_scheduled_task_with_low_occurrence_frequency.md +++ b/hunting/windows/docs/execution_via_windows_scheduled_task_with_low_occurrence_frequency.md @@ -1,10 +1,12 @@ -# Execution via Windows Scheduled Task with low occurrence frequency +# Low Frequency of Process Execution via Windows Scheduled Task by Unique Agent --- ## Metadata - **Author:** Elastic +- **Description:** Aggregating by paths/hash, this hunt identifies rare instances where a program executes as a child process of the Tasks Scheduler service. This could be the result of persistence as a Windows Scheduled Task. + - **UUID:** `96d5afc8-1f25-4265-8a0e-9998091a2e1f` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) - **Language:** `ES|QL` @@ -13,9 +15,9 @@ ```sql from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "process" and - event.action in ("start", "Process creation") and process.code_signature.trusted != true and +| where @timestamp > now(-) - 7 day +| where host.os.family == "windows" and event.category == "process" and + event.action in ("start", "Process creation") and process.code_signature.trusted != true and /* child process of the Tasks Schedule service */ process.parent.name == "svchost.exe" and ends_with(process.parent.command_line, "Schedule") | stats hosts = count_distinct(host.id) by process.hash.sha256, process.name @@ -25,8 +27,8 @@ from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* ## Notes -- Windows security event 4688 lacks process.parent.command_line needed for this hunt to identify the Schedule svchost instance. -- Unique process.hash.sha256 and agent is not necessarily malicious, this help surface ones worth further investigation. +- Windows security event 4688 lacks `process.parent.command_line` needed for this hunt to identify the Schedule `svchost` instance. +- Unique `process.hash.sha256` and agent is not necessarily malicious, however this helps surface signals worth further investigation. ## MITRE ATT&CK Techniques - [T1053](https://attack.mitre.org/techniques/T1053) diff --git a/hunting/windows/docs/execution_via_windows_services_with_low_occurrence_frequency.md b/hunting/windows/docs/execution_via_windows_services_with_low_occurrence_frequency.md new file mode 100644 index 000000000..37878a0a0 --- /dev/null +++ b/hunting/windows/docs/execution_via_windows_services_with_low_occurrence_frequency.md @@ -0,0 +1,51 @@ +# Low Occurence of Process Execution via Windows Services with Unique Agent + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** This hunt looks for a low occurrence of process execution via the Windows Services Control Manager by unique agent. The Services Control Manager is responsible for starting, stopping, and interacting with system services. This could be a sign of persistence as a Windows service. + +- **UUID:** `858b7022-b587-4b95-afd6-8ce597bedce3` +- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows), [system](https://docs.elastic.co/integrations/system) +- **Language:** `ES|QL` + +## Query + +```sql +from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation") and + process.parent.name == "services.exe" and process.code_signature.trusted != true +| stats hosts = count_distinct(host.id) by process.hash.sha256, process.name + /* unique hash observed in one unique agent */ +| where hosts == 1 +``` + +```sql +from logs-system.security-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "process" and event.code == "4688" and + event.action == "created-process" and process.parent.name == "services.exe" +| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval process_path = replace(process_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") +| stats hosts = count_distinct(host.id) by process_path + /* unique path observed in one unique agent */ +| where hosts == 1 +``` + +## Notes + +- This hunt contains two queries for Elastic Defend and Windows Security event 4688. +- Windows security event 4688 lacks code signature and hash information, hence the use of `process.executable` for aggregation. +- Unique `process.hash.sha256` and agent is not necessarily malicious, this help surface ones worth further investigation. +- Suspicious `process.executable` paths and LOLBins should be reviewed further. +## MITRE ATT&CK Techniques + +- [T1543](https://attack.mitre.org/techniques/T1543) +- [T1543.003](https://attack.mitre.org/techniques/T1543/003) + +## License + +- `Elastic License v2` diff --git a/hunting/windows/docs/execution_via_windows_services_with_low_occurrence_frequency_elastic_defend_sysmon.md b/hunting/windows/docs/execution_via_windows_services_with_low_occurrence_frequency_elastic_defend_sysmon.md deleted file mode 100644 index 9f0bda9cd..000000000 --- a/hunting/windows/docs/execution_via_windows_services_with_low_occurrence_frequency_elastic_defend_sysmon.md +++ /dev/null @@ -1,36 +0,0 @@ -# Execution via Windows Services with low occurrence frequency - Elastic Defend - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `858b7022-b587-4b95-afd6-8ce597bedce3` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation") and - process.parent.name == "services.exe" and process.code_signature.trusted != true -| stats hosts = count_distinct(host.id) by process.hash.sha256, process.name - /* unique hash observed in one unique agent */ -| where hosts == 1 -``` - -## Notes - -- Windows security event 4688 lacks code signature and hash information, hence the use of process.executable for aggregation. -- Unique process.hash.sha256 and agent is not necessarily malicious, this help surface ones worth further investigation. -- Suspicious process.executable paths and lolbins should be reviewed further. -## MITRE ATT&CK Techniques - -- [T1543](https://attack.mitre.org/techniques/T1543) -- [T1543.003](https://attack.mitre.org/techniques/T1543/003) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/execution_via_windows_services_with_low_occurrence_frequency_windows_security.md b/hunting/windows/docs/execution_via_windows_services_with_low_occurrence_frequency_windows_security.md deleted file mode 100644 index 0fd0f5425..000000000 --- a/hunting/windows/docs/execution_via_windows_services_with_low_occurrence_frequency_windows_security.md +++ /dev/null @@ -1,38 +0,0 @@ -# Execution via Windows Services with low occurrence frequency - Windows Security - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `5fdc9f73-c6a4-4ea4-8e16-347ed675e236` -- **Integration:** [system](https://docs.elastic.co/integrations/system) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-system.security-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.code == "4688" and - event.action == "created-process" and process.parent.name == "services.exe" -| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval process_path = replace(process_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") -| stats hosts = count_distinct(host.id) by process_path - /* unique path observed in one unique agent */ -| where hosts == 1 -``` - -## Notes - -- Windows security event 4688 lacks code signature and hash information, hence the use of process.executable for aggregation. -- Unique process.hash.sha256 and agent is not necessarily malicious, this help surface ones worth further investigation. -- Suspicious process.executable paths and lolbins should be reviewed further. -## MITRE ATT&CK Techniques - -- [T1543](https://attack.mitre.org/techniques/T1543) -- [T1543.003](https://attack.mitre.org/techniques/T1543/003) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/high_count_of_network_connection_over_extended_period_by_process.md b/hunting/windows/docs/high_count_of_network_connection_over_extended_period_by_process.md new file mode 100644 index 000000000..ed98002ee --- /dev/null +++ b/hunting/windows/docs/high_count_of_network_connection_over_extended_period_by_process.md @@ -0,0 +1,77 @@ +# High Count of Network Connection Over Extended Period by Process + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** This hunt identifies browser or svchost instances performing a considerable number of connections per hour over an extended period of hours to a specific destination address, limited to a unique host of the monitored agents. Browsers and svchost are both good targets for masquerading network traffic on the endpoint. + +- **UUID:** `ed254a22-e7bb-4a36-9291-196b77762dd8` +- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) +- **Language:** `ES|QL` + +## Query + +```sql +from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* +| where host.os.family == "windows" and event.category == "network" and + network.direction == "egress" and process.name in ("chrome.exe", "msedge.exe", "iexplore.exe", "firefox.exe", "svchost.exe") and + /* excluding DNS */ + destination.port != 53 and + /* excluding private IP ranges */ + not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") +| keep source.bytes, destination.address, process.name, process.entity_id, @timestamp, host.id + /* calc total duration and the number of connections per hour */ +| stats count_connections = count(*), start_time = min(@timestamp), end_time = max(@timestamp), hosts= count_distinct(host.id), count_unique_pids = count_distinct(process.entity_id) by destination.address, process.name +| eval dur = TO_DOUBLE(end_time)-TO_DOUBLE(start_time), duration_hours=TO_INT(dur/3600000), number_of_con_per_hour = (count_connections / duration_hours) +| keep process.name, duration_hours, destination.address, hosts, count_unique_pids, count_connections, number_of_con_per_hour + /* threshold is set to 120 connections per minute during 4 hours and limited to 1 agent and 1 pid, you can adjust this values to your hunting needs */ +| where number_of_con_per_hour >= 120 and duration_hours >= 4 and hosts == 1 and count_unique_pids == 1 +``` + +```sql +from logs-endpoint.events.network-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "network" and + network.direction == "egress" and +(process.code_signature.exists == false or process.code_signature.trusted != true or starts_with(process.executable, "C:\\Users\\Public\\")) and + /* excluding private IP ranges */ + not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") +| keep source.bytes, destination.address, process.name, process.entity_id, @timestamp + /* calc total duration , total MB out and the number of connections per hour */ +| stats total_bytes_out = sum(source.bytes), count_connections = count(*), start_time = min(@timestamp), end_time = max(@timestamp) by process.entity_id, destination.address, process.name +| eval dur = TO_DOUBLE(end_time)-TO_DOUBLE(start_time), duration_hours=TO_INT(dur/3600000), MB_out=TO_DOUBLE(total_bytes_out) / (1024*1024), number_of_con_per_hour = (count_connections / duration_hours) +| keep process.entity_id, process.name, duration_hours, destination.address, MB_out, count_connections, number_of_con_per_hour + /* threshold is set to 120 connections per minute , you can adjust it to your env/FP rate */ +| where duration_hours >= 1 and number_of_con_per_hour >= 120 +``` + +```sql +from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "network" and + network.direction == "egress" and (process.executable like "C:\\\\Windows\\\\System32*" or process.executable like "C:\\\\Windows\\\\SysWOW64\\\\*") and not user.id in ("S-1-5-19", "S-1-5-20") and +/* multiple Windows svchost services perform long term connection to MS ASN, can be covered in a dedicated hunt */ +not (process.name == "svchost.exe" and user.id == "S-1-5-18") and +/* excluding private IP ranges */ + not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") +| keep source.bytes, destination.address, process.name, process.entity_id, @timestamp + /* calc total duration , total MB out and the number of connections per hour */ +| stats total_bytes_out = sum(source.bytes), count_connections = count(*), start_time = min(@timestamp), end_time = max(@timestamp) by process.entity_id, destination.address, process.name +| eval dur = TO_DOUBLE(end_time)-TO_DOUBLE(start_time), duration_hours=TO_INT(dur/3600000), MB_out=TO_DOUBLE(total_bytes_out) / (1024*1024), number_of_con_per_hour = (count_connections / duration_hours) +| keep process.entity_id, process.name, duration_hours, destination.address, MB_out, count_connections, number_of_con_per_hour +/* threshold is set to 120 connections per minute , you can adjust it to your env/FP rate */ +| where duration_hours >= 1 and number_of_con_per_hour >= 120 +``` + +## Notes + +- This hunt includes three queries for Elastic Defend and Sysmon data sources. +## MITRE ATT&CK Techniques + +- [T1071](https://attack.mitre.org/techniques/T1071) + +## License + +- `Elastic License v2` diff --git a/hunting/windows/docs/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network.md b/hunting/windows/docs/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network.md deleted file mode 100644 index 537f63226..000000000 --- a/hunting/windows/docs/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network.md +++ /dev/null @@ -1,40 +0,0 @@ -# High count of network connection over extended period by process - Elastic Defend Network - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `76843f1f-404d-42b8-9c25-fcc14e270240` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.network-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "network" and - network.direction == "egress" and -(process.code_signature.exists == false or process.code_signature.trusted != true or starts_with(process.executable, "C:\\Users\\Public\\")) and - /* excluding private IP ranges */ - not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") -| keep source.bytes, destination.address, process.name, process.entity_id, @timestamp - /* calc total duration , total MB out and the number of connections per hour */ -| stats total_bytes_out = sum(source.bytes), count_connections = count(*), start_time = min(@timestamp), end_time = max(@timestamp) by process.entity_id, destination.address, process.name -| eval dur = TO_DOUBLE(end_time)-TO_DOUBLE(start_time), duration_hours=TO_INT(dur/3600000), MB_out=TO_DOUBLE(total_bytes_out) / (1024*1024), number_of_con_per_hour = (count_connections / duration_hours) -| keep process.entity_id, process.name, duration_hours, destination.address, MB_out, count_connections, number_of_con_per_hour - /* threshold is set to 120 connections per minute , you can adjust it to your env/FP rate */ -| where duration_hours >= 1 and number_of_con_per_hour >= 120 -``` - -## Notes - -- This hunt aggregate by process Id and destination ip the number of connections per hour over a period of time greater than a defined threshold. The process paths are scoped to Microsoft signed binaries often injected or used as a lolbin to masquerade malicious execution. This could be a sign of long term network activity to perform command and control from an injected process. Scoped for unsigned processes or ones running from suspicious paths, the Sysmon network events don't include process code signature information -## MITRE ATT&CK Techniques - -- [T1071](https://attack.mitre.org/techniques/T1071) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network_sysmon.md b/hunting/windows/docs/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network_sysmon.md deleted file mode 100644 index 4bf9f5d39..000000000 --- a/hunting/windows/docs/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network_sysmon.md +++ /dev/null @@ -1,40 +0,0 @@ -# High count of network connection over extended period by process - Elastic Defend Network - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `ed254a22-e7bb-4a36-9291-196b77762dd8` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where host.os.family == "windows" and event.category == "network" and - network.direction == "egress" and process.name in ("chrome.exe", "msedge.exe", "iexplore.exe", "firefox.exe", "svchost.exe") and - /* excluding DNS */ - destination.port != 53 and - /* excluding private IP ranges */ - not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") -| keep source.bytes, destination.address, process.name, process.entity_id, @timestamp, host.id - /* calc total duration and the number of connections per hour */ -| stats count_connections = count(*), start_time = min(@timestamp), end_time = max(@timestamp), hosts= count_distinct(host.id), count_unique_pids = count_distinct(process.entity_id) by destination.address, process.name -| eval dur = TO_DOUBLE(end_time)-TO_DOUBLE(start_time), duration_hours=TO_INT(dur/3600000), number_of_con_per_hour = (count_connections / duration_hours) -| keep process.name, duration_hours, destination.address, hosts, count_unique_pids, count_connections, number_of_con_per_hour - /* threshold is set to 120 connections per minute during 4 hours and limited to 1 agent and 1 pid, you can adjust this values to your hunting needs */ -| where number_of_con_per_hour >= 120 and duration_hours >= 4 and hosts == 1 and count_unique_pids == 1 -``` - -## Notes - -- This hunt identify browser or svchost instances performing a considerable number of connections per hour over an extended period of hours to a specific destination address and this is limited to a unique host of the monitored agents. Browsers and svchost are both good targets for masquerading network traffic on the endpoint. -## MITRE ATT&CK Techniques - -- [T1071](https://attack.mitre.org/techniques/T1071) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_sysmon.md b/hunting/windows/docs/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_sysmon.md deleted file mode 100644 index 823e41219..000000000 --- a/hunting/windows/docs/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_sysmon.md +++ /dev/null @@ -1,41 +0,0 @@ -# High count of network connection over extended period by process - Elastic Defend - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `7ee9a5a7-3ce1-47eb-b15a-1b148299fcf0` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "network" and - network.direction == "egress" and (process.executable like "C:\\\\Windows\\\\System32*" or process.executable like "C:\\\\Windows\\\\SysWOW64\\\\*") and not user.id in ("S-1-5-19", "S-1-5-20") and -/* multiple Windows svchost services perform long term connection to MS ASN, can be covered in a dedicated hunt */ -not (process.name == "svchost.exe" and user.id == "S-1-5-18") and -/* excluding private IP ranges */ - not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") -| keep source.bytes, destination.address, process.name, process.entity_id, @timestamp - /* calc total duration , total MB out and the number of connections per hour */ -| stats total_bytes_out = sum(source.bytes), count_connections = count(*), start_time = min(@timestamp), end_time = max(@timestamp) by process.entity_id, destination.address, process.name -| eval dur = TO_DOUBLE(end_time)-TO_DOUBLE(start_time), duration_hours=TO_INT(dur/3600000), MB_out=TO_DOUBLE(total_bytes_out) / (1024*1024), number_of_con_per_hour = (count_connections / duration_hours) -| keep process.entity_id, process.name, duration_hours, destination.address, MB_out, count_connections, number_of_con_per_hour -/* threshold is set to 120 connections per minute , you can adjust it to your env/FP rate */ -| where duration_hours >= 1 and number_of_con_per_hour >= 120 -``` - -## Notes - -- This hunt aggregate by process Id and destination ip the number of connections per hour over a period of time greater than a defined threshold. The process paths are scoped to Microsoft signed binaries often injected or used as a lolbin to masquerade malicious execution. This could be a sign of long term network activity to perform command and control from an injected process. -## MITRE ATT&CK Techniques - -- [T1071](https://attack.mitre.org/techniques/T1071) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/libraries_loaded_by_svchost_with_low_occurrence_frequency.md b/hunting/windows/docs/libraries_loaded_by_svchost_with_low_occurrence_frequency.md new file mode 100644 index 000000000..db39a5dfe --- /dev/null +++ b/hunting/windows/docs/libraries_loaded_by_svchost_with_low_occurrence_frequency.md @@ -0,0 +1,60 @@ +# Libraries Loaded by svchost with Low Occurrence Frequency + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** This hunt returns the SHA256 hash and the `dll.path` of unsigned libraries loaded by svchost where the presence of unique path/hash is limited to a unique host. Adversaries may use Windows service DLLs to maintain persistence or run with System privileges. + +- **UUID:** `e37fe0b9-1b70-4800-8989-58bac5a0a9bb` +- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) +- **Language:** `ES|QL` + +## Query + +```sql +from logs-endpoint.events.library-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "library" and event.action == "load" and + process.name == "svchost.exe" and (dll.code_signature.trusted == false or dll.code_signature.exists == false) and dll.hash.sha256 like "?*" and + (dll.Ext.relative_file_creation_time <= 900 or dll.Ext.relative_file_name_modify_time <= 900) +| keep dll.name, dll.path, dll.hash.sha256, host.id +| eval dll_folder = substring(dll.path, 1, length(dll.path) - (length(dll.name) + 1)) + /* paths normalization by removing random patterns */ +| eval dll_path = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "replaced") +| eval dll_path = replace(dll_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") +| eval dll_path = replace(dll_path, """SoftwareDistribution\\Download\\[a-z0-9]+""", """SoftwareDistribution\\Download\\""") +| stats hosts = count_distinct(host.id), count_dlls_per_folder = count(dll_path) by dll_path, dll.name, dll.hash.sha256 +| where hosts == 1 and count_dlls_per_folder == 1 +``` + +```sql +from logs-windows.sysmon_operational-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "process" and event.action == "Image loaded" and + process.name == "svchost.exe" and file.code_signature.status != "Valid" and file.hash.sha256 like "?*" +| keep file.name, file.path, file.hash.sha256, host.id +| eval dll_folder = substring(file.path, 1, length(file.path) - (length(file.name) + 1)) +/* paths normalization by removing random patterns */ +| eval dll_path = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "replaced") +| eval dll_path = replace(dll_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") +| eval dll_path = replace(dll_path, """SoftwareDistribution\\Download\\[a-z0-9]+""", """SoftwareDistribution\\Download\\""") +| stats hosts = count_distinct(host.id), count_dlls_per_folder = count(dll_path) by dll_path, file.name, file.hash.sha256 +| where hosts == 1 and count_dlls_per_folder == 1 +``` + +## Notes + +- This hunt includes two queries to cover both Sysmon and Elastic Defend data sources. +- The hunt uses Elastic Defend library events for an extra optional condition `dll.Ext.relative_file_creation_time` to scope if for recently dropped DLLs. +- The `count_dlls_per_folder` variable filter is used to avoid cases where multiple DLLs with different names are loaded from same directory (often observed in FPs loaded multiple dependencies from same dir). +- Pay close attention unknown hashes and suspicious paths, usually ServiceDLLs are located in trusted directories like `%programfiles%` and `system32/syswow64`. +## MITRE ATT&CK Techniques + +- [T1543](https://attack.mitre.org/techniques/T1543) +- [T1543.003](https://attack.mitre.org/techniques/T1543/003) + +## License + +- `Elastic License v2` diff --git a/hunting/windows/docs/libraries_loaded_by_svchost_with_low_occurrence_frequency_elastic_defend.md b/hunting/windows/docs/libraries_loaded_by_svchost_with_low_occurrence_frequency_elastic_defend.md deleted file mode 100644 index ddadc0d65..000000000 --- a/hunting/windows/docs/libraries_loaded_by_svchost_with_low_occurrence_frequency_elastic_defend.md +++ /dev/null @@ -1,42 +0,0 @@ -# Libraries loaded by svchost with low occurrence frequency - Elastic Defend - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `e37fe0b9-1b70-4800-8989-58bac5a0a9bb` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.library-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "library" and event.action == "load" and - process.name == "svchost.exe" and (dll.code_signature.trusted == false or dll.code_signature.exists == false) and dll.hash.sha256 like "?*" and - (dll.Ext.relative_file_creation_time <= 900 or dll.Ext.relative_file_name_modify_time <= 900) -| keep dll.name, dll.path, dll.hash.sha256, host.id -| eval dll_folder = substring(dll.path, 1, length(dll.path) - (length(dll.name) + 1)) - /* paths normalization by removing random patterns */ -| eval dll_path = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "replaced") -| eval dll_path = replace(dll_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") -| eval dll_path = replace(dll_path, """SoftwareDistribution\\Download\\[a-z0-9]+""", """SoftwareDistribution\\Download\\""") -| stats hosts = count_distinct(host.id), count_dlls_per_folder = count(dll_path) by dll_path, dll.name, dll.hash.sha256 -| where hosts == 1 and count_dlls_per_folder == 1 -``` - -## Notes - -- The hunt using Elastic Defend library events uses an extra optional condition dll.Ext.relative_file_creation_time to scope if for recently dropped DLLs. -- The count_dlls_per_folder variable filter is used to avoid cases where multiple DLLs with different names are loaded from same directory (often observed in FPs loaded multiple dependencies from same dir). -- Pay close attention unknown hashes and suspicious paths, usually ServiceDLLs are located in trusted directories like %programfiles% and system32/syswow64. -## MITRE ATT&CK Techniques - -- [T1543](https://attack.mitre.org/techniques/T1543) -- [T1543.003](https://attack.mitre.org/techniques/T1543/003) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/libraries_loaded_by_svchost_with_low_occurrence_frequency_sysmon.md b/hunting/windows/docs/libraries_loaded_by_svchost_with_low_occurrence_frequency_sysmon.md deleted file mode 100644 index cb61f28d4..000000000 --- a/hunting/windows/docs/libraries_loaded_by_svchost_with_low_occurrence_frequency_sysmon.md +++ /dev/null @@ -1,41 +0,0 @@ -# Libraries loaded by svchost with low occurrence frequency - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `1ae6bfd7-34ce-4d7b-b956-f12d3797ac68` -- **Integration:** [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-windows.sysmon_operational-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.action == "Image loaded" and - process.name == "svchost.exe" and file.code_signature.status != "Valid" and file.hash.sha256 like "?*" -| keep file.name, file.path, file.hash.sha256, host.id -| eval dll_folder = substring(file.path, 1, length(file.path) - (length(file.name) + 1)) -/* paths normalization by removing random patterns */ -| eval dll_path = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "replaced") -| eval dll_path = replace(dll_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") -| eval dll_path = replace(dll_path, """SoftwareDistribution\\Download\\[a-z0-9]+""", """SoftwareDistribution\\Download\\""") -| stats hosts = count_distinct(host.id), count_dlls_per_folder = count(dll_path) by dll_path, file.name, file.hash.sha256 -| where hosts == 1 and count_dlls_per_folder == 1 -``` - -## Notes - -- The hunt using Elastic Defend library events uses an extra optional condition dll.Ext.relative_file_creation_time to scope if for recently dropped DLLs. -- The count_dlls_per_folder variable filter is used to avoid cases where multiple DLLs with different names are loaded from same directory (often observed in FPs loaded multiple dependencies from same dir). -- Pay close attention unknown hashes and suspicious paths, usually ServiceDLLs are located in trusted directories like %programfiles% and system32/syswow64. -## MITRE ATT&CK Techniques - -- [T1543](https://attack.mitre.org/techniques/T1543) -- [T1543.003](https://attack.mitre.org/techniques/T1543/003) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/microsoft_office_child_processes_with_low_occurrence_frequency.md b/hunting/windows/docs/microsoft_office_child_processes_with_low_occurrence_frequency.md index 362d216fb..16074a0d3 100644 --- a/hunting/windows/docs/microsoft_office_child_processes_with_low_occurrence_frequency.md +++ b/hunting/windows/docs/microsoft_office_child_processes_with_low_occurrence_frequency.md @@ -1,10 +1,12 @@ -# Microsoft Office Child Processes with low occurrence frequency +# Microsoft Office Child Processes with Low Occurrence Frequency by Unique Agent --- ## Metadata - **Author:** Elastic +- **Description:** This hunt looks for Microsoft Office child processes with low occurrence frequency. This could be a normal rare behavior as well as potential execution via a malicious document. Adversaries may use Microsoft Office applications to execute malicious code, such as macros, scripts, or other payloads. + - **UUID:** `74b2e54b-7002-4201-83d6-7fd9bd5dcf0f` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows), [system](https://docs.elastic.co/integrations/system) - **Language:** `ES|QL` @@ -13,18 +15,18 @@ ```sql from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-system.security-* -| where host.os.family == "windows" and @timestamp > NOW() - 15 day and - event.category == "process" and event.action in ("start", "Process creation", "created-process") and - process.parent.name.caseless in ("winword.exe", "excel.exe", "powerpnt.exe") and not starts_with(process.executable, "C:\\Program Files") +| where host.os.family == "windows" and @timestamp > NOW() - 15 day and + event.category == "process" and event.action in ("start", "Process creation", "created-process") and + to_lower(process.parent.name) in ("winword.exe", "excel.exe", "powerpnt.exe") and not starts_with(process.executable, "C:\\Program Files") // normalize user home profile paths -| eval process_path = replace(process.executable.caseless, """[c]:\\[u][s][e][r][s]\\[a-zA-Z0-9\.\-\_\$]+\\""", "c:\\\\users\\\\user\\\\") -| stats occurrences = count(*), agents = count_distinct(agent.id) by process_path, process.parent.name +| eval process_path = replace(to_lower(process.executable), """[c]:\\[u][s][e][r][s]\\[a-zA-Z0-9\.\-\_\$]+\\""", "c:\\\\users\\\\user\\\\") +| stats occurrences = count(*), agents = count_distinct(agent.id) by process_path, process.parent.name | where occurrences == 1 and agents == 1 ``` ## Notes -- Certain processes like WerFault.exe, dw20.exe and dwwin.exe are often related to application crash. +- Certain processes like `WerFault.exe`, `dw20.exe` and `dwwin.exe` are often related to application crash. - Closer attention should be attributed to lolbins and unsigned executables (Windows 4688 is not capturing process code signature information). ## MITRE ATT&CK Techniques diff --git a/hunting/windows/docs/network_discovery_via_sensitive_ports_by_unusual_process.md b/hunting/windows/docs/network_discovery_via_sensitive_ports_by_unusual_process.md index 4525f0e7a..0fc9f7462 100644 --- a/hunting/windows/docs/network_discovery_via_sensitive_ports_by_unusual_process.md +++ b/hunting/windows/docs/network_discovery_via_sensitive_ports_by_unusual_process.md @@ -1,10 +1,12 @@ -# Network Discovery via sensitive ports by unusual process +# Network Discovery via Sensitive Ports by Unusual Process --- ## Metadata - **Author:** Elastic +- **Description:** This hunt looks for either processes connecting to multiple sensitive TCP ports (SMB, RDP, LDAP, Kerberos and ADWS), a high number of SMB/RDP connections to unique destinations or the same process connecting to both RDP and SMB (should be rare). + - **UUID:** `e0acab7d-30bd-4be0-9682-5c3457bbeb4f` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) - **Language:** `ES|QL` @@ -12,10 +14,10 @@ ## Query ```sql -from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* +from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* | where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "network" and network.direction == "egress" and - network.transport == "tcp"and destination.port in (3389, 445, 389, 9389, 88, 5985, 5986, 22) and source.port >= 49152 and +| where host.os.family == "windows" and event.category == "network" and network.direction == "egress" and + network.transport == "tcp"and destination.port in (3389, 445, 389, 9389, 88, 5985, 5986, 22) and source.port >= 49152 and process.pid != 4 | keep process.executable, destination.port, destination.ip, process.entity_id /* network events with SMB or RDP as a target */ diff --git a/hunting/windows/docs/pe_file_transfer_via_smb_admin_shares_by_agent.md b/hunting/windows/docs/pe_file_transfer_via_smb_admin_shares_by_agent.md index 6abb43423..7652222b0 100644 --- a/hunting/windows/docs/pe_file_transfer_via_smb_admin_shares_by_agent.md +++ b/hunting/windows/docs/pe_file_transfer_via_smb_admin_shares_by_agent.md @@ -1,10 +1,12 @@ -# PE File Transfer via SMB_Admin Shares by Agent +# PE File Transfer via SMB_Admin Shares by Agent or User --- ## Metadata - **Author:** Elastic +- **Description:** This hunt looks for a high number of executable file transfers via the SMB protocol by the same user or agent to more than a defined maxium threshold of targets. This could be a sign of lateral movement via the Windows Admin Shares. + - **UUID:** `3e66fc1a-2ea0-43a6-ba51-0280c693d152` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) - **Language:** `ES|QL` @@ -13,17 +15,26 @@ ```sql from logs-endpoint.events.file-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "file" and event.action != "deletion" and process.pid == 4 and - starts_with(file.Ext.header_bytes, "4d5a*") and (starts_with(user.id, "S-1-5-21-") or starts_with(user.id, "S-1-12-1-")) +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "file" and event.action != "deletion" and process.pid == 4 and + starts_with(file.Ext.header_bytes, "4d5a*") and (starts_with(user.id, "S-1-5-21-") or starts_with(user.id, "S-1-12-1-")) | stats agents = count_distinct(host.id), total = count(*) by user.name | where agents == 1 and total <= 3 ``` +```sql +from logs-endpoint.events.file-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "file" and event.action != "deletion" and process.pid == 4 and + starts_with(file.Ext.header_bytes, "4d5a*") and (starts_with(user.id, "S-1-5-21-") or starts_with(user.id, "S-1-12-1-")) +| stats agents = count_distinct(host.id), total = count(*) by user.name + /* threshold set to 10 but can be adjusted to reduce normal baseline in your env */ +| where agents >= 10 +``` + ## Notes -- This hunt looks for high number of executable file transfer via the SMB protocol by the same user.name to more than a defined maxium threshold of targets. This could be a sign of lateral movement via the Windows Admin Shares. -- Further investigation can done pivoting by host.id and user name. +- Further investigation can done pivoting by `host.id` and `user.name`. ## MITRE ATT&CK Techniques - [T1021](https://attack.mitre.org/techniques/T1021) diff --git a/hunting/windows/docs/pe_file_transfer_via_smb_admin_shares_by_user.md b/hunting/windows/docs/pe_file_transfer_via_smb_admin_shares_by_user.md deleted file mode 100644 index 0721eb43e..000000000 --- a/hunting/windows/docs/pe_file_transfer_via_smb_admin_shares_by_user.md +++ /dev/null @@ -1,35 +0,0 @@ -# PE File Transfer via SMB_Admin Shares by User - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `ef9def35-0671-4599-8a18-5a1b833ef4c4` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.file-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "file" and event.action != "deletion" and process.pid == 4 and - starts_with(file.Ext.header_bytes, "4d5a*") and (starts_with(user.id, "S-1-5-21-") or starts_with(user.id, "S-1-12-1-")) -| stats agents = count_distinct(host.id), total = count(*) by user.name - /* threshold set to 10 but can be adjusted to reduce normal baseline in your env */ -| where agents >= 10 -``` - -## Notes - -- This hunt looks for high number of executable file transfer via the SMB protocol by the same user.name to more than a defined maxium threshold of targets. This could be a sign of lateral movement via the Windows Admin Shares. -- PE File Transfer via SMB/Admin Shares by User -## MITRE ATT&CK Techniques - -- [T1021](https://attack.mitre.org/techniques/T1021) -- [T1021.002](https://attack.mitre.org/techniques/T1021/002) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/persistence_via_run_key_with_low_occurrence_frequency.md b/hunting/windows/docs/persistence_via_run_key_with_low_occurrence_frequency.md new file mode 100644 index 000000000..3131f94a0 --- /dev/null +++ b/hunting/windows/docs/persistence_via_run_key_with_low_occurrence_frequency.md @@ -0,0 +1,57 @@ +# Persistence via Run Key with Low Occurrence Frequency + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** Leveraging frequency based analysis and random values normalization, this hunt identifies instances where a program adds a persistence entry with rare values or are imited to unique hosts. Run registry key cause programs to run each time that a user logs on and are often abused by adversaries to maintain persistence on an endpoint. + +- **UUID:** `1078e906-0485-482e-bcf3-7ee939e07020` +- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) +- **Language:** `ES|QL` + +## Query + +```sql +from logs-endpoint.events.registry-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "registry" and event.action == "modification" and + (process.code_signature.exists == false or starts_with(process.code_signature.subject_name, "Microsoft")) and + ends_with(registry.key,"\\Microsoft\\Windows\\CurrentVersion\\Run") and + not registry.data.strings rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.{1,2}[c-fC-F]:\\WINDOWS\\System32\\DriverStore\\FileRepository\\.+)""" +| keep registry.key, registry.data.strings, process.name, host.id + /* Paths normalization in registry.data.strings to ease aggregation */ +| eval registry_data = replace(registry.data.strings, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval registry_data = replace(registry_data, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats cc = count(*), hosts = count_distinct(host.id) by process.name, registry_data +| where hosts == 1 and cc == 1 +``` + +```sql +from logs-windows.sysmon_operational-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "registry" and event.action == "RegistryEvent (Value Set)" and + ends_with(registry.key,"\\Microsoft\\Windows\\CurrentVersion\\Run") and + not registry.data.strings rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.{1,2}[c-fC-F]:\\WINDOWS\\System32\\DriverStore\\FileRepository\\.+)""" +| keep registry.key, registry.data.strings, process.name, host.id + /* Paths normalization in registry.data.strings to ease aggregation */ +| eval registry_data = replace(registry.data.strings, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval registry_data = replace(registry_data, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats cc = count(*), hosts = count_distinct(host.id) by process.name, registry_data +| where hosts == 1 and cc == 1 +``` + +## Notes + +- This hunt includes two queries to cover both Sysmon and Elastic Defend data sources. +- Sysmon registry events do not populate process code signature information (hence the separation of the queries). +- Suspicious paths and LOLBins in the `registry.data.strings` value should be reviewed further. +## MITRE ATT&CK Techniques + +- [T1547](https://attack.mitre.org/techniques/T1547) +- [T1547.001](https://attack.mitre.org/techniques/T1547/001) + +## License + +- `Elastic License v2` diff --git a/hunting/windows/docs/persistence_via_run_key_with_low_occurrence_frequency_elastic_defend.md b/hunting/windows/docs/persistence_via_run_key_with_low_occurrence_frequency_elastic_defend.md deleted file mode 100644 index 4d900190b..000000000 --- a/hunting/windows/docs/persistence_via_run_key_with_low_occurrence_frequency_elastic_defend.md +++ /dev/null @@ -1,40 +0,0 @@ -# Persistence via Run Key with low occurrence frequency - Elastic Defend - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `1078e906-0485-482e-bcf3-7ee939e07020` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.registry-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "registry" and event.action == "modification" and - (process.code_signature.exists == false or starts_with(process.code_signature.subject_name, "Microsoft")) and - ends_with(registry.key,"\\Microsoft\\Windows\\CurrentVersion\\Run") and - not registry.data.strings rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.{1,2}[c-fC-F]:\\WINDOWS\\System32\\DriverStore\\FileRepository\\.+)""" -| keep registry.key, registry.data.strings, process.name, host.id - /* Paths normalization in registry.data.strings to ease aggregation */ -| eval registry_data = replace(registry.data.strings, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval registry_data = replace(registry_data, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats cc = count(*), hosts = count_distinct(host.id) by process.name, registry_data -| where hosts == 1 and cc == 1 -``` - -## Notes - -- Sysmon registry event don't populate process code signature information (hence the separation of the queries). -- Suspicious paths and lolbins in the registry.data.strings value should be reviewed further. -## MITRE ATT&CK Techniques - -- [T1547](https://attack.mitre.org/techniques/T1547) -- [T1547.001](https://attack.mitre.org/techniques/T1547/001) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/persistence_via_run_key_with_low_occurrence_frequency_sysmon.md b/hunting/windows/docs/persistence_via_run_key_with_low_occurrence_frequency_sysmon.md deleted file mode 100644 index 3f59ef9ac..000000000 --- a/hunting/windows/docs/persistence_via_run_key_with_low_occurrence_frequency_sysmon.md +++ /dev/null @@ -1,39 +0,0 @@ -# Persistence via Run Key with low occurrence frequency - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `cb2d8acc-123a-4578-bd33-7004c2be9843` -- **Integration:** [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-windows.sysmon_operational-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "registry" and event.action == "RegistryEvent (Value Set)" and - ends_with(registry.key,"\\Microsoft\\Windows\\CurrentVersion\\Run") and - not registry.data.strings rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.{1,2}[c-fC-F]:\\WINDOWS\\System32\\DriverStore\\FileRepository\\.+)""" -| keep registry.key, registry.data.strings, process.name, host.id - /* Paths normalization in registry.data.strings to ease aggregation */ -| eval registry_data = replace(registry.data.strings, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval registry_data = replace(registry_data, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats cc = count(*), hosts = count_distinct(host.id) by process.name, registry_data -| where hosts == 1 and cc == 1 -``` - -## Notes - -- Sysmon registry event don't populate process code signature information (hence the separation of the queries). -- Suspicious paths and lolbins in the registry.data.strings value should be reviewed further. -## MITRE ATT&CK Techniques - -- [T1547](https://attack.mitre.org/techniques/T1547) -- [T1547.001](https://attack.mitre.org/techniques/T1547/001) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/persistence_via_startup_with_low_occurrence_frequency.md b/hunting/windows/docs/persistence_via_startup_with_low_occurrence_frequency.md index 31d4d40ce..962e47392 100644 --- a/hunting/windows/docs/persistence_via_startup_with_low_occurrence_frequency.md +++ b/hunting/windows/docs/persistence_via_startup_with_low_occurrence_frequency.md @@ -1,10 +1,11 @@ -# Persistence via Startup with low occurrence frequency +# Persistence via Startup with Low Occurrence Frequency by Unique Host --- ## Metadata - **Author:** Elastic +- **Description:** Leveraging frequency based analysis and path normalization, this hunt identifies rare instances where a program adds a Startup persistence via file creation. Startup entries cause programs to run each time that a user logs on and are often abused by adversaries to maintain persistence on an endpoint. - **UUID:** `9d8c79fd-0006-4988-8aaa-d5f9b9a7df8e` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) - **Language:** `ES|QL` @@ -14,7 +15,7 @@ ```sql from logs-endpoint.events.file-*, logs-windows.sysmon_operational-default-* | where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "file" and event.action in ("creation", "FileCreate") and +| where host.os.family == "windows" and event.category == "file" and event.action in ("creation", "FileCreate") and file.path rlike """(C:\\Users\\.+\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\.+*|C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\StartUp\\.+)""" | keep process.executable, host.id, file.name /* Paths normalization in registry.data.strings to ease aggregation */ @@ -26,9 +27,9 @@ from logs-endpoint.events.file-*, logs-windows.sysmon_operational-default-* ## Notes -- Elastic Defend file event captures the process.code_signature information, this can be added to the hunt to limit to unsigned and Microsoft signed programs. -- Unique file.name and limited to 1 agent is not necessarily malicious, this help surface ones worth further investigation. -- Suspicious process.executable paths and lolbins should be reviewed further. +- Elastic Defend file event captures the `process.code_signature` information, this can be added to the hunt to limit to unsigned and Microsoft signed programs. +- Unique `file.name` and limited to one agent is not necessarily malicious, however helps surface ones worth further investigation. +- Suspicious `process.executable` paths and LOLBins should be reviewed further. ## MITRE ATT&CK Techniques - [T1547](https://attack.mitre.org/techniques/T1547) diff --git a/hunting/windows/docs/persistence_via_suspicious_launch_agent_or_launch_daemon_with_low_occurrence.md b/hunting/windows/docs/persistence_via_suspicious_launch_agent_or_launch_daemon_with_low_occurrence.md index 2a9cc267e..51661b8cf 100644 --- a/hunting/windows/docs/persistence_via_suspicious_launch_agent_or_launch_daemon_with_low_occurrence.md +++ b/hunting/windows/docs/persistence_via_suspicious_launch_agent_or_launch_daemon_with_low_occurrence.md @@ -1,10 +1,12 @@ -# Persistence via Suspicious Launch Agent or Launch Daemon with low occurrence +# Low Occurrence of Suspicious Launch Agent or Launch Daemon --- ## Metadata - **Author:** Elastic +- **Description:** This hunt looks for persistence via Launch agent or daemon where the distribution is limited to one unique host. + - **UUID:** `a7dcd1a1-2860-491e-8802-31169a607167` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) - **Language:** `ES|QL` @@ -14,7 +16,7 @@ ```sql from logs-endpoint.events.file-* | where @timestamp > now() - 7 day -| where host.os.family == "macos" and event.category == "file" and event.action == "launch_daemon" and +| where host.os.family == "macos" and event.category == "file" and event.action == "launch_daemon" and (Persistence.runatload == true or Persistence.keepalive == true) and process.executable is not null | eval args = MV_CONCAT(Persistence.args, ",") /* normalizing users home profile */ @@ -25,8 +27,7 @@ from logs-endpoint.events.file-* ## Notes -- This hunt looks for persistence via Launch agent or daemon where the distribution is limited to one unique host. -- Further investigation can done pivoting by Persistence.name and args. +- Further investigation can done pivoting by `Persistence.name` and `args`. ## MITRE ATT&CK Techniques - [T1547](https://attack.mitre.org/techniques/T1547) diff --git a/hunting/windows/docs/potential_exfiltration_by_process_total_egress_bytes.md b/hunting/windows/docs/potential_exfiltration_by_process_total_egress_bytes.md index 6bebfef69..ea5970208 100644 --- a/hunting/windows/docs/potential_exfiltration_by_process_total_egress_bytes.md +++ b/hunting/windows/docs/potential_exfiltration_by_process_total_egress_bytes.md @@ -1,10 +1,12 @@ -# Potential Exfiltration by process total egress bytes +# Egress Network Connections with Total Bytes Greater than Threshold --- ## Metadata - **Author:** Elastic +- **Description:** Using aggregation and the ES|QL `SUM` function, this hunt identifies processes that performed egress connections with total bytes greater or equal to a defined maximum threshold. This may indicate exfiltration or long term command and control activity. + - **UUID:** `977d77f9-86e0-4df6-bdc7-aed87c048290` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) - **Language:** `ES|QL` @@ -13,9 +15,9 @@ ```sql from logs-endpoint.events.network-* -| where @timestamp > now() - 8 hour -| where host.os.family == "windows" and event.category == "network" and - event.action == "disconnect_received" and +| where @timestamp > now() - 8 hour +| where host.os.family == "windows" and event.category == "network" and + event.action == "disconnect_received" and not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") | keep source.bytes, destination.address, process.executable, process.entity_id | stats total_bytes_out = sum(source.bytes) by process.entity_id, destination.address, process.executable @@ -25,10 +27,10 @@ from logs-endpoint.events.network-* ## Notes -- This hunt is not compatible with Sysmon event 3 (Network connection) and Windows security event 5156 as both don't log source.bytes. -- The use of host.os.family is to optimise the query and avoid timeout. You can duplicate the same query for other platforms (linux, macos etc.) +- This hunt is not compatible with Sysmon event 3 (Network connection) and Windows security event 5156 as both don't log `source.bytes`. +- The use of `host.os.family` is to optimise the query and avoid timeout. You can duplicate the same query for other platforms (linux, macos etc.) - Based on limited testing it's recommended to set the query time window to 8 hours. -- Pivoting by process.entity_id will allow further investigation (parent process, hash, child processes, other network events etc.). +- Pivoting by `process.entity_id` will allow further investigation (parent process, hash, child processes, other network events etc.). ## MITRE ATT&CK Techniques - [T1071](https://attack.mitre.org/techniques/T1071) diff --git a/hunting/windows/docs/rundll32_execution_aggregated_by_cmdline.md b/hunting/windows/docs/rundll32_execution_aggregated_by_cmdline.md index cc20e1537..6c7a77c70 100644 --- a/hunting/windows/docs/rundll32_execution_aggregated_by_cmdline.md +++ b/hunting/windows/docs/rundll32_execution_aggregated_by_cmdline.md @@ -1,10 +1,12 @@ -# Rundll32 execution aggregated by cmdline +# Rundll32 Execution Aggregated by Command Line --- ## Metadata - **Author:** Elastic +- **Description:** This hunt aggregate Rundll32 execution by normalized `process.command_line` and returns instances that are unique by frequency. Rundll32 is one of the most abused binaries to proxy execution of malicious commands and modules. + - **UUID:** `30f37cd2-c1d8-4554-bb4a-ed76de9e6857` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows), [system](https://docs.elastic.co/integrations/system) - **Language:** `ES|QL` @@ -14,8 +16,8 @@ ```sql from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-system.security-* | where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation", "created-process") and - process.name.caseless == "rundll32.exe" and +| where host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation", "created-process") and + to_lower(process.name) == "rundll32.exe" and not process.command_line rlike """.*(zzzzInvokeManagedCustomActionOutOfProc|GeneralTel.dll,RunInUserCxt|ShOpenVerbApplication|davclnt.dll,DavSetCookie|FileProtocolHandler|EDGEHTML.dll|FirewallControlPanel.dll,ShowNotificationDialog|printui.dll,PrintUIEntryDPIAware|Program Files|SHCreateLocalServerRunDll|ImageView_Fullscreen|StatusMonitorEntryPoint|Control_RunDLL|HotPlugSafeRemovalDriveNotification|AppxDeploymentClient.dll|acproxy.dll,PerformAutochkOperations|CapabilityAccessManagerDoStoreMaintenance|dfshim.dll|display.dll,ShowAdapterSettings|ForceProxyDetectionOnNextRun|PfSvWsSwapAssessmentTask|acmigration.dll,ApplyMigrationShims|LenovoBatteryGaugePackage.dll|-localserver|DriverStore|CnmDxPEntryPoint|DeferredDelete|DeviceProperties_RunDLL|AppxDeploymentClient.dll|spool\\DRIVERS|printui.dll,PrintUIEntry|DfdGetDefaultPolicyAndSMART|cryptext.dll,CryptExt|WininetPlugin.dll|ClearMyTracksByProcess|SusRunTask|OpenURL|CleanupTemporaryState).*""" | keep process.parent.name, process.command_line, host.id | eval cmdline = replace(process.command_line, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") @@ -27,7 +29,7 @@ from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-sys ## Notes - Execution of DLLs from suspicious paths or with suspicious export function names or from suspicious parent should be further reviewed. -- Parents such as svchost, explorer.exe, wmiprvse.exe, winword.exe and others should be carefully reviewed. +- Parents such as svchost, `explorer.exe`, `wmiprvse.exe`, `winword.exe` and others should be carefully reviewed. ## MITRE ATT&CK Techniques - [T1127](https://attack.mitre.org/techniques/T1127) diff --git a/hunting/windows/docs/scheduled_task_creation_by_action_via_registry.md b/hunting/windows/docs/scheduled_task_creation_by_action_via_registry.md index 58f74c3f7..7e5f3769c 100644 --- a/hunting/windows/docs/scheduled_task_creation_by_action_via_registry.md +++ b/hunting/windows/docs/scheduled_task_creation_by_action_via_registry.md @@ -1,10 +1,12 @@ -# Scheduled tasks creation by action via registry +# Scheduled tasks Creation by Action via Registry --- ## Metadata - **Author:** Elastic +- **Description:** This hunt looks for scheduled tasks creation by action using registry events. Scheduled tasks actions are saved under the TaskCache registry key in base64 encoded blob. Malware often abuse LOLBins to proxy execution or run executables from unusual paths, you can add more patterns to the query. + - **UUID:** `344c0690-ebc3-4794-b123-272a5c09c57b` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) - **Language:** `ES|QL` @@ -28,8 +30,7 @@ from logs-endpoint.events.registry-* ## Notes -- This hunt aggregate created scheduled tasks by action using registry events. -- Malware often abuse lolbins to proxy execution or run executables from unusual paths, you can add more patterns to the query. +- Malware often abuse LOLBins to proxy execution or run executables from unusual paths, you can add more patterns to the query. ## MITRE ATT&CK Techniques - [T1053](https://attack.mitre.org/techniques/T1053) diff --git a/hunting/windows/docs/scheduled_tasks_creation_with_low_occurrence_frequency.md b/hunting/windows/docs/scheduled_tasks_creation_for_unique_hosts_by_task_command.md similarity index 76% rename from hunting/windows/docs/scheduled_tasks_creation_with_low_occurrence_frequency.md rename to hunting/windows/docs/scheduled_tasks_creation_for_unique_hosts_by_task_command.md index 75e25e80e..685fcae5e 100644 --- a/hunting/windows/docs/scheduled_tasks_creation_with_low_occurrence_frequency.md +++ b/hunting/windows/docs/scheduled_tasks_creation_for_unique_hosts_by_task_command.md @@ -1,10 +1,12 @@ -# Scheduled tasks creation with low occurrence frequency +# Scheduled Tasks Creation for Unique Hosts by Task Command --- ## Metadata - **Author:** Elastic +- **Description:** Using aggregation and strings extraction, this hunt identifies instances where a scheduled task is created and set to run a command unique to a specific host. This could be the result of persistence as a Windows Scheduled Task. + - **UUID:** `75804319-122c-4bdc-976e-d6355bca0d78` - **Integration:** [system](https://docs.elastic.co/integrations/system) - **Language:** `ES|QL` @@ -16,23 +18,23 @@ from logs-system.security-default-* | where @timestamp > now() - 7 day | where host.os.family == "windows" and event.code == "4698" and event.action == "scheduled-task-created" /* parsing unstructured data from winlog message to extract a scheduled task Exec command */ -| grok message "(?.+)" | eval Command = replace(Command, "(|)", "") +| grok message "(?.+)" | eval Command = replace(Command, "(|)", "") | where Command is not null /* normalise task name by removing usersid and uuid string patterns */ -| eval TaskName = replace(winlog.event_data.TaskName, """((-S-1-5-.*)|\{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\})""", "") +| eval TaskName = replace(winlog.event_data.TaskName, """((-S-1-5-.*)|\{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\})""", "") /* normalise task name by removing random patterns in a file path */ | eval Task_Command = replace(Command, """(ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") /* normalize user home profile path */ | eval Task_Command = replace(Task_Command, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") | where Task_Command like "?*" and not starts_with(Task_Command, "C:\\Program Files") and not starts_with(Task_Command, "\"C:\\Program Files") -| stats tasks_count = count(*), hosts_count = count_distinct(host.id) by Task_Command, TaskName +| stats tasks_count = count(*), hosts_count = count_distinct(host.id) by Task_Command, TaskName | where hosts_count == 1 ``` ## Notes - This hunt returns the aggregation of created tasks by task name, command to execute and number of hosts where this task is present. -- Close attention should be paid to suspicious paths like C:\Users\Public and C:\ProgramData\ as well as lolbins. +- Close attention should be paid to suspicious paths like `C:\Users\Public and C:\ProgramData\` as well as LOLBins. ## MITRE ATT&CK Techniques - [T1053](https://attack.mitre.org/techniques/T1053) diff --git a/hunting/windows/docs/suspicious_base64_encoded_powershell_commands.md b/hunting/windows/docs/suspicious_base64_encoded_powershell_commands.md index 5d9bd80da..eb3ed872e 100644 --- a/hunting/windows/docs/suspicious_base64_encoded_powershell_commands.md +++ b/hunting/windows/docs/suspicious_base64_encoded_powershell_commands.md @@ -1,10 +1,12 @@ -# Suspicious Base64 Encoded PowerShell Command +# Suspicious Base64 Encoded Powershell Command --- ## Metadata - **Author:** Elastic +- **Description:** This hunt identifies base64 encoded powershell commands in process start events and filters ones with suspicious keywords like downloaders and evasion related commands. + - **UUID:** `8bf800de-b3a2-4b36-9484-7d9dae2a1992` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows), [system](https://docs.elastic.co/integrations/system) - **Language:** `ES|QL` @@ -27,7 +29,8 @@ from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-sys ## Notes -- This hunt decode base64 obfuscated powershell commands in process start events and filter ones with suspicious keywords like downloaders and evasion related commands. +- This hunt can be expanded to include more evasion techniques and downloaders. +- Pivoting by `agent.id` can provide more context on the affected hosts. ## MITRE ATT&CK Techniques - [T1059](https://attack.mitre.org/techniques/T1059) diff --git a/hunting/windows/docs/suspicious_dns_txt_record_lookups_by_process.md b/hunting/windows/docs/suspicious_dns_txt_record_lookups_by_process.md index 72943cfdb..772919234 100644 --- a/hunting/windows/docs/suspicious_dns_txt_record_lookups_by_process.md +++ b/hunting/windows/docs/suspicious_dns_txt_record_lookups_by_process.md @@ -1,10 +1,13 @@ -# Suspicious DNS TXT Record lookups by process +# Suspicious DNS TXT Record Lookups by Process --- ## Metadata - **Author:** Elastic +- **Description:** Leveraging aggregation by process executable entities, this hunt identifies identifies a high number of DNS TXT record queries from same process. +Adversaries may leverage DNS TXT queries to stage malicious content or exfiltrate data. + - **UUID:** `0b7343f7-2d16-43c7-af28-9d1f012b1093` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) - **Language:** `ES|QL` @@ -12,20 +15,1583 @@ ## Query ```sql -from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where host.os.family == "windows" and event.category == "network" and - event.action in ("lookup_requested", "DNSEvent (DNS query)") and - (dns.question.type == "TXT" or dns.answers.type == "TXT") and process.executable != "C:\\Windows\\system32\\svchost.exe" -| keep process.executable, process.entity_id -| stats occurrences = count(*) by process.entity_id, process.executable - /* threshold can be adjusted to your env */ -| where occurrences >= 50 +f``` + +```sql +r``` + +```sql +o``` + +```sql +m``` + +```sql + ``` + +```sql +l``` + +```sql +o``` + +```sql +g``` + +```sql +s``` + +```sql +-``` + +```sql +e``` + +```sql +n``` + +```sql +d``` + +```sql +p``` + +```sql +o``` + +```sql +i``` + +```sql +n``` + +```sql +t``` + +```sql +.``` + +```sql +e``` + +```sql +v``` + +```sql +e``` + +```sql +n``` + +```sql +t``` + +```sql +s``` + +```sql +.``` + +```sql +n``` + +```sql +e``` + +```sql +t``` + +```sql +w``` + +```sql +o``` + +```sql +r``` + +```sql +k``` + +```sql +-``` + +```sql +*``` + +```sql +,``` + +```sql + ``` + +```sql +l``` + +```sql +o``` + +```sql +g``` + +```sql +s``` + +```sql +-``` + +```sql +w``` + +```sql +i``` + +```sql +n``` + +```sql +d``` + +```sql +o``` + +```sql +w``` + +```sql +s``` + +```sql +.``` + +```sql +s``` + +```sql +y``` + +```sql +s``` + +```sql +m``` + +```sql +o``` + +```sql +n``` + +```sql +_``` + +```sql +o``` + +```sql +p``` + +```sql +e``` + +```sql +r``` + +```sql +a``` + +```sql +t``` + +```sql +i``` + +```sql +o``` + +```sql +n``` + +```sql +a``` + +```sql +l``` + +```sql +-``` + +```sql +*``` + +```sql + +``` + +```sql +|``` + +```sql + ``` + +```sql +w``` + +```sql +h``` + +```sql +e``` + +```sql +r``` + +```sql +e``` + +```sql + ``` + +```sql +h``` + +```sql +o``` + +```sql +s``` + +```sql +t``` + +```sql +.``` + +```sql +o``` + +```sql +s``` + +```sql +.``` + +```sql +f``` + +```sql +a``` + +```sql +m``` + +```sql +i``` + +```sql +l``` + +```sql +y``` + +```sql + ``` + +```sql +=``` + +```sql +=``` + +```sql + ``` + +```sql +"``` + +```sql +w``` + +```sql +i``` + +```sql +n``` + +```sql +d``` + +```sql +o``` + +```sql +w``` + +```sql +s``` + +```sql +"``` + +```sql + ``` + +```sql +a``` + +```sql +n``` + +```sql +d``` + +```sql + ``` + +```sql +e``` + +```sql +v``` + +```sql +e``` + +```sql +n``` + +```sql +t``` + +```sql +.``` + +```sql +c``` + +```sql +a``` + +```sql +t``` + +```sql +e``` + +```sql +g``` + +```sql +o``` + +```sql +r``` + +```sql +y``` + +```sql + ``` + +```sql +=``` + +```sql +=``` + +```sql + ``` + +```sql +"``` + +```sql +n``` + +```sql +e``` + +```sql +t``` + +```sql +w``` + +```sql +o``` + +```sql +r``` + +```sql +k``` + +```sql +"``` + +```sql + ``` + +```sql +a``` + +```sql +n``` + +```sql +d``` + +```sql + +``` + +```sql + ``` + +```sql + ``` + +```sql +e``` + +```sql +v``` + +```sql +e``` + +```sql +n``` + +```sql +t``` + +```sql +.``` + +```sql +a``` + +```sql +c``` + +```sql +t``` + +```sql +i``` + +```sql +o``` + +```sql +n``` + +```sql + ``` + +```sql +i``` + +```sql +n``` + +```sql + ``` + +```sql +(``` + +```sql +"``` + +```sql +l``` + +```sql +o``` + +```sql +o``` + +```sql +k``` + +```sql +u``` + +```sql +p``` + +```sql +_``` + +```sql +r``` + +```sql +e``` + +```sql +q``` + +```sql +u``` + +```sql +e``` + +```sql +s``` + +```sql +t``` + +```sql +e``` + +```sql +d``` + +```sql +"``` + +```sql +,``` + +```sql + ``` + +```sql +"``` + +```sql +D``` + +```sql +N``` + +```sql +S``` + +```sql +E``` + +```sql +v``` + +```sql +e``` + +```sql +n``` + +```sql +t``` + +```sql + ``` + +```sql +(``` + +```sql +D``` + +```sql +N``` + +```sql +S``` + +```sql + ``` + +```sql +q``` + +```sql +u``` + +```sql +e``` + +```sql +r``` + +```sql +y``` + +```sql +)``` + +```sql +"``` + +```sql +)``` + +```sql + ``` + +```sql +a``` + +```sql +n``` + +```sql +d``` + +```sql + +``` + +```sql + ``` + +```sql + ``` + +```sql +(``` + +```sql +d``` + +```sql +n``` + +```sql +s``` + +```sql +.``` + +```sql +q``` + +```sql +u``` + +```sql +e``` + +```sql +s``` + +```sql +t``` + +```sql +i``` + +```sql +o``` + +```sql +n``` + +```sql +.``` + +```sql +t``` + +```sql +y``` + +```sql +p``` + +```sql +e``` + +```sql + ``` + +```sql +=``` + +```sql +=``` + +```sql + ``` + +```sql +"``` + +```sql +T``` + +```sql +X``` + +```sql +T``` + +```sql +"``` + +```sql + ``` + +```sql +o``` + +```sql +r``` + +```sql + ``` + +```sql +d``` + +```sql +n``` + +```sql +s``` + +```sql +.``` + +```sql +a``` + +```sql +n``` + +```sql +s``` + +```sql +w``` + +```sql +e``` + +```sql +r``` + +```sql +s``` + +```sql +.``` + +```sql +t``` + +```sql +y``` + +```sql +p``` + +```sql +e``` + +```sql + ``` + +```sql +=``` + +```sql +=``` + +```sql + ``` + +```sql +"``` + +```sql +T``` + +```sql +X``` + +```sql +T``` + +```sql +"``` + +```sql +)``` + +```sql + ``` + +```sql +a``` + +```sql +n``` + +```sql +d``` + +```sql + ``` + +```sql +p``` + +```sql +r``` + +```sql +o``` + +```sql +c``` + +```sql +e``` + +```sql +s``` + +```sql +s``` + +```sql +.``` + +```sql +e``` + +```sql +x``` + +```sql +e``` + +```sql +c``` + +```sql +u``` + +```sql +t``` + +```sql +a``` + +```sql +b``` + +```sql +l``` + +```sql +e``` + +```sql + ``` + +```sql +!``` + +```sql +=``` + +```sql + ``` + +```sql +"``` + +```sql +C``` + +```sql +:``` + +```sql +\``` + +```sql +\``` + +```sql +W``` + +```sql +i``` + +```sql +n``` + +```sql +d``` + +```sql +o``` + +```sql +w``` + +```sql +s``` + +```sql +\``` + +```sql +\``` + +```sql +s``` + +```sql +y``` + +```sql +s``` + +```sql +t``` + +```sql +e``` + +```sql +m``` + +```sql +3``` + +```sql +2``` + +```sql +\``` + +```sql +\``` + +```sql +s``` + +```sql +v``` + +```sql +c``` + +```sql +h``` + +```sql +o``` + +```sql +s``` + +```sql +t``` + +```sql +.``` + +```sql +e``` + +```sql +x``` + +```sql +e``` + +```sql +"``` + +```sql + +``` + +```sql +|``` + +```sql + ``` + +```sql +k``` + +```sql +e``` + +```sql +e``` + +```sql +p``` + +```sql + ``` + +```sql +p``` + +```sql +r``` + +```sql +o``` + +```sql +c``` + +```sql +e``` + +```sql +s``` + +```sql +s``` + +```sql +.``` + +```sql +e``` + +```sql +x``` + +```sql +e``` + +```sql +c``` + +```sql +u``` + +```sql +t``` + +```sql +a``` + +```sql +b``` + +```sql +l``` + +```sql +e``` + +```sql +,``` + +```sql + ``` + +```sql + ``` + +```sql +p``` + +```sql +r``` + +```sql +o``` + +```sql +c``` + +```sql +e``` + +```sql +s``` + +```sql +s``` + +```sql +.``` + +```sql +e``` + +```sql +n``` + +```sql +t``` + +```sql +i``` + +```sql +t``` + +```sql +y``` + +```sql +_``` + +```sql +i``` + +```sql +d``` + +```sql + +``` + +```sql +|``` + +```sql + ``` + +```sql +s``` + +```sql +t``` + +```sql +a``` + +```sql +t``` + +```sql +s``` + +```sql + ``` + +```sql +o``` + +```sql +c``` + +```sql +c``` + +```sql +u``` + +```sql +r``` + +```sql +r``` + +```sql +e``` + +```sql +n``` + +```sql +c``` + +```sql +e``` + +```sql +s``` + +```sql + ``` + +```sql +=``` + +```sql + ``` + +```sql +c``` + +```sql +o``` + +```sql +u``` + +```sql +n``` + +```sql +t``` + +```sql +(``` + +```sql +*``` + +```sql +)``` + +```sql + ``` + +```sql +b``` + +```sql +y``` + +```sql + ``` + +```sql +p``` + +```sql +r``` + +```sql +o``` + +```sql +c``` + +```sql +e``` + +```sql +s``` + +```sql +s``` + +```sql +.``` + +```sql +e``` + +```sql +n``` + +```sql +t``` + +```sql +i``` + +```sql +t``` + +```sql +y``` + +```sql +_``` + +```sql +i``` + +```sql +d``` + +```sql +,``` + +```sql + ``` + +```sql +p``` + +```sql +r``` + +```sql +o``` + +```sql +c``` + +```sql +e``` + +```sql +s``` + +```sql +s``` + +```sql +.``` + +```sql +e``` + +```sql +x``` + +```sql +e``` + +```sql +c``` + +```sql +u``` + +```sql +t``` + +```sql +a``` + +```sql +b``` + +```sql +l``` + +```sql +e``` + +```sql + +``` + +```sql + ``` + +```sql +/``` + +```sql +*``` + +```sql + ``` + +```sql +t``` + +```sql +h``` + +```sql +r``` + +```sql +e``` + +```sql +s``` + +```sql +h``` + +```sql +o``` + +```sql +l``` + +```sql +d``` + +```sql + ``` + +```sql +c``` + +```sql +a``` + +```sql +n``` + +```sql + ``` + +```sql +b``` + +```sql +e``` + +```sql + ``` + +```sql +a``` + +```sql +d``` + +```sql +j``` + +```sql +u``` + +```sql +s``` + +```sql +t``` + +```sql +e``` + +```sql +d``` + +```sql + ``` + +```sql +t``` + +```sql +o``` + +```sql + ``` + +```sql +y``` + +```sql +o``` + +```sql +u``` + +```sql +r``` + +```sql + ``` + +```sql +e``` + +```sql +n``` + +```sql +v``` + +```sql + ``` + +```sql +*``` + +```sql +/``` + +```sql + +``` + +```sql +|``` + +```sql + ``` + +```sql +w``` + +```sql +h``` + +```sql +e``` + +```sql +r``` + +```sql +e``` + +```sql + ``` + +```sql +o``` + +```sql +c``` + +```sql +c``` + +```sql +u``` + +```sql +r``` + +```sql +r``` + +```sql +e``` + +```sql +n``` + +```sql +c``` + +```sql +e``` + +```sql +s``` + +```sql + ``` + +```sql +>``` + +```sql +=``` + +```sql + ``` + +```sql +5``` + +```sql +0``` + +```sql + ``` ## Notes -- This hunt returns a list of processes unique pids and executable path that performs a high number of DNS TXT lookups. -- Pivoting by process.entity_id will allow further investigation (parent process, hash, child processes, other network events etc.). +- This hunt returns a list of processes unique pids and executable paths that performs a high number of DNS TXT lookups. +- Pivoting by `process.entity_id` will allow further investigation (parent process, hash, child processes, other network events etc.). ## MITRE ATT&CK Techniques - [T1071](https://attack.mitre.org/techniques/T1071) diff --git a/hunting/windows/docs/unique_windows_services_creation_by_servicefilename.md b/hunting/windows/docs/unique_windows_services_creation_by_servicefilename.md new file mode 100644 index 000000000..3a98c9023 --- /dev/null +++ b/hunting/windows/docs/unique_windows_services_creation_by_servicefilename.md @@ -0,0 +1,77 @@ +# Unique Windows Services Creation by Service File Name + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** This hunt aggregates created Windows services by service file name and distribution limited to unique hosts. Using the ES|QL `Replace` command we can also further remove random patterns to reduce results to interesting events. More investigation can be conducted on instance that looks suspicious based on service file path, names and LOLBins. + +- **UUID:** `ebf79207-16dc-44f8-b10c-317d4a034bad` +- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows), [system](https://docs.elastic.co/integrations/system) +- **Language:** `ES|QL` + +## Query + +```sql +from logs-endpoint.events.registry-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "registry" and event.action in ("modification", "RegistryEvent (Value Set)") and + registry.value in ("ServiceDLL", "ImagePath") and starts_with(registry.path, "HKLM\\SYSTEM\\") and + process.executable != "C:\\Windows\\System32\\services.exe" +| eval process_path = replace(process.executable, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats hosts = count_distinct(host.id), occurrences = count(*) by process_path +/* unique process.executable found in one agent */ +| where hosts == 1 and occurrences == 1 +``` + +```sql +from logs-endpoint.events.registry-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "registry" and event.action in ("modification", "RegistryEvent (Value Set)") and + registry.value in ("ServiceDLL", "ImagePath") and starts_with(registry.path, "HKLM\\SYSTEM\\") and + not registry.data.strings rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.*\\System32\\DriverStore\\FileRepository\\.+)""" +| eval ServiceFileName = replace(registry.data.strings, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") +| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName + /* unique ServiceFileName observed in 1 host*/ +| where hosts == 1 and cc == 1 +``` + +```sql +from logs-system.security-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "configuration" and event.code == "4697" and + not winlog.event_data.ServiceFileName rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.*\\System32\\DriverStore\\FileRepository\\.+)""" +| eval ServiceFileName = replace(winlog.event_data.ServiceFileName, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") +| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName +| where hosts == 1 and cc == 1 +``` + +```sql +from logs-system.system-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.code == "7045" and + not winlog.event_data.ImagePath rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.*\\System32\\DriverStore\\FileRepository\\.+)""" +| eval ServiceFileName = replace(winlog.event_data.ImagePath, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") +| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName +| where hosts == 1 and cc == 1 +``` + +## Notes + +- This hunt also identifies services registry modification by unusual process based on number of hosts and occurrences history. +- Windows event IDs 4697 and 7045 are used to identify service creation and modification. +## MITRE ATT&CK Techniques + +- [T1543](https://attack.mitre.org/techniques/T1543) +- [T1543.003](https://attack.mitre.org/techniques/T1543/003) + +## License + +- `Elastic License v2` diff --git a/hunting/windows/docs/unique_windows_services_creation_by_servicefilename_elastic_defend_registry_sysmon.md b/hunting/windows/docs/unique_windows_services_creation_by_servicefilename_elastic_defend_registry_sysmon.md deleted file mode 100644 index 1a5002562..000000000 --- a/hunting/windows/docs/unique_windows_services_creation_by_servicefilename_elastic_defend_registry_sysmon.md +++ /dev/null @@ -1,36 +0,0 @@ -# Unique Windows Services Creation by ServiceFileName - Elastic Defend Registry - Sysmon - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `ebf79207-16dc-44f8-b10c-317d4a034bad` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.registry-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "registry" and event.action in ("modification", "RegistryEvent (Value Set)") and - registry.value in ("ServiceDLL", "ImagePath") and starts_with(registry.path, "HKLM\\SYSTEM\\") and - process.executable != "C:\\Windows\\System32\\services.exe" -| eval process_path = replace(process.executable, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats hosts = count_distinct(host.id), occurrences = count(*) by process_path -/* unique process.executable found in one agent */ -| where hosts == 1 and occurrences == 1 -``` - -## Notes - -- This hunt identify services registry modification by unusual process based on number of hosts and occurrences history. -## MITRE ATT&CK Techniques - -- [T1543](https://attack.mitre.org/techniques/T1543) -- [T1543.003](https://attack.mitre.org/techniques/T1543/003) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/unique_windows_services_creation_by_servicefilename_elastic_defend_sysmon_registry.md b/hunting/windows/docs/unique_windows_services_creation_by_servicefilename_elastic_defend_sysmon_registry.md deleted file mode 100644 index 9d156cac1..000000000 --- a/hunting/windows/docs/unique_windows_services_creation_by_servicefilename_elastic_defend_sysmon_registry.md +++ /dev/null @@ -1,38 +0,0 @@ -# Unique Windows Services Creation by ServiceFileName - Elastic Defend - Sysmon Registry - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `688dc79d-f52a-49ad-829d-89343e68b0f7` -- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-endpoint.events.registry-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "registry" and event.action in ("modification", "RegistryEvent (Value Set)") and - registry.value in ("ServiceDLL", "ImagePath") and starts_with(registry.path, "HKLM\\SYSTEM\\") and - not registry.data.strings rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.*\\System32\\DriverStore\\FileRepository\\.+)""" -| eval ServiceFileName = replace(registry.data.strings, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") -| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName - /* unique ServiceFileName observed in 1 host*/ -| where hosts == 1 and cc == 1 -``` - -## Notes - -- This hunt aggregates created Windows services by service file name and distribution limited to unique hosts. Using the Replace command we can also further remove random pattern to reduce results to interesting events. More investigation can be conducted on instance that looks suspicious based on service file path, names and lolbins. -## MITRE ATT&CK Techniques - -- [T1543](https://attack.mitre.org/techniques/T1543) -- [T1543.003](https://attack.mitre.org/techniques/T1543/003) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/unique_windows_services_creation_by_servicefilename_windows_security_4697.md b/hunting/windows/docs/unique_windows_services_creation_by_servicefilename_windows_security_4697.md deleted file mode 100644 index fcc0dbd89..000000000 --- a/hunting/windows/docs/unique_windows_services_creation_by_servicefilename_windows_security_4697.md +++ /dev/null @@ -1,36 +0,0 @@ -# Unique Windows Services Creation by ServiceFileName - Windows Security 4697 - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `b6b14385-4ed2-44af-98fe-dad5b1581174` -- **Integration:** [system](https://docs.elastic.co/integrations/system) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-system.security-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "configuration" and event.code == "4697" and - not winlog.event_data.ServiceFileName rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.*\\System32\\DriverStore\\FileRepository\\.+)""" -| eval ServiceFileName = replace(winlog.event_data.ServiceFileName, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") -| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName -| where hosts == 1 and cc == 1 -``` - -## Notes - -- This hunt aggregates created Windows services by service file name and distribution limited to unique hosts. Using the Replace command we can also further remove random pattern to reduce results to interesting events. More investigation can be conducted on instance that looks suspicious based on service file path, names and lolbins. -## MITRE ATT&CK Techniques - -- [T1543](https://attack.mitre.org/techniques/T1543) -- [T1543.003](https://attack.mitre.org/techniques/T1543/003) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/unique_windows_services_creation_by_servicefilename_windows_security_7045.md b/hunting/windows/docs/unique_windows_services_creation_by_servicefilename_windows_security_7045.md deleted file mode 100644 index 0a4813198..000000000 --- a/hunting/windows/docs/unique_windows_services_creation_by_servicefilename_windows_security_7045.md +++ /dev/null @@ -1,36 +0,0 @@ -# Unique Windows Services Creation by ServiceFileName - Windows Security 7045 - ---- - -## Metadata - -- **Author:** Elastic -- **UUID:** `1749a45b-98f0-4b27-8c2f-2287230e52b7` -- **Integration:** [system](https://docs.elastic.co/integrations/system) -- **Language:** `ES|QL` - -## Query - -```sql -from logs-system.system-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.code == "7045" and - not winlog.event_data.ImagePath rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.*\\System32\\DriverStore\\FileRepository\\.+)""" -| eval ServiceFileName = replace(winlog.event_data.ImagePath, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") -| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName -| where hosts == 1 and cc == 1 -``` - -## Notes - -- This hunt aggregates created Windows services by service file name and distribution limited to unique hosts. Using the Replace command we can also further remove random pattern to reduce results to interesting events. More investigation can be conducted on instance that looks suspicious based on service file path, names and lolbins. -## MITRE ATT&CK Techniques - -- [T1543](https://attack.mitre.org/techniques/T1543) -- [T1543.003](https://attack.mitre.org/techniques/T1543/003) - -## License - -- `Elastic License v2` diff --git a/hunting/windows/docs/windows_command_and_scripting_interpreter_from_unusual_parent.md b/hunting/windows/docs/windows_command_and_scripting_interpreter_from_unusual_parent.md index 3fd574ca4..bd5755a49 100644 --- a/hunting/windows/docs/windows_command_and_scripting_interpreter_from_unusual_parent.md +++ b/hunting/windows/docs/windows_command_and_scripting_interpreter_from_unusual_parent.md @@ -1,10 +1,12 @@ -# Windows Command and Scripting Interpreter from unusual parent +# Windows Command and Scripting Interpreter from Unusual Parent Process --- ## Metadata - **Author:** Elastic +- **Description:** This hunt looks for unusual Microsoft native processes spawning `cmd.exe`, `powershell.exe` or `conhost.exe` and limited to a unique host. This could be normal rare behavior as well as an interactive shell activity from an injected parent process to execute system commands. + - **UUID:** `de929347-c04a-4a94-8be2-cbe87b25bb25` - **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint), [windows](https://docs.elastic.co/integrations/windows), [system](https://docs.elastic.co/integrations/system) - **Language:** `ES|QL` @@ -14,9 +16,9 @@ ```sql from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-system.security-* | where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation", "created-process") and - process.name.caseless in ("cmd.exe", "powershell.exe", "conhost.exe") and - (starts_with(process.parent.executable.caseless, "c:\\windows\\system32") or starts_with(process.parent.executable.caseless, "c:\\windows\\syswow64")) +| where host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation", "created-process") and + to_lower(process.name) in ("cmd.exe", "powershell.exe", "conhost.exe") and + (starts_with(to_lower(process.parent.executable), "c:\\windows\\system32") or starts_with(to_lower(process.parent.executable), "c:\\windows\\syswow64")) | keep process.name, process.parent.name, host.id | stats hosts = count_distinct(host.id), cc = count(*) by process.parent.name | where cc <= 10 and hosts == 1 @@ -24,8 +26,8 @@ from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-sys ## Notes -- Pivoting can be done via process.parent.name. -- Certain Microsoft binaries like LSASS, winlogon, spoolsv and others should never spawn cmd.exe powershell.exe or conhost.exe, if so it's highly likely malicious. +- Further pivoting can be done via `process.parent.name`. +- Certain Microsoft binaries like LSASS, winlogon, spoolsv and others should never spawn `cmd.exe`, `powershell.exe` or `conhost.exe`, if so it's highly likely malicious. ## MITRE ATT&CK Techniques - [T1059](https://attack.mitre.org/techniques/T1059) diff --git a/hunting/windows/docs/windows_logon_activity_by_source_ip.md b/hunting/windows/docs/windows_logon_activity_by_source_ip.md index f19f53ff9..5c71659ee 100644 --- a/hunting/windows/docs/windows_logon_activity_by_source_ip.md +++ b/hunting/windows/docs/windows_logon_activity_by_source_ip.md @@ -1,10 +1,12 @@ -# Windows logon activity by source IP +# Windows Logon Activity by Source IP --- ## Metadata - **Author:** Elastic +- **Description:** This hunt returns a summary of network logon activity by `source.ip` using Windows event IDs 4624 and 4625. The higher the number of failures, low success and multiple accounts the more suspicious the behavior is. + - **UUID:** `7bdea198-eb09-4eca-ae3d-bfc3b52c89a9` - **Integration:** [system](https://docs.elastic.co/integrations/system) - **Language:** `ES|QL` @@ -14,10 +16,10 @@ ```sql from logs-system.security-* | where @timestamp > now() - 7 day -| where host.os.family == "windows" and - event.category == "authentication" and event.action in ("logon-failed", "logged-in") and winlog.logon.type == "Network" and - source.ip is not null and - /* noisy failure status codes often associated to authentication misconfiguration */ +| where host.os.family == "windows" and + event.category == "authentication" and event.action in ("logon-failed", "logged-in") and winlog.logon.type == "Network" and + source.ip is not null and + /* noisy failure status codes often associated to authentication misconfiguration */ not (event.action == "logon-failed" and winlog.event_data.Status in ("0xC000015B", "0XC000005E", "0XC0000133", "0XC0000192")) | eval failed = case(event.action == "logon-failed", source.ip, null), success = case(event.action == "logged-in", source.ip, null) | stats count_failed = count(failed), count_success = count(success), count_user = count_distinct(winlog.event_data.TargetUserName) by source.ip @@ -27,8 +29,7 @@ from logs-system.security-* ## Notes -- This hunt returns the total number of failed logons, successful ones and the number of unique account names grouped by source.ip. -- Pay close attention to IP addresses source of a high number of failures associated with low success attempts and high number of used accounts. +- Pay close attention to IP address sources with a high number of failed connections associated with low success attempts and high number of user accounts. ## MITRE ATT&CK Techniques - [T1110](https://attack.mitre.org/techniques/T1110) diff --git a/hunting/windows/queries/createremotethread_by_source_process_with_low_occurrence.toml b/hunting/windows/queries/createremotethread_by_source_process_with_low_occurrence.toml index a59a13dda..4fea380c3 100644 --- a/hunting/windows/queries/createremotethread_by_source_process_with_low_occurrence.toml +++ b/hunting/windows/queries/createremotethread_by_source_process_with_low_occurrence.toml @@ -1,22 +1,23 @@ [hunt] author = "Elastic" +description = "This hunt attempts to identify remote process injection by aggregating Sysmon `CreateRemoteThread` events by source process and returns the ones that we observed in only one unique host." integration = ["windows"] uuid = "0545f23f-84a7-4b88-9b5b-b8cfcfdc9276" -name = "CreateRemoteThread by source process with low occurrence" +name = "Low Occurrence Rate of CreateRemoteThread by Source Process" language = "ES|QL" license = "Elastic License v2" notes = [ - "This hunt aggregates Sysmon CreateRemoteThread events by source process and returns the ones that we observed in only one unique host. This may indicate remote process injection.", - "Adding winlog.event_data.TargetImage to the group by clause can be beneficial but may introduce more legit hits.", + "Adding `winlog.event_data.TargetImage` to the aggregation clause can be beneficial but may introduce more false-positives.", ] mitre = ["T1055"] - -query = ''' -from logs-windows.sysmon_operational-* +query = [ +''' +from logs-windows.sysmon_operational-* | where @timestamp > now() - 7 day | where host.os.family == "windows" and event.category == "process" and event.action == "CreateRemoteThread" | eval source_process = replace(process.executable, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") | stats cc = count(*), hosts = count_distinct(host.id) by source_process /* unique source and target processes combined and observed in 1 host */ | where hosts == 1 and cc == 1 -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries.toml b/hunting/windows/queries/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries.toml new file mode 100644 index 000000000..cdbfab814 --- /dev/null +++ b/hunting/windows/queries/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries.toml @@ -0,0 +1,50 @@ +[hunt] +author = "Elastic" +description = "This hunt identifies when a process loads a DLL normally located in `System32` or `SysWOW64` folders from an unusual path. Adversaries may execute their own malicious payloads by side-loading malicious DLLs. The host count also should help exclude false-positives by looking at low occurrences when this abnormal behavior is limited to unique agents." +integration = ["endpoint", "windows"] +uuid = "87c97865-fdaa-48b2-bfa6-67bed7cf56ef" +name = "DLL Hijack via Masquerading as Microsoft Native Libraries" +language = "ES|QL" +license = "Elastic License v2" +mitre = ["T1574", "T1574.001"] +notes = [ + "This hunt has two optional queries, one for Elastic Defend data and another for Sysmon data.", + "This hunt requires the creation of an [enrichment policy](https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-enrich-data.html) to use with the ES|QL (ENRICH command).", + "The `dll.hash.sha256` field can be used to pivot and further investigate the DLL origin and purpose.", + "Paths like `C:\\Users\\Public and C:\\ProgramData\\` are often observed in malware employing DLL side-loading.", +] + +query = [ +''' +from logs-endpoint.events.library-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.action == "load" and process.code_signature.status == "trusted" and dll.code_signature.status != "trusted" and + not dll.path rlike """[c-fC-F]:\\(Windows|windows|WINDOWS)\\(System32|SysWOW64|system32|syswow64)\\[a-zA-Z0-9_]+.dll""" +| keep dll.name, dll.path, dll.hash.sha256, process.executable, host.id + /* steps how to create DL enrichment policy https://gist.github.com/Samirbous/9f9c3237a0ada745e71cc2ba3425311c */ +| ENRICH libs-policy-defend + /* if the DLL is normally located is system32 or syswow64 folders, native tag will be equal to yes */ +| where native == "yes" and not starts_with(dll.path, "C:\\Windows\\assembly\\NativeImages") + /* normalize paths by removing random patterns */ +| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), + dll_path = replace(dll.path, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| stats host_count = count_distinct(host.id) by dll.name, dll_path, process_path, dll.hash.sha256 +| sort host_count asc +''', +''' +from logs-windows.sysmon_operational-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "process" and event.action == "Image loaded" and file.code_signature.status != "Valid" and + not file.path rlike """[c-fC-F]:\\(Windows|windows|WINDOWS)\\(System32|SysWOW64|system32|syswow64)\\[a-zA-Z0-9_]+.dll""" +| keep file.name, file.path, file.hash.sha256, process.executable, host.id + /* steps to create DL enrichment policy https://gist.github.com/Samirbous/9f9c3237a0ada745e71cc2ba3425311c - just replace dll by file */ +| ENRICH libs-policy-sysmon + /* if the DLL is normally located is system32 or syswow64 folders, native tag will be equal to yes */ +| where native == "yes" and not starts_with(file.path, "C:\\Windows\\assembly\\NativeImages") + /* normalize paths by removing random patterns */ +| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), + dll_path = replace(file.path, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| stats host_count = count_distinct(host.id) by file.name, dll_path, process_path, file.hash.sha256 +| sort host_count asc +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_elastic_defend.toml b/hunting/windows/queries/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_elastic_defend.toml deleted file mode 100644 index cc5b45c01..000000000 --- a/hunting/windows/queries/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_elastic_defend.toml +++ /dev/null @@ -1,30 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint"] -uuid = "87c97865-fdaa-48b2-bfa6-67bed7cf56ef" -name = "Detect DLL Hijack via Masquerading as Microsoft Native Libraries - Elastic Defend" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt require the creation of an enrichment policy to use with the ES|QL (ENRICH command).", - "The `dll.hash.sha256` field can be used to pivot and further investigate the DLL origin and purpose.", - "Paths like C:\\Users\\Public and C:\\ProgramData\\ are often observed in malware employing DLL side-loading.", -] -mitre = ["T1574", "T1574.001"] - -query = ''' -from logs-endpoint.events.library-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.action == "load" and process.code_signature.status == "trusted" and dll.code_signature.status != "trusted" and - not dll.path rlike """[c-fC-F]:\\(Windows|windows|WINDOWS)\\(System32|SysWOW64|system32|syswow64)\\[a-zA-Z0-9_]+.dll""" -| keep dll.name, dll.path, dll.hash.sha256, process.executable, host.id - /* steps how to create DL enrichment policy https://gist.github.com/Samirbous/9f9c3237a0ada745e71cc2ba3425311c */ -| ENRICH libs-policy-defend - /* if the DLL is normally located is system32 or syswow64 folders, native tag will be equal to yes */ -| where native == "yes" and not starts_with(dll.path, "C:\\Windows\\assembly\\NativeImages") - /* normalize paths by removing random patterns */ -| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), - dll_path = replace(dll.path, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| stats host_count = count_distinct(host.id) by dll.name, dll_path, process_path, dll.hash.sha256 -| sort host_count asc -''' \ No newline at end of file diff --git a/hunting/windows/queries/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_sysmon.toml b/hunting/windows/queries/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_sysmon.toml deleted file mode 100644 index f88d93c97..000000000 --- a/hunting/windows/queries/detect_dll_hijack_via_masquerading_as_microsoft_native_libraries_sysmon.toml +++ /dev/null @@ -1,31 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["windows"] -uuid = "68314691-1460-4ac5-ae0d-6b3514e43254" -name = "Detect DLL Hijack via Masquerading as Microsoft Native Libraries - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt require the creation of an enrichment policy to use with the ES|QL (ENRICH command).", - "Using dll.hash.sha256 for Elastic Defend or file.hash.sha256 for Sysmon you can pivot to further investigate the DLL origin and purpose.", - "Paths like C:\\Users\\Public and C:\\ProgramData\\ are often observed in malware employing DLL side-loading.", - "Process code signature information is not captured in Sysmon Image Load Events (not present in the ES|QL hunt).", -] -mitre = [ "T1574", "T1574.001",] - -query = ''' -from logs-windows.sysmon_operational-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.action == "Image loaded" and file.code_signature.status != "Valid" and - not file.path rlike """[c-fC-F]:\\(Windows|windows|WINDOWS)\\(System32|SysWOW64|system32|syswow64)\\[a-zA-Z0-9_]+.dll""" -| keep file.name, file.path, file.hash.sha256, process.executable, host.id - /* steps to create DL enrichment policy https://gist.github.com/Samirbous/9f9c3237a0ada745e71cc2ba3425311c - just replace dll by file */ -| ENRICH libs-policy-sysmon - /* if the DLL is normally located is system32 or syswow64 folders, native tag will be equal to yes */ -| where native == "yes" and not starts_with(file.path, "C:\\Windows\\assembly\\NativeImages") - /* normalize paths by removing random patterns */ -| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), - dll_path = replace(file.path, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| stats host_count = count_distinct(host.id) by file.name, dll_path, process_path, file.hash.sha256 -| sort host_count asc -''' \ No newline at end of file diff --git a/hunting/windows/queries/detect_masquerading_attempts_as_native_windows_binaries.toml b/hunting/windows/queries/detect_masquerading_attempts_as_native_windows_binaries.toml index 2b3f0ab80..96f9ef336 100644 --- a/hunting/windows/queries/detect_masquerading_attempts_as_native_windows_binaries.toml +++ b/hunting/windows/queries/detect_masquerading_attempts_as_native_windows_binaries.toml @@ -1,11 +1,13 @@ [hunt] author = "Elastic" +description = "This hunt detects processes named as legit Microsoft native binaries located in the system32 folder. Adversaries may attempt to manipulate features of their artifacts to make them appear legitimate or benign to users and/or security tools. Masquerading occurs when the name or location of an object, legitimate or malicious, is manipulated or abused for the sake of evading defenses and observation. " integration = ["endpoint"] uuid = "93a72542-a1f7-4407-9175-8f066343db60" -name = "Detect masquerading attempts as native Windows binaries" +mitre = ["T1036"] +name = "Masquerading Attempts as Native Windows Binaries" language = "ES|QL" license = "Elastic License v2" -query = ''' +query = [''' from logs-endpoint.events.process-* | where @timestamp > NOW() - 7 day | where event.type == "start" and event.action == "start" and host.os.name == "Windows" and not starts_with(process.executable, "C:\\Program Files\\WindowsApps\\") and not starts_with(process.executable, "C:\\Windows\\System32\\DriverStore\\") and process.name != "setup.exe" @@ -17,10 +19,10 @@ from logs-endpoint.events.process-* | stats count_system_bin = count(system_bin), count_non_system_bin = count(non_system_bin) by process.name.caseless, host.id /* filter where the same process.name is present in both system_bin and non_system_bin */ | where count_system_bin >= 1 and count_non_system_bin >= 1 -''' +'''] notes = [ - "Output of the query is the process.name and host.id, you can pivot by host.id and process.name(non Microsoft signed) to find the specific suspicious instances.", - "Potential false positives include processes with missing code signature details due to enrichment bugs.", + "Output of the query is the `process.name` and `host.id` where you can pivot by `host.id` and `process.name` (non Microsoft signed) to find the specific suspicious instances.", + "Potential false-positives include processes with missing code signature details due to enrichment bugs.", "The queried index must capture process start events with code signature information (e.g. Windows event 4688 is not supported).", ] -mitre = ["T1036"] + diff --git a/hunting/windows/queries/detect_rare_dll_sideload_by_occurrence.toml b/hunting/windows/queries/detect_rare_dll_sideload_by_occurrence.toml new file mode 100644 index 000000000..9374520bd --- /dev/null +++ b/hunting/windows/queries/detect_rare_dll_sideload_by_occurrence.toml @@ -0,0 +1,45 @@ +[hunt] +author = "Elastic" +description = """This hunt identifies instances where a signed Windows process attempts to load an unsigned DLL from the same process folder. Matches are limited to a unique host with low library load occurrence. Adversaries may execute their own malicious payloads by side-loading malicious DLLs.""" +integration = ["endpoint", "windows"] +uuid = "bcdb7c29-1312-4974-8f2e-10ddeb09cf5c" +name = "Rare DLL Side-Loading by Occurrence" +language = "ES|QL" +license = "Elastic License v2" +query = [ +''' +from logs-endpoint.events.library-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.action == "load" and process.code_signature.status == "trusted" and dll.code_signature.status != "trusted" and dll.Ext.relative_file_creation_time <= 86400 +| eval dll_folder = substring(dll.path, 1, length(dll.path) - (length(dll.name) + 1)) +| eval process_folder = substring(process.executable, 1, length(process.executable) - (length(process.name) + 1)) +| where process_folder is not null and dll_folder is not null and process_folder == dll_folder and process.name != dll.name +| eval dll_folder = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), process_folder = replace(process_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval dll_folder = replace(dll_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\"), process_folder = replace(process_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\") +| stats host_count = count_distinct(host.id), total_count = count(*) by dll_folder, dll.name, process.name, dll.hash.sha256 +/* total_count can be adjusted to higher or lower values depending on env */ +| where host_count == 1 and total_count <= 10 | keep total_count, host_count, dll_folder, dll.name, process.name, dll.hash.sha256 +''', +''' +from logs-windows.sysmon_operational-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "process" and event.action == "Image loaded" and file.code_signature.status != "Valid" and + not file.path rlike """[c-fC-F]:\\(Windows|windows|WINDOWS)\\(System32|SysWOW64|system32|syswow64)\\[a-zA-Z0-9_]+.dll""" +| eval dll_folder = substring(file.path, 1, length(file.path) - (length(file.name) + 1)) +| eval process_folder = substring(process.executable, 1, length(process.executable) - (length(process.name) + 1)) +| where process_folder is not null and dll_folder is not null and process_folder == dll_folder and file.name != process.name +/* paths normalization by removing random patterns */ +| eval dll_folder = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), process_folder = replace(process_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), dll_folder = replace(dll_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\"), process_folder = replace(process_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\") +| stats host_count = count_distinct(host.id), total_count = count(*) by dll_folder, file.name, process.name, file.hash.sha256 +/* total_count can be adjusted to higher or lower values depending on env */ +| where host_count == 1 and total_count <= 10 +| keep total_count, host_count, dll_folder, file.name, process.name, file.hash.sha256 +''' +] +notes = [ + 'This hunt has two optional queries, one for Elastic Defend data and another for Sysmon data.', + 'Based on the returned results you can further investigate suspicious DLLs by sha256 and library path.', + 'Paths like `C:\\Users\\Public` and `C:\\ProgramData\\` are often observed in malware employing DLL side-loading.', + 'Elastic Defned DLL Events include `dll.Ext.relative_file_creation_time` which help us limit the hunt to recently dropped DLLs.' +] +mitre = ["T1574", "T1574.002"] diff --git a/hunting/windows/queries/detect_rare_dll_sideload_by_occurrence_elastic_defend.toml b/hunting/windows/queries/detect_rare_dll_sideload_by_occurrence_elastic_defend.toml deleted file mode 100644 index 8764dffaa..000000000 --- a/hunting/windows/queries/detect_rare_dll_sideload_by_occurrence_elastic_defend.toml +++ /dev/null @@ -1,26 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint"] -uuid = "bcdb7c29-1312-4974-8f2e-10ddeb09cf5c" -name = "Detect Rare DLL SideLoad by Occurrence - Elastic Defend" -language = "ES|QL" -license = "Elastic License v2" -query = ''' -from logs-endpoint.events.library-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.action == "load" and process.code_signature.status == "trusted" and dll.code_signature.status != "trusted" and dll.Ext.relative_file_creation_time <= 86400 -| eval dll_folder = substring(dll.path, 1, length(dll.path) - (length(dll.name) + 1)) -| eval process_folder = substring(process.executable, 1, length(process.executable) - (length(process.name) + 1)) -| where process_folder is not null and dll_folder is not null and process_folder == dll_folder and process.name != dll.name -| eval dll_folder = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), process_folder = replace(process_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval dll_folder = replace(dll_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\"), process_folder = replace(process_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\") -| stats host_count = count_distinct(host.id), total_count = count(*) by dll_folder, dll.name, process.name, dll.hash.sha256 -/* total_count can be adjusted to higher or lower values depending on env */ -| where host_count == 1 and total_count <= 10 | keep total_count, host_count, dll_folder, dll.name, process.name, dll.hash.sha256 -''' -notes = [ - 'Based on the returned results you can further investigate suspicious DLLs by sha256 and library path.', - 'Paths like C:\\Users\\Public and C:\\ProgramData\\ are often observed in malware employing DLL side-loading.', - 'Elastic Defned DLL Events include dll.Ext.relative_file_creation_time which help us limit the hunt to recently dropped DLLs.' -] -mitre = ["T1574", "T1574.002"] diff --git a/hunting/windows/queries/detect_rare_dll_sideload_by_occurrence_sysmon.toml b/hunting/windows/queries/detect_rare_dll_sideload_by_occurrence_sysmon.toml deleted file mode 100644 index 37dfd5e92..000000000 --- a/hunting/windows/queries/detect_rare_dll_sideload_by_occurrence_sysmon.toml +++ /dev/null @@ -1,28 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["windows"] -uuid = "0df1e142-7d70-4112-be8d-6c60ac812883" -name = "Detect Rare DLL SideLoad by Occurrence - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -query = ''' -from logs-windows.sysmon_operational-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.action == "Image loaded" and file.code_signature.status != "Valid" and - not file.path rlike """[c-fC-F]:\\(Windows|windows|WINDOWS)\\(System32|SysWOW64|system32|syswow64)\\[a-zA-Z0-9_]+.dll""" -| eval dll_folder = substring(file.path, 1, length(file.path) - (length(file.name) + 1)) -| eval process_folder = substring(process.executable, 1, length(process.executable) - (length(process.name) + 1)) -| where process_folder is not null and dll_folder is not null and process_folder == dll_folder and file.name != process.name -/* paths normalization by removing random patterns */ -| eval dll_folder = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), process_folder = replace(process_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", ""), dll_folder = replace(dll_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\"), process_folder = replace(process_folder, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$]+\\""", "C:\\\\users\\\\user\\\\") -| stats host_count = count_distinct(host.id), total_count = count(*) by dll_folder, file.name, process.name, file.hash.sha256 -/* total_count can be adjusted to higher or lower values depending on env */ -| where host_count == 1 and total_count <= 10 -| keep total_count, host_count, dll_folder, file.name, process.name, file.hash.sha256 -''' -notes = [ - 'Based on the returned results you can further investigate suspicious DLLs by sha256 and library path.', - 'Paths like C:\\Users\\Public and C:\\ProgramData\\ are often observed in malware employing DLL side-loading.', - 'Elastic Defned DLL Events include dll.Ext.relative_file_creation_time which help us limit the hunt to recently dropped DLLs.' -] -mitre = ["T1574", "T1574.002"] diff --git a/hunting/windows/queries/detect_rare_lsass_process_access_attempts.toml b/hunting/windows/queries/detect_rare_lsass_process_access_attempts.toml new file mode 100644 index 000000000..60e6a54b7 --- /dev/null +++ b/hunting/windows/queries/detect_rare_lsass_process_access_attempts.toml @@ -0,0 +1,40 @@ +[hunt] +author = "Elastic" +description = """ +This hunt identifies instances where a process attempts to open the Local Security Authority Subsystem Service (LSASS) memory and where the number of occurences is limited to one unique agent and a low number of attempts. This may indicate either a rare legitimate condition or a malicious process attempting to obtain credentials or inject code into the LSASS. +""" +integration = ["endpoint", "windows"] +uuid = "3978e183-0b70-4e1c-8c40-24e367f6db5a" +name = "Rare LSASS Process Access Attempts" +language = "ES|QL" +license = "Elastic License v2" +query = [ +''' +from logs-endpoint.events.api* +| where @timestamp > NOW() - 7 day +| where event.category == "api" and host.os.family == "windows" and process.Ext.api.name in ("OpenProcess", "OpenThread", "ReadProcessMemory") and + Target.process.name == "lsass.exe" +| keep process.executable.caseless, host.id + /* normalize process paths to reduce known random patterns in process.executable */ +| eval process = replace(process.executable.caseless, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| stats occurences = count(process), agents = count_distinct(host.id) by process +| where agents == 1 and occurences <= 10 +''', +''' +from logs-windows.sysmon_operational-* +| where @timestamp > NOW() - 7 day +| where event.category == "process" and host.os.family == "windows" and event.action == "ProcessAccess" and + winlog.event_data.TargetImage in ("C:\\Windows\\system32\\lsass.exe", "c:\\Windows\\system32\\lsass.exe", "c:\\Windows\\System32\\lsass.exe") +| keep process.executable, host.id + /* normalize process paths to reduce known random patterns in process.executable */ +| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval process_path = replace(process_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") +| stats occurences = count(process_path), agents = count_distinct(host.id) by process_path +| where agents == 1 and occurences <= 10 +''' +] +notes = [ + "Based on the process.executable and process.name you can pivot and investigate further for the matching instances.", + "Potential false-positives include rare legitimate conditions that may trigger this behavior due to third-party software or LSASS crashing.", +] +mitre = ["T1003", "T1003.001"] diff --git a/hunting/windows/queries/detect_rare_lsass_process_access_attempts_elastic_defend.toml b/hunting/windows/queries/detect_rare_lsass_process_access_attempts_elastic_defend.toml deleted file mode 100644 index 2fab804ef..000000000 --- a/hunting/windows/queries/detect_rare_lsass_process_access_attempts_elastic_defend.toml +++ /dev/null @@ -1,23 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint"] -uuid = "3978e183-0b70-4e1c-8c40-24e367f6db5a" -name = "Detect Rare LSASS Process Access Attempts - Elastic Defend" -language = "ES|QL" -license = "Elastic License v2" -query = ''' -from logs-endpoint.events.api* -| where @timestamp > NOW() - 7 day -| where event.category == "api" and host.os.family == "windows" and process.Ext.api.name in ("OpenProcess", "OpenThread", "ReadProcessMemory") and - Target.process.name == "lsass.exe" -| keep process.executable.caseless, host.id - /* normalize process paths to reduce known random patterns in process.executable */ -| eval process = replace(process.executable.caseless, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| stats occurences = count(process), agents = count_distinct(host.id) by process -| where agents == 1 and occurences <= 10 -''' -notes = [ - "Based on the process.executable and process.name you can pivot and investigate further the matching instances.", - "Potential false positives include rare legit condition that may trigger this behavior due to third party software or Lsass crash.", -] -mitre = ["T1003", "T1003.001"] diff --git a/hunting/windows/queries/detect_rare_lsass_process_access_attempts_sysmon.toml b/hunting/windows/queries/detect_rare_lsass_process_access_attempts_sysmon.toml deleted file mode 100644 index b74087bdf..000000000 --- a/hunting/windows/queries/detect_rare_lsass_process_access_attempts_sysmon.toml +++ /dev/null @@ -1,24 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["windows"] -uuid = "3978e183-0b70-4e1c-8c40-24e367f6db5a" -name = "Detect Rare LSASS Process Access Attempts - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -query = ''' -from logs-windows.sysmon_operational-* -| where @timestamp > NOW() - 7 day -| where event.category == "process" and host.os.family == "windows" and event.action == "ProcessAccess" and - winlog.event_data.TargetImage in ("C:\\Windows\\system32\\lsass.exe", "c:\\Windows\\system32\\lsass.exe", "c:\\Windows\\System32\\lsass.exe") -| keep process.executable, host.id - /* normalize process paths to reduce known random patterns in process.executable */ -| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval process_path = replace(process_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") -| stats occurences = count(process_path), agents = count_distinct(host.id) by process_path -| where agents == 1 and occurences <= 10 -''' -notes = [ - "Based on the process.executable and process.name you can pivot and investigate further the matching instances.", - "Potential false positives include rare legit condition that may trigger this behavior due to third party software or Lsass crash.", -] -mitre = ["T1003", "T1003.001"] diff --git a/hunting/windows/queries/doamin_names_queries_via_lolbins_and_with_low_occurence_frequency.toml b/hunting/windows/queries/domain_names_queried_via_lolbins_and_with_low_occurence_frequency.toml similarity index 56% rename from hunting/windows/queries/doamin_names_queries_via_lolbins_and_with_low_occurence_frequency.toml rename to hunting/windows/queries/domain_names_queried_via_lolbins_and_with_low_occurence_frequency.toml index 67486279d..d14755d19 100644 --- a/hunting/windows/queries/doamin_names_queries_via_lolbins_and_with_low_occurence_frequency.toml +++ b/hunting/windows/queries/domain_names_queried_via_lolbins_and_with_low_occurence_frequency.toml @@ -1,22 +1,26 @@ [hunt] author = "Elastic" +description = """ +This hunt looks for DNS queries performed by commonly abused Microsoft binaries that perform remote file transfer or binary proxy execution. Aggregations for the number of occurrences is limited to one host to reduce the number of potentially legitimate hits. +""" integration = ["endpoint", "windows"] uuid = "ebf8eb13-c98a-4d2c-8bdb-3f72a3a3961b" -name = "Doamin Names queries via Lolbins and with low occurence frequency" +name = "DNS Queries via LOLBins with Low Occurence Frequency" language = "ES|QL" license = "Elastic License v2" notes = [ - "Utilities like curl and SSL verification web-servvices are noisy, while others are rare like scripting utilities and are worth further investigation.", - "Connection to legit domains like github, discord, telegram and many other legit web-services by lolbins is still suspicious and require further investigation.", + "Utilities like curl and SSL verification for web services are noisy, while others are rare such as scripting utilities and are worth further investigation.", + "Connection to legit domains like Github, Discord, Telegram and many other legit web services by LOLBins is still suspicious and require further investigation.", ] -mitre = [ "T1071",] - -query = ''' +mitre = [ "T1071"] +query = [ +''' from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day and host.os.family == "windows" and event.category == "network" and - event.action in ("lookup_requested", "DNSEvent (DNS query)") and - process.name in ("powershell.exe", "rundll32.exe", "certutil.exe", "curl.exe", "wget.exe", "CertReq.exe", "bitsadmin.exe", "mshta.exe", "pwsh.exe", "wmic.exe", "wscript.exe", "cscript.exe", "msbuild.exe", "regsvr32.exe", "MSBuild.exe", "InstallUtil.exe", "RegAsm.exe", "RegSvcs.exe", "msxsl.exe", "CONTROL.EXE", "Microsoft.Workflow.Compiler.exe", "msiexec.exe") and dns.question.name rlike """.+\.[a-z-A-Z]{2,3}""" +| where @timestamp > now() - 7 day and host.os.family == "windows" and event.category == "network" and + event.action in ("lookup_requested", "DNSEvent (DNS query)") and + process.name in ("powershell.exe", "rundll32.exe", "certutil.exe", "curl.exe", "wget.exe", "CertReq.exe", "bitsadmin.exe", "mshta.exe", "pwsh.exe", "wmic.exe", "wscript.exe", "cscript.exe", "msbuild.exe", "regsvr32.exe", "MSBuild.exe", "InstallUtil.exe", "RegAsm.exe", "RegSvcs.exe", "msxsl.exe", "CONTROL.EXE", "Microsoft.Workflow.Compiler.exe", "msiexec.exe") and dns.question.name rlike """.+\.[a-z-A-Z]{2,3}""" | keep process.name, dns.question.name, host.id | stats occurrences = count(*), hosts = count_distinct(host.id) by process.name, dns.question.name | where hosts == 1 -''' \ No newline at end of file +''', +] \ No newline at end of file diff --git a/hunting/windows/queries/drivers_load_with_low_occurrence_frequency.toml b/hunting/windows/queries/drivers_load_with_low_occurrence_frequency.toml new file mode 100644 index 000000000..e4c7d0a23 --- /dev/null +++ b/hunting/windows/queries/drivers_load_with_low_occurrence_frequency.toml @@ -0,0 +1,45 @@ +[hunt] +author = "Elastic" +description = """ +This hunt helps identify drivers loaded once on a unique host and with a unique hash over a 15 day period of time. Advanced adversaries may leverage legit vulnerable driver to tamper with existing defences or execute code in Kernel mode. +""" +integration = ["endpoint", "windows", "system"] +uuid = "99818ad6-c242-4da7-a41a-df64fe7314d6" +name = "Low Occurrence of Drivers Loaded on Unique Hosts" +language = "ES|QL" +license = "Elastic License v2" +notes = [ + "This hunt has three optional queries, one for Elastic Defend data, another for Sysmon data and the last one for Windows 7045 events.", + "Further investigation can be done pivoting by `dll.pe.imphash` or `dll.name.`", + "`dll.Ext.relative_file_creation_time` is used in the first query to limit the result to recently dropped drivers (populated in Elastic Defend).", + "Aggregation can also be done by `dll.hash.sha256` / `file.hash.sha256` but will return more results.", + "Bring Your Own Vulnerable Driver (BYOVD) are all signed and not malicious, further investigation should be done to check the surrounding events (service creation, process that dropped the driver etc.).", +] +mitre = [ "T1068"] +query = [ +''' +from logs-endpoint.events.library-* +| where @timestamp > now() - 15 day +| where host.os.family == "windows" and event.category == "driver" and event.action == "load" and dll.Ext.relative_file_creation_time <= 900 +| stats host_count = count_distinct(host.id), total_count = count(*), hash_count = count_distinct(dll.hash.sha256) by dll.name, dll.pe.imphash +| where host_count == 1 and total_count == 1 and hash_count == 1 +''', +''' +from logs-windows.sysmon_operational-* +| where @timestamp > now() - 15 day +| where host.os.family == "windows" and event.category == "driver" +| stats host_count = count_distinct(host.id), total_count = count(*), hash_count = count_distinct(file.hash.sha256) by file.name +| where host_count == 1 and total_count == 1 and hash_count == 1 +''', +''' +from logs-system.system-* +| where @timestamp > now() - 15day +| where host.os.family == "windows" and event.code == "7045" and + winlog.event_data.ServiceType == "kernel mode driver" +| eval ServiceFileName = replace(winlog.event_data.ImagePath, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") +| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName +| where hosts == 1 and cc == 1 +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/drivers_load_with_low_occurrence_frequency_elastic_defend.toml b/hunting/windows/queries/drivers_load_with_low_occurrence_frequency_elastic_defend.toml deleted file mode 100644 index baa16b712..000000000 --- a/hunting/windows/queries/drivers_load_with_low_occurrence_frequency_elastic_defend.toml +++ /dev/null @@ -1,22 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint"] -uuid = "99818ad6-c242-4da7-a41a-df64fe7314d6" -name = "Drivers Load with low occurrence frequency - Elastic Defend" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt helps identify drivers loaded once, on a unique host and with a unique hash over a 15 days period of time. Further investigation can be done pivoting by dll.pe.imphash or dll.name. Advanced adversaries may leverage legit vulnerable driver to tamper with existing defences or execute code in Kernel mode.", - "dll.Ext.relative_file_creation_time is used in the first query to limit the result to recently dropped drivers (populated in Elastic Defend).", - "aggregation can be done also by dll.hash.sha256 / file.hash.sha256 but will return more results.", - "Bring Your Own Vulnerable Driver (BYOVD) are all signed and not malicious, further investigation should be done to check the surrounding events (service creation, process that dropped the driver etc.).", -] -mitre = [ "T1068",] - -query = ''' -from logs-endpoint.events.library-* -| where @timestamp > now() - 15 day -| where host.os.family == "windows" and event.category == "driver" and event.action == "load" and dll.Ext.relative_file_creation_time <= 900 -| stats host_count = count_distinct(host.id), total_count = count(*), hash_count = count_distinct(dll.hash.sha256) by dll.name, dll.pe.imphash -| where host_count == 1 and total_count == 1 and hash_count == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/drivers_load_with_low_occurrence_frequency_sysmon.toml b/hunting/windows/queries/drivers_load_with_low_occurrence_frequency_sysmon.toml deleted file mode 100644 index 3e9ed7877..000000000 --- a/hunting/windows/queries/drivers_load_with_low_occurrence_frequency_sysmon.toml +++ /dev/null @@ -1,22 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["windows"] -uuid = "6bb90aba-af6b-4128-a9b2-160e164a15ff" -name = "Drivers Load with low occurrence frequency - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt helps identify drivers loaded once, on a unique host and with a unique hash over a 15 days period of time. Further investigation can be done pivoting by dll.pe.imphash or dll.name. Advanced adversaries may leverage legit vulnerable driver to tamper with existing defences or execute code in Kernel mode.", - "dll.Ext.relative_file_creation_time is used in the first query to limit the result to recently dropped drivers (populated in Elastic Defend).", - "aggregation can be done also by dll.hash.sha256 / file.hash.sha256 but will return more results.", - "Bring Your Own Vulnerable Driver (BYOVD) are all signed and not malicious, further investigation should be done to check the surrounding events (service creation, process that dropped the driver etc.).", -] -mitre = [ "T1068",] - -query = ''' -from logs-windows.sysmon_operational-* -| where @timestamp > now() - 15 day -| where host.os.family == "windows" and event.category == "driver" -| stats host_count = count_distinct(host.id), total_count = count(*), hash_count = count_distinct(file.hash.sha256) by file.name -| where host_count == 1 and total_count == 1 and hash_count == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/drivers_load_with_low_occurrence_frequency_windows_7045.toml b/hunting/windows/queries/drivers_load_with_low_occurrence_frequency_windows_7045.toml deleted file mode 100644 index 6fe4224ae..000000000 --- a/hunting/windows/queries/drivers_load_with_low_occurrence_frequency_windows_7045.toml +++ /dev/null @@ -1,26 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["system"] -uuid = "bc4848ce-5323-42b4-a559-3333c11ca938" -name = "Drivers Load with low occurrence frequency - Windows 7045" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt helps identify drivers loaded once, on a unique host and with a unique hash over a 15 days period of time. Further investigation can be done pivoting by dll.pe.imphash or dll.name. Advanced adversaries may leverage legit vulnerable driver to tamper with existing defences or execute code in Kernel mode.", - "dll.Ext.relative_file_creation_time is used in the first query to limit the result to recently dropped drivers (populated in Elastic Defend).", - "aggregation can be done also by dll.hash.sha256 / file.hash.sha256 but will return more results.", - "Bring Your Own Vulnerable Driver (BYOVD) are all signed and not malicious, further investigation should be done to check the surrounding events (service creation, process that dropped the driver etc.).", -] -mitre = [ "T1068",] - -query = ''' -from logs-system.system-* -| where @timestamp > now() - 15day -| where host.os.family == "windows" and event.code == "7045" and - winlog.event_data.ServiceType == "kernel mode driver" -| eval ServiceFileName = replace(winlog.event_data.ImagePath, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") -| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName -| where hosts == 1 and cc == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/excessive_rdp_network_activity_by_source_host_and_user-_elastic_defend_sysmon.toml b/hunting/windows/queries/excessive_rdp_network_activity_by_source_host_and_user-_elastic_defend_sysmon.toml deleted file mode 100644 index d57b7cd51..000000000 --- a/hunting/windows/queries/excessive_rdp_network_activity_by_source_host_and_user-_elastic_defend_sysmon.toml +++ /dev/null @@ -1,24 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint", "windows"] -uuid = "fe01a8a5-6367-4c4c-a57b-be513ab80e42" -name = "Excessive RDP Network Activity by Source Host and User- Elastic Defend - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt looks for high number of Remote Desktop connections from same host and user.name to more than a defined threshold of unique destination Ip addresses. This could be a sign of discovery or lateral movement via the Remote Desktop Protocol.", - "Further investigation can done pivoting by host.id and user name.", - "Depending on normal SysAdmin RDP activity the 10 threshold can be adjusted to reduce normal noisy activity.", -] -mitre = [ "T1021", "T1021.001",] - -query = ''' -from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "network" and network.direction == "egress" and - network.transport == "tcp"and destination.port == 3389 and source.port >= 49152 -| keep destination.ip, host.id, user.name -| stats count_unique_dst = count_distinct(destination.ip) by host.id, user.name - /* threshold set to 10 but can be adjusted to reduce normal baseline in your env */ -| where count_unique_dst >= 10 -''' \ No newline at end of file diff --git a/hunting/windows/queries/excessive_rdp_network_activity_by_source_host_and_user.toml b/hunting/windows/queries/excessive_rdp_network_activity_by_source_host_and_user.toml new file mode 100644 index 000000000..607fc20fa --- /dev/null +++ b/hunting/windows/queries/excessive_rdp_network_activity_by_source_host_and_user.toml @@ -0,0 +1,37 @@ +[hunt] +author = "Elastic" +description = """ +This hunt looks for a high occurrence of remote desktop connections from the same host and user. The number of unique destination IP addresses is compared to a defined threshold. This could be a sign of discovery or lateral movement via the Remote Desktop Protocol (RDP). +""" +integration = ["endpoint", "windows"] +uuid = "fe01a8a5-6367-4c4c-a57b-be513ab80e42" +name = "Excessive RDP Network Activity by Host and User" +language = "ES|QL" +license = "Elastic License v2" +notes = [ + "Further investigation can done pivoting by `host.id` and `user.name`.", + "Depending on normal SysAdmin RDP activity, the threshold of 10 can be adjusted to reduce normal noisy activity.", + "The second query uses Windows Security log event ID 4624 to summarize numbers of RDP connections by `source.ip` and `user.name` and duration." +] +mitre = [ "T1021", "T1021.001"] + +query = [ +''' +from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "network" and network.direction == "egress" and + network.transport == "tcp"and destination.port == 3389 and source.port >= 49152 +| keep destination.ip, host.id, user.name +| stats count_unique_dst = count_distinct(destination.ip) by host.id, user.name + /* threshold set to 10 but can be adjusted to reduce normal baseline in your env */ +| where count_unique_dst >= 10 +''', +''' +from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "network" and process.name == "svchost.exe" and network.direction == "ingress" and + network.transport == "tcp"and destination.port == 3389 and source.port >= 49152 +| stats agents = count_distinct(host.id) by source.ip +| where agents >= 10 +''' +] diff --git a/hunting/windows/queries/excessive_rdp_network_activity_by_source_host_elastic_defend_sysmon.toml b/hunting/windows/queries/excessive_rdp_network_activity_by_source_host_elastic_defend_sysmon.toml deleted file mode 100644 index 3b5d03a41..000000000 --- a/hunting/windows/queries/excessive_rdp_network_activity_by_source_host_elastic_defend_sysmon.toml +++ /dev/null @@ -1,21 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint", "windows"] -uuid = "6ff3a518-3bf4-4e7d-9a66-2ef7aaa68cfc" -name = "Excessive RDP Network Activity by Source Host - Elastic Defend - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt looks for high number of Remote Desktop connections from same host and user.name to more than a defined threshold of unique destination Ip addresses. This could be a sign of discovery or lateral movement via the Remote Desktop Protocol.", - "Further investigation can done pivoting by host.id and user name.", - "Depending on normal SysAdmin RDP activity the 10 threshold can be adjusted to reduce normal noisy activity.",] -mitre = [ "T1021", "T1021.001",] - -query = ''' -from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "network" and process.name == "svchost.exe" and network.direction == "ingress" and - network.transport == "tcp"and destination.port == 3389 and source.port >= 49152 -| stats agents = count_distinct(host.id) by source.ip -| where agents >= 10 -''' \ No newline at end of file diff --git a/hunting/windows/queries/excessive_smb_network_activity_by_process_id.toml b/hunting/windows/queries/excessive_smb_network_activity_by_process_id.toml index 5ca5fce97..8292137eb 100644 --- a/hunting/windows/queries/excessive_smb_network_activity_by_process_id.toml +++ b/hunting/windows/queries/excessive_smb_network_activity_by_process_id.toml @@ -1,23 +1,26 @@ [hunt] author = "Elastic" +description = """ +This hunt looks for a high occurrence of SMB connections from the same process by unique destination IP addresses. The number of unique destination IP addresses is compared to a defined threshold. This could be a sign of SMB scanning or lateral movement via remote services that depend on the SMB protocol. +""" integration = ["endpoint", "windows"] uuid = "6949135b-76d7-47a3-ae95-ef482508fb7c" -name = "Excessive SMB Network Activity by process Id" +name = "Excessive SMB Network Activity by Process ID" language = "ES|QL" license = "Elastic License v2" notes = [ - "This hunt looks for high number of SMB connections from same process to more than a defined threshold of unique destination Ip addresses. This could be a sign of SMB scanning or some lateral movement via remote services that depend on SMB protocol.", - "Further investigation can done pivoting by process.entity_id and host.id.", - "Maximum number of unique destination.ip by process can be adjusted to your environment to reduce normal noisy hosts by Id.",] + "Further investigation can done pivoting by `process.entity_id` and `host.id.`", + "Maximum number of unique `destination.ip` by process can be adjusted to your environment to reduce normal noisy hosts by process ID.",] mitre = [ "T1021", "T1021.002",] - -query = ''' -from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day and - host.os.family == "windows" and event.category == "network" and network.direction == "egress" and +query = [ +''' +from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day and + host.os.family == "windows" and event.category == "network" and network.direction == "egress" and network.transport == "tcp"and destination.port == 445 and source.port >= 49152 and process.pid == 4 | keep destination.ip, process.entity_id, host.id | stats count_unique_dst = count_distinct(destination.ip) by process.entity_id, host.id /* threshold set to 20 but can be adjusted to reduce normal baseline in your env */ | where count_unique_dst >= 20 -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/executable_file_creation_by_an_unusual_microsoft_binary.toml b/hunting/windows/queries/executable_file_creation_by_an_unusual_microsoft_binary.toml new file mode 100644 index 000000000..94df6676a --- /dev/null +++ b/hunting/windows/queries/executable_file_creation_by_an_unusual_microsoft_binary.toml @@ -0,0 +1,39 @@ +[hunt] +author = "Elastic" +description = """ +This hunt identifies executable file creation by an unusual Microsoft native binary. This could be the result of +code injection or some other form of exploitation for defense evasion. +""" +integration = ["endpoint", "windows"] +uuid = "3b2900fe-74d9-4c49-b3df-cbeceb02e841" +name = "Executable File Creation by an Unusual Microsoft Binary" +language = "ES|QL" +license = "Elastic License v2" +notes = [ + "This hunt includes two optional queries, one for Elastic Defend data and another for Sysmon data.", + "Sysmon file events don't populate file header and process code signature information thus we use `file.extension`.", + "Some exploits may result in the creation of an executable file by the exploited process.", + "Further investigation can be done by pivoting on `process.executable` and filtering for executable file creation.", +] +mitre = [ "T1211", "T1055",] +query = [ +''' +from logs-endpoint.events.file-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "file" and event.action == "creation" and + starts_with(file.Ext.header_bytes, "4d5a") and process.code_signature.status == "trusted" and + starts_with(process.code_signature.subject_name, "Microsoft") and process.executable rlike """[c-fC-F]:\\Windows\\(System32|SysWOW64)\\[a-zA-Z0-9_]+.exe""" +| keep process.executable, host.id +| stats occurences = count(*), agents = count_distinct(host.id) by process.executable +| where agents == 1 and occurences <= 10 +''', +''' +from logs-windows.sysmon_operational-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "file" and event.action == "FileCreate" and + file.extension in ("exe", "dll") and process.executable rlike """[c-fC-F]:\\Windows\\(System32|SysWOW64)\\[a-zA-Z0-9_]+.exe""" +| keep process.executable, host.id +| stats occurences = count(*), agents = count_distinct(host.id) by process.executable +| where agents == 1 and occurences <= 10 +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/executable_file_creation_by_an_unusual_microsoft_binary_elastic_defend.toml b/hunting/windows/queries/executable_file_creation_by_an_unusual_microsoft_binary_elastic_defend.toml deleted file mode 100644 index 12708944b..000000000 --- a/hunting/windows/queries/executable_file_creation_by_an_unusual_microsoft_binary_elastic_defend.toml +++ /dev/null @@ -1,24 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint"] -uuid = "3b2900fe-74d9-4c49-b3df-cbeceb02e841" -name = "Executable File creation by an Unusual Microsoft Binary - Elastic Defend" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "Sysmon file event don't populate file header and process code signature information thus the use of file.extension.", - "Some exploits may result in the creation of an executable file by the exploited process.", - "Further investigation can be done pivoting by process.executable and filter for executable file creation.", -] -mitre = [ "T1211", "T1055",] - -query = ''' -from logs-endpoint.events.file-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "file" and event.action == "creation" and - starts_with(file.Ext.header_bytes, "4d5a") and process.code_signature.status == "trusted" and - starts_with(process.code_signature.subject_name, "Microsoft") and process.executable rlike """[c-fC-F]:\\Windows\\(System32|SysWOW64)\\[a-zA-Z0-9_]+.exe""" -| keep process.executable, host.id -| stats occurences = count(*), agents = count_distinct(host.id) by process.executable -| where agents == 1 and occurences <= 10 -''' \ No newline at end of file diff --git a/hunting/windows/queries/executable_file_creation_by_an_unusual_microsoft_binary_sysmon.toml b/hunting/windows/queries/executable_file_creation_by_an_unusual_microsoft_binary_sysmon.toml deleted file mode 100644 index e8454e89c..000000000 --- a/hunting/windows/queries/executable_file_creation_by_an_unusual_microsoft_binary_sysmon.toml +++ /dev/null @@ -1,23 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["windows"] -uuid = "36c94354-9d6e-4dc5-b2aa-a7cf578a4169" -name = "Executable File creation by an Unusual Microsoft Binary - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "Sysmon file event don't populate file header and process code signature information thus the use of file.extension.", - "Some exploits may result in the creation of an executable file by the exploited process.", - "Further investigation can be done pivoting by process.executable and filter for executable file creation.", -] -mitre = [ "T1211", "T1055",] - -query = ''' -from logs-windows.sysmon_operational-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "file" and event.action == "FileCreate" and - file.extension in ("exe", "dll") and process.executable rlike """[c-fC-F]:\\Windows\\(System32|SysWOW64)\\[a-zA-Z0-9_]+.exe""" -| keep process.executable, host.id -| stats occurences = count(*), agents = count_distinct(host.id) by process.executable -| where agents == 1 and occurences <= 10 -''' \ No newline at end of file diff --git a/hunting/windows/queries/execution_via_network_logon_by_occurrence_frequency.toml b/hunting/windows/queries/execution_via_network_logon_by_occurrence_frequency.toml deleted file mode 100644 index 5da7f9f9b..000000000 --- a/hunting/windows/queries/execution_via_network_logon_by_occurrence_frequency.toml +++ /dev/null @@ -1,24 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint"] -uuid = "fd3f9982-fd8c-4f0f-bbe6-e589752c34db" -name = "Execution via Network Logon by occurrence frequency" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "process.Ext.session_info.* is populated for Elastic Defend version 8.6 and above.", - "Execution via legit Microsoft processes like powershell and cmd need to further investigated via aggregation by process.command_line.", - "Aggregation can be also done by process.executable, normalizing process path by removing random patterns using the REPLACE function via regex.", -] -mitre = [ "T1021",] - -query = ''' -from logs-endpoint.events.process-* -| where @timestamp > now() - 7 day and host.os.family == "windows" and - event.category == "process" and event.action == "start" and - /* network logon type and the execution is within 30 seconds of the logon time */ - process.Ext.session_info.logon_type == "Network" and process.Ext.session_info.relative_logon_time <= 30 -| stats total = count(*), hosts = count_distinct(host.id) by process.hash.sha256, process.Ext.session_info.client_address, user.name, process.parent.name - /* unique hash limited to one host and number of execution is 1 */ -| where hosts == 1 and total == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/execution_via_network_logon_by_occurrence_frequency_by_top_source_ip.toml b/hunting/windows/queries/execution_via_network_logon_by_occurrence_frequency_by_top_source_ip.toml index ab7da37ec..0f05183e6 100644 --- a/hunting/windows/queries/execution_via_network_logon_by_occurrence_frequency_by_top_source_ip.toml +++ b/hunting/windows/queries/execution_via_network_logon_by_occurrence_frequency_by_top_source_ip.toml @@ -1,24 +1,40 @@ [hunt] author = "Elastic" +description = """ +This hunt aggregates process execution and remote network logons by source address, account name and process hash. It then limits the results by unique host within a 7 day period. This may indicate lateral movement via remote services. +""" integration = ["endpoint"] uuid = "ae07c580-290e-4421-add8-d6ca30509b6a" -name = "Execution via Network Logon by occurrence frequency by top Source IP" +name = "Frequency of Process Execution via Network Logon by Source Address" language = "ES|QL" license = "Elastic License v2" notes = [ - "process.Ext.session_info.* is populated for Elastic Defend version 8.6 and above.", - "Execution via legit Microsoft processes like powershell and cmd need to further investigated via aggregation by process.command_line.", - "Aggregation can be also done by process.executable, normalizing process path by removing random patterns using the REPLACE function via regex.", + "The second query highest occurrence of source addresses/accounts performing remote process execution", + "`process.Ext.session_info.*` is populated for Elastic Defend versions 8.6.0+ and above.", + "Execution via legitimate Microsoft processes for PowerShell and cmd need to be further investigated via aggregation by `process.command_line`.", + "Aggregation can be also done by `process.executable`, normalizing process path by removing random patterns using the ES|QL REPLACE function.", ] mitre = [ "T1021",] -query = ''' +query = [ +''' from logs-endpoint.events.process-* -| where @timestamp > now() - 7 day and host.os.family == "windows" and - event.category == "process" and event.action == "start" and +| where @timestamp > now() - 7 day and host.os.family == "windows" and + event.category == "process" and event.action == "start" and /* network logon type and the execution is within 30 seconds of the logon time */ process.Ext.session_info.logon_type == "Network" and process.Ext.session_info.relative_logon_time <= 30 | stats total = count(*) by process.Ext.session_info.client_address, user.name /* sort by top source.ip and account */ | sort total desc -''' \ No newline at end of file +''', +''' +from logs-endpoint.events.process-* +| where @timestamp > now() - 7 day and host.os.family == "windows" and + event.category == "process" and event.action == "start" and + /* network logon type and the execution is within 30 seconds of the logon time */ + process.Ext.session_info.logon_type == "Network" and process.Ext.session_info.relative_logon_time <= 30 +| stats total = count(*), hosts = count_distinct(host.id) by process.hash.sha256, process.Ext.session_info.client_address, user.name, process.parent.name + /* unique hash limited to one host and number of execution is 1 */ +| where hosts == 1 and total == 1 +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/execution_via_remote_services_by_client_address.toml b/hunting/windows/queries/execution_via_remote_services_by_client_address.toml index 623506451..b84ccd666 100644 --- a/hunting/windows/queries/execution_via_remote_services_by_client_address.toml +++ b/hunting/windows/queries/execution_via_remote_services_by_client_address.toml @@ -1,23 +1,28 @@ [hunt] author = "Elastic" +description = """ +This hunt aggregates process execution via remote network logon by source address, account name and where the parent process is related to remote services such as WMI, WinRM, DCOM and remote PowerShell. This may indicate lateral movement via remote services. +""" integration = ["endpoint"] uuid = "e6e54717-2676-4785-a4a6-503577bfb0ea" name = "Execution via Remote Services by Client Address" language = "ES|QL" license = "Elastic License v2" notes = [ - "process.Ext.session_info.* is populated for Elastic Defend version 8.6 and above.", + "`process.Ext.session_info.*` is populated for Elastic Defend versions 8.6.0+.", ] -mitre = [ "T1021", "T1021.003", "T1021.006", "T1047",] +mitre = [ "T1021", "T1021.003", "T1021.006", "T1047"] -query = ''' +query = [ +''' from logs-endpoint.events.process-* -| where @timestamp > now() - 7 day and host.os.family == "windows" and - event.category == "process" and event.action == "start" and +| where @timestamp > now() - 7 day and host.os.family == "windows" and + event.category == "process" and event.action == "start" and /* network logon type */ - process.Ext.session_info.logon_type == "Network" and + process.Ext.session_info.logon_type == "Network" and (process.parent.name .caseless in ("wmiprvse.exe", "wsmprovhost.exe", "winrshost.exe") or (process.parent.name == "svchost.exe" and process.parent.args == "DcomLaunch")) | stats total = count(*), hosts = count_distinct(host.id) by process.Ext.session_info.client_address, user.name, process.parent.name /* sort by top source.ip and account */ | sort total desc -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/execution_via_startup_with_low_occurrence_frequency.toml b/hunting/windows/queries/execution_via_startup_with_low_occurrence_frequency.toml index b01195d19..f31fe0239 100644 --- a/hunting/windows/queries/execution_via_startup_with_low_occurrence_frequency.toml +++ b/hunting/windows/queries/execution_via_startup_with_low_occurrence_frequency.toml @@ -1,22 +1,25 @@ [hunt] author = "Elastic" +description = """ +This hunt identifies programs started shortly after user logon and presence limited to a unique host. Run registry key and Startup folder cause programs to run each time that a user logs on and are often abused by malwares to maintain persistence on an endpoint. +""" integration = ["endpoint"] uuid = "a447df80-d3d5-48b3-a175-a864264ec487" -name = "Execution via Startup with low occurrence frequency" +name = "Startup Execution with Low Occurrence Frequency by Unique Host" language = "ES|QL" license = "Elastic License v2" notes = [ - "Items set to persist via Startup like Run key and Startup folder will be executed by Explorer.exe shortly after user logon (process.Ext.session_info.relative_logon_time help us to capture that time difference).", - "Pay close attention to unknown hashes, suspicious paths and lolbins.", + "Items set to persist via Startup such as Run keys and Startup folder will be executed by `Explorer.exe` shortly after user logon (`process.Ext.session_info.relative_logon_time` helps us to capture that time difference).", + "Special attention to unknown hashes, suspicious paths and LOLBins should be given.", ] mitre = [ "T1547", "T1547.001",] - -query = ''' +query = [ +''' from logs-endpoint.events.process-* -| where host.os.family == "windows" and event.category == "process" and event.action == "start" and +| where host.os.family == "windows" and event.category == "process" and event.action == "start" and /* programs started shortly after user logon like startup items */ - process.parent.executable.caseless == "c:\\windows\\explorer.exe" and process.Ext.session_info.relative_logon_time <= 100 and - not starts_with(process.executable, "C:\\Program Files") and not starts_with(process.executable, "C:\\Windows\\System32\\DriverStore\\FileRepository\\") and + process.parent.executable.caseless == "c:\\windows\\explorer.exe" and process.Ext.session_info.relative_logon_time <= 100 and + not starts_with(process.executable, "C:\\Program Files") and not starts_with(process.executable, "C:\\Windows\\System32\\DriverStore\\FileRepository\\") and /* this hunt is scoped to unsigned or untrusted code-sig or Microsoft signed binaries to not miss lolbins */ (process.code_signature.exists == false or process.code_signature.trusted == false or starts_with(process.code_signature.subject_name, "Microsoft")) | keep process.executable, host.id, process.hash.sha256 @@ -24,4 +27,5 @@ from logs-endpoint.events.process-* | eval process_path = replace(process_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~' ]+\\""", "C:\\\\users\\\\user\\\\") | stats hosts = count_distinct(host.id) by process_path, process.hash.sha256 | where hosts == 1 -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent.toml b/hunting/windows/queries/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent.toml new file mode 100644 index 000000000..41aa38bf7 --- /dev/null +++ b/hunting/windows/queries/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent.toml @@ -0,0 +1,45 @@ +[hunt] +author = "Elastic" +description = """ +This hunt looks for unique process execution via Windows Management Instrumentation (WMI) by removing random patterns from `process.command_line` and aggregating execution by count of agents with the same command line to limit results to unique ones. +""" +integration = ["endpoint", "windows", "system"] +uuid = "b5efeb92-9b51-45b9-839f-be4cdc054ef4" +name = "Low Frequency of Process Execution via WMI by Unique Agent" +language = "ES|QL" +license = "Elastic License v2" +notes = [ + "This hunt contains three queries for Elastic Defend, Sysmon, and Windows Security event 4688."] +mitre = [ "T1047"] +query = [ +''' +from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day and + host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation") and + process.parent.name == "WmiPrvSE.exe" and starts_with(process.code_signature.subject_name, "Microsoft") +| keep process.hash.sha256, host.id, process.name +| stats agents = count_distinct(host.id) by process.name +| where agents == 1 +''', +''' +from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-system.security-* +| where @timestamp > now() - 7 day and + host.os.family == "windows" and event.category == "process" and + event.action in ("start", "Process creation", "created-process") and + to_lower(process.parent.name) == "wmiprvse.exe" +| keep process.command_line, host.id +| eval cmdline = replace(process.command_line, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| eval cmdline = replace(cmdline, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| stats agents = count_distinct(host.id) by cmdline +| where agents == 1 +''', +''' +from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day and + host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation") and + process.parent.name.caseless == "wmiprvse.exe" and (process.code_signature.exists == false or process.code_signature.trusted == false) +| keep process.hash.sha256, host.id, process.name +| stats agents = count_distinct(host.id) by process.hash.sha256 +| where agents == 1 +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent_elastic_defend_sysmon.toml b/hunting/windows/queries/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent_elastic_defend_sysmon.toml deleted file mode 100644 index 54b188623..000000000 --- a/hunting/windows/queries/execution_via_windows_management_instrumentation_by_occurrence_frequency_by_unique_agent_elastic_defend_sysmon.toml +++ /dev/null @@ -1,21 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint", "windows"] -uuid = "b5efeb92-9b51-45b9-839f-be4cdc054ef4" -name = "Execution via Windows Management Instrumentation by occurrence frequency by Unique Agent - Elastic Defend - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt looks for unique process execution via Windows Management Instrumentation by removing random patterns from process.command_line and aggregating execution by count of agents with same cmdline to limit result to unique ones.", - "This hunt is compatible with Sysmon, Elastic Defend and Windows Security event 4688.",] -mitre = [ "T1047",] - -query = ''' -from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day and - host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation") and - process.parent.name.caseless == "wmiprvse.exe" and starts_with(process.code_signature.subject_name, "Microsoft") -| keep process.hash.sha256, host.id, process.name -| stats agents = count_distinct(host.id) by process.name -| where agents == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon.toml b/hunting/windows/queries/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon.toml deleted file mode 100644 index d56344cf5..000000000 --- a/hunting/windows/queries/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon.toml +++ /dev/null @@ -1,22 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint", "windows"] -uuid = "958a9027-2c6f-4eb0-a9ca-d1116a3bec76" -name = "Execution via Windows Management Instrumentation by occurrence frequency - Elastic Defend - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt looks for unique process execution via Windows Management Instrumentation by removing random patterns from process.command_line and aggregating execution by count of agents with same cmdline to limit result to unique ones.", - "This hunt is compatible with Sysmon, Elastic Defend and Windows Security event 4688.", -] -mitre = [ "T1047",] - -query = ''' -from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day and - host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation") and - process.parent.name.caseless == "wmiprvse.exe" and (process.code_signature.exists == false or process.code_signature.trusted == false) -| keep process.hash.sha256, host.id, process.name -| stats agents = count_distinct(host.id) by process.hash.sha256 -| where agents == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon_windows_security.toml b/hunting/windows/queries/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon_windows_security.toml deleted file mode 100644 index 928f28637..000000000 --- a/hunting/windows/queries/execution_via_windows_management_instrumentation_by_occurrence_frequency_elastic_defend_sysmon_windows_security.toml +++ /dev/null @@ -1,25 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint", "windows", "system"] -uuid = "793d5655-d7d9-422a-ba9d-1fa75029265e" -name = "Execution via Windows Management Instrumentation by occurrence frequency - Elastic Defend - Sysmon - Windows Security" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt looks for unique process execution via Windows Management Instrumentation by removing random patterns from process.command_line and aggregating execution by count of agents with same cmdline to limit result to unique ones.", - "This hunt is compatible with Sysmon, Elastic Defend and Windows Security event 4688.", -] -mitre = [ "T1047",] - -query = ''' -from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-system.security-* -| where @timestamp > now() - 7 day and - host.os.family == "windows" and event.category == "process" and - event.action in ("start", "Process creation", "created-process") and - process.parent.name.caseless == "wmiprvse.exe" -| keep process.command_line, host.id -| eval cmdline = replace(process.command_line, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| eval cmdline = replace(cmdline, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| stats agents = count_distinct(host.id) by cmdline -| where agents == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/execution_via_windows_scheduled_task_with_low_occurrence_frequency.toml b/hunting/windows/queries/execution_via_windows_scheduled_task_with_low_occurrence_frequency.toml index 0602934bd..edd5db88e 100644 --- a/hunting/windows/queries/execution_via_windows_scheduled_task_with_low_occurrence_frequency.toml +++ b/hunting/windows/queries/execution_via_windows_scheduled_task_with_low_occurrence_frequency.toml @@ -1,24 +1,28 @@ [hunt] author = "Elastic" +description = """ +Aggregating by paths/hash, this hunt identifies rare instances where a program executes as a child process of the Tasks Scheduler service. This could be the result of persistence as a Windows Scheduled Task. +""" integration = ["endpoint", "windows"] uuid = "96d5afc8-1f25-4265-8a0e-9998091a2e1f" -name = "Execution via Windows Scheduled Task with low occurrence frequency" +name = "Low Frequency of Process Execution via Windows Scheduled Task by Unique Agent" language = "ES|QL" license = "Elastic License v2" notes = [ - "Windows security event 4688 lacks process.parent.command_line needed for this hunt to identify the Schedule svchost instance.", - "Unique process.hash.sha256 and agent is not necessarily malicious, this help surface ones worth further investigation.", + "Windows security event 4688 lacks `process.parent.command_line` needed for this hunt to identify the Schedule `svchost` instance.", + "Unique `process.hash.sha256` and agent is not necessarily malicious, however this helps surface signals worth further investigation.", ] -mitre = [ "T1053", "T1053.005",] - -query = ''' +mitre = [ "T1053", "T1053.005"] +query = [ +''' from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "process" and - event.action in ("start", "Process creation") and process.code_signature.trusted != true and +| where @timestamp > now(-) - 7 day +| where host.os.family == "windows" and event.category == "process" and + event.action in ("start", "Process creation") and process.code_signature.trusted != true and /* child process of the Tasks Schedule service */ process.parent.name == "svchost.exe" and ends_with(process.parent.command_line, "Schedule") | stats hosts = count_distinct(host.id) by process.hash.sha256, process.name /* unique hash observed in one unique agent */ | where hosts == 1 -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/execution_via_windows_services_with_low_occurrence_frequency.toml b/hunting/windows/queries/execution_via_windows_services_with_low_occurrence_frequency.toml new file mode 100644 index 000000000..bad8e38a7 --- /dev/null +++ b/hunting/windows/queries/execution_via_windows_services_with_low_occurrence_frequency.toml @@ -0,0 +1,39 @@ +[hunt] +author = "Elastic" +description = """ +This hunt looks for a low occurrence of process execution via the Windows Services Control Manager by unique agent. The Services Control Manager is responsible for starting, stopping, and interacting with system services. This could be a sign of persistence as a Windows service. +""" +integration = ["endpoint", "windows", "system"] +uuid = "858b7022-b587-4b95-afd6-8ce597bedce3" +name = "Low Occurence of Process Execution via Windows Services with Unique Agent" +language = "ES|QL" +license = "Elastic License v2" +notes = [ + "This hunt contains two queries for Elastic Defend and Windows Security event 4688.", + "Windows security event 4688 lacks code signature and hash information, hence the use of `process.executable` for aggregation.", + "Unique `process.hash.sha256` and agent is not necessarily malicious, this help surface ones worth further investigation.", + "Suspicious `process.executable` paths and LOLBins should be reviewed further.", +] +mitre = [ "T1543", "T1543.003"] +query = [ +''' +from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation") and + process.parent.name == "services.exe" and process.code_signature.trusted != true +| stats hosts = count_distinct(host.id) by process.hash.sha256, process.name + /* unique hash observed in one unique agent */ +| where hosts == 1 +''', +''' +from logs-system.security-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "process" and event.code == "4688" and + event.action == "created-process" and process.parent.name == "services.exe" +| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval process_path = replace(process_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") +| stats hosts = count_distinct(host.id) by process_path + /* unique path observed in one unique agent */ +| where hosts == 1 +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/execution_via_windows_services_with_low_occurrence_frequency_elastic_defend_sysmon.toml b/hunting/windows/queries/execution_via_windows_services_with_low_occurrence_frequency_elastic_defend_sysmon.toml deleted file mode 100644 index d9a1dae27..000000000 --- a/hunting/windows/queries/execution_via_windows_services_with_low_occurrence_frequency_elastic_defend_sysmon.toml +++ /dev/null @@ -1,23 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint", "windows"] -uuid = "858b7022-b587-4b95-afd6-8ce597bedce3" -name = "Execution via Windows Services with low occurrence frequency - Elastic Defend - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "Windows security event 4688 lacks code signature and hash information, hence the use of process.executable for aggregation.", - "Unique process.hash.sha256 and agent is not necessarily malicious, this help surface ones worth further investigation.", - "Suspicious process.executable paths and lolbins should be reviewed further.", -] -mitre = [ "T1543", "T1543.003",] - -query = ''' -from logs-endpoint.events.process-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation") and - process.parent.name == "services.exe" and process.code_signature.trusted != true -| stats hosts = count_distinct(host.id) by process.hash.sha256, process.name - /* unique hash observed in one unique agent */ -| where hosts == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/execution_via_windows_services_with_low_occurrence_frequency_windows_security.toml b/hunting/windows/queries/execution_via_windows_services_with_low_occurrence_frequency_windows_security.toml deleted file mode 100644 index 621e174fc..000000000 --- a/hunting/windows/queries/execution_via_windows_services_with_low_occurrence_frequency_windows_security.toml +++ /dev/null @@ -1,25 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["system"] -uuid = "5fdc9f73-c6a4-4ea4-8e16-347ed675e236" -name = "Execution via Windows Services with low occurrence frequency - Windows Security" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "Windows security event 4688 lacks code signature and hash information, hence the use of process.executable for aggregation.", - "Unique process.hash.sha256 and agent is not necessarily malicious, this help surface ones worth further investigation.", - "Suspicious process.executable paths and lolbins should be reviewed further.", -] -mitre = [ "T1543", "T1543.003",] - -query = ''' -from logs-system.security-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.code == "4688" and - event.action == "created-process" and process.parent.name == "services.exe" -| eval process_path = replace(process.executable, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval process_path = replace(process_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") -| stats hosts = count_distinct(host.id) by process_path - /* unique path observed in one unique agent */ -| where hosts == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/high_count_of_network_connection_over_extended_period_by_process.toml b/hunting/windows/queries/high_count_of_network_connection_over_extended_period_by_process.toml new file mode 100644 index 000000000..44ae0c424 --- /dev/null +++ b/hunting/windows/queries/high_count_of_network_connection_over_extended_period_by_process.toml @@ -0,0 +1,65 @@ +[hunt] +author = "Elastic" +description = """ +This hunt identifies browser or svchost instances performing a considerable number of connections per hour over an extended period of hours to a specific destination address, limited to a unique host of the monitored agents. Browsers and svchost are both good targets for masquerading network traffic on the endpoint. +""" +integration = ["endpoint", "windows"] +uuid = "ed254a22-e7bb-4a36-9291-196b77762dd8" +name = "High Count of Network Connection Over Extended Period by Process" +language = "ES|QL" +license = "Elastic License v2" +notes = [ + "This hunt includes three queries for Elastic Defend and Sysmon data sources.", +] +mitre = [ "T1071"] +query = [ +''' +from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* +| where host.os.family == "windows" and event.category == "network" and + network.direction == "egress" and process.name in ("chrome.exe", "msedge.exe", "iexplore.exe", "firefox.exe", "svchost.exe") and + /* excluding DNS */ + destination.port != 53 and + /* excluding private IP ranges */ + not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") +| keep source.bytes, destination.address, process.name, process.entity_id, @timestamp, host.id + /* calc total duration and the number of connections per hour */ +| stats count_connections = count(*), start_time = min(@timestamp), end_time = max(@timestamp), hosts= count_distinct(host.id), count_unique_pids = count_distinct(process.entity_id) by destination.address, process.name +| eval dur = TO_DOUBLE(end_time)-TO_DOUBLE(start_time), duration_hours=TO_INT(dur/3600000), number_of_con_per_hour = (count_connections / duration_hours) +| keep process.name, duration_hours, destination.address, hosts, count_unique_pids, count_connections, number_of_con_per_hour + /* threshold is set to 120 connections per minute during 4 hours and limited to 1 agent and 1 pid, you can adjust this values to your hunting needs */ +| where number_of_con_per_hour >= 120 and duration_hours >= 4 and hosts == 1 and count_unique_pids == 1 +''', +''' +from logs-endpoint.events.network-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "network" and + network.direction == "egress" and +(process.code_signature.exists == false or process.code_signature.trusted != true or starts_with(process.executable, "C:\\Users\\Public\\")) and + /* excluding private IP ranges */ + not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") +| keep source.bytes, destination.address, process.name, process.entity_id, @timestamp + /* calc total duration , total MB out and the number of connections per hour */ +| stats total_bytes_out = sum(source.bytes), count_connections = count(*), start_time = min(@timestamp), end_time = max(@timestamp) by process.entity_id, destination.address, process.name +| eval dur = TO_DOUBLE(end_time)-TO_DOUBLE(start_time), duration_hours=TO_INT(dur/3600000), MB_out=TO_DOUBLE(total_bytes_out) / (1024*1024), number_of_con_per_hour = (count_connections / duration_hours) +| keep process.entity_id, process.name, duration_hours, destination.address, MB_out, count_connections, number_of_con_per_hour + /* threshold is set to 120 connections per minute , you can adjust it to your env/FP rate */ +| where duration_hours >= 1 and number_of_con_per_hour >= 120 +''', +''' +from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "network" and + network.direction == "egress" and (process.executable like "C:\\\\Windows\\\\System32*" or process.executable like "C:\\\\Windows\\\\SysWOW64\\\\*") and not user.id in ("S-1-5-19", "S-1-5-20") and +/* multiple Windows svchost services perform long term connection to MS ASN, can be covered in a dedicated hunt */ +not (process.name == "svchost.exe" and user.id == "S-1-5-18") and +/* excluding private IP ranges */ + not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") +| keep source.bytes, destination.address, process.name, process.entity_id, @timestamp + /* calc total duration , total MB out and the number of connections per hour */ +| stats total_bytes_out = sum(source.bytes), count_connections = count(*), start_time = min(@timestamp), end_time = max(@timestamp) by process.entity_id, destination.address, process.name +| eval dur = TO_DOUBLE(end_time)-TO_DOUBLE(start_time), duration_hours=TO_INT(dur/3600000), MB_out=TO_DOUBLE(total_bytes_out) / (1024*1024), number_of_con_per_hour = (count_connections / duration_hours) +| keep process.entity_id, process.name, duration_hours, destination.address, MB_out, count_connections, number_of_con_per_hour +/* threshold is set to 120 connections per minute , you can adjust it to your env/FP rate */ +| where duration_hours >= 1 and number_of_con_per_hour >= 120 +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network.toml b/hunting/windows/queries/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network.toml deleted file mode 100644 index 75556ed23..000000000 --- a/hunting/windows/queries/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network.toml +++ /dev/null @@ -1,28 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint"] -uuid = "76843f1f-404d-42b8-9c25-fcc14e270240" -name = "High count of network connection over extended period by process - Elastic Defend Network" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt aggregate by process Id and destination ip the number of connections per hour over a period of time greater than a defined threshold. The process paths are scoped to Microsoft signed binaries often injected or used as a lolbin to masquerade malicious execution. This could be a sign of long term network activity to perform command and control from an injected process. Scoped for unsigned processes or ones running from suspicious paths, the Sysmon network events don't include process code signature information", -] -mitre = [ "T1071",] - -query = ''' -from logs-endpoint.events.network-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "network" and - network.direction == "egress" and -(process.code_signature.exists == false or process.code_signature.trusted != true or starts_with(process.executable, "C:\\Users\\Public\\")) and - /* excluding private IP ranges */ - not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") -| keep source.bytes, destination.address, process.name, process.entity_id, @timestamp - /* calc total duration , total MB out and the number of connections per hour */ -| stats total_bytes_out = sum(source.bytes), count_connections = count(*), start_time = min(@timestamp), end_time = max(@timestamp) by process.entity_id, destination.address, process.name -| eval dur = TO_DOUBLE(end_time)-TO_DOUBLE(start_time), duration_hours=TO_INT(dur/3600000), MB_out=TO_DOUBLE(total_bytes_out) / (1024*1024), number_of_con_per_hour = (count_connections / duration_hours) -| keep process.entity_id, process.name, duration_hours, destination.address, MB_out, count_connections, number_of_con_per_hour - /* threshold is set to 120 connections per minute , you can adjust it to your env/FP rate */ -| where duration_hours >= 1 and number_of_con_per_hour >= 120 -''' \ No newline at end of file diff --git a/hunting/windows/queries/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network_sysmon.toml b/hunting/windows/queries/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network_sysmon.toml deleted file mode 100644 index 9362fdb25..000000000 --- a/hunting/windows/queries/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_network_sysmon.toml +++ /dev/null @@ -1,28 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint", "windows"] -uuid = "ed254a22-e7bb-4a36-9291-196b77762dd8" -name = "High count of network connection over extended period by process - Elastic Defend Network - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt identify browser or svchost instances performing a considerable number of connections per hour over an extended period of hours to a specific destination address and this is limited to a unique host of the monitored agents. Browsers and svchost are both good targets for masquerading network traffic on the endpoint.", -] -mitre = [ "T1071",] - -query = ''' -from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where host.os.family == "windows" and event.category == "network" and - network.direction == "egress" and process.name in ("chrome.exe", "msedge.exe", "iexplore.exe", "firefox.exe", "svchost.exe") and - /* excluding DNS */ - destination.port != 53 and - /* excluding private IP ranges */ - not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") -| keep source.bytes, destination.address, process.name, process.entity_id, @timestamp, host.id - /* calc total duration and the number of connections per hour */ -| stats count_connections = count(*), start_time = min(@timestamp), end_time = max(@timestamp), hosts= count_distinct(host.id), count_unique_pids = count_distinct(process.entity_id) by destination.address, process.name -| eval dur = TO_DOUBLE(end_time)-TO_DOUBLE(start_time), duration_hours=TO_INT(dur/3600000), number_of_con_per_hour = (count_connections / duration_hours) -| keep process.name, duration_hours, destination.address, hosts, count_unique_pids, count_connections, number_of_con_per_hour - /* threshold is set to 120 connections per minute during 4 hours and limited to 1 agent and 1 pid, you can adjust this values to your hunting needs */ -| where number_of_con_per_hour >= 120 and duration_hours >= 4 and hosts == 1 and count_unique_pids == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_sysmon.toml b/hunting/windows/queries/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_sysmon.toml deleted file mode 100644 index 68aa9903d..000000000 --- a/hunting/windows/queries/high_count_of_network_connection_over_extended_period_by_process_elastic_defend_sysmon.toml +++ /dev/null @@ -1,29 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint", "windows"] -uuid = "7ee9a5a7-3ce1-47eb-b15a-1b148299fcf0" -name = "High count of network connection over extended period by process - Elastic Defend - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt aggregate by process Id and destination ip the number of connections per hour over a period of time greater than a defined threshold. The process paths are scoped to Microsoft signed binaries often injected or used as a lolbin to masquerade malicious execution. This could be a sign of long term network activity to perform command and control from an injected process.", -] -mitre = [ "T1071",] - -query = ''' -from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "network" and - network.direction == "egress" and (process.executable like "C:\\\\Windows\\\\System32*" or process.executable like "C:\\\\Windows\\\\SysWOW64\\\\*") and not user.id in ("S-1-5-19", "S-1-5-20") and -/* multiple Windows svchost services perform long term connection to MS ASN, can be covered in a dedicated hunt */ -not (process.name == "svchost.exe" and user.id == "S-1-5-18") and -/* excluding private IP ranges */ - not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") -| keep source.bytes, destination.address, process.name, process.entity_id, @timestamp - /* calc total duration , total MB out and the number of connections per hour */ -| stats total_bytes_out = sum(source.bytes), count_connections = count(*), start_time = min(@timestamp), end_time = max(@timestamp) by process.entity_id, destination.address, process.name -| eval dur = TO_DOUBLE(end_time)-TO_DOUBLE(start_time), duration_hours=TO_INT(dur/3600000), MB_out=TO_DOUBLE(total_bytes_out) / (1024*1024), number_of_con_per_hour = (count_connections / duration_hours) -| keep process.entity_id, process.name, duration_hours, destination.address, MB_out, count_connections, number_of_con_per_hour -/* threshold is set to 120 connections per minute , you can adjust it to your env/FP rate */ -| where duration_hours >= 1 and number_of_con_per_hour >= 120 -''' \ No newline at end of file diff --git a/hunting/windows/queries/libraries_loaded_by_svchost_with_low_occurrence_frequency.toml b/hunting/windows/queries/libraries_loaded_by_svchost_with_low_occurrence_frequency.toml new file mode 100644 index 000000000..7858e9060 --- /dev/null +++ b/hunting/windows/queries/libraries_loaded_by_svchost_with_low_occurrence_frequency.toml @@ -0,0 +1,48 @@ +[hunt] +author = "Elastic" +description = """ +This hunt returns the SHA256 hash and the `dll.path` of unsigned libraries loaded by svchost where the presence of unique path/hash is limited to a unique host. Adversaries may use Windows service DLLs to maintain persistence or run with System privileges. +""" +integration = ["endpoint", "windows"] +uuid = "e37fe0b9-1b70-4800-8989-58bac5a0a9bb" +name = "Libraries Loaded by svchost with Low Occurrence Frequency" +language = "ES|QL" +license = "Elastic License v2" +notes = [ + "This hunt includes two queries to cover both Sysmon and Elastic Defend data sources.", + "The hunt uses Elastic Defend library events for an extra optional condition `dll.Ext.relative_file_creation_time` to scope if for recently dropped DLLs.", + "The `count_dlls_per_folder` variable filter is used to avoid cases where multiple DLLs with different names are loaded from same directory (often observed in FPs loaded multiple dependencies from same dir).", + "Pay close attention unknown hashes and suspicious paths, usually ServiceDLLs are located in trusted directories like `%programfiles%` and `system32/syswow64`.", +] +mitre = [ "T1543", "T1543.003"] +query = [ +''' +from logs-endpoint.events.library-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "library" and event.action == "load" and + process.name == "svchost.exe" and (dll.code_signature.trusted == false or dll.code_signature.exists == false) and dll.hash.sha256 like "?*" and + (dll.Ext.relative_file_creation_time <= 900 or dll.Ext.relative_file_name_modify_time <= 900) +| keep dll.name, dll.path, dll.hash.sha256, host.id +| eval dll_folder = substring(dll.path, 1, length(dll.path) - (length(dll.name) + 1)) + /* paths normalization by removing random patterns */ +| eval dll_path = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "replaced") +| eval dll_path = replace(dll_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") +| eval dll_path = replace(dll_path, """SoftwareDistribution\\Download\\[a-z0-9]+""", """SoftwareDistribution\\Download\\""") +| stats hosts = count_distinct(host.id), count_dlls_per_folder = count(dll_path) by dll_path, dll.name, dll.hash.sha256 +| where hosts == 1 and count_dlls_per_folder == 1 +''', +''' +from logs-windows.sysmon_operational-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "process" and event.action == "Image loaded" and + process.name == "svchost.exe" and file.code_signature.status != "Valid" and file.hash.sha256 like "?*" +| keep file.name, file.path, file.hash.sha256, host.id +| eval dll_folder = substring(file.path, 1, length(file.path) - (length(file.name) + 1)) +/* paths normalization by removing random patterns */ +| eval dll_path = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "replaced") +| eval dll_path = replace(dll_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") +| eval dll_path = replace(dll_path, """SoftwareDistribution\\Download\\[a-z0-9]+""", """SoftwareDistribution\\Download\\""") +| stats hosts = count_distinct(host.id), count_dlls_per_folder = count(dll_path) by dll_path, file.name, file.hash.sha256 +| where hosts == 1 and count_dlls_per_folder == 1 +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/libraries_loaded_by_svchost_with_low_occurrence_frequency_elastic_defend.toml b/hunting/windows/queries/libraries_loaded_by_svchost_with_low_occurrence_frequency_elastic_defend.toml deleted file mode 100644 index 09a479e75..000000000 --- a/hunting/windows/queries/libraries_loaded_by_svchost_with_low_occurrence_frequency_elastic_defend.toml +++ /dev/null @@ -1,29 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint"] -uuid = "e37fe0b9-1b70-4800-8989-58bac5a0a9bb" -name = "Libraries loaded by svchost with low occurrence frequency - Elastic Defend" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "The hunt using Elastic Defend library events uses an extra optional condition dll.Ext.relative_file_creation_time to scope if for recently dropped DLLs.", - "The count_dlls_per_folder variable filter is used to avoid cases where multiple DLLs with different names are loaded from same directory (often observed in FPs loaded multiple dependencies from same dir).", - "Pay close attention unknown hashes and suspicious paths, usually ServiceDLLs are located in trusted directories like %programfiles% and system32/syswow64.", -] -mitre = [ "T1543", "T1543.003",] - -query = ''' -from logs-endpoint.events.library-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "library" and event.action == "load" and - process.name == "svchost.exe" and (dll.code_signature.trusted == false or dll.code_signature.exists == false) and dll.hash.sha256 like "?*" and - (dll.Ext.relative_file_creation_time <= 900 or dll.Ext.relative_file_name_modify_time <= 900) -| keep dll.name, dll.path, dll.hash.sha256, host.id -| eval dll_folder = substring(dll.path, 1, length(dll.path) - (length(dll.name) + 1)) - /* paths normalization by removing random patterns */ -| eval dll_path = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "replaced") -| eval dll_path = replace(dll_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") -| eval dll_path = replace(dll_path, """SoftwareDistribution\\Download\\[a-z0-9]+""", """SoftwareDistribution\\Download\\""") -| stats hosts = count_distinct(host.id), count_dlls_per_folder = count(dll_path) by dll_path, dll.name, dll.hash.sha256 -| where hosts == 1 and count_dlls_per_folder == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/libraries_loaded_by_svchost_with_low_occurrence_frequency_sysmon.toml b/hunting/windows/queries/libraries_loaded_by_svchost_with_low_occurrence_frequency_sysmon.toml deleted file mode 100644 index 593e75175..000000000 --- a/hunting/windows/queries/libraries_loaded_by_svchost_with_low_occurrence_frequency_sysmon.toml +++ /dev/null @@ -1,28 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["windows"] -uuid = "1ae6bfd7-34ce-4d7b-b956-f12d3797ac68" -name = "Libraries loaded by svchost with low occurrence frequency - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "The hunt using Elastic Defend library events uses an extra optional condition dll.Ext.relative_file_creation_time to scope if for recently dropped DLLs.", - "The count_dlls_per_folder variable filter is used to avoid cases where multiple DLLs with different names are loaded from same directory (often observed in FPs loaded multiple dependencies from same dir).", - "Pay close attention unknown hashes and suspicious paths, usually ServiceDLLs are located in trusted directories like %programfiles% and system32/syswow64.", -] -mitre = [ "T1543", "T1543.003",] - -query = ''' -from logs-windows.sysmon_operational-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.action == "Image loaded" and - process.name == "svchost.exe" and file.code_signature.status != "Valid" and file.hash.sha256 like "?*" -| keep file.name, file.path, file.hash.sha256, host.id -| eval dll_folder = substring(file.path, 1, length(file.path) - (length(file.name) + 1)) -/* paths normalization by removing random patterns */ -| eval dll_path = replace(dll_folder, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "replaced") -| eval dll_path = replace(dll_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") -| eval dll_path = replace(dll_path, """SoftwareDistribution\\Download\\[a-z0-9]+""", """SoftwareDistribution\\Download\\""") -| stats hosts = count_distinct(host.id), count_dlls_per_folder = count(dll_path) by dll_path, file.name, file.hash.sha256 -| where hosts == 1 and count_dlls_per_folder == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/microsoft_office_child_processes_with_low_occurrence_frequency.toml b/hunting/windows/queries/microsoft_office_child_processes_with_low_occurrence_frequency.toml index ae4da0641..fe1709bbe 100644 --- a/hunting/windows/queries/microsoft_office_child_processes_with_low_occurrence_frequency.toml +++ b/hunting/windows/queries/microsoft_office_child_processes_with_low_occurrence_frequency.toml @@ -1,23 +1,27 @@ [hunt] author = "Elastic" +description = """ +This hunt looks for Microsoft Office child processes with low occurrence frequency. This could be a normal rare behavior as well as potential execution via a malicious document. Adversaries may use Microsoft Office applications to execute malicious code, such as macros, scripts, or other payloads. +""" integration = ["endpoint", "windows", "system"] uuid = "74b2e54b-7002-4201-83d6-7fd9bd5dcf0f" -name = "Microsoft Office Child Processes with low occurrence frequency" +name = "Microsoft Office Child Processes with Low Occurrence Frequency by Unique Agent" language = "ES|QL" license = "Elastic License v2" notes = [ - "Certain processes like WerFault.exe, dw20.exe and dwwin.exe are often related to application crash.", + "Certain processes like `WerFault.exe`, `dw20.exe` and `dwwin.exe` are often related to application crash.", "Closer attention should be attributed to lolbins and unsigned executables (Windows 4688 is not capturing process code signature information).", ] -mitre = [ "T1566", "T1566.001",] - -query = ''' +mitre = [ "T1566", "T1566.001"] +query = [ +''' from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-system.security-* -| where host.os.family == "windows" and @timestamp > NOW() - 15 day and - event.category == "process" and event.action in ("start", "Process creation", "created-process") and +| where host.os.family == "windows" and @timestamp > NOW() - 15 day and + event.category == "process" and event.action in ("start", "Process creation", "created-process") and process.parent.name.caseless in ("winword.exe", "excel.exe", "powerpnt.exe") and not starts_with(process.executable, "C:\\Program Files") // normalize user home profile paths | eval process_path = replace(process.executable.caseless, """[c]:\\[u][s][e][r][s]\\[a-zA-Z0-9\.\-\_\$]+\\""", "c:\\\\users\\\\user\\\\") -| stats occurrences = count(*), agents = count_distinct(agent.id) by process_path, process.parent.name +| stats occurrences = count(*), agents = count_distinct(agent.id) by process_path, process.parent.name | where occurrences == 1 and agents == 1 -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/network_discovery_via_sensitive_ports_by_unusual_process.toml b/hunting/windows/queries/network_discovery_via_sensitive_ports_by_unusual_process.toml index 927569779..5f80401f4 100644 --- a/hunting/windows/queries/network_discovery_via_sensitive_ports_by_unusual_process.toml +++ b/hunting/windows/queries/network_discovery_via_sensitive_ports_by_unusual_process.toml @@ -1,8 +1,11 @@ [hunt] author = "Elastic" +description = """ +This hunt looks for either processes connecting to multiple sensitive TCP ports (SMB, RDP, LDAP, Kerberos and ADWS), a high number of SMB/RDP connections to unique destinations or the same process connecting to both RDP and SMB (should be rare). +""" integration = ["endpoint", "windows"] uuid = "e0acab7d-30bd-4be0-9682-5c3457bbeb4f" -name = "Network Discovery via sensitive ports by unusual process" +name = "Network Discovery via Sensitive Ports by Unusual Process" language = "ES|QL" license = "Elastic License v2" notes = [ @@ -10,13 +13,13 @@ notes = [ "You can add more sensitive ports to the list like FTP, SSH and others.", "Elastic Network events include process code signature information, this can be added to filter out signed third party false positives.", ] -mitre = [ "T1021", "T1021.002", "T1021.001",] - -query = ''' -from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* +mitre = [ "T1021", "T1021.002", "T1021.001"] +query = [ +''' +from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* | where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "network" and network.direction == "egress" and - network.transport == "tcp"and destination.port in (3389, 445, 389, 9389, 88, 5985, 5986, 22) and source.port >= 49152 and +| where host.os.family == "windows" and event.category == "network" and network.direction == "egress" and + network.transport == "tcp"and destination.port in (3389, 445, 389, 9389, 88, 5985, 5986, 22) and source.port >= 49152 and process.pid != 4 | keep process.executable, destination.port, destination.ip, process.entity_id /* network events with SMB or RDP as a target */ @@ -24,4 +27,5 @@ from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* /* unique count by destination.port, number of distinct SMB and RDP destinations */ | stats count_unique_ports = count_distinct(destination.port), count_smb_dst = count_distinct(smb_dip), count_rdp_dst = count_distinct(rdp_dip) by process.entity_id, process.executable | where count_unique_ports >= 3 or count_rdp_dst >= 10 or count_smb_dst >= 10 or (count_rdp_dst >= 1 and count_rdp_dst >= 1) -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/pe_file_transfer_via_smb_admin_shares_by_agent.toml b/hunting/windows/queries/pe_file_transfer_via_smb_admin_shares_by_agent.toml index 243b98600..39a2bc1d8 100644 --- a/hunting/windows/queries/pe_file_transfer_via_smb_admin_shares_by_agent.toml +++ b/hunting/windows/queries/pe_file_transfer_via_smb_admin_shares_by_agent.toml @@ -1,21 +1,33 @@ [hunt] author = "Elastic" +description = """ +This hunt looks for a high number of executable file transfers via the SMB protocol by the same user or agent to more than a defined maxium threshold of targets. This could be a sign of lateral movement via the Windows Admin Shares. +""" integration = ["endpoint"] uuid = "3e66fc1a-2ea0-43a6-ba51-0280c693d152" -name = "PE File Transfer via SMB_Admin Shares by Agent" +name = "PE File Transfer via SMB_Admin Shares by Agent or User" language = "ES|QL" license = "Elastic License v2" notes = [ - "This hunt looks for high number of executable file transfer via the SMB protocol by the same user.name to more than a defined maxium threshold of targets. This could be a sign of lateral movement via the Windows Admin Shares.", - "Further investigation can done pivoting by host.id and user name.", + "Further investigation can done pivoting by `host.id` and `user.name`.", ] -mitre = [ "T1021", "T1021.002",] - -query = ''' +mitre = [ "T1021", "T1021.002"] +query = [ +''' from logs-endpoint.events.file-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "file" and event.action != "deletion" and process.pid == 4 and - starts_with(file.Ext.header_bytes, "4d5a*") and (starts_with(user.id, "S-1-5-21-") or starts_with(user.id, "S-1-12-1-")) +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "file" and event.action != "deletion" and process.pid == 4 and + starts_with(file.Ext.header_bytes, "4d5a*") and (starts_with(user.id, "S-1-5-21-") or starts_with(user.id, "S-1-12-1-")) | stats agents = count_distinct(host.id), total = count(*) by user.name | where agents == 1 and total <= 3 -''' \ No newline at end of file +''', +''' +from logs-endpoint.events.file-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "file" and event.action != "deletion" and process.pid == 4 and + starts_with(file.Ext.header_bytes, "4d5a*") and (starts_with(user.id, "S-1-5-21-") or starts_with(user.id, "S-1-12-1-")) +| stats agents = count_distinct(host.id), total = count(*) by user.name + /* threshold set to 10 but can be adjusted to reduce normal baseline in your env */ +| where agents >= 10 +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/pe_file_transfer_via_smb_admin_shares_by_user.toml b/hunting/windows/queries/pe_file_transfer_via_smb_admin_shares_by_user.toml deleted file mode 100644 index c55b0243f..000000000 --- a/hunting/windows/queries/pe_file_transfer_via_smb_admin_shares_by_user.toml +++ /dev/null @@ -1,22 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint"] -uuid = "ef9def35-0671-4599-8a18-5a1b833ef4c4" -name = "PE File Transfer via SMB_Admin Shares by User" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt looks for high number of executable file transfer via the SMB protocol by the same user.name to more than a defined maxium threshold of targets. This could be a sign of lateral movement via the Windows Admin Shares.", - "PE File Transfer via SMB/Admin Shares by User", -] -mitre = [ "T1021", "T1021.002",] - -query = ''' -from logs-endpoint.events.file-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "file" and event.action != "deletion" and process.pid == 4 and - starts_with(file.Ext.header_bytes, "4d5a*") and (starts_with(user.id, "S-1-5-21-") or starts_with(user.id, "S-1-12-1-")) -| stats agents = count_distinct(host.id), total = count(*) by user.name - /* threshold set to 10 but can be adjusted to reduce normal baseline in your env */ -| where agents >= 10 -''' \ No newline at end of file diff --git a/hunting/windows/queries/persistence_via_run_key_with_low_occurrence_frequency.toml b/hunting/windows/queries/persistence_via_run_key_with_low_occurrence_frequency.toml new file mode 100644 index 000000000..28cdf4285 --- /dev/null +++ b/hunting/windows/queries/persistence_via_run_key_with_low_occurrence_frequency.toml @@ -0,0 +1,45 @@ +[hunt] +author = "Elastic" +description = """ +Leveraging frequency based analysis and random values normalization, this hunt identifies instances where a program adds a persistence entry with rare values or are imited to unique hosts. Run registry key cause programs to run each time that a user logs on and are often abused by adversaries to maintain persistence on an endpoint. +""" +integration = ["endpoint", "windows"] +uuid = "1078e906-0485-482e-bcf3-7ee939e07020" +name = "Persistence via Run Key with Low Occurrence Frequency" +language = "ES|QL" +license = "Elastic License v2" +notes = [ + "This hunt includes two queries to cover both Sysmon and Elastic Defend data sources.", + "Sysmon registry events do not populate process code signature information (hence the separation of the queries).", + "Suspicious paths and LOLBins in the `registry.data.strings` value should be reviewed further.", +] +mitre = [ "T1547", "T1547.001"] +query = [ +''' +from logs-endpoint.events.registry-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "registry" and event.action == "modification" and + (process.code_signature.exists == false or starts_with(process.code_signature.subject_name, "Microsoft")) and + ends_with(registry.key,"\\Microsoft\\Windows\\CurrentVersion\\Run") and + not registry.data.strings rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.{1,2}[c-fC-F]:\\WINDOWS\\System32\\DriverStore\\FileRepository\\.+)""" +| keep registry.key, registry.data.strings, process.name, host.id + /* Paths normalization in registry.data.strings to ease aggregation */ +| eval registry_data = replace(registry.data.strings, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval registry_data = replace(registry_data, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats cc = count(*), hosts = count_distinct(host.id) by process.name, registry_data +| where hosts == 1 and cc == 1 +''', +''' +from logs-windows.sysmon_operational-* +| where @timestamp > NOW() - 7 day +| where host.os.family == "windows" and event.category == "registry" and event.action == "RegistryEvent (Value Set)" and + ends_with(registry.key,"\\Microsoft\\Windows\\CurrentVersion\\Run") and + not registry.data.strings rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.{1,2}[c-fC-F]:\\WINDOWS\\System32\\DriverStore\\FileRepository\\.+)""" +| keep registry.key, registry.data.strings, process.name, host.id + /* Paths normalization in registry.data.strings to ease aggregation */ +| eval registry_data = replace(registry.data.strings, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval registry_data = replace(registry_data, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats cc = count(*), hosts = count_distinct(host.id) by process.name, registry_data +| where hosts == 1 and cc == 1 +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/persistence_via_run_key_with_low_occurrence_frequency_elastic_defend.toml b/hunting/windows/queries/persistence_via_run_key_with_low_occurrence_frequency_elastic_defend.toml deleted file mode 100644 index c0a26f9a9..000000000 --- a/hunting/windows/queries/persistence_via_run_key_with_low_occurrence_frequency_elastic_defend.toml +++ /dev/null @@ -1,27 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint"] -uuid = "1078e906-0485-482e-bcf3-7ee939e07020" -name = "Persistence via Run Key with low occurrence frequency - Elastic Defend" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "Sysmon registry event don't populate process code signature information (hence the separation of the queries).", - "Suspicious paths and lolbins in the registry.data.strings value should be reviewed further.", -] -mitre = [ "T1547", "T1547.001",] - -query = ''' -from logs-endpoint.events.registry-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "registry" and event.action == "modification" and - (process.code_signature.exists == false or starts_with(process.code_signature.subject_name, "Microsoft")) and - ends_with(registry.key,"\\Microsoft\\Windows\\CurrentVersion\\Run") and - not registry.data.strings rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.{1,2}[c-fC-F]:\\WINDOWS\\System32\\DriverStore\\FileRepository\\.+)""" -| keep registry.key, registry.data.strings, process.name, host.id - /* Paths normalization in registry.data.strings to ease aggregation */ -| eval registry_data = replace(registry.data.strings, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval registry_data = replace(registry_data, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats cc = count(*), hosts = count_distinct(host.id) by process.name, registry_data -| where hosts == 1 and cc == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/persistence_via_run_key_with_low_occurrence_frequency_sysmon.toml b/hunting/windows/queries/persistence_via_run_key_with_low_occurrence_frequency_sysmon.toml deleted file mode 100644 index 1ec10dc55..000000000 --- a/hunting/windows/queries/persistence_via_run_key_with_low_occurrence_frequency_sysmon.toml +++ /dev/null @@ -1,26 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["windows"] -uuid = "cb2d8acc-123a-4578-bd33-7004c2be9843" -name = "Persistence via Run Key with low occurrence frequency - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "Sysmon registry event don't populate process code signature information (hence the separation of the queries).", - "Suspicious paths and lolbins in the registry.data.strings value should be reviewed further.", -] -mitre = [ "T1547", "T1547.001",] - -query = ''' -from logs-windows.sysmon_operational-* -| where @timestamp > NOW() - 7 day -| where host.os.family == "windows" and event.category == "registry" and event.action == "RegistryEvent (Value Set)" and - ends_with(registry.key,"\\Microsoft\\Windows\\CurrentVersion\\Run") and - not registry.data.strings rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.{1,2}[c-fC-F]:\\WINDOWS\\System32\\DriverStore\\FileRepository\\.+)""" -| keep registry.key, registry.data.strings, process.name, host.id - /* Paths normalization in registry.data.strings to ease aggregation */ -| eval registry_data = replace(registry.data.strings, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval registry_data = replace(registry_data, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats cc = count(*), hosts = count_distinct(host.id) by process.name, registry_data -| where hosts == 1 and cc == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/persistence_via_startup_with_low_occurrence_frequency.toml b/hunting/windows/queries/persistence_via_startup_with_low_occurrence_frequency.toml index a1019041f..65e1ed470 100644 --- a/hunting/windows/queries/persistence_via_startup_with_low_occurrence_frequency.toml +++ b/hunting/windows/queries/persistence_via_startup_with_low_occurrence_frequency.toml @@ -1,21 +1,22 @@ [hunt] author = "Elastic" +description = "Leveraging frequency based analysis and path normalization, this hunt identifies rare instances where a program adds a Startup persistence via file creation. Startup entries cause programs to run each time that a user logs on and are often abused by adversaries to maintain persistence on an endpoint." integration = ["endpoint", "windows"] uuid = "9d8c79fd-0006-4988-8aaa-d5f9b9a7df8e" -name = "Persistence via Startup with low occurrence frequency" +name = "Persistence via Startup with Low Occurrence Frequency by Unique Host" language = "ES|QL" license = "Elastic License v2" notes = [ - "Elastic Defend file event captures the process.code_signature information, this can be added to the hunt to limit to unsigned and Microsoft signed programs.", - "Unique file.name and limited to 1 agent is not necessarily malicious, this help surface ones worth further investigation.", - "Suspicious process.executable paths and lolbins should be reviewed further.", + "Elastic Defend file event captures the `process.code_signature` information, this can be added to the hunt to limit to unsigned and Microsoft signed programs.", + "Unique `file.name` and limited to one agent is not necessarily malicious, however helps surface ones worth further investigation.", + "Suspicious `process.executable` paths and LOLBins should be reviewed further.", ] -mitre = [ "T1547", "T1547.001",] - -query = ''' +mitre = [ "T1547", "T1547.001"] +query = [ +''' from logs-endpoint.events.file-*, logs-windows.sysmon_operational-default-* | where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "file" and event.action in ("creation", "FileCreate") and +| where host.os.family == "windows" and event.category == "file" and event.action in ("creation", "FileCreate") and file.path rlike """(C:\\Users\\.+\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\.+*|C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\StartUp\\.+)""" | keep process.executable, host.id, file.name /* Paths normalization in registry.data.strings to ease aggregation */ @@ -23,4 +24,5 @@ from logs-endpoint.events.file-*, logs-windows.sysmon_operational-default-* | eval process_path = replace(process_path, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") | stats number_hosts = count_distinct(host.id) by process_path, file.name | where number_hosts == 1 -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/persistence_via_suspicious_launch_agent_or_launch_daemon_with_low_occurrence.toml b/hunting/windows/queries/persistence_via_suspicious_launch_agent_or_launch_daemon_with_low_occurrence.toml index a11fc9dea..2e6cb0244 100644 --- a/hunting/windows/queries/persistence_via_suspicious_launch_agent_or_launch_daemon_with_low_occurrence.toml +++ b/hunting/windows/queries/persistence_via_suspicious_launch_agent_or_launch_daemon_with_low_occurrence.toml @@ -1,24 +1,27 @@ [hunt] author = "Elastic" +description = """ +This hunt looks for persistence via Launch agent or daemon where the distribution is limited to one unique host. +""" integration = ["endpoint"] uuid = "a7dcd1a1-2860-491e-8802-31169a607167" -name = "Persistence via Suspicious Launch Agent or Launch Daemon with low occurrence" +name = "Low Occurrence of Suspicious Launch Agent or Launch Daemon" language = "ES|QL" license = "Elastic License v2" notes = [ - "This hunt looks for persistence via Launch agent or daemon where the distribution is limited to one unique host.", - "Further investigation can done pivoting by Persistence.name and args.", + "Further investigation can done pivoting by `Persistence.name` and `args`.", ] -mitre = [ "T1547", "T1547.011", "T1543", "T1543.001", "T1543.004",] - -query = ''' +mitre = [ "T1547", "T1547.011", "T1543", "T1543.001", "T1543.004"] +query = [ +''' from logs-endpoint.events.file-* | where @timestamp > now() - 7 day -| where host.os.family == "macos" and event.category == "file" and event.action == "launch_daemon" and +| where host.os.family == "macos" and event.category == "file" and event.action == "launch_daemon" and (Persistence.runatload == true or Persistence.keepalive == true) and process.executable is not null | eval args = MV_CONCAT(Persistence.args, ",") /* normalizing users home profile */ | eval args = replace(args, """/Users/[a-zA-Z0-9ñ\.\-\_\$~ ]+/""", "/Users/user/") | stats agents = count_distinct(host.id), total = count(*) by process.name, Persistence.name, args | where starts_with(args, "/") and agents == 1 and total == 1 -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/potential_exfiltration_by_process_total_egress_bytes.toml b/hunting/windows/queries/potential_exfiltration_by_process_total_egress_bytes.toml index 972a4437c..48f192b0f 100644 --- a/hunting/windows/queries/potential_exfiltration_by_process_total_egress_bytes.toml +++ b/hunting/windows/queries/potential_exfiltration_by_process_total_egress_bytes.toml @@ -1,26 +1,31 @@ [hunt] author = "Elastic" +description = """ +Using aggregation and the ES|QL `SUM` function, this hunt identifies processes that performed egress connections with total bytes greater or equal to a defined maximum threshold. This may indicate exfiltration or long term command and control activity. +""" integration = ["endpoint"] uuid = "977d77f9-86e0-4df6-bdc7-aed87c048290" -name = "Potential Exfiltration by process total egress bytes" +name = "Egress Network Connections with Total Bytes Greater than Threshold" language = "ES|QL" license = "Elastic License v2" notes = [ - "This hunt is not compatible with Sysmon event 3 (Network connection) and Windows security event 5156 as both don't log source.bytes.", - "The use of host.os.family is to optimise the query and avoid timeout. You can duplicate the same query for other platforms (linux, macos etc.)", + "This hunt is not compatible with Sysmon event 3 (Network connection) and Windows security event 5156 as both don't log `source.bytes`.", + "The use of `host.os.family` is to optimise the query and avoid timeout. You can duplicate the same query for other platforms (linux, macos etc.)", "Based on limited testing it's recommended to set the query time window to 8 hours.", - "Pivoting by process.entity_id will allow further investigation (parent process, hash, child processes, other network events etc.).", + "Pivoting by `process.entity_id` will allow further investigation (parent process, hash, child processes, other network events etc.).", ] mitre = [ "T1071",] -query = ''' +query = [ +''' from logs-endpoint.events.network-* -| where @timestamp > now() - 8 hour -| where host.os.family == "windows" and event.category == "network" and - event.action == "disconnect_received" and +| where @timestamp > now() - 8 hour +| where host.os.family == "windows" and event.category == "network" and + event.action == "disconnect_received" and not CIDR_MATCH(destination.ip, "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.0.0/29", "192.0.0.8/32", "192.0.0.9/32", "192.0.0.10/32", "192.0.0.170/32", "192.0.0.171/32", "192.0.2.0/24", "192.31.196.0/24", "192.52.193.0/24", "192.168.0.0/16", "192.88.99.0/24", "224.0.0.0/4", "100.64.0.0/10", "192.175.48.0/24","198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4", "::1","FE80::/10", "FF00::/8") | keep source.bytes, destination.address, process.executable, process.entity_id | stats total_bytes_out = sum(source.bytes) by process.entity_id, destination.address, process.executable /* more than 1GB out by same process.pid in 8 hours */ | where total_bytes_out >= 1073741824 -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/rundll32_execution_aggregated_by_cmdline.toml b/hunting/windows/queries/rundll32_execution_aggregated_by_cmdline.toml index 0f970eb89..c266a04eb 100644 --- a/hunting/windows/queries/rundll32_execution_aggregated_by_cmdline.toml +++ b/hunting/windows/queries/rundll32_execution_aggregated_by_cmdline.toml @@ -1,25 +1,29 @@ [hunt] author = "Elastic" +description = """ +This hunt aggregate Rundll32 execution by normalized `process.command_line` and returns instances that are unique by frequency. Rundll32 is one of the most abused binaries to proxy execution of malicious commands and modules. +""" integration = ["endpoint", "windows", "system"] uuid = "30f37cd2-c1d8-4554-bb4a-ed76de9e6857" -name = "Rundll32 execution aggregated by cmdline" +name = "Rundll32 Execution Aggregated by Command Line" language = "ES|QL" license = "Elastic License v2" notes = [ "Execution of DLLs from suspicious paths or with suspicious export function names or from suspicious parent should be further reviewed.", - "Parents such as svchost, explorer.exe, wmiprvse.exe, winword.exe and others should be carefully reviewed.", + "Parents such as svchost, `explorer.exe`, `wmiprvse.exe`, `winword.exe` and others should be carefully reviewed.", ] -mitre = [ "T1127", "T1218", "T1218.011",] - -query = ''' +mitre = [ "T1127", "T1218", "T1218.011"] +query = [ +''' from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-system.security-* | where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation", "created-process") and - process.name.caseless == "rundll32.exe" and +| where host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation", "created-process") and + process.name.caseless == "rundll32.exe" and not process.command_line rlike """.*(zzzzInvokeManagedCustomActionOutOfProc|GeneralTel.dll,RunInUserCxt|ShOpenVerbApplication|davclnt.dll,DavSetCookie|FileProtocolHandler|EDGEHTML.dll|FirewallControlPanel.dll,ShowNotificationDialog|printui.dll,PrintUIEntryDPIAware|Program Files|SHCreateLocalServerRunDll|ImageView_Fullscreen|StatusMonitorEntryPoint|Control_RunDLL|HotPlugSafeRemovalDriveNotification|AppxDeploymentClient.dll|acproxy.dll,PerformAutochkOperations|CapabilityAccessManagerDoStoreMaintenance|dfshim.dll|display.dll,ShowAdapterSettings|ForceProxyDetectionOnNextRun|PfSvWsSwapAssessmentTask|acmigration.dll,ApplyMigrationShims|LenovoBatteryGaugePackage.dll|-localserver|DriverStore|CnmDxPEntryPoint|DeferredDelete|DeviceProperties_RunDLL|AppxDeploymentClient.dll|spool\\DRIVERS|printui.dll,PrintUIEntry|DfdGetDefaultPolicyAndSMART|cryptext.dll,CryptExt|WininetPlugin.dll|ClearMyTracksByProcess|SusRunTask|OpenURL|CleanupTemporaryState).*""" | keep process.parent.name, process.command_line, host.id | eval cmdline = replace(process.command_line, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") | eval cmdline = replace(cmdline, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") | stats hosts =count_distinct(host.id), total = count() by cmdline, process.parent.name | where hosts == 1 -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/scheduled_task_creation_by_action_via_registry.toml b/hunting/windows/queries/scheduled_task_creation_by_action_via_registry.toml index 5cea1a745..f473169b0 100644 --- a/hunting/windows/queries/scheduled_task_creation_by_action_via_registry.toml +++ b/hunting/windows/queries/scheduled_task_creation_by_action_via_registry.toml @@ -1,17 +1,19 @@ [hunt] author = "Elastic" +description = """ +This hunt looks for scheduled tasks creation by action using registry events. Scheduled tasks actions are saved under the TaskCache registry key in base64 encoded blob. Malware often abuse LOLBins to proxy execution or run executables from unusual paths, you can add more patterns to the query. +""" integration = ["endpoint"] uuid = "344c0690-ebc3-4794-b123-272a5c09c57b" -name = "Scheduled tasks creation by action via registry" +name = "Scheduled tasks Creation by Action via Registry" language = "ES|QL" license = "Elastic License v2" notes = [ - "This hunt aggregate created scheduled tasks by action using registry events.", - "Malware often abuse lolbins to proxy execution or run executables from unusual paths, you can add more patterns to the query.", + "Malware often abuse LOLBins to proxy execution or run executables from unusual paths, you can add more patterns to the query.", ] mitre = [ "T1053", "T1053.005"] - -query = ''' +query = [ +''' from logs-endpoint.events.registry-* | where @timestamp > now() - 7 day | where host.os.type == "windows" and event.category == "registry" and event.action == "modification" and @@ -25,3 +27,4 @@ from logs-endpoint.events.registry-* /* helps reduce result to instances limited to one agent */ | where count_agents == 1 ''' +] diff --git a/hunting/windows/queries/scheduled_tasks_creation_with_low_occurrence_frequency.toml b/hunting/windows/queries/scheduled_tasks_creation_for_unique_hosts_by_task_command.toml similarity index 70% rename from hunting/windows/queries/scheduled_tasks_creation_with_low_occurrence_frequency.toml rename to hunting/windows/queries/scheduled_tasks_creation_for_unique_hosts_by_task_command.toml index e6fc2b288..8d236b4c9 100644 --- a/hunting/windows/queries/scheduled_tasks_creation_with_low_occurrence_frequency.toml +++ b/hunting/windows/queries/scheduled_tasks_creation_for_unique_hosts_by_task_command.toml @@ -1,30 +1,34 @@ [hunt] author = "Elastic" +description = """ +Using aggregation and strings extraction, this hunt identifies instances where a scheduled task is created and set to run a command unique to a specific host. This could be the result of persistence as a Windows Scheduled Task. +""" integration = ["system"] uuid = "75804319-122c-4bdc-976e-d6355bca0d78" -name = "Scheduled tasks creation with low occurrence frequency" +name = "Scheduled Tasks Creation for Unique Hosts by Task Command" language = "ES|QL" license = "Elastic License v2" notes = [ "This hunt returns the aggregation of created tasks by task name, command to execute and number of hosts where this task is present.", - "Close attention should be paid to suspicious paths like C:\\Users\\Public and C:\\ProgramData\\ as well as lolbins.", + "Close attention should be paid to suspicious paths like `C:\\Users\\Public and C:\\ProgramData\\` as well as LOLBins.", ] -mitre = [ "T1053", "T1053.005",] - -query = ''' +mitre = [ "T1053", "T1053.005"] +query = [ +''' from logs-system.security-default-* | where @timestamp > now() - 7 day | where host.os.family == "windows" and event.code == "4698" and event.action == "scheduled-task-created" /* parsing unstructured data from winlog message to extract a scheduled task Exec command */ -| grok message "(?.+)" | eval Command = replace(Command, "(|)", "") +| grok message "(?.+)" | eval Command = replace(Command, "(|)", "") | where Command is not null /* normalise task name by removing usersid and uuid string patterns */ -| eval TaskName = replace(winlog.event_data.TaskName, """((-S-1-5-.*)|\{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\})""", "") +| eval TaskName = replace(winlog.event_data.TaskName, """((-S-1-5-.*)|\{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\})""", "") /* normalise task name by removing random patterns in a file path */ | eval Task_Command = replace(Command, """(ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") /* normalize user home profile path */ | eval Task_Command = replace(Task_Command, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9\.\-\_\$~]+\\""", "C:\\\\users\\\\user\\\\") | where Task_Command like "?*" and not starts_with(Task_Command, "C:\\Program Files") and not starts_with(Task_Command, "\"C:\\Program Files") -| stats tasks_count = count(*), hosts_count = count_distinct(host.id) by Task_Command, TaskName +| stats tasks_count = count(*), hosts_count = count_distinct(host.id) by Task_Command, TaskName | where hosts_count == 1 -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/suspicious_base64_encoded_powershell_commands.toml b/hunting/windows/queries/suspicious_base64_encoded_powershell_commands.toml index 4f6957b37..88e89486a 100644 --- a/hunting/windows/queries/suspicious_base64_encoded_powershell_commands.toml +++ b/hunting/windows/queries/suspicious_base64_encoded_powershell_commands.toml @@ -1,16 +1,20 @@ [hunt] author = "Elastic" +description = """ +This hunt identifies base64 encoded powershell commands in process start events and filters ones with suspicious keywords like downloaders and evasion related commands. +""" integration = ["endpoint", "windows", "system"] uuid = "8bf800de-b3a2-4b36-9484-7d9dae2a1992" name = "Suspicious Base64 Encoded Powershell Command" language = "ES|QL" license = "Elastic License v2" notes = [ - "This hunt decode base64 obfuscated powershell commands in process start events and filter ones with suspicious keywords like downloaders and evasion related commands.", + "This hunt can be expanded to include more evasion techniques and downloaders.", + "Pivoting by `agent.id` can provide more context on the affected hosts." ] mitre = [ "T1059", "T1059.001", "T1027", "T1027.010"] - -query = ''' +query = [ +''' from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-system.security-* | where host.os.type == "windows" and event.category == "process" and event.type == "start" and TO_LOWER(process.name) == "powershell.exe" and process.command_line rlike ".+ -(e|E).*" | keep agent.id, process.command_line @@ -23,3 +27,4 @@ from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-sys | where decoded_base64_cmdline rlike """.*(http|webclient|download|mppreference|sockets|bxor|.replace|reflection|assembly|load|bits|start-proc|iwr|frombase64).*""" | keep agent.id, process.command_line, decoded_base64_cmdline ''' +] diff --git a/hunting/windows/queries/suspicious_dns_txt_record_lookups_by_process.toml b/hunting/windows/queries/suspicious_dns_txt_record_lookups_by_process.toml index 9cbdc33cd..344f9675a 100644 --- a/hunting/windows/queries/suspicious_dns_txt_record_lookups_by_process.toml +++ b/hunting/windows/queries/suspicious_dns_txt_record_lookups_by_process.toml @@ -1,20 +1,23 @@ [hunt] author = "Elastic" +description = """ +Leveraging aggregation by process executable entities, this hunt identifies identifies a high number of DNS TXT record queries from same process. +Adversaries may leverage DNS TXT queries to stage malicious content or exfiltrate data. +""" integration = ["endpoint", "windows"] uuid = "0b7343f7-2d16-43c7-af28-9d1f012b1093" -name = "Suspicious DNS TXT Record lookups by process" +name = "Suspicious DNS TXT Record Lookups by Process" language = "ES|QL" license = "Elastic License v2" notes = [ - "This hunt returns a list of processes unique pids and executable path that performs a high number of DNS TXT lookups.", - "Pivoting by process.entity_id will allow further investigation (parent process, hash, child processes, other network events etc.).", + "This hunt returns a list of processes unique pids and executable paths that performs a high number of DNS TXT lookups.", + "Pivoting by `process.entity_id` will allow further investigation (parent process, hash, child processes, other network events etc.).", ] -mitre = [ "T1071", "T1071.004",] - +mitre = [ "T1071", "T1071.004"] query = ''' from logs-endpoint.events.network-*, logs-windows.sysmon_operational-* -| where host.os.family == "windows" and event.category == "network" and - event.action in ("lookup_requested", "DNSEvent (DNS query)") and +| where host.os.family == "windows" and event.category == "network" and + event.action in ("lookup_requested", "DNSEvent (DNS query)") and (dns.question.type == "TXT" or dns.answers.type == "TXT") and process.executable != "C:\\Windows\\system32\\svchost.exe" | keep process.executable, process.entity_id | stats occurrences = count(*) by process.entity_id, process.executable diff --git a/hunting/windows/queries/unique_windows_services_creation_by_servicefilename.toml b/hunting/windows/queries/unique_windows_services_creation_by_servicefilename.toml new file mode 100644 index 000000000..8bc916f29 --- /dev/null +++ b/hunting/windows/queries/unique_windows_services_creation_by_servicefilename.toml @@ -0,0 +1,64 @@ +[hunt] +author = "Elastic" +description = """ +This hunt aggregates created Windows services by service file name and distribution limited to unique hosts. Using the ES|QL `Replace` command we can also further remove random patterns to reduce results to interesting events. More investigation can be conducted on instance that looks suspicious based on service file path, names and LOLBins. +""" +integration = ["endpoint", "windows", "system"] +uuid = "ebf79207-16dc-44f8-b10c-317d4a034bad" +name = "Unique Windows Services Creation by Service File Name" +language = "ES|QL" +license = "Elastic License v2" +notes = [ + "This hunt also identifies services registry modification by unusual process based on number of hosts and occurrences history.", + "Windows event IDs 4697 and 7045 are used to identify service creation and modification.", +] +mitre = [ "T1543", "T1543.003"] + +query = [ +''' +from logs-endpoint.events.registry-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "registry" and event.action in ("modification", "RegistryEvent (Value Set)") and + registry.value in ("ServiceDLL", "ImagePath") and starts_with(registry.path, "HKLM\\SYSTEM\\") and + process.executable != "C:\\Windows\\System32\\services.exe" +| eval process_path = replace(process.executable, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats hosts = count_distinct(host.id), occurrences = count(*) by process_path +/* unique process.executable found in one agent */ +| where hosts == 1 and occurrences == 1 +''', +''' +from logs-endpoint.events.registry-*, logs-windows.sysmon_operational-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "registry" and event.action in ("modification", "RegistryEvent (Value Set)") and + registry.value in ("ServiceDLL", "ImagePath") and starts_with(registry.path, "HKLM\\SYSTEM\\") and + not registry.data.strings rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.*\\System32\\DriverStore\\FileRepository\\.+)""" +| eval ServiceFileName = replace(registry.data.strings, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") +| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName + /* unique ServiceFileName observed in 1 host*/ +| where hosts == 1 and cc == 1 +''', +''' +from logs-system.security-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.category == "configuration" and event.code == "4697" and + not winlog.event_data.ServiceFileName rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.*\\System32\\DriverStore\\FileRepository\\.+)""" +| eval ServiceFileName = replace(winlog.event_data.ServiceFileName, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") +| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName +| where hosts == 1 and cc == 1 +''', +''' +from logs-system.system-* +| where @timestamp > now() - 7 day +| where host.os.family == "windows" and event.code == "7045" and + not winlog.event_data.ImagePath rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.*\\System32\\DriverStore\\FileRepository\\.+)""" +| eval ServiceFileName = replace(winlog.event_data.ImagePath, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") +| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") +| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") +| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName +| where hosts == 1 and cc == 1 +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/unique_windows_services_creation_by_servicefilename_elastic_defend_registry_sysmon.toml b/hunting/windows/queries/unique_windows_services_creation_by_servicefilename_elastic_defend_registry_sysmon.toml deleted file mode 100644 index e7f69e6e1..000000000 --- a/hunting/windows/queries/unique_windows_services_creation_by_servicefilename_elastic_defend_registry_sysmon.toml +++ /dev/null @@ -1,23 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint", "windows"] -uuid = "ebf79207-16dc-44f8-b10c-317d4a034bad" -name = "Unique Windows Services Creation by ServiceFileName - Elastic Defend Registry - Sysmon" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt identify services registry modification by unusual process based on number of hosts and occurrences history.", -] -mitre = [ "T1543", "T1543.003",] - -query = ''' -from logs-endpoint.events.registry-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "registry" and event.action in ("modification", "RegistryEvent (Value Set)") and - registry.value in ("ServiceDLL", "ImagePath") and starts_with(registry.path, "HKLM\\SYSTEM\\") and - process.executable != "C:\\Windows\\System32\\services.exe" -| eval process_path = replace(process.executable, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats hosts = count_distinct(host.id), occurrences = count(*) by process_path -/* unique process.executable found in one agent */ -| where hosts == 1 and occurrences == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/unique_windows_services_creation_by_servicefilename_elastic_defend_sysmon_registry.toml b/hunting/windows/queries/unique_windows_services_creation_by_servicefilename_elastic_defend_sysmon_registry.toml deleted file mode 100644 index f13c7927f..000000000 --- a/hunting/windows/queries/unique_windows_services_creation_by_servicefilename_elastic_defend_sysmon_registry.toml +++ /dev/null @@ -1,25 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["endpoint", "windows"] -uuid = "688dc79d-f52a-49ad-829d-89343e68b0f7" -name = "Unique Windows Services Creation by ServiceFileName - Elastic Defend - Sysmon Registry" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt aggregates created Windows services by service file name and distribution limited to unique hosts. Using the Replace command we can also further remove random pattern to reduce results to interesting events. More investigation can be conducted on instance that looks suspicious based on service file path, names and lolbins.", -] -mitre = [ "T1543", "T1543.003",] - -query = ''' -from logs-endpoint.events.registry-*, logs-windows.sysmon_operational-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "registry" and event.action in ("modification", "RegistryEvent (Value Set)") and - registry.value in ("ServiceDLL", "ImagePath") and starts_with(registry.path, "HKLM\\SYSTEM\\") and - not registry.data.strings rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.*\\System32\\DriverStore\\FileRepository\\.+)""" -| eval ServiceFileName = replace(registry.data.strings, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") -| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName - /* unique ServiceFileName observed in 1 host*/ -| where hosts == 1 and cc == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/unique_windows_services_creation_by_servicefilename_windows_security_4697.toml b/hunting/windows/queries/unique_windows_services_creation_by_servicefilename_windows_security_4697.toml deleted file mode 100644 index 87d45175a..000000000 --- a/hunting/windows/queries/unique_windows_services_creation_by_servicefilename_windows_security_4697.toml +++ /dev/null @@ -1,23 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["system"] -uuid = "b6b14385-4ed2-44af-98fe-dad5b1581174" -name = "Unique Windows Services Creation by ServiceFileName - Windows Security 4697" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt aggregates created Windows services by service file name and distribution limited to unique hosts. Using the Replace command we can also further remove random pattern to reduce results to interesting events. More investigation can be conducted on instance that looks suspicious based on service file path, names and lolbins.", -] -mitre = [ "T1543", "T1543.003",] - -query = ''' -from logs-system.security-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "configuration" and event.code == "4697" and - not winlog.event_data.ServiceFileName rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.*\\System32\\DriverStore\\FileRepository\\.+)""" -| eval ServiceFileName = replace(winlog.event_data.ServiceFileName, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") -| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName -| where hosts == 1 and cc == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/unique_windows_services_creation_by_servicefilename_windows_security_7045.toml b/hunting/windows/queries/unique_windows_services_creation_by_servicefilename_windows_security_7045.toml deleted file mode 100644 index 3be2538e9..000000000 --- a/hunting/windows/queries/unique_windows_services_creation_by_servicefilename_windows_security_7045.toml +++ /dev/null @@ -1,23 +0,0 @@ -[hunt] -author = "Elastic" -integration = ["system"] -uuid = "1749a45b-98f0-4b27-8c2f-2287230e52b7" -name = "Unique Windows Services Creation by ServiceFileName - Windows Security 7045" -language = "ES|QL" -license = "Elastic License v2" -notes = [ - "This hunt aggregates created Windows services by service file name and distribution limited to unique hosts. Using the Replace command we can also further remove random pattern to reduce results to interesting events. More investigation can be conducted on instance that looks suspicious based on service file path, names and lolbins.", -] -mitre = [ "T1543", "T1543.003",] - -query = ''' -from logs-system.system-* -| where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.code == "7045" and - not winlog.event_data.ImagePath rlike """(.{1,2}[c-fC-F]:\\Program Files.+)|([c-fC-F]:\\Program Files.+)|(.*\\System32\\DriverStore\\FileRepository\\.+)""" -| eval ServiceFileName = replace(winlog.event_data.ImagePath, """([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\.tmp|DX[A-Z0-9]{3,4}\.tmp|7z[A-Z0-9]{3,5}\.tmp|[0-9\.\-\_]{3,})""", "") -| eval ServiceFileName = replace(ServiceFileName, """.inf_amd[a-z0-9]{5,}\\""", "_replaced_") -| eval ServiceFileName = replace(ServiceFileName, """[cC]:\\[uU][sS][eE][rR][sS]\\[a-zA-Z0-9ñ\.\-\_\$~ ]+\\""", "C:\\\\users\\\\user\\\\") -| stats cc = count(*), hosts = count_distinct(host.id) by ServiceFileName -| where hosts == 1 and cc == 1 -''' \ No newline at end of file diff --git a/hunting/windows/queries/windows_command_and_scripting_interpreter_from_unusual_parent.toml b/hunting/windows/queries/windows_command_and_scripting_interpreter_from_unusual_parent.toml index 2e0ba4e33..a4d0d9a8c 100644 --- a/hunting/windows/queries/windows_command_and_scripting_interpreter_from_unusual_parent.toml +++ b/hunting/windows/queries/windows_command_and_scripting_interpreter_from_unusual_parent.toml @@ -1,23 +1,27 @@ [hunt] author = "Elastic" +description = """ +This hunt looks for unusual Microsoft native processes spawning `cmd.exe`, `powershell.exe` or `conhost.exe` and limited to a unique host. This could be normal rare behavior as well as an interactive shell activity from an injected parent process to execute system commands. +""" integration = ["endpoint", "windows", "system"] uuid = "de929347-c04a-4a94-8be2-cbe87b25bb25" -name = "Windows Command and Scripting Interpreter from unusual parent" +name = "Windows Command and Scripting Interpreter from Unusual Parent Process" language = "ES|QL" license = "Elastic License v2" notes = [ - "Pivoting can be done via process.parent.name.", - "Certain Microsoft binaries like LSASS, winlogon, spoolsv and others should never spawn cmd.exe powershell.exe or conhost.exe, if so it's highly likely malicious.", + "Further pivoting can be done via `process.parent.name`.", + "Certain Microsoft binaries like LSASS, winlogon, spoolsv and others should never spawn `cmd.exe`, `powershell.exe` or `conhost.exe`, if so it's highly likely malicious.", ] -mitre = [ "T1059", "T1059.001", "T1059.003",] - -query = ''' +mitre = [ "T1059", "T1059.001", "T1059.003"] +query = [ +''' from logs-endpoint.events.process-*, logs-windows.sysmon_operational-*, logs-system.security-* | where @timestamp > now() - 7 day -| where host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation", "created-process") and - process.name.caseless in ("cmd.exe", "powershell.exe", "conhost.exe") and +| where host.os.family == "windows" and event.category == "process" and event.action in ("start", "Process creation", "created-process") and + process.name.caseless in ("cmd.exe", "powershell.exe", "conhost.exe") and (starts_with(process.parent.executable.caseless, "c:\\windows\\system32") or starts_with(process.parent.executable.caseless, "c:\\windows\\syswow64")) | keep process.name, process.parent.name, host.id | stats hosts = count_distinct(host.id), cc = count(*) by process.parent.name | where cc <= 10 and hosts == 1 -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/hunting/windows/queries/windows_logon_activity_by_source_ip.toml b/hunting/windows/queries/windows_logon_activity_by_source_ip.toml index 60049f100..454724128 100644 --- a/hunting/windows/queries/windows_logon_activity_by_source_ip.toml +++ b/hunting/windows/queries/windows_logon_activity_by_source_ip.toml @@ -1,26 +1,29 @@ [hunt] author = "Elastic" +description = """ +This hunt returns a summary of network logon activity by `source.ip` using Windows event IDs 4624 and 4625. The higher the number of failures, low success and multiple accounts the more suspicious the behavior is. +""" integration = ["system"] uuid = "7bdea198-eb09-4eca-ae3d-bfc3b52c89a9" -name = "Windows logon activity by source IP" +name = "Windows Logon Activity by Source IP" language = "ES|QL" license = "Elastic License v2" notes = [ - "This hunt returns the total number of failed logons, successful ones and the number of unique account names grouped by source.ip.", - "Pay close attention to IP addresses source of a high number of failures associated with low success attempts and high number of used accounts.", + "Pay close attention to IP address sources with a high number of failed connections associated with low success attempts and high number of user accounts.", ] -mitre = [ "T1110", "T1110.001", "T1110.003",] - -query = ''' +mitre = [ "T1110", "T1110.001", "T1110.003"] +query = [ +''' from logs-system.security-* | where @timestamp > now() - 7 day -| where host.os.family == "windows" and - event.category == "authentication" and event.action in ("logon-failed", "logged-in") and winlog.logon.type == "Network" and - source.ip is not null and - /* noisy failure status codes often associated to authentication misconfiguration */ +| where host.os.family == "windows" and + event.category == "authentication" and event.action in ("logon-failed", "logged-in") and winlog.logon.type == "Network" and + source.ip is not null and + /* noisy failure status codes often associated to authentication misconfiguration */ not (event.action == "logon-failed" and winlog.event_data.Status in ("0xC000015B", "0XC000005E", "0XC0000133", "0XC0000192")) | eval failed = case(event.action == "logon-failed", source.ip, null), success = case(event.action == "logged-in", source.ip, null) | stats count_failed = count(failed), count_success = count(success), count_user = count_distinct(winlog.event_data.TargetUserName) by source.ip /* below threshold should be adjusted to your env logon patterns */ | where count_failed >= 100 and count_success <= 10 and count_user >= 20 -''' \ No newline at end of file +''' +] \ No newline at end of file diff --git a/tests/test_hunt_data.py b/tests/test_hunt_data.py index e1ba3ed8a..2c39fccb4 100644 --- a/tests/test_hunt_data.py +++ b/tests/test_hunt_data.py @@ -17,12 +17,13 @@ class TestHunt(unittest.TestCase): example_toml = """ [hunt] author = "Elastic" + description = "Detects denial of service or resource exhaustion attacks." integration = "aws_bedrock.invocation" uuid = "dc181967-c32c-46c9-b84b-ec4c8811c6a0" name = "Denial of Service or Resource Exhaustion Attacks Detection" language = "ES|QL" license = "Elastic License v2" - query = 'SELECT * FROM logs' + query = ['SELECT * FROM logs'] notes = ["High token usage can strain system resources."] mitre = ["AML.T0034"] references = ["https://www.elastic.co"] @@ -43,6 +44,7 @@ class TestHunt(unittest.TestCase): toml_contents = toml_file.read_text() hunt = load_toml(toml_contents) self.assertTrue(hunt.author) + self.assertTrue(hunt.description) self.assertTrue(hunt.integration) self.assertTrue(hunt.uuid) self.assertTrue(hunt.name)