Detection rules › Kusto

Hunt domains with Seamless SSO enabled in Entra ID Connect

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

With below KQL query you can search through the IdentityLogon events of Microsoft Defender for Identity to find users and devices still using Seamless SSO in Entra ID Connect. This feature has been marked by the community multiple times as a security risk, and should be disabled if not in use. The KQL query returns the domains where Seamless SSO is enabled, allong with the related users and devices. On top of that, devices get enriched to find their OS distribution, version, and join type and tells you if Seamless SSO is expected to be used for the related device or not. If there are no results or if all results are showing 'No' for the 'Seamless SSO Expected' column, it should be save to disable the feature in Entra ID connect. !Important: This query relies on the Domain Controller EventID 4769 and Defender for Identity. Make sure the EventID is being logged and Defender for Identity is healthy. For more information see references!

References

Telemetry coverage

Rule body

// Get all device info we can find
let devices = (
    DeviceInfo
    // Search for 14 days
    | where TimeGenerated > ago(14d)
    // Normalize DeviceName 
    // --> if it is an IP Address we keep it
    // --> If it is not an IP Address we only use the hostname for correlation
    | extend DeviceName = iff(ipv4_is_private(DeviceName), DeviceName, tolower(split(DeviceName, ".")[0]))
    // Only get interesting data
    | distinct DeviceName, OSPlatform, OSVersion, DeviceId, OnboardingStatus, Model, JoinType
);
IdentityLogonEvents
// Get the last 30 days of logon events on Domain Controllers
| where TimeGenerated > ago(30d)
// Search for Seamless SSO events
| where Application == "Active Directory" and Protocol == "Kerberos"
| where TargetDeviceName == "AZUREADSSOACC"
// Save the domain name of the Domain Controller
| extend OnPremisesDomainName = strcat(split(DestinationDeviceName, ".")[-2], ".", split(DestinationDeviceName, ".")[-1])
// Normalize DeviceName 
// --> if it is an IP Address we keep it
// --> If it is not an IP Address we only use the hostname for correlation
| extend DeviceName = iff(ipv4_is_private(DeviceName), DeviceName, tolower(split(DeviceName, ".")[0]))
// Only use interesting data and find more info regarding the source device
| distinct AccountUpn, OnPremisesDomainName, DeviceName
| join kind=leftouter devices on DeviceName 
| project-away DeviceName1
// Check if Seamless SSO usage is expected
| extend ['Seamless SSO Expected'] = case(
    // Cases where we do not expect Seamless SSO to be used
    JoinType == "Hybrid Azure AD Join" or 
    JoinType == "AAD Joined" or
    JoinType == "AAD Registered", "No",
    // Cases where we do expect Seamless SSO to be used
    JoinType == "Domain Joined" or 
    (OSPlatform startswith "Windows" and toreal(OSVersion) < 10.0) , "Yes", 
    // Cases that need to be verified
    "Unknown (to verify)"
)

Stages and Predicates

Let binding: devices used in Stages 1, 9

let devices = (
    DeviceInfo
    | where TimeGenerated > ago(14d)
    | extend DeviceName = iff(ipv4_is_private(DeviceName), DeviceName, tolower(split(DeviceName, ".")[0]))
    | distinct DeviceName, OSPlatform, OSVersion, DeviceId, OnboardingStatus, Model, JoinType
);

Stage 1: source

let devices

Stage 2: source

IdentityLogonEvents

Stage 3: where

where TimeGenerated > ago(2592000s)

Stage 4: where

where Application =~ "Active Directory" and Protocol =~ "Kerberos"

Stage 5: where

where TargetDeviceName =~ "AZUREADSSOACC"

Stage 6: extend

extend OnPremisesDomainName

Stage 7: extend

extend DeviceName
DeviceName =
if(ipv4_is_in_range(DeviceName, "10.0.0.0/8") or ipv4_is_in_range(DeviceName, "172.16.0.0/12") or ipv4_is_in_range(DeviceName, "192.168.0.0/16") or ipv4_is_in_range(DeviceName, "169.254.0.0/16") or ipv4_is_in_range(DeviceName, "127.0.0.0/8"))DeviceName
elsetolower(split(DeviceName, ".")[0])

Stage 8: distinct

distinct AccountUpn, DeviceName, OnPremisesDomainName

Stage 9: join

join kind=leftouter (devices) on DeviceName

Stage 10: project-away

project-away DeviceName1

Stage 11: extend

extend Seamless SSO Expected
Seamless SSO Expected =
if((JoinType == "Hybrid Azure AD Join" or JoinType == "AAD Joined") or JoinType == "AAD Registered")"No"
elif(JoinType == "Domain Joined" or (OSPlatform startswith "Windows" and OSVersion < 10.0))"Yes"
else"Unknown (to verify)"

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
OnPremisesDomainNameextend
DeviceNameextend
Seamless SSO Expectedextend