Detection rules › Kusto
AD FS Abnormal EKU object identifier attribute
'This detection uses Security events from the "AD FS Auditing" provider to detect suspicious object identifiers (OIDs) as part EventID 501 and specifically part of the Enhanced Key Usage attributes. This query checks to see if you have any new OIDs in the last hour that have not been seen in the previous day. New OIDs should be validated and OIDs that are very long, as indicated by the OID_Length field, could also be an indicator of malicious activity. In order to use this query you need to enable AD FS auditing on the AD FS Server. References: https://www.microsoft.com/security/blog/2022/08/24/magicweb-nobeliums-post-compromise-trick-to-authenticate-as-anyone/ https://docs.microsoft.com/windows-server/identity/ad-fs/troubleshooting/ad-fs-tshoot-logging '
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Credential Access | T1552 Unsecured Credentials |
Rule body kusto
id: cfc1ae62-db63-4a3e-b88b-dc04030c2257
name: AD FS Abnormal EKU object identifier attribute
description: |
'This detection uses Security events from the "AD FS Auditing" provider to detect suspicious object identifiers (OIDs) as part EventID 501 and specifically part of the Enhanced Key Usage attributes.
This query checks to see if you have any new OIDs in the last hour that have not been seen in the previous day. New OIDs should be validated and OIDs that are very long, as indicated
by the OID_Length field, could also be an indicator of malicious activity.
In order to use this query you need to enable AD FS auditing on the AD FS Server.
References:
https://www.microsoft.com/security/blog/2022/08/24/magicweb-nobeliums-post-compromise-trick-to-authenticate-as-anyone/
https://docs.microsoft.com/windows-server/identity/ad-fs/troubleshooting/ad-fs-tshoot-logging
'
severity: High
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1552
tags:
- Nobelium
- MagicWeb
query: |
// change the starttime value for a longer period of known OIDs
let starttime = 1d;
// change the lookback value for a longer period of lookback for suspicious/abnormal
let lookback = 1h;
let OIDList = SecurityEvent
| where TimeGenerated >= ago(starttime)
| where EventSourceName == 'AD FS Auditing'
| where EventID == 501
| where EventData has '/eku'
| extend OIDs = extract_all(@"<Data>([\d+\.]+)</Data>", EventData)
| mv-expand OIDs
| extend OID = tostring(OIDs)
| extend OID_Length = strlen(OID)
| project TimeGenerated, Computer, EventSourceName, EventID, OID, OID_Length, EventData
;
OIDList
| where TimeGenerated >= ago(lookback)
| join kind=leftanti (
OIDList
| where TimeGenerated between (ago(starttime) .. ago(lookback))
| summarize by OID
) on OID
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: HostNameDomain
version: 1.0.4
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Others", "Identity" ]
Stages and Predicates
Parameters
let starttime = 1d;
let lookback = 1h;
The stages below define let OIDList (the rule's main pipeline source).
Stage 1: source
let OIDList
Stage 2: source
SecurityEvent
Stage 3: where
| where TimeGenerated >= ago(starttime)
Stage 4: where
| where EventSourceName == 'AD FS Auditing'
Stage 5: where
| where EventID == 501
Stage 6: where
| where EventData has '/eku'
Stage 7: extend
| extend OIDs = extract_all(@"<Data>([\d+\.]+)</Data>", EventData)
Stage 8: mv-expand
| mv-expand OIDs
Stage 9: extend
| extend OID = tostring(OIDs)
Stage 10: extend
| extend OID_Length = strlen(OID)
Stage 11: project
| project TimeGenerated, Computer, EventSourceName, EventID, OID, OID_Length, EventData
The stages below run on OIDList (the outer pipeline).
Stage 12: where
OIDList
| where TimeGenerated >= ago(lookback)
Stage 13: join (negated)
| join kind=leftanti (
OIDList
| where TimeGenerated between (ago(starttime) .. ago(lookback))
| summarize by OID
) on OID
Stage 14: extend
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
Stage 15: extend
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
HostNameDomain =DomainIndex != -1substring(Computer, (DomainIndex + 1))ComputerStage 16: summarize
summarize by OID
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
EventData | match | /eku |
EventID | eq | 501 |
EventSourceName | eq | AD FS Auditing |
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 |
|---|---|---|
EventData | match |
|
EventID | eq |
|
EventSourceName | 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 |
|---|---|
OID | summarize |