Detection rules › Kusto

Hunt for Defender for Identity not installed but eligible

Group by
MdiDeviceName, NodeName
Author
Robbe Van den Daele
Source
github.com/HybridBrothers/Hunting-Queries-Detection-Rules

This query shows you which servers are eligible for Defender for identity but does not have the Defender for Identity agent installed. The query seach the eligible servers via Defender for Endpoint (requirement for this query to work), and is based on the server roles that MDE recongnizes.

Telemetry coverage

Rule body

let device_roles = dynamic(["EntraConnectServer", "AzureADConnectServer", "ActiveDirectoryCertificateServicesServer", "DomainController", "ADFS"]);
let mdi_servers = (
    DeviceTvmSoftwareInventory
    | where SoftwareName == "azure_advanced_threat_protection_sensor"
    | distinct MdiDeviceName=tolower(DeviceName)
);
let mdi_eligible_servers = (
    ExposureGraphNodes
    | extend DeviceRoles= parse_json(NodeProperties)["rawData"]["deviceRole"]
    | extend CriticalityRuleNames = parse_json(NodeProperties)["rawData"]["criticalityLevel"]["ruleNames"]
    | where DeviceRoles has_any (device_roles) or
        CriticalityRuleNames has_any (device_roles)
    | distinct NodeName=tolower(NodeName), tostring(DeviceRoles), tostring(CriticalityRuleNames)
);
mdi_servers
| join kind=rightouter mdi_eligible_servers on $left.MdiDeviceName == $right.NodeName
| extend Issue = iff(isempty(MdiDeviceName), "This server is eligible for MDI but does not have MDI installed", "None")
| where Issue != "None"

Stages and Predicates

Parameters

let device_roles = dynamic(["EntraConnectServer", "AzureADConnectServer", "ActiveDirectoryCertificateServicesServer", "DomainController", "ADFS"]);

let mdi_servers is inlined into the numbered stages below.

Let binding: mdi_eligible_servers used in Stages 2, 6

let mdi_eligible_servers = (
    ExposureGraphNodes
    | extend DeviceRoles= parse_json(NodeProperties)["rawData"]["deviceRole"]
    | extend CriticalityRuleNames = parse_json(NodeProperties)["rawData"]["criticalityLevel"]["ruleNames"]
    | where DeviceRoles has_any (device_roles) or
        CriticalityRuleNames has_any (device_roles)
    | distinct NodeName=tolower(NodeName), tostring(DeviceRoles), tostring(CriticalityRuleNames)
);

Stage 1: source

let mdi_servers

Stage 2: source

let mdi_eligible_servers

Stage 3: source

DeviceTvmSoftwareInventory

Stage 4: where

where SoftwareName =~ "azure_advanced_threat_protection_sensor"

Stage 5: distinct

distinct

Stage 6: join

join kind=rightouter (mdi_eligible_servers) on MdiDeviceName, NodeName

Stage 7: extend

extend Issue
Issue =
ifisempty(MdiDeviceName)"This server is eligible for MDI but does not have MDI installed"
else"None"

Stage 8: where

where Issue !~ "None"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CriticalityRuleNamesmatch
  • ADFS transforms: term
  • ActiveDirectoryCertificateServicesServer transforms: term
  • AzureADConnectServer transforms: term
  • DomainController transforms: term
  • EntraConnectServer transforms: term
field:"CriticalityRuleNames" kind:match
DeviceRolesmatch
  • ADFS transforms: term
  • ActiveDirectoryCertificateServicesServer transforms: term
  • AzureADConnectServer transforms: term
  • DomainController transforms: term
  • EntraConnectServer transforms: term
field:"DeviceRoles" kind:match
Issuene
  • None
field:"Issue" kind:ne value:"None"
SoftwareNameeq
  • azure_advanced_threat_protection_sensor
field:"SoftwareName" kind:eq value:"azure_advanced_threat_protection_sensor"

Output fields

These fields are emitted when the rule matches.

FieldSource
Issueextend