Project Atlas · Threat-informed defence

Detecting an identity-led Scattered Spider-style intrusion.

A defensive case study connecting threat modelling, MITRE ATT&CK coverage and practical KQL detections against the fictional Atlas platform.

Identity firstHelp desk and MFA abuse
8-stage pathInitial access to exfiltration
KQLTestable detection logic
Purple teamValidation over assumptions

Threat scenario

Attack the identity, inherit the trust.

The model assumes a sophisticated, financially motivated actor targets Project Atlas through social engineering rather than exploiting the public application. The attacker impersonates a user to the help desk, resets credentials, registers or abuses MFA, and then uses legitimate access to move toward privileged roles and sensitive documents.

This is a Scattered Spider-style scenario, not an attribution claim or prediction. The behaviours are selected because they expose connected weaknesses across people, identity controls, SaaS access and cloud administration.

Attack path

Eight opportunities to prevent or detect

01Help-desk impersonationT1566 / social engineering
02Password resetValid account preparation
03MFA change or fatigueT1621
04New device sign-inT1078
05Privilege escalationT1098 / T1136
06Remote toolingT1219
07Bulk document accessCollection
08Cloud exfiltrationT1567.002

Control strategy

Layer friction, evidence and response.

PREVENT

Protect recovery paths

Verified help-desk procedures, phishing-resistant MFA for privileged users, protected authentication changes, Conditional Access and PIM.

DETECT

Correlate identity change

Join password resets, MFA changes, unfamiliar sign-ins, new devices and role assignments into a time-bounded identity story.

RESPOND

Contain the identity

Revoke sessions, disable compromised factors, preserve identity audit evidence, remove persistence and assess data access before recovery.

Detection 01 · MFA pressure

Repeated failures in a short window

HypothesisAn attacker generates repeated prompts or failures before the user accepts one.DataSigninLogsScheduleEvery 10 minutes · 30-minute lookback
let Window = 30m;
SigninLogs
| where TimeGenerated > ago(Window)
| summarize Attempts=count(),
    Failures=countif(ResultType != "0"),
    Apps=make_set(AppDisplayName, 5),
    IPs=make_set(IPAddress, 5)
  by UserPrincipalName, bin(TimeGenerated, 10m)
| where Attempts >= 8 and Failures >= 5
| order by Failures desc

False positives: stale mobile sessions, shared terminals and a legitimate user retrying a changed password.

Triage: compare device, IP, location, application and authentication details; contact the user through a trusted channel.

Validation: generate failed test sign-ins from a controlled account and confirm threshold, alert evidence and response routing.

Detection 02 · Reset-to-login correlation

Successful sign-in after password reset

HypothesisA social-engineered reset is quickly followed by attacker access.DataAuditLogs + SigninLogsLookback60 minutes
let ResetWindow = 60m;
let resets = AuditLogs
| where TimeGenerated > ago(ResetWindow)
| where OperationName has_any ("Reset password", "Change password")
| project ResetTime=TimeGenerated,
    UserPrincipalName=tostring(TargetResources[0].userPrincipalName),
    ResetBy=tostring(InitiatedBy.user.userPrincipalName);
SigninLogs
| where TimeGenerated > ago(ResetWindow)
| where ResultType == "0"
| join kind=inner resets on UserPrincipalName
| where TimeGenerated between (ResetTime .. ResetTime + 30m)
| project TimeGenerated, UserPrincipalName, ResetBy,
    IPAddress, Location, AppDisplayName, DeviceDetail

False positives: legitimate recovery naturally produces the same sequence. Risk comes from the sign-in context and reset actor.

Triage: validate the help-desk record, reset initiator, device registration, IP reputation and subsequent resource access.

Blind spot: operation names and nested audit fields can vary; the query must be validated against the tenant schema before production use.

Detection 03 · Privilege persistence

Role membership change

HypothesisA compromised identity adds persistent or eligible privileged access.DataAuditLogsPriorityHigh for privileged roles
AuditLogs
| where TimeGenerated > ago(1h)
| where OperationName has_any (
    "Add member to role",
    "Add eligible member to role"
  )
| extend Actor=tostring(InitiatedBy.user.userPrincipalName),
    Target=tostring(TargetResources[0].displayName),
    Role=tostring(TargetResources[0].modifiedProperties[1].newValue)
| project TimeGenerated, Actor, Target, Role, CorrelationId

Triage: validate the change ticket and approver, inspect the actor session, enumerate other changes sharing the correlation ID and remove unauthorised assignment.

Tuning: maintain a privileged-role watchlist and suppress only known automation identities with tightly controlled change paths.

Coverage & gaps

A query is not a capability.

These detections cover identity pressure, recovery abuse and privilege change, but they do not by themselves prove malicious intent. The next validation layer would simulate remote-support tooling, abnormal document enumeration and high-volume cloud transfers, then measure whether the complete attack chain is visible.

Known gaps include unavailable endpoint telemetry, attackers operating through trusted residential infrastructure, legitimate help-desk activity that resembles compromise, and schema differences between environments. Those gaps become backlog items with owners—not footnotes hidden after deployment.

Back to the lab book

Back to all projects →