[New hunts] 50 ES|QL Windows Hunt Queries (#3642)

* [New Hunt] Initial add of Windows hunt queries

* Add markdown files

* Added license to schema and md generation

* add hunt index; minor tweaks to script

* minor tweaks from feedback

Co-authored-by: Jonhnathan <26856693+w0rk3r@users.noreply.github.com>
Co-authored-by: Samirbous <64742097+Samirbous@users.noreply.github.com>
Co-authored-by: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com>

* Update hunting/macos/queries/suspicious_network_connections_by_unsigned_macho.toml

Co-authored-by: Jonhnathan <26856693+w0rk3r@users.noreply.github.com>

* convert integrations to list

* Update script to generate integration links

* validate generated integrations links

* Update hunting/windows/docs/execution_via_remote_services_by_client_address.md

* Update hunting/windows/queries/execution_via_network_logon_by_occurrence_frequency_by_top_source_ip.toml

* Update hunting/windows/queries/execution_via_remote_services_by_client_address.toml

* Update hunting/windows/docs/execution_via_network_logon_by_occurrence_frequency_by_top_source_ip.md

* Update hunting/windows/queries/execution_via_network_logon_by_occurrence_frequency.toml

* Update hunting/windows/docs/execution_via_network_logon_by_occurrence_frequency.md

* update docs with naming information

* Create suspicious_base64_encoded_powershell_commands.toml

* Create scheduled_task_creation_by_action_via_registry.toml

* Create suspicious_base64_encoded_powershell_commands.md

* Create scheduled_task_creation_by_action_via_registry.md

* Update index.md

---------

Co-authored-by: brokensound77 <brokensound77@users.noreply.github.com>
Co-authored-by: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com>
Co-authored-by: Jonhnathan <26856693+w0rk3r@users.noreply.github.com>
Co-authored-by: Samirbous <64742097+Samirbous@users.noreply.github.com>
This commit is contained in:
Justin Ibarra
2024-06-12 09:09:09 -07:00
committed by GitHub
parent 89d89f15d2
commit 48e85439e0
116 changed files with 3466 additions and 17 deletions
+12
View File
@@ -16,6 +16,18 @@ These queries are designed for use with the Elastic Security platform, part of t
Contributing to the `hunting` folder is a great way to share your expertise and enhance the security community's capabilities. Heres how you can contribute:
### Names and Related Queries
All query names should be unique and descriptive. If a query's intent is identical or related to another query, consider
adding a suffix with the integration(s) to the name to indicate the relationship and distinguish them from each other.
Otherwise, the names do not require the integration, since it is already annotated within the `integration` field.
The filename should reflect the query name.
For example:
- `Detect DLL Hijack via Masquerading as Microsoft Native Libraries - Elastic Defend`
- `Detect DLL Hijack via Masquerading as Microsoft Native Libraries - Sysmon`
### Adding New Queries
- **TOML File Naming and Organization**: Ensure that any new queries are named descriptively and grouped by the type of threat they address. Place your TOML files inside the `queries` folder and ensure they are named in a way that reflects the nature of the threat or behavior they are designed to detect.
- **TOML Fields**: To ensure the hunt queries are consistent and comprehensive, it's important to structure the threat detection rules with specific fields. When contributing a new rule, please include the following fields in the TOML file to describe and configure the analytic:
+39 -5
View File
@@ -6,6 +6,7 @@
"""Lightweight builtin toml-markdown converter."""
import tomllib
import urllib3
from dataclasses import dataclass, field
from pathlib import Path
from typing import List, Optional
@@ -14,16 +15,24 @@ HUNTING_DIR = Path(__file__).parent
ATLAS_URL = "https://atlas.mitre.org/techniques/"
ATTACK_URL = "https://attack.mitre.org/techniques/"
# the standard link takes `integration.package` and converts the link to `integration/package`, however, there are
# some exceptions such as `aws_bedrock.invocation` which should be linked to `aws_bedrock` instead
# https://docs.elastic.co/integrations/aws_bedrock
STATIC_INTEGRATION_LINK_MAP = {
'aws_bedrock.invocation': 'aws_bedrock'
}
@dataclass
class Hunt:
"""Dataclass to represent a hunt."""
author: str
integration: str
integration: list[str]
uuid: str
name: str
language: str
license: str
query: str
notes: Optional[List[str]] = field(default_factory=list)
mitre: Optional[List[str]] = field(default_factory=list)
@@ -45,34 +54,59 @@ def load_all_toml(base_path: Path) -> List[tuple[Hunt, Path]]:
return hunts
def validate_link(link: str):
"""Validate and return the link."""
response = urllib3.request('get', link)
if response.status != 200:
raise ValueError(f"Invalid link: {link}")
def generate_integration_links(integrations: list[str]) -> list[str]:
base_url = 'https://docs.elastic.co/integrations'
generated = []
for integration in integrations:
if integration in STATIC_INTEGRATION_LINK_MAP:
link_str = STATIC_INTEGRATION_LINK_MAP[integration]
else:
link_str = integration.replace('.', '/')
link = f'{base_url}/{link_str}'
validate_link(link)
generated.append(f'[{integration}]({link})')
return generated
def convert_toml_to_markdown(hunt_config: Hunt, file_path: Path) -> str:
"""Convert Hunt to Markdown format."""
markdown = f"# {hunt_config.name}\n\n---\n\n"
markdown += "## Metadata\n\n"
markdown += f"- **Author:** {hunt_config.author}\n"
markdown += f"- **UUID:** `{hunt_config.uuid}`\n"
markdown += f"- **Integration:** `{hunt_config.integration}`\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"
if hunt_config.notes:
markdown += "## Notes\n\n" + "\n".join(f"- {note}" for note in hunt_config.notes)
if hunt_config.mitre:
markdown += "\n## MITRE ATT&CK Techniques\n\n" + "\n".join(
f"- [{tech}]({ATLAS_URL if tech.startswith('AML') else ATTACK_URL}"
f"{tech.replace('.', '/') if tech.startswith('T') else tech})\n"
f"{tech.replace('.', '/') if tech.startswith('T') else tech})"
for tech in hunt_config.mitre
)
if hunt_config.references:
markdown += "\n## References\n\n" + "\n".join(f"- {ref}" for ref in hunt_config.references)
markdown += f"\n- [{hunt_config.name}]({Path('../queries') / file_path.name})"
markdown += f"\n\n## License\n\n- `{hunt_config.license}`\n"
return markdown
def process_toml_files(base_path: Path) -> None:
"""Process all TOML files in the directory recursively and convert them to Markdown."""
hunts = load_all_toml(base_path)
index_content = "# List of Available Queries\n\nHere are the queries currently available:\n\n"
index_content = "# List of Available Queries\n\nHere are the queries currently available:"
directories = {}
for hunt_config, toml_file in hunts:
@@ -87,7 +121,7 @@ def process_toml_files(base_path: Path) -> None:
# Build index content
for folder, files in sorted(directories.items()):
index_content += f"## {folder}\n"
index_content += f"\n\n## {folder}\n"
for file_path, rule_name, language in sorted(files):
index_path = "./" + str(file_path)
index_content += f"- [{rule_name}]({index_path}) ({language})\n"
+59
View File
@@ -6,3 +6,62 @@ Here are the queries currently available:
- [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)
## macos
- [Suspicious Network Connections by Unsigned Mach-O](./macos/docs/suspicious_network_connections_by_unsigned_macho.md) (ES|QL)
## 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)
- [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)
@@ -6,7 +6,7 @@
- **Author:** Elastic
- **UUID:** `dc181967-c32c-46c9-b84b-ec4c8811c6a0`
- **Integration:** `aws_bedrock.invocation`
- **Integration:** [aws_bedrock.invocation](https://docs.elastic.co/integrations/aws_bedrock)
- **Language:** `ES|QL`
## Query
@@ -36,9 +36,12 @@ from logs-aws_bedrock.invocation-*
## MITRE ATT&CK Techniques
- [AML.T0034](https://atlas.mitre.org/techniques/AML.T0034)
## References
- 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)
- [Denial of Service or Resource Exhaustion Attacks Detection](../queries/llm_dos_resource_exhaustion_detection.toml)
## License
- `Elastic License v2`
@@ -6,7 +6,7 @@
- **Author:** Elastic
- **UUID:** `3708787b-811b-43b1-b2e7-c7276b8db48c`
- **Integration:** `aws_bedrock.invocation`
- **Integration:** [aws_bedrock.invocation](https://docs.elastic.co/integrations/aws_bedrock)
- **Language:** `ES|QL`
## Query
@@ -31,9 +31,12 @@ from logs-aws_bedrock.invocation-*
## MITRE ATT&CK Techniques
- [AML.T0029](https://atlas.mitre.org/techniques/AML.T0029)
## References
- 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)
- [Monitoring for Latency Anomalies](../queries/llm_latency_anomalies_detection.toml)
## License
- `Elastic License v2`
@@ -6,7 +6,7 @@
- **Author:** Elastic
- **UUID:** `8fabae86-7ed2-4006-9623-5db28164f374`
- **Integration:** `aws_bedrock.invocation`
- **Integration:** [aws_bedrock.invocation](https://docs.elastic.co/integrations/aws_bedrock)
- **Language:** `ES|QL`
## Query
@@ -31,9 +31,12 @@ from logs-aws_bedrock.invocation-*
## MITRE ATT&CK Techniques
- [AML.T0051](https://atlas.mitre.org/techniques/AML.T0051)
## References
- 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)
- [Sensitive Content Refusal Detection](../queries/llm_sensitive_content_refusal_detection.toml)
## License
- `Elastic License v2`
@@ -1,9 +1,10 @@
[hunt]
author = "Elastic"
integration = "aws_bedrock.invocation"
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 = '''
from logs-aws_bedrock.invocation-*
| WHERE @timestamp > NOW() - 1 DAY
@@ -1,9 +1,10 @@
[hunt]
author = "Elastic"
integration = "aws_bedrock.invocation"
integration = ["aws_bedrock.invocation"]
uuid = "3708787b-811b-43b1-b2e7-c7276b8db48c"
name = "Monitoring for Latency Anomalies"
language = "ES|QL"
license = "Elastic License v2"
query = '''
from logs-aws_bedrock.invocation-*
| WHERE @timestamp > NOW() - 1 DAY
@@ -1,9 +1,10 @@
[hunt]
author = "Elastic"
integration = "aws_bedrock.invocation"
integration = ["aws_bedrock.invocation"]
uuid = "8fabae86-7ed2-4006-9623-5db28164f374"
name = "Sensitive Content Refusal Detection"
language = "ES|QL"
license = "Elastic License v2"
query = '''
from logs-aws_bedrock.invocation-*
| WHERE @timestamp > NOW() - 1 DAY
@@ -0,0 +1,40 @@
# Suspicious Network Connections by Unsigned Mach-O
---
## Metadata
- **Author:** Elastic
- **UUID:** `44aff0e3-e0d7-4dca-a94f-2dd0b96f18bd`
- **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 == "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
/* 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 >= 8 and number_of_con_per_hour >= 120
```
## 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.
## MITRE ATT&CK Techniques
- [T1071](https://attack.mitre.org/techniques/T1071)
## License
- `Elastic License v2`
@@ -0,0 +1,27 @@
[hunt]
author = "Elastic"
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.",
]
mitre = ["T1071"]
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
/* 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 >= 8 and number_of_con_per_hour >= 120
'''
@@ -0,0 +1,34 @@
# CreateRemoteThread by source process with low occurrence
---
## Metadata
- **Author:** Elastic
- **UUID:** `0545f23f-84a7-4b88-9b5b-b8cfcfdc9276`
- **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 == "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
```
## 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.
## MITRE ATT&CK Techniques
- [T1055](https://attack.mitre.org/techniques/T1055)
## License
- `Elastic License v2`
@@ -0,0 +1,43 @@
# 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`
@@ -0,0 +1,44 @@
# 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`
@@ -0,0 +1,39 @@
# Detect masquerading attempts as native Windows binaries
---
## Metadata
- **Author:** Elastic
- **UUID:** `93a72542-a1f7-4407-9175-8f066343db60`
- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint)
- **Language:** `ES|QL`
## Query
```sql
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"
| keep process.name.caseless, process.executable.caseless, process.code_signature.subject_name, process.code_signature.trusted, process.code_signature.exists, host.id
/* system_bin contain Microsoft signed and located in system32 folder process names */
/* non_system_bin contain non Microsoft signed process names */
| eval system_bin = case(starts_with(process.executable.caseless, "c:\\windows\\system32") and starts_with(process.code_signature.subject_name, "Microsoft") and process.code_signature.trusted == true, process.name.caseless, null), non_system_bin = case(process.code_signature.exists == false or process.code_signature.trusted != true or not starts_with(process.code_signature.subject_name, "Microsoft"), process.name.caseless, null)
/* aggregate unique process name counts by process.name and host.id */
| 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.
- The queried index must capture process start events with code signature information (e.g. Windows event 4688 is not supported).
## MITRE ATT&CK Techniques
- [T1036](https://attack.mitre.org/techniques/T1036)
## License
- `Elastic License v2`
@@ -0,0 +1,40 @@
# 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`
@@ -0,0 +1,42 @@
# 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`
@@ -0,0 +1,37 @@
# 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`
@@ -0,0 +1,38 @@
# 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`
@@ -0,0 +1,34 @@
# Doamin Names queries via Lolbins and with low occurence frequency
---
## Metadata
- **Author:** Elastic
- **UUID:** `ebf8eb13-c98a-4d2c-8bdb-3f72a3a3961b`
- **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 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
```
## 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.
## MITRE ATT&CK Techniques
- [T1071](https://attack.mitre.org/techniques/T1071)
## License
- `Elastic License v2`
@@ -0,0 +1,34 @@
# 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`
@@ -0,0 +1,34 @@
# 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`
@@ -0,0 +1,38 @@
# 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`
@@ -0,0 +1,37 @@
# 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`
@@ -0,0 +1,35 @@
# 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`
@@ -0,0 +1,37 @@
# Excessive SMB Network Activity by process Id
---
## Metadata
- **Author:** Elastic
- **UUID:** `6949135b-76d7-47a3-ae95-ef482508fb7c`
- **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 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
```
## 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.
## MITRE ATT&CK Techniques
- [T1021](https://attack.mitre.org/techniques/T1021)
- [T1021.002](https://attack.mitre.org/techniques/T1021/002)
## License
- `Elastic License v2`
@@ -0,0 +1,37 @@
# 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`
@@ -0,0 +1,36 @@
# 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`
@@ -0,0 +1,36 @@
# 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`
@@ -0,0 +1,36 @@
# Execution via Network Logon by occurrence frequency by top Source IP
---
## Metadata
- **Author:** Elastic
- **UUID:** `ae07c580-290e-4421-add8-d6ca30509b6a`
- **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(*) by process.Ext.session_info.client_address, user.name
/* sort by top source.ip and account */
| sort total desc
```
## 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`
@@ -0,0 +1,38 @@
# Execution via Remote Services by Client Address
---
## Metadata
- **Author:** Elastic
- **UUID:** `e6e54717-2676-4785-a4a6-503577bfb0ea`
- **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 */
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
```
## Notes
- process.Ext.session_info.* is populated for Elastic Defend version 8.6 and above.
## MITRE ATT&CK Techniques
- [T1021](https://attack.mitre.org/techniques/T1021)
- [T1021.003](https://attack.mitre.org/techniques/T1021/003)
- [T1021.006](https://attack.mitre.org/techniques/T1021/006)
- [T1047](https://attack.mitre.org/techniques/T1047)
## License
- `Elastic License v2`
@@ -0,0 +1,40 @@
# Execution via Startup with low occurrence frequency
---
## Metadata
- **Author:** Elastic
- **UUID:** `a447df80-d3d5-48b3-a175-a864264ec487`
- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint)
- **Language:** `ES|QL`
## Query
```sql
from logs-endpoint.events.process-*
| 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
/* 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
| 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, process.hash.sha256
| where hosts == 1
```
## 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.
## MITRE ATT&CK Techniques
- [T1547](https://attack.mitre.org/techniques/T1547)
- [T1547.001](https://attack.mitre.org/techniques/T1547/001)
## License
- `Elastic License v2`
@@ -0,0 +1,34 @@
# 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`
@@ -0,0 +1,34 @@
# 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`
@@ -0,0 +1,37 @@
# 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`
@@ -0,0 +1,37 @@
# Execution via Windows Scheduled Task with low occurrence frequency
---
## Metadata
- **Author:** Elastic
- **UUID:** `96d5afc8-1f25-4265-8a0e-9998091a2e1f`
- **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.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
```
## 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.
## MITRE ATT&CK Techniques
- [T1053](https://attack.mitre.org/techniques/T1053)
- [T1053.005](https://attack.mitre.org/techniques/T1053/005)
## License
- `Elastic License v2`
@@ -0,0 +1,36 @@
# 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`
@@ -0,0 +1,38 @@
# 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`
@@ -0,0 +1,40 @@
# 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`
@@ -0,0 +1,40 @@
# 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`
@@ -0,0 +1,41 @@
# 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`
@@ -0,0 +1,42 @@
# 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`
@@ -0,0 +1,41 @@
# 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`
@@ -0,0 +1,36 @@
# Microsoft Office Child Processes with low occurrence frequency
---
## Metadata
- **Author:** Elastic
- **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`
## Query
```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")
// 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
| where occurrences == 1 and agents == 1
```
## Notes
- 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
- [T1566](https://attack.mitre.org/techniques/T1566)
- [T1566.001](https://attack.mitre.org/techniques/T1566/001)
## License
- `Elastic License v2`
@@ -0,0 +1,41 @@
# Network Discovery via sensitive ports by unusual process
---
## Metadata
- **Author:** Elastic
- **UUID:** `e0acab7d-30bd-4be0-9682-5c3457bbeb4f`
- **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 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 */
| eval smb_dip = case(destination.port == 445, destination.ip, null), rdp_dip = case(destination.port == 389, destination.ip, null)
/* 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)
```
## Notes
- The query thresholds for SMB or RDP need to be adjusted to your environment.
- 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 ATT&CK Techniques
- [T1021](https://attack.mitre.org/techniques/T1021)
- [T1021.002](https://attack.mitre.org/techniques/T1021/002)
- [T1021.001](https://attack.mitre.org/techniques/T1021/001)
## License
- `Elastic License v2`
@@ -0,0 +1,34 @@
# PE File Transfer via SMB_Admin Shares by Agent
---
## Metadata
- **Author:** Elastic
- **UUID:** `3e66fc1a-2ea0-43a6-ba51-0280c693d152`
- **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
| where agents == 1 and total <= 3
```
## 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.
## MITRE ATT&CK Techniques
- [T1021](https://attack.mitre.org/techniques/T1021)
- [T1021.002](https://attack.mitre.org/techniques/T1021/002)
## License
- `Elastic License v2`
@@ -0,0 +1,35 @@
# 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`
@@ -0,0 +1,40 @@
# 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`
@@ -0,0 +1,39 @@
# 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`
@@ -0,0 +1,39 @@
# Persistence via Startup with low occurrence frequency
---
## Metadata
- **Author:** Elastic
- **UUID:** `9d8c79fd-0006-4988-8aaa-d5f9b9a7df8e`
- **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-*, 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
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 */
| 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 number_hosts = count_distinct(host.id) by process_path, file.name
| where number_hosts == 1
```
## 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.
## MITRE ATT&CK Techniques
- [T1547](https://attack.mitre.org/techniques/T1547)
- [T1547.001](https://attack.mitre.org/techniques/T1547/001)
## License
- `Elastic License v2`
@@ -0,0 +1,40 @@
# Persistence via Suspicious Launch Agent or Launch Daemon with low occurrence
---
## Metadata
- **Author:** Elastic
- **UUID:** `a7dcd1a1-2860-491e-8802-31169a607167`
- **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 == "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
```
## 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.
## MITRE ATT&CK Techniques
- [T1547](https://attack.mitre.org/techniques/T1547)
- [T1547.011](https://attack.mitre.org/techniques/T1547/011)
- [T1543](https://attack.mitre.org/techniques/T1543)
- [T1543.001](https://attack.mitre.org/techniques/T1543/001)
- [T1543.004](https://attack.mitre.org/techniques/T1543/004)
## License
- `Elastic License v2`
@@ -0,0 +1,38 @@
# Potential Exfiltration by process total egress bytes
---
## Metadata
- **Author:** Elastic
- **UUID:** `977d77f9-86e0-4df6-bdc7-aed87c048290`
- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint)
- **Language:** `ES|QL`
## Query
```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
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
```
## 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.)
- 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.).
## MITRE ATT&CK Techniques
- [T1071](https://attack.mitre.org/techniques/T1071)
## License
- `Elastic License v2`
@@ -0,0 +1,39 @@
# Rundll32 execution aggregated by cmdline
---
## Metadata
- **Author:** Elastic
- **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`
## Query
```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
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
```
## 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.
## MITRE ATT&CK Techniques
- [T1127](https://attack.mitre.org/techniques/T1127)
- [T1218](https://attack.mitre.org/techniques/T1218)
- [T1218.011](https://attack.mitre.org/techniques/T1218/011)
## License
- `Elastic License v2`
@@ -0,0 +1,40 @@
# Scheduled tasks creation by action via registry
---
## Metadata
- **Author:** Elastic
- **UUID:** `344c0690-ebc3-4794-b123-272a5c09c57b`
- **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.type == "windows" and event.category == "registry" and event.action == "modification" and
registry.path like """HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\Tasks\\*Actions*"""
/* scheduled task actions are saved under the TaskCache registry key in base64 encoded blob */
| eval scheduled_task_action = replace(TO_LOWER(FROM_BASE64(registry.data.bytes)), """\u0000""", "")
/* commonly abused lolbin set to run as a scheduled task */
| where scheduled_task_action rlike """.*(users\\public\\|\\appdata\\roaming|programdata|powershell.exe|rundll32.exe|regsvr32.exe|mshta.exe|cscript.exe|wscript.exe|cmd.exe|forfiles|msiexec.exe|wmic.exe|msbuild.exe|http|cmstp.exe|msxsl.exe|ie4uinit.exe).*""" and not scheduled_task_action like "localsystem*"
| keep scheduled_task_action, registry.path, agent.id
| stats count_agents = count_distinct(agent.id) by scheduled_task_action
/* helps reduce result to instances limited to one agent */
| where count_agents == 1
```
## 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.
## MITRE ATT&CK Techniques
- [T1053](https://attack.mitre.org/techniques/T1053)
- [T1053.005](https://attack.mitre.org/techniques/T1053/005)
## License
- `Elastic License v2`
@@ -0,0 +1,43 @@
# Scheduled tasks creation with low occurrence frequency
---
## Metadata
- **Author:** Elastic
- **UUID:** `75804319-122c-4bdc-976e-d6355bca0d78`
- **Integration:** [system](https://docs.elastic.co/integrations/system)
- **Language:** `ES|QL`
## Query
```sql
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 "(?<Command><Command>.+</Command>)" | eval Command = replace(Command, "(<Command>|</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}\})""", "")
/* 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
| 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.
## MITRE ATT&CK Techniques
- [T1053](https://attack.mitre.org/techniques/T1053)
- [T1053.005](https://attack.mitre.org/techniques/T1053/005)
## License
- `Elastic License v2`
@@ -0,0 +1,40 @@
# Suspicious Base64 Encoded PowerShell Command
---
## Metadata
- **Author:** Elastic
- **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`
## Query
```sql
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
/* simplified regex to extract base64 encoded blob */
| grok process.command_line """(?<base64_data>([A-Za-z0-9+/]+={1,2}$|[A-Za-z0-9+/]{100,}))"""
| where base64_data is not null
/* base64 decode added in 8.14 */
| eval decoded_base64_cmdline = replace(TO_LOWER(FROM_BASE64(base64_data)), """\u0000""", "")
/* most common suspicious keywords, you can add more patterns here */
| 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
```
## Notes
- This hunt decode base64 obfuscated powershell commands in process start events and filter ones with suspicious keywords like downloaders and evasion related commands.
## MITRE ATT&CK Techniques
- [T1059](https://attack.mitre.org/techniques/T1059)
- [T1059.001](https://attack.mitre.org/techniques/T1059/001)
- [T1027](https://attack.mitre.org/techniques/T1027)
- [T1027.010](https://attack.mitre.org/techniques/T1027/010)
## License
- `Elastic License v2`
@@ -0,0 +1,36 @@
# Suspicious DNS TXT Record lookups by process
---
## Metadata
- **Author:** Elastic
- **UUID:** `0b7343f7-2d16-43c7-af28-9d1f012b1093`
- **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
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
```
## 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.).
## MITRE ATT&CK Techniques
- [T1071](https://attack.mitre.org/techniques/T1071)
- [T1071.004](https://attack.mitre.org/techniques/T1071/004)
## License
- `Elastic License v2`
@@ -0,0 +1,36 @@
# 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`
@@ -0,0 +1,38 @@
# 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`
@@ -0,0 +1,36 @@
# 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`
@@ -0,0 +1,36 @@
# 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`
@@ -0,0 +1,37 @@
# Windows Command and Scripting Interpreter from unusual parent
---
## Metadata
- **Author:** Elastic
- **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`
## Query
```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"))
| 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
```
## 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.
## MITRE ATT&CK Techniques
- [T1059](https://attack.mitre.org/techniques/T1059)
- [T1059.001](https://attack.mitre.org/techniques/T1059/001)
- [T1059.003](https://attack.mitre.org/techniques/T1059/003)
## License
- `Elastic License v2`
@@ -0,0 +1,40 @@
# Windows logon activity by source IP
---
## Metadata
- **Author:** Elastic
- **UUID:** `7bdea198-eb09-4eca-ae3d-bfc3b52c89a9`
- **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 == "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
```
## 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.
## MITRE ATT&CK Techniques
- [T1110](https://attack.mitre.org/techniques/T1110)
- [T1110.001](https://attack.mitre.org/techniques/T1110/001)
- [T1110.003](https://attack.mitre.org/techniques/T1110/003)
## License
- `Elastic License v2`
@@ -0,0 +1,22 @@
[hunt]
author = "Elastic"
integration = ["windows"]
uuid = "0545f23f-84a7-4b88-9b5b-b8cfcfdc9276"
name = "CreateRemoteThread by source process with low occurrence"
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.",
]
mitre = ["T1055"]
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
'''
@@ -0,0 +1,30 @@
[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
'''
@@ -0,0 +1,31 @@
[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
'''
@@ -0,0 +1,26 @@
[hunt]
author = "Elastic"
integration = ["endpoint"]
uuid = "93a72542-a1f7-4407-9175-8f066343db60"
name = "Detect masquerading attempts as native Windows binaries"
language = "ES|QL"
license = "Elastic License v2"
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"
| keep process.name.caseless, process.executable.caseless, process.code_signature.subject_name, process.code_signature.trusted, process.code_signature.exists, host.id
/* system_bin contain Microsoft signed and located in system32 folder process names */
/* non_system_bin contain non Microsoft signed process names */
| eval system_bin = case(starts_with(process.executable.caseless, "c:\\windows\\system32") and starts_with(process.code_signature.subject_name, "Microsoft") and process.code_signature.trusted == true, process.name.caseless, null), non_system_bin = case(process.code_signature.exists == false or process.code_signature.trusted != true or not starts_with(process.code_signature.subject_name, "Microsoft"), process.name.caseless, null)
/* aggregate unique process name counts by process.name and host.id */
| 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.",
"The queried index must capture process start events with code signature information (e.g. Windows event 4688 is not supported).",
]
mitre = ["T1036"]
@@ -0,0 +1,26 @@
[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"]
@@ -0,0 +1,28 @@
[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"]
@@ -0,0 +1,23 @@
[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"]
@@ -0,0 +1,24 @@
[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"]
@@ -0,0 +1,22 @@
[hunt]
author = "Elastic"
integration = ["endpoint", "windows"]
uuid = "ebf8eb13-c98a-4d2c-8bdb-3f72a3a3961b"
name = "Doamin Names queries via Lolbins and 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.",
]
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}"""
| 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
'''
@@ -0,0 +1,22 @@
[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
'''
@@ -0,0 +1,22 @@
[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
'''
@@ -0,0 +1,26 @@
[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
'''
@@ -0,0 +1,24 @@
[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
'''
@@ -0,0 +1,21 @@
[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
'''
@@ -0,0 +1,23 @@
[hunt]
author = "Elastic"
integration = ["endpoint", "windows"]
uuid = "6949135b-76d7-47a3-ae95-ef482508fb7c"
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.",]
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
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
'''
@@ -0,0 +1,24 @@
[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
'''
@@ -0,0 +1,23 @@
[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
'''
@@ -0,0 +1,24 @@
[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
'''
@@ -0,0 +1,24 @@
[hunt]
author = "Elastic"
integration = ["endpoint"]
uuid = "ae07c580-290e-4421-add8-d6ca30509b6a"
name = "Execution via Network Logon by occurrence frequency by top Source IP"
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(*) by process.Ext.session_info.client_address, user.name
/* sort by top source.ip and account */
| sort total desc
'''
@@ -0,0 +1,23 @@
[hunt]
author = "Elastic"
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.",
]
mitre = [ "T1021", "T1021.003", "T1021.006", "T1047",]
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 */
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
'''
@@ -0,0 +1,27 @@
[hunt]
author = "Elastic"
integration = ["endpoint"]
uuid = "a447df80-d3d5-48b3-a175-a864264ec487"
name = "Execution via Startup with low occurrence frequency"
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.",
]
mitre = [ "T1547", "T1547.001",]
query = '''
from logs-endpoint.events.process-*
| 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
/* 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
| 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, process.hash.sha256
| where hosts == 1
'''
@@ -0,0 +1,21 @@
[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
'''
@@ -0,0 +1,22 @@
[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
'''
@@ -0,0 +1,25 @@
[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
'''
@@ -0,0 +1,24 @@
[hunt]
author = "Elastic"
integration = ["endpoint", "windows"]
uuid = "96d5afc8-1f25-4265-8a0e-9998091a2e1f"
name = "Execution via Windows Scheduled Task with low occurrence frequency"
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.",
]
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
/* 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
'''
@@ -0,0 +1,23 @@
[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
'''
@@ -0,0 +1,25 @@
[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
'''
@@ -0,0 +1,28 @@
[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
'''
@@ -0,0 +1,28 @@
[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
'''
@@ -0,0 +1,29 @@
[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
'''
@@ -0,0 +1,29 @@
[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
'''
@@ -0,0 +1,28 @@
[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
'''
@@ -0,0 +1,23 @@
[hunt]
author = "Elastic"
integration = ["endpoint", "windows", "system"]
uuid = "74b2e54b-7002-4201-83d6-7fd9bd5dcf0f"
name = "Microsoft Office Child Processes with low occurrence frequency"
language = "ES|QL"
license = "Elastic License v2"
notes = [
"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 = '''
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")
// 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
| where occurrences == 1 and agents == 1
'''
@@ -0,0 +1,27 @@
[hunt]
author = "Elastic"
integration = ["endpoint", "windows"]
uuid = "e0acab7d-30bd-4be0-9682-5c3457bbeb4f"
name = "Network Discovery via sensitive ports by unusual process"
language = "ES|QL"
license = "Elastic License v2"
notes = [
"The query thresholds for SMB or RDP need to be adjusted to your environment.",
"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-*
| 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
process.pid != 4
| keep process.executable, destination.port, destination.ip, process.entity_id
/* network events with SMB or RDP as a target */
| eval smb_dip = case(destination.port == 445, destination.ip, null), rdp_dip = case(destination.port == 389, destination.ip, null)
/* 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)
'''
@@ -0,0 +1,21 @@
[hunt]
author = "Elastic"
integration = ["endpoint"]
uuid = "3e66fc1a-2ea0-43a6-ba51-0280c693d152"
name = "PE File Transfer via SMB_Admin Shares by Agent"
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.",
]
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
| where agents == 1 and total <= 3
'''
@@ -0,0 +1,22 @@
[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
'''
@@ -0,0 +1,27 @@
[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
'''

Some files were not shown because too many files have changed in this diff Show More