Unusual Azure VM Extension Detected

Last updated a month ago on 2026-06-15
Created a month ago on 2026-06-15

About

Identifies the first time a given VM extension name is created or updated on an Azure virtual machine or VM scale set within the rule's lookback window. VM extensions run with high privilege on the guest (SYSTEM on Windows, root on Linux) and are a common code-execution and persistence primitive. The extension instance name is attacker-controlled and the Azure activity log records only that name, not the publisher or type, so the control plane cannot reliably identify the extension family (for example CustomScript). This rule therefore takes a type-agnostic ES|QL new-terms approach: it derives the host and the extension instance name from `azure.resource.name` and alerts the first time a given (host, extension name) pair is observed in the window, surfacing novel extension deployments while suppressing names a host routinely uses.
Tags
Domain: CloudData Source: AzureData Source: Azure Activity LogsUse Case: Threat DetectionTactic: ExecutionTactic: PersistenceLanguage: esql
Severity
medium
Risk Score
47
MITRE ATT&CK™

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

Persistence (TA0003)(external, opens in a new tab or window)

False Positive Examples
Legitimate provisioning, patching, and configuration-management automation may deploy an extension to a host for the first time. The first occurrence per host will alert. Baseline expected automation principals and hosts and exclude verified-benign ones.
License
Elastic License v2(external, opens in a new tab or window)

Definition

Integration Pack
Prebuilt Security Detection Rules
Related Integrations

azure(external, opens in a new tab or window)

Query
text code block:
FROM logs-azure.activitylogs-* | WHERE event.dataset == "azure.activitylogs" AND event.action IN ( "MICROSOFT.COMPUTE/VIRTUALMACHINES/EXTENSIONS/WRITE", "MICROSOFT.COMPUTE/VIRTUALMACHINESCALESETS/EXTENSIONS/WRITE" ) AND event.outcome IN ("success", "Success") // azure.resource.name is "<host>/EXTENSIONS/<instance-name>"; the instance name is attacker-controlled, // so key on the host (first path element) rather than the spoofable extension name | EVAL Esql.vm_name = MV_FIRST(SPLIT(azure.resource.name, "/")) | EVAL Esql.extension_name = MV_LAST(SPLIT(azure.resource.name, "/")) | STATS Esql.first_time_seen = MIN(@timestamp), Esql.last_time_seen = MAX(@timestamp), Esql.event_count = COUNT(*), Esql.resource_name_values = VALUES(azure.resource.name), Esql.resource_id_values = VALUES(azure.resource.id), Esql.principal_id_values = VALUES(azure.activitylogs.identity.authorization.evidence.principal_id), Esql.principal_type_values = VALUES(azure.activitylogs.identity.authorization.evidence.principal_type), Esql.appid_values = VALUES(azure.activitylogs.identity.claims.appid), Esql.source_ip_values = VALUES(source.ip), Esql.source_as_number_values = VALUES(source.`as`.number), Esql.source_country_values = VALUES(source.geo.country_name), Esql.subscription_id_values = VALUES(azure.subscription_id) BY Esql.vm_name, Esql.extension_name // new terms emulation: fire only when the (host, extension name) pair is the single occurrence in the // 7-day window (event_count == 1) and it is recent (within the schedule interval + ingest-lag buffer) | EVAL Esql.recent_minutes = DATE_DIFF("minute", Esql.first_time_seen, NOW()) | WHERE Esql.recent_minutes <= 10 AND Esql.event_count == 1 // surface real fields for the analyst and rule exceptions | EVAL azure.resource.name = MV_FIRST(Esql.resource_name_values), azure.resource.id = MV_FIRST(Esql.resource_id_values), source.ip = MV_FIRST(Esql.source_ip_values) | KEEP azure.resource.name, azure.resource.id, source.ip, Esql.*

Install detection rules in Elastic Security

Detect Unusual Azure VM Extension Detected in the Elastic Security detection engine by installing this rule into your Elastic Stack.

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