Detection rules › Kusto
Hunt domains with Seamless SSO enabled in Entra ID Connect
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
- https://nathanmcnulty.com/blog/2025/08/finding-seamless-sso-usage/#
- https://ourcloudnetwork.com/why-you-should-disable-seamless-sso-in-microsoft-entra-connect/
- https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/how-to-connect-sso-faq#how-can-i-disable-seamless-sso-
- https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/how-to-connect-sso
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
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 =(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"))DeviceNametolower(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 =((JoinType == "Hybrid Azure AD Join" or JoinType == "AAD Joined") or JoinType == "AAD Registered")"No"(JoinType == "Domain Joined" or (OSPlatform startswith "Windows" and OSVersion < 10.0))"Yes""Unknown (to verify)"Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Application | eq |
| field:"Image" kind:eq value:"Active Directory" |
Protocol | eq |
| field:"Protocol" kind:eq value:"Kerberos" |
TargetDeviceName | eq |
| field:"TargetDeviceName" kind:eq value:"AZUREADSSOACC" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
OnPremisesDomainName | extend |
DeviceName | extend |
Seamless SSO Expected | extend |