Detection rules › Kusto

Detect Custom Script or Run Command deployment by risky user

Group by
Caller, CallerIpAddress, CorrelationId, ResourceGroup, UserPrincipalName, VMName
Author
Robbe Van den Daele
Source
github.com/HybridBrothers/Hunting-Queries-Detection-Rules

This detection rule flags when a user with risk events in Entra ID Identity Protection is deploying Custom Scripts or Run Commands on Azure or Azure Arc machines. This may indicate a compromised cloud user that is now performaring lateral movement from the Azure control plane to Virtual Machines in other environments.

MITRE ATT&CK coverage

References

Event coverage

Rules detecting the same action

Other rules on this platform that filter on the same API call or operation.

Rule body yaml

AzureActivity 
| where TimeGenerated > ago(1h)
| where CategoryValue == "Administrative"
| where OperationNameValue =~ "Microsoft.Compute/virtualMachines/runCommand/action"
    or OperationNameValue =~ "MICROSOFT.COMPUTE/VIRTUALMACHINES/EXTENSIONS/WRITE"
| extend VMName = tostring(todynamic(Properties).resource)
| summarize make_list(ActivityStatusValue), TimeGenerated = max(TimeGenerated) by CorrelationId, CallerIpAddress, Caller, ResourceGroup, VMName
| join kind=inner (AADUserRiskEvents | where TimeGenerated > ago(14d) ) on $left.Caller == $right.UserPrincipalName

Stages and Predicates

Stage 1: source

AzureActivity

Stage 2: where

| where TimeGenerated > ago(1h)

Stage 3: where

| where CategoryValue == "Administrative"

Stage 4: where

| where OperationNameValue =~ "Microsoft.Compute/virtualMachines/runCommand/action"
    or OperationNameValue =~ "MICROSOFT.COMPUTE/VIRTUALMACHINES/EXTENSIONS/WRITE"

Stage 5: extend

| extend VMName = tostring(todynamic(Properties).resource)

Stage 6: summarize

| summarize make_list(ActivityStatusValue), TimeGenerated = max(TimeGenerated) by CorrelationId, CallerIpAddress, Caller, ResourceGroup, VMName

Stage 7: join

| join kind=inner (AADUserRiskEvents | where TimeGenerated > ago(14d) ) on $left.Caller == $right.UserPrincipalName

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
CategoryValueeq
  • Administrative transforms: cased
OperationNameValueeq
  • MICROSOFT.COMPUTE/VIRTUALMACHINES/EXTENSIONS/WRITE
  • Microsoft.Compute/virtualMachines/runCommand/action

Output fields

Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.

FieldSource
Callersummarize
CallerIpAddresssummarize
CorrelationIdsummarize
ResourceGroupsummarize
TimeGeneratedsummarize
VMNamesummarize