external/anthropic-cybersecurity-skills/skills/hunting-saas-sso-token-abuse/SKILL.md
Hunts for stolen-session and OAuth/PRT token replay (T1550.001) by correlating Microsoft Entra ID SigninLogs SessionId/UniqueTokenIdentifier fields and Okta System Log sso/session events to spot impossible travel, refresh-token reuse, and token use from anomalous ASNs. Use when hunting MFA-bypass via stolen cookies/tokens, investigating impossible-travel alerts, or scoping SaaS lateral movement after phishing.
npx skillsauth add seikaikyo/dash-skills hunting-saas-sso-token-abuseInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Adversaries increasingly bypass MFA not by defeating it but by stealing the artifacts issued after a successful authentication — session cookies, OAuth access/refresh tokens, and Primary Refresh Tokens (PRTs). With a stolen token an attacker replays the existing session ("pass-the-cookie" / token replay), inheriting the victim's authenticated state across federated SaaS without ever prompting for credentials or MFA. Mandiant's M-Trends reporting and Microsoft/Okta incident data both highlight token theft as a dominant cloud lateral-movement technique, mapped to MITRE ATT&CK T1550.001 Use Alternate Authentication Material: Application Access Token.
Detection relies on correlating identity telemetry rather than watching for failed logins. In Microsoft Entra ID the key tables are SigninLogs (interactive), AADNonInteractiveUserSignInLogs (where replayed cookies/refresh tokens commonly surface), and AADServicePrincipalSignInLogs. Entra now exposes linkable identifiers — SessionId and UniqueTokenIdentifier — that let a hunter stitch every artifact derived from one root authentication event together and spot a single session being used from multiple IPs, ASNs, or device fingerprints. In Okta the System Log carries authentication.sso, policy.evaluate_sign_on, and user.session.start events with a deviceToken/session context; the same session token appearing from divergent IPs/user-agents is the tell. Okta Identity Threat Protection (ITP) can natively flag "suspected session hijacking."
This skill provides a hypothesis-driven hunt: baseline normal session behavior, then look for impossible travel within a single session, refresh-token reuse, token use from anomalous infrastructure (hosting/VPS ASNs), and SaaS access patterns inconsistent with the user's device. Source: MITRE ATT&CK T1550.001; Microsoft Entra ID sign-in log documentation; Okta System Log reference; Mandiant M-Trends.
# Confirm the diagnostic settings export SigninLogs + non-interactive logs to a workspace
az monitor diagnostic-settings list --resource \
/providers/Microsoft.aadiam/diagnosticSettings -o table
curl -s -H "Authorization: SSWS $OKTA_API_TOKEN" \
"https://<org>.okta.com/api/v1/logs?filter=eventType eq \"user.session.start\"&since=2026-06-01T00:00:00Z"
requests for the Okta API)SessionId / UniqueTokenIdentifier| ID | Name | Use in this skill | |----|------|-------------------| | T1550.001 | Use Alternate Authentication Material: Application Access Token | Core technique — replaying stolen OAuth tokens/cookies | | T1539 | Steal Web Session Cookie | The cookie theft that precedes pass-the-cookie replay | | T1528 | Steal Application Access Token | Acquisition of OAuth tokens via phishing/illicit consent | | T1078.004 | Valid Accounts: Cloud Accounts | Replayed tokens grant valid-account access to SaaS | | T1098.001 | Account Manipulation: Additional Cloud Credentials | Follow-on persistence after token abuse |
Stitch interactive, non-interactive, and SP sign-ins for one session to see the full chain.
union SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(7d)
| where isnotempty(SessionId)
| summarize IPs=make_set(IPAddress), Apps=make_set(AppDisplayName),
Locations=make_set(tostring(LocationDetails.countryOrRegion)),
Count=count() by SessionId, UserPrincipalName
| where array_length(IPs) > 1
AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(24h)
| extend ASN = tostring(parse_json(tostring(NetworkLocationDetails))[0].networkType)
| summarize distinctIPs = dcount(IPAddress),
ipset = make_set(IPAddress) by SessionId, UserPrincipalName
| where distinctIPs >= 2
SigninLogs
| where TimeGenerated > ago(7d)
| project TimeGenerated, UserPrincipalName, IPAddress,
City=tostring(LocationDetails.city),
Country=tostring(LocationDetails.countryOrRegion), SessionId
| order by UserPrincipalName, TimeGenerated asc
| serialize
| extend prevCountry = prev(Country), prevTime = prev(TimeGenerated),
prevUser = prev(UserPrincipalName)
| where UserPrincipalName == prevUser and Country != prevCountry
and datetime_diff('minute', TimeGenerated, prevTime) < 60
Replayed tokens are frequently used from datacenter ASNs, unlike the user's residential/corporate ranges.
AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(24h)
| where ResultType == 0
| extend asnOrg = tostring(parse_json(tostring(AutonomousSystemNumber)))
| where IPAddress in (toscalar(externaldata(ip:string)["<hosting-asn-iplist>"]))
| project TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress
AuditLogs
| where TimeGenerated > ago(30d)
| where OperationName in ("Consent to application", "Add OAuth2PermissionGrant",
"Add delegated permission grant")
| extend app = tostring(TargetResources[0].displayName)
| project TimeGenerated, InitiatedBy, app, Result
A single Okta session (deviceToken) used from divergent IPs/clients indicates hijack.
curl -s -H "Authorization: SSWS $OKTA_API_TOKEN" \
"https://<org>.okta.com/api/v1/logs?filter=eventType eq \"policy.evaluate_sign_on\"&since=2026-06-15T00:00:00Z" \
| jq -r '.[] | [.authenticationContext.externalSessionId, .client.ipAddress, .client.userAgent.rawUserAgent] | @tsv' \
| sort | uniq -c | sort -rn
index=okta eventType="policy.evaluate_sign_on"
| stats dc(client.ipAddress) as ip_count
values(client.ipAddress) as ips
values(client.userAgent.rawUserAgent) as agents
by authenticationContext.externalSessionId actor.alternateId
| where ip_count > 1
For confirmed token abuse, revoke sessions and rotate, then promote the hunt to a rule.
# Revoke all refresh tokens / sessions for the user in Entra
az rest --method POST \
--url "https://graph.microsoft.com/v1.0/users/<userId>/revokeSignInSessions"
See scripts/agent.py to pull Okta logs and flag reused session tokens automatically.
| Resource | Purpose | Link | |----------|---------|------| | MITRE ATT&CK T1550.001 | Technique reference | https://attack.mitre.org/techniques/T1550/001/ | | Entra sign-in logs schema | KQL hunting field reference | https://learn.microsoft.com/en-us/entra/identity/monitoring-health/reference-azure-monitor-sign-ins-log-schema | | Azure-Sentinel hunting repo | Community KQL detections | https://github.com/Azure/Azure-Sentinel | | Okta System Log API | Event hunting source | https://developer.okta.com/docs/reference/api/system-log/ | | Mandiant M-Trends | Token-theft threat landscape | https://www.mandiant.com/m-trends | | AzureAD-Attack-Defense | PRT/token replay detection guidance | https://github.com/Cloud-Architekt/AzureAD-Attack-Defense |
Detection should pair with controls that make stolen tokens far less useful:
| Benign cause | Tuning | |--------------|--------| | Corporate VPN/proxy egress (many users, few IPs) | Allowlist known egress IPs/ASNs | | Mobile carrier IP rotation | Widen impossible-travel time/distance thresholds | | Legitimate multi-device users | Correlate device IDs, not just IPs | | Backend/API calls within one session | Exclude expected service principals |
| Indicator | Signal |
|-----------|--------|
| One SessionId across multiple IPs/ASNs | Token/cookie replay |
| Non-interactive sign-in from new datacenter IP | Replayed refresh token |
| Impossible travel within < 1h | Concurrent session use |
| Refresh-token reuse after rotation | Strong compromise signal |
| New OAuth consent to unfamiliar app | Illicit-consent token theft |
| Okta session token from divergent user-agents | Session hijack |
SigninLogs and AADNonInteractiveUserSignInLogs queryableSessionId produces resultstools
Conduct comprehensive GDPR compliance assessments by evaluating data processing activities against EU Regulation 2016/679, including Article 30 records of processing, lawful basis validation, data subject rights implementation, Data Protection Impact Assessments (DPIAs) under Article 35, breach notification procedures, international transfer safeguards (SCCs, adequacy decisions), and technical/organizational measures under Article 32. Use when processing personal data of EU residents, preparing for supervisory authority audits, implementing privacy-by-design for new systems, scoping compliance gaps for M&A due diligence, assessing third-party processors, or responding to data subject access requests at scale. Incorporates 2026 guidance from ICO, EDPB, and post-Data (Use and Access) Act 2025 UK-GDPR considerations. Do not use for implementing specific Article 32 controls — use implementing-gdpr-data-protection-controls; or for DSAR automation — use implementing-gdpr-data-subject-access-request.
tools
Parse Windows forensic artifacts—$MFT/$J (MFTECmd), Prefetch (PECmd), registry hives (RECmd), shellbags, and Amcache—into normalized CSV/JSON with Eric Zimmerman's EZ Tools, then load results into Timeline Explorer for analysis. Use during DFIR/incident-response investigations, after triage collection (e.g. with KAPE), to establish program execution, file/folder access, and persistence evidence from acquired forensic images.
development
Build automated multi-turn adversarial attacks against conversational LLM targets using Microsoft PyRIT's RedTeamingOrchestrator, CrescendoOrchestrator (gradual escalation), and TreeOfAttacksWithPruningOrchestrator (adaptive branching), with scorer feedback loops and persisted conversation memory. Use when single-shot LLM scanning is insufficient and you need multi-turn, scorer-driven AI red-team campaigns against a chatbot or agent.
testing
Stand up MISP, enable and cache curated threat feeds (CIRCL, abuse.ch, Feodo Tracker), apply warninglists to suppress false positives, query indicators with PyMISP, and export attributes as auto-generated Suricata/Sigma/Wazuh detection rules. Use when maturing a MISP instance to actively drive detection, curating threat feeds with quality controls, or automating IOC-to-detection pipelines for the SIEM/IDS.