- What it is: detection engineering for identity-first attacks is the practice of building alerts around authentication behavior, session token lineage, and access-pattern anomalies instead of process execution or file writes. The attacker is using real credentials through real services, so the signal lives in identity logs, not on the endpoint.
- Why it matters: modern intrusions rarely start with a CVE. They start with a phished session cookie or a stuffed credential, and the intruder then moves laterally as a legitimate user. Endpoint-centric tooling is blind to this because nothing malicious ever runs on a managed host.
- The gotcha: an identity rule that has never been validated against the tool it is meant to catch fails silently in production, and a rule noisy enough to be ignored is the same as no rule. Both failures are invisible until the breach review.
Who this is for: detection engineers, SOC analysts, and security architects who already ship logs from an identity provider (Entra ID, Okta, Google Workspace) and at least one cloud’s IAM into a SIEM, but whose detection content is still mostly endpoint and network rules. The assumption is that you can write a SIEM query and read an OAuth flow, but you have not yet built behavioral detections that span login, token, and privilege events. If your identity alerting today is “impossible travel” and not much else, this is the layer that turns scattered login noise into a correlated kill chain.
The login that was real
A user completes a phishing prompt on a page that looks exactly like the company SSO. They enter their password, approve the MFA push, and land on what appears to be the expected app. Nothing on their laptop is malicious. No malware ran, no file was written, no process spawned. Behind the page, an adversary-in-the-middle proxy relayed every keystroke to the genuine identity provider and, at the moment MFA succeeded, captured the authenticated session cookie. Minutes later that cookie is replayed from a datacenter IP in another country, and the attacker is inside as the user, MFA already satisfied.
Endpoint detection and response sees none of this, because none of it happens on an endpoint it controls. The browser is real, the OAuth handshake is real, the token is real. This is the defining property of identity-first attacks, and it is why the discipline of detecting them is different from everything that came before. The 2024 and 2025 breach reporting made the shift explicit: the Verizon DBIR has tracked stolen credentials as the single most common initial access vector for years [1], and CrowdStrike’s 2024 Global Threat Report described a move toward “malware-free” intrusions that lean on valid accounts rather than exploits [2]. MITRE ATT&CK codifies the techniques as Valid Accounts (T1078) and Steal Web Session Cookie (T1539) [3], and they are deliberately hard to catch precisely because the activity is indistinguishable from legitimate use at the level of any single event.
Detection engineering for this world means giving up on the idea that one event is the alert. The signal is in the relationship between events: this login, then this token used from there, then this permission granted to that principal. The rest of this article walks the four layers where those relationships live, with the queries to surface them, the operational reality of running them, and the honest list of where they break.
How identity attacks actually execute
Before writing a single rule it helps to have the attack’s shape in mind, because every useful detection is keyed to a transition between these phases rather than to a phase itself.
I refer to the phase model in Figure 1. An identity-first intrusion moves through credential capture, session establishment, lateral movement using the legitimate identity, and finally privilege escalation or persistence. The detections that work are joins across the arrows, not alerts on the boxes.
flowchart LR
A[credential or<br/>session capture] --> B[authenticated<br/>session established]
B --> C[lateral movement<br/>as valid user]
C --> D[privilege escalation<br/>+ persistence]
A -. phishing / AiTM / stuffing .-> A
B -. token replay .-> B
D -. IAM mutation / new grant .-> D
Figure 1. The four phases of an identity-first intrusion, from credential capture through escalation. Look at the arrows, not the boxes: the high-fidelity detections fire on the transitions between phases, where a benign-looking event in one phase is made suspicious by what preceded it.
Three capture techniques dominate. Credential stuffing replays passwords leaked elsewhere, generating a burst of failures against many accounts before one succeeds. Adversary-in-the-middle (AiTM) phishing, popularized by open-source proxies like Evilginx, sits between the victim and the real IdP, harvesting the post-MFA session cookie so the attacker inherits an already-authenticated session. Refresh token theft goes one level deeper: rather than a short-lived session cookie, the attacker captures or is issued a long-lived refresh token and mints fresh access tokens at will, surviving the original session’s expiry.
What unites them is that the end state is a valid token the attacker holds but did not legitimately obtain. So the most powerful detection primitive is token lineage: tying a token to the context in which it was first issued (IP, ASN, device, user agent) and flagging when that token is later used from a materially different context. Identity providers now emit the raw material for this. Microsoft Entra ID exposes sign-in logs with device and conditional-access detail plus Continuous Access Evaluation (CAE) signals [4]; Okta’s System Log carries authentication and policy.evaluate_sign_on events with client and geolocation context [5]. The gap is almost never the data. It is whether anyone is routing that data somewhere queryable and writing joins against it.
Worked example: four detections that span the kill chain
Detections are concrete or they are nothing, so here are four, one per phase, in the query languages a detection engineer actually ships. Each is intentionally small enough to read and adapt.
Baseline deviation: the impossible combination
Single-signal anomalies are noisy. A login from a new country is common for any traveling workforce. The signal density comes from combining weak signals and, crucially, joining the authentication to the action that immediately follows it. A new country plus a new device plus an off-hours time plus an inbox-forwarding rule created within minutes is not noise, it is a kill chain.
Here is that join in Microsoft Sentinel KQL, correlating a risky sign-in with a mailbox forwarding rule created in the same short window:
let window = 30m;
SigninLogs
| where RiskLevelDuringSignIn in ("medium", "high")
| project SigninTime = TimeGenerated, UserPrincipalName,
IPAddress, Loc = LocationDetails.countryOrRegion
| join kind=inner (
OfficeActivity
| where Operation in ("New-InboxRule", "Set-InboxRule")
| where Parameters has_any ("ForwardTo", "RedirectTo", "DeleteMessage")
| project RuleTime = TimeGenerated, UserId, Operation
) on $left.UserPrincipalName == $right.UserId
| where RuleTime between (SigninTime .. (SigninTime + window))
| project SigninTime, RuleTime, UserPrincipalName, IPAddress, Loc, Operation
The rule fires only when both halves are true, which is what keeps it quiet. A risky sign-in alone is too noisy to action; a forwarding rule alone is sometimes legitimate; the two within 30 minutes of each other is the pattern that maps to real business email compromise.
Token replay: context mismatch on a refresh
Token theft is detected by the lineage mismatch described above. The cleanest expression compares the context where a refresh token is used against the context where the session was established. In Okta System Log terms, you watch for a token refresh whose client context diverges from the originating authentication. Expressed as a Sigma rule so it ports across backends [6]:
title: OAuth refresh token used from new ASN mid-session
id: 7c1f0e2a-9b3d-4a6e-8f51-2d9c4e0a1b77
status: experimental
logsource:
product: okta
service: system
detection:
selection:
eventType: 'app.oauth2.token.refresh'
filter_known:
debugContext.debugData.requestUri|contains: 'trusted-proxy'
timeframe: 1h
condition: selection and not filter_known
fields:
- actor.alternateId
- client.geographicalContext.country
- securityContext.asNumber
- debugContext.debugData.dtHash
level: high
The dtHash device token, the ASN, and the geographic country are the lineage fields. The detection that matters in practice is stateful: cache the (user, dtHash, ASN) tuple seen at interactive authentication, then alert when a token.refresh for that user arrives carrying a different ASN or device hash. Sigma expresses the trigger; the join to first-seen context happens in the SIEM. Look also for refresh tokens used at an unusual rate or at unusual hours for that identity, both classic signs of automated abuse rather than a human session.
Credential stuffing: the failure burst before the success
Stuffing is one of the few identity attacks with a high-volume signature, which makes it the easiest to catch and the easiest to drown in. The signal is not the failures alone, it is a spray of failures across many accounts from one source followed by a success. In SQL against a normalized auth-events table:
SELECT source_ip,
COUNT(*) FILTER (WHERE outcome = 'failure') AS failures,
COUNT(DISTINCT user_id) FILTER (WHERE outcome = 'failure') AS sprayed_users,
COUNT(*) FILTER (WHERE outcome = 'success') AS successes,
MIN(event_time) AS first_seen,
MAX(event_time) AS last_seen
FROM auth_events
WHERE event_time > now() - INTERVAL '15 minutes'
GROUP BY source_ip
HAVING COUNT(DISTINCT user_id) FILTER (WHERE outcome = 'failure') >= 20
AND COUNT(*) FILTER (WHERE outcome = 'success') >= 1;
The sprayed_users count is what separates stuffing from one person fat-fingering their password 20 times. A single source failing against 20-plus distinct accounts and then landing one success in a 15-minute window is the canonical pattern. Tune the thresholds to your environment, but keep the shape: breadth of targets, not depth of attempts, is the discriminator.
Privilege escalation: the self-grant in cloud IAM
Once inside, the attacker escalates, and cloud IAM mutations are the highest-fidelity events in the whole chain because legitimate users almost never modify their own permissions. The strongest rule alerts when the principal granting a permission and the principal receiving it belong to the same session. In AWS CloudTrail terms, watch for AttachUserPolicy, AttachRolePolicy, CreateAccessKey, or AddUserToGroup where the actor escalates itself, especially attaching broad policies like AdministratorAccess:
SELECT event_time, user_identity_arn, event_name,
request_parameters
FROM cloudtrail_events
WHERE event_name IN ('AttachUserPolicy', 'AttachRolePolicy',
'AddUserToGroup', 'CreateAccessKey')
AND (request_parameters LIKE '%AdministratorAccess%'
OR request_parameters LIKE '%"userName":"' ||
split_part(user_identity_arn, '/', 2) || '"%')
AND event_time > now() - INTERVAL '1 hour';
Pair this with the baseline signal: a role assumed for the first time in 30 days, or a policy attachment from a session whose login already tripped the risk signals in the first detection, is the correlated evidence that lets you act in minutes instead of reconstructing the timeline in a post-incident review. The architecture that makes this correlation possible is the subject of the next section.
How the pieces fit: a correlation pipeline
The four detections above are worthless as four disconnected alerts. Their power is in correlation, and that requires a pipeline that lands identity logs from every source into one queryable store with a shared notion of identity and time. Figure 2 shows the shape of that pipeline.
I describe the flow in Figure 2: identity providers and cloud audit logs feed a normalization layer that resolves every event to a canonical identity, which lands in the SIEM where stateful, cross-source rules run and feed the SOC.
flowchart LR
IdP[IdP sign-in logs<br/>Entra / Okta] --> Norm[normalize<br/>+ resolve identity]
M365[M365 / Workspace<br/>audit] --> Norm
Cloud[cloud IAM<br/>CloudTrail / Audit] --> Norm
Norm --> Store[(SIEM /<br/>data lake)]
Store --> Rules[correlated<br/>detections]
Rules --> SOC[SOC triage<br/>+ response]
Store -. enrichment: device,<br/>ASN, prior baseline .-> Rules
Figure 2. The correlation pipeline that identity detection depends on. The load-bearing component is the normalization step that resolves a user principal name, an Okta actor ID, and an AWS ARN to one canonical identity, because without it the cross-source joins in the worked example cannot be written.
The load-bearing part is the normalization layer. The same human is jane.doe@corp.com in Entra, 00u1a2b3c in Okta, and arn:aws:iam::...:user/jane in AWS, and the entire value of identity detection is the join across those three. If your pipeline does not resolve them to one canonical identity, you cannot write the rule that says “this login was followed by that escalation,” because the login and the escalation carry different identifiers. Get the identity graph right and the detections in the previous section are short queries. Get it wrong and they are impossible no matter how good your rule logic is.
The hard part: false positives are the whole game
The non-obvious lesson, the one every detection engineer learns the slow way, is that writing the rule is the easy 20 percent; making it quiet enough to be trusted is the other 80. An identity detection that fires 50 times a day on legitimate behavior is not a detection, it is a training exercise that teaches your SOC to close the alert without reading it. By the time the real one fires, the muscle memory is “dismiss,” and you have built a very expensive way to ignore breaches.
Behavioral identity rules are especially prone to this because the baseline genuinely shifts. People travel, so impossible travel fires on VPN egress and on a phone that just connected to airport wifi. People get promoted, so the role they assume for the first time in 30 days is their new job. Corporate VPNs and CGNAT collapse thousands of users behind a handful of egress IPs, which both hides real ASN changes and creates spurious ones. A rule that ignores these realities will be tuned into oblivion within a week of going live.
The disciplines that keep false positives survivable are unglamorous. Allowlist known-good infrastructure, the corporate VPN ranges, the SSO proxy, the sanctioned automation accounts, and exclude them explicitly rather than letting them generate noise. Build per-identity baselines with enough history that “new” means genuinely new, not “new since we deployed the rule yesterday.” Combine weak signals, as the first worked example does, because the product of two 5-percent-false-positive signals is far rarer than either alone. And accept that service accounts and break-glass admins need their own detection logic, because their normal behavior, headless, high-volume, broad-permission, looks exactly like an attacker by every human-centric heuristic. The goal is not zero false positives, which is unachievable, but a true-positive rate high enough that an analyst believes the alert when it fires.
Integration into the detection lifecycle
A good identity detection is not a file you commit once. It belongs in a lifecycle that treats detections like the production code they are. In day-to-day practice that means detection-as-code: rules live in version control, ideally in a portable format like Sigma so they survive a SIEM migration, and they go through pull-request review the same as application code. A change to a high-severity identity rule gets a second pair of eyes before it ships, because a careless edit either floods the SOC or silently disables coverage.
The piece teams most often skip is validation, and it separates detections that work from detections that look like they work. Test every identity detection against the tooling it is meant to catch, before it ships and on a recurring schedule after. Adversary-emulation frameworks make this routine. Atomic Red Team provides small, ATT&CK-mapped tests to confirm a technique generates the telemetry your rule depends on [7], and Stratus Red Team does the cloud-native equivalent, detonating techniques like a suspicious IAM policy attachment against a sandbox cloud account [8]. For the identity scenarios, emulate an AiTM session-cookie replay, a cross-tenant OAuth consent-grant abuse, and a credential-stuffing burst that ends in a success, then confirm each of the four detections fires. If a rule does not fire in the drill, it will not fire against the real adversary, and you would rather learn that on a Tuesday than during an incident.
This validation belongs in CI. A scheduled pipeline that detonates the emulation and asserts the corresponding alert appeared turns “we think our identity coverage is good” into “we verified it fired last night,” which is the only version of that claim worth making to a CISO.
Related work and positioning
Identity detection engineering sits among a few adjacent approaches, and being honest about where each fits keeps you from buying a tool to solve a problem you have not framed.
Identity Threat Detection and Response (ITDR) is the vendor category that has grown up around exactly this problem, with products from CrowdStrike, Microsoft, and others bundling identity-centric detections out of the box. ITDR is a reasonable buy when you lack the team to build and maintain detection content, and the trade is the usual one: faster coverage in exchange for less control over the logic and a dependency on the vendor’s view of what matters. The detections in this article are what an ITDR product runs internally; building them yourself makes sense when you have the engineering capacity and want the rules to encode your environment’s specific baselines.
User and Entity Behavior Analytics (UEBA) comes at the same data with machine-learning anomaly scoring rather than explicit rules. It excels at catching the deviation you did not think to write a rule for, and it struggles with explainability: an analyst can read the KQL above and understand exactly why it fired, whereas a UEBA risk score of 0.87 is harder to action and harder to tune. The two are complementary. Explicit rules give you precise, explainable coverage of known techniques; UEBA gives you a wider net for the unknown. Running only one leaves a gap the other would have caught.
Zero-trust and continuous access evaluation operate at the prevention layer rather than detection. CAE in Entra ID can revoke a token mid-session when risk rises, which is genuinely powerful, but prevention and detection are not substitutes. Conditional access reduces the attack surface; detection catches what gets through, and something always gets through. Treating CAE as a reason not to build detections is the same category error as treating MFA as a reason not to monitor logins, and AiTM exists precisely because attackers adapted to MFA.
Where it breaks
Identity detection fails in recognizable ways, and naming them up front is most of avoiding them.
The log you need is not being collected. Every detection here assumes the relevant event is flowing to your SIEM. Refresh-token events, OAuth consent grants, and CAE signals are often not on by default, or are dropped at an ingestion tier to save cost. A rule against a log source you are not collecting is a rule that silently never fires, the most dangerous failure mode because it looks like coverage on a dashboard.
Federation and shared-context blindness. SSO is a single point of observation but also a single point of blindness. When thousands of users share a federation proxy’s egress IPs, ASN-based lineage detection degrades, and a token replayed from inside the corporate network looks identical to legitimate use. The richest lineage signals weaken exactly where the network is flattest.
Service accounts and automation defeat behavioral baselines. Non-human identities are now the majority of identities in most cloud estates, and they break every human-centric heuristic: no working hours, no home country, high volume, broad permissions. Pointing user-behavior rules at them produces either constant false positives or, if you exclude them, a blind spot an attacker will use a stolen service-account key to walk straight through.
Alert fatigue erodes the whole program. As covered in the hard-part section, a SOC that has learned to dismiss identity alerts is worse off than one with no alerts, because the dismissal is now habitual and the real alert dies with the rest. This is an organizational failure mode, not a technical one, and no rule tuning fixes a team that has stopped trusting the queue.
Detection lags a fast attack. Token replay to data exfiltration can happen in minutes. A pipeline with a 15-minute ingestion delay and an hourly scheduled rule may surface the alert after the damage is done. Detection latency is part of the design, not an afterthought, and for the fastest techniques near-real-time streaming detection, or prevention via CAE, is the only thing quick enough.
Validation drift. A rule validated at ship time silently breaks when a log schema changes, a field is renamed in an IdP update, or an ingestion path is refactored. Without recurring emulation the rule rots into a comforting line of code that no longer fires, which is why the validation-in-CI discipline is a continuous gate and not a launch checklist item.
Further reading
Start with the threat-landscape grounding: the Verizon DBIR [1] and the CrowdStrike Global Threat Report [2] together establish why valid-account abuse, not exploitation, is the dominant initial-access path. Map the techniques through MITRE ATT&CK [3], specifically T1078 Valid Accounts and T1539 Steal Web Session Cookie. For the provider-side signals, the Entra ID sign-in log and Continuous Access Evaluation documentation [4] and the Okta System Log reference [5] are the canonical sources for the lineage fields the detections key on. For the detection-as-code practice, the Sigma project [6] is the portable rule format, and Atomic Red Team [7] and Stratus Red Team [8] are the emulation frameworks the validation section is built on. For the architecture pattern of joining identity logs into one correlation surface, the MITRE Cyber Analytics Repository [9] catalogs analytics that span event types in the way this article argues for.
Written with AI assistance, editorially reviewed.
References
[1] Verizon, “2024 Data Breach Investigations Report.” https://www.verizon.com/business/resources/reports/dbir/
[2] CrowdStrike, “2024 Global Threat Report.” https://www.crowdstrike.com/global-threat-report/
[3] MITRE ATT&CK, “Valid Accounts (T1078) and Steal Web Session Cookie (T1539).” https://attack.mitre.org/techniques/T1078/
[4] Microsoft, “Continuous Access Evaluation and Sign-in Logs in Microsoft Entra ID.” https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-continuous-access-evaluation
[5] Okta, “System Log Query and Event Types.” https://developer.okta.com/docs/reference/api/system-log/
[6] SigmaHQ, “Sigma: Generic Signature Format for SIEM Systems.” https://github.com/SigmaHQ/sigma
[7] Red Canary, “Atomic Red Team.” https://github.com/redcanaryco/atomic-red-team
[8] DataDog, “Stratus Red Team: Cloud Adversary Emulation.” https://github.com/DataDog/stratus-red-team
[9] MITRE, “Cyber Analytics Repository (CAR).” https://car.mitre.org/
[10] Microsoft, “Azure Monitor and Microsoft Sentinel KQL Reference.” https://learn.microsoft.com/en-us/azure/azure-monitor/logs/get-started-queries