Detection rules › Kusto
Detect Custom Script or Run Command deployment by risky user
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
| Tactic | Techniques |
|---|---|
| Execution | T1651 Cloud Administration Command |
| Lateral Movement | T1021.008 Remote Services: Direct Cloud VM Connections |
References
Event coverage
Rules detecting the same action
Other rules on this platform that filter on the same API call or operation.
- Azure Active Directory Hybrid Health AD FS New Server (Sigma)
- Azure Active Directory Hybrid Health AD FS Service Delete (Sigma)
- Azure Subscription Permission Elevation Via AuditLogs (Sigma)
- Microsoft Entra ID Hybrid Health AD FS New Server (Kusto)
- Microsoft Entra ID Hybrid Health AD FS Service Delete (Kusto)
- Microsoft Entra ID Hybrid Health AD FS Suspicious Application (Kusto)
- NRT Microsoft Entra ID Hybrid Health AD FS New Server (Kusto)
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.
| Field | Kind | Values |
|---|---|---|
CategoryValue | eq |
|
OperationNameValue | eq |
|
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.
| Field | Source |
|---|---|
Caller | summarize |
CallerIpAddress | summarize |
CorrelationId | summarize |
ResourceGroup | summarize |
TimeGenerated | summarize |
VMName | summarize |