| Attribute | Details |
|---|---|
| Technique ID | COLLECT-DEFENDER-001 |
| MITRE ATT&CK v18.1 | T1123 - Audio Capture |
| Tactic | Collection |
| Platforms | M365, Microsoft Defender for Endpoint |
| Severity | Medium |
| CVE | N/A |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | Windows Server 2016-2025, Windows 10-11 Enterprise, E5/P2 Defender License Required |
| Patched In | N/A (Operational Feature) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Defender for Endpoint (MDE) is a cloud-based threat intelligence platform that continuously collects endpoint telemetry, behavioral analytics, and security events from deployed agents. Red teams can extract this collected data to understand what has been observed, what was detected, and what remains undetected. Blue teams leverage this data to hunt threats, investigate incidents, and validate detection coverage. Data collection via MDE Advanced Hunting, Custom Data Collection Rules, and API queries allows operators to gain comprehensive visibility into device activities, process execution chains, file operations, and network behavior across the enterprise environment.
Attack Surface: MDE Advanced Hunting portal, MDE API endpoints, Custom Data Collection Rules, device telemetry database (30-day retention in cloud), export functionality.
Business Impact: Unauthorized data exfiltration of security telemetry, forensic evidence collection enabling post-breach analysis, adversary capability assessment. An attacker with administrative access can extract weeks of behavioral data showing which techniques were detected and which bypassed defenses. This intelligence feeds threat-driven adversary research and process refinement.
Technical Context: MDE retains raw event data for 30 days in Advanced Hunting and 180 days in cold storage. Data is collected passively from endpoints without additional action needed once the agent is deployed. Exfiltration via API requires valid credentials but no special permissions beyond read-access roles.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 17.1 | Audit logging and alerting of Advanced Hunting queries must be monitored |
| DISA STIG | SI-4 (SV-71859r1) | Information System Monitoring – Audit log retention and access controls |
| CISA SCuBA | ID.GV-2 | Cybersecurity Governance – Data collection and retention policies |
| NIST 800-53 | SI-4 | Information System Monitoring |
| GDPR | Art. 32 | Security of Processing – Technical measures for safeguarding data |
| DORA | Art. 9 | ICT-related incident reporting and system security |
| NIS2 | Art. 21 | Cybersecurity Risk Management Measures – Monitoring and logging |
| ISO 27001 | A.12.4.1 | Recording user activities, administrator activities, and system events |
| ISO 27005 | 8.3 | Risk Assessment – Data exfiltration scenarios must be evaluated |
Supported Versions:
Tools:
# Verify MDE agent is deployed and communicating
Get-MpComputerStatus
# Check Advanced Hunting data availability
# This requires authentication to security.microsoft.com first
$token = (Get-AzAccessToken -ResourceUrl "https://securitycenter.onmicrosoft.com").Token
What to Look For:
Version Note: PowerShell cmdlet output varies between Windows Server 2016 and 2025 due to defender service architecture changes.
Command (Server 2016-2019):
# Check if Defender agent is installed and running
Get-Service WinDefend
Get-MpPreference | Select-Object DisableRealtimeMonitoring
Command (Server 2022+):
# Enhanced diagnostic reporting
Get-MpComputerStatus | Select-Object AntivirusEnabled, FullScanRequired, QuickScanRequired
Get-MpComputerStatus | Select-Object AntiSpywareEnabled, OnAccessProtectionEnabled
Supported Versions: Server 2016-2025 (all versions supported via cloud portal)
Objective: Establish authenticated session to security.microsoft.com Advanced Hunting interface
Command:
Expected Output:
What This Means:
OpSec & Evasion:
Troubleshooting:
References & Proofs:
Objective: Construct Kusto Query Language (KQL) query to retrieve specific endpoint data
Command (Example 1: All Process Creation Events for Last 7 Days):
DeviceProcessEvents
| where Timestamp > ago(7d)
| project Timestamp, DeviceName, ProcessName, CommandLine, InitiatingProcessName, InitiatingProcessCommandLine, AccountName
| order by Timestamp desc
Command (Example 2: Network Connections from Sensitive Processes):
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where ProcessName in ("lsass.exe", "svchost.exe", "rundll32.exe")
| project Timestamp, DeviceName, ProcessName, RemoteIP, RemotePort, ActionType
| order by Timestamp desc
Command (Example 3: File Creation by Suspicious Extensions):
DeviceFileEvents
| where Timestamp > ago(7d)
| where FileExtension in (".exe", ".dll", ".sys", ".vbs", ".ps1")
| project Timestamp, DeviceName, FolderPath, FileName, ActionType, InitiatingProcessName
| order by Timestamp desc
Expected Output:
What This Means:
OpSec & Evasion:
Troubleshooting:
where Timestamp > ago(1d) instead of ago(30d)| where DeviceId == "specific-device-id"References & Proofs:
Objective: Download query results for offline analysis or exfiltration
Command:
export_yyyy-mm-dd_hhmm.csv to local machineExpected Output:
Timestamp,DeviceName,ProcessName,CommandLine,InitiatingProcessName,InitiatingProcessCommandLine,AccountName
2026-01-10T14:32:15Z,DESKTOP-ABC123,notepad.exe,notepad.exe C:\Users\admin\passwords.txt,explorer.exe,explorer.exe,CORP\admin
2026-01-10T14:33:02Z,SERVER-XYZ,cmd.exe,cmd.exe /c "net group domain admins",svchost.exe,svchost.exe,CORP\system
What This Means:
OpSec & Evasion:
Troubleshooting:
References & Proofs:
Supported Versions: Server 2016-2025 (API available globally)
Objective: Create service principal with permissions to call Advanced Hunting API
Manual Steps (Azure Portal):
MDE-DataCollection-AppMDE API SecretExpected Output:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx~A............................ (64+ characters)What This Means:
OpSec & Evasion:
Troubleshooting:
References & Proofs:
Objective: Authenticate service principal and obtain JWT bearer token for API requests
Command (PowerShell - Server 2016-2025):
$TenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # Your Entra ID Tenant ID
$ClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # Your App Registration Client ID
$ClientSecret = "~A............................" # Your Client Secret (DO NOT SHARE)
$uri = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token"
$body = @{
grant_type = "client_credentials"
client_id = $ClientId
client_secret = $ClientSecret
scope = "https://securitycenter.onmicrosoft.com/.default"
}
$response = Invoke-RestMethod -Method Post -Uri $uri -Body $body -ContentType "application/x-www-form-urlencoded"
$token = $response.access_token
Write-Host "Bearer Token: $token"
Command (Bash/cURL - Linux/macOS):
#!/bin/bash
TENANT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
CLIENT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
CLIENT_SECRET="~A............................"
SCOPE="https://securitycenter.onmicrosoft.com/.default"
TOKEN_RESPONSE=$(curl -X POST \
"https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=${CLIENT_ID}" \
-d "client_secret=${CLIENT_SECRET}" \
-d "scope=${SCOPE}")
TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.access_token')
echo "Bearer Token: $TOKEN"
Expected Output:
{
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5ub3dBREhyRlZfd0FERTFpSXJrNWtIWk5TQSJ9.eyJhdWQiOiJodHRwczovL3NlY3VyaXR5Y2VudGVyLm9ubWljcm9zb2Z0LmNvbSIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0L2RiM2EzNmJhLTUyNGYtNGYxOS05YzcwLTQxYzE1MzhkMmZlNy8iLCJpYXQiOjE2NzMyNDQwMDAsImV4cCI6MTY3MzI0Nzg1MCwiY2lyY0F1dGgiOiIxOTAuMTAwLjIwLjAiLCJzY3AiOiJIVUN0aUFDQ0lUSUFDU0EiLCJzdWIiOiI1M2RiMWI1Ni04MzE0LTQxNjctOGQ2Ni0xNzVhNmU4ZjAwYzUiLCJ1aWQiOiI1M2RiMWI1Ni04MzE0LTQxNjctOGQ2Ni0xNzVhNmU4ZjAwYzUifQ.Signature_Token_Data"
}
What This Means:
aud (audience), exp (expiration), sub (subject)OpSec & Evasion:
Troubleshooting:
https://securitycenter.onmicrosoft.com/.defaultReferences & Proofs:
Objective: Call Advanced Hunting API with bearer token to fetch endpoint telemetry
Command (PowerShell - Server 2016-2025):
$token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs..." # Token from Step 2
$headers = @{
"Authorization" = "Bearer $token"
"Content-Type" = "application/json"
}
$queryBody = @{
"Query" = @"
DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessName in ("lsass.exe", "svchost.exe", "powershell.exe")
| project Timestamp, DeviceName, ProcessName, CommandLine, AccountName
| order by Timestamp desc
"@
} | ConvertTo-Json
$apiUrl = "https://api.securitycenter.microsoft.com/api/advancedhunting/run"
$response = Invoke-RestMethod -Method Post -Uri $apiUrl -Headers $headers -Body $queryBody
# Save results to CSV
$response.Results | Export-Csv -Path "C:\temp\mde_telemetry.csv" -NoTypeInformation
Write-Host "Query executed. Results saved to C:\temp\mde_telemetry.csv"
Write-Host "Total rows: $($response.Results.Count)"
Command (Python - Linux/macOS):
#!/usr/bin/env python3
import requests
import json
import csv
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs..." # Token from Step 2
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
query = """
DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessName in ("lsass.exe", "svchost.exe", "powershell.exe")
| project Timestamp, DeviceName, ProcessName, CommandLine, AccountName
| order by Timestamp desc
"""
api_url = "https://api.securitycenter.microsoft.com/api/advancedhunting/run"
payload = {"Query": query}
response = requests.post(api_url, headers=headers, json=payload)
if response.status_code == 200:
data = response.json()
results = data.get("Results", [])
# Save to CSV
with open("/tmp/mde_telemetry.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=results[0].keys() if results else [])
writer.writeheader()
writer.writerows(results)
print(f"Query executed. Results saved to /tmp/mde_telemetry.csv")
print(f"Total rows: {len(results)}")
else:
print(f"Error: {response.status_code} - {response.text}")
Expected Output:
{
"Schema": [
{"Name": "Timestamp", "DataType": "DateTime"},
{"Name": "DeviceName", "DataType": "String"},
{"Name": "ProcessName", "DataType": "String"},
{"Name": "CommandLine", "DataType": "String"},
{"Name": "AccountName", "DataType": "String"}
],
"Results": [
{
"Timestamp": "2026-01-10T14:32:15Z",
"DeviceName": "DESKTOP-ABC123",
"ProcessName": "powershell.exe",
"CommandLine": "powershell.exe -NoProfile -ExecutionPolicy Bypass -Command (Get-Content C:\\Users\\admin\\password.txt)",
"AccountName": "CORP\\admin"
}
],
"Stats": {
"ExecutionTime": 1.234,
"IngestedRecordCount": 1500000,
"TablesQueried": ["DeviceProcessEvents"],
"CloudLogsBatchesQueried": 50
}
}
What This Means:
project clauseOpSec & Evasion:
Troubleshooting:
References & Proofs:
Supported Versions: Server 2019+ (requires MDE version 101.1234+)
Objective: Configure enhanced telemetry collection for specific process/file/network events beyond default logging
Manual Steps (Microsoft Defender XDR Portal):
Sensitive_Process_MonitoringCollect lsass.exe, svchost.exe, powershell.exe activityDeviceProcessEventsProcessCreatedProcessNameContains any oflsass.exe|svchost.exe|powershell.exe|cmd.exeExpected Output:
What This Means:
DeviceCustomProcessEvents table (named separately)OpSec & Evasion:
Troubleshooting:
References & Proofs:
Objective: Retrieve events collected by custom rule from DeviceCustomProcessEvents table
Command (Advanced Hunting KQL):
DeviceCustomProcessEvents
| where Timestamp > ago(1d)
| where ActionType == "ProcessCreated"
| project Timestamp, DeviceName, ProcessName, CommandLine, AccountName, ProcessId, ParentProcessId
| order by Timestamp desc
Expected Output:
lsass.exe creates child process 1000 times in 1 day, you see all 1000 events (not just 1)What This Means:
OpSec & Evasion:
Troubleshooting:
References & Proofs:
Rule Configuration:
SPL Query:
index=main source="*Purview*" OR source="*AuditLogs*"
| search ActionType="AdvancedHuntingQuery" OR ActionType="RunQuery"
| where NOT (UserRole="Global Administrator" OR UserRole="Security Administrator")
| stats count by user_name, Timestamp, QueryText
| where count >= 1
What This Detects:
Manual Configuration Steps:
MDE_Advanced_Hunting_Unauthorized_QuerySource: Splunk Security Essentials - Cloud Account Monitoring
Rule Configuration:
SPL Query:
index=main source="*Purview*" ActionType="ExportAdvancedHuntingResults"
| stats sum(RecordCount) as TotalRecords by user_name, Timestamp
| where TotalRecords > 50000
| table user_name, TotalRecords, Timestamp
What This Detects:
Manual Configuration Steps:
Version: v1.0 (stable, in production since 2018) Minimum Version: MDE 101.1000+ Supported Platforms: Windows, Linux, macOS (API client language-agnostic)
Version-Specific Notes:
Installation:
# PowerShell (Windows)
Install-Module Microsoft.Graph.Security -Force
# Python
pip install requests azure-identity
# Bash (cURL)
# No installation needed; native tool
Usage:
# PowerShell example (see METHOD 2 for full implementation)
$query = "DeviceProcessEvents | where Timestamp > ago(1d) | project ProcessName | distinct ProcessName"
Invoke-RestMethod -Method Post -Uri "https://api.securitycenter.microsoft.com/api/advancedhunting/run" -Headers $headers -Body ($query | ConvertTo-Json)
Version: 2.3.2 (as of Jan 2026) Minimum Version: MDE 101.1234+, PowerShell 7.0+ Supported Platforms: Windows
Installation:
# Download from GitHub
git clone https://github.com/FalconForce/TelemetryCollectionManager.git
cd TelemetryCollectionManager
# Place YAML config files in ./rules/ directory
Usage:
# Convert YAML rule to MDE JSON format
python3 TelemetryCollectionManager.py --input rules/custom_rule.yaml --output rule.json
# Deploy rule via API
$json = Get-Content rule.json
Invoke-RestMethod -Method Post -Uri "https://api.securitycenter.microsoft.com/api/customdatacollection/rules" -Headers $headers -Body $json
# One-liner to fetch, transform, and export MDE telemetry
$token = (Get-AzAccessToken -ResourceUrl "https://securitycenter.onmicrosoft.com" -AsSecureString).Token | ConvertFrom-SecureString -AsPlainText; $query = "DeviceProcessEvents | where Timestamp > ago(7d) | project Timestamp, DeviceName, ProcessName, CommandLine | top 10000 by Timestamp desc"; $body = @{"Query"=$query} | ConvertTo-Json; (Invoke-RestMethod -Method Post -Uri "https://api.securitycenter.microsoft.com/api/advancedhunting/run" -Headers @{"Authorization"="Bearer $token"} -Body $body).Results | Export-Csv -Path "C:\temp\mde_export_$(Get-Date -Format 'yyyyMMdd_HHmm').csv" -NoTypeInformation
Rule Configuration:
KQL Query:
LAQueryLogs
| where QueryText has_any ("lsass", "Mimikatz", "credentials", "password", "ntds.dit", "DPAPI")
| where UserId !in ("AutomatedHunting", "SOC_Service_Principal")
| project TimeGenerated, UserId, QueryText, Status
| join kind=inner (
AuditLogs
| where OperationName == "Run query"
| project UserId, OperationName, TimeGenerated
) on UserId, TimeGenerated
| project-away TimeGenerated1
What This Detects:
Manual Configuration Steps (Azure Portal):
MDE_Advanced_Hunting_Credential_QueryHigh5 minutes1 hourUserIdManual Configuration Steps (PowerShell):
Connect-AzAccount
$ResourceGroup = "MyResourceGroup"
$WorkspaceName = "MySentinelWorkspace"
New-AzSentinelAlertRule -ResourceGroupName $ResourceGroup -WorkspaceName $WorkspaceName `
-DisplayName "MDE_Advanced_Hunting_Credential_Query" `
-Query @"
LAQueryLogs
| where QueryText has_any ("lsass", "credentials", "password")
| where UserId !in ("AutomatedHunting")
| project TimeGenerated, UserId, QueryText, Status
"@ `
-Severity "High" `
-Enabled $true `
-Frequency "PT5M" -Period "PT1H"
Source: Microsoft Sentinel MDE Integration Guide
Rule Configuration:
KQL Query:
AuditLogs
| where OperationName == "Export query results"
| where Result == "Success"
| summarize ExportCount = count() by UserId, TimeGenerated
| where ExportCount > 3 // More than 3 exports in 10 minutes = bulk activity
| project TimeGenerated, UserId, ExportCount
What This Detects:
Source: Azure Audit Logging Reference
Event ID: 4688 (Process Creation)
CommandLine contains "securitycenter" AND ProcessName contains "powershell"Manual Configuration Steps (Group Policy):
gpupdate /forceManual Configuration Steps (Server 2022+):
gpupdate /force and restartExpected Event Format:
Event ID: 4688
Source: Security
Task Category: Process Creation
Level: Information
Computer: DESKTOP-ABC123
User: CORP\admin
Process Information:
New Process ID: 0x1234
New Process Name: C:\Windows\System32\powershell.exe
Creator Process ID: 0x5678
Creator Process Name: C:\Windows\System32\explorer.exe
Process Command Line: powershell.exe -NoProfile -Command Invoke-RestMethod -Uri "https://api.securitycenter.microsoft.com/api/advancedhunting/run" -Headers $headers -Body $queryBody
Restrict Access to Advanced Hunting: Limit Advanced Hunting portal access to Security Administrators and designated SOC personnel only. Disable self-service access for non-security roles. Applies To Versions: All versions
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
# Remove user from Security Reader role
$user = Get-AzAdUser -ObjectId "user@company.com"
Remove-AzRoleAssignment -ObjectId $user.Id -RoleDefinitionName "Security Reader"
# Add designated SOC user to Security Reader role
$socUser = Get-AzAdUser -ObjectId "soc@company.com"
New-AzRoleAssignment -ObjectId $socUser.Id -RoleDefinitionName "Security Reader" -Scope "/subscriptions/YOUR_SUB_ID"
Enable Conditional Access for Advanced Hunting API: Require MFA, compliant device, and specific IP ranges for API calls.
Manual Steps:
MDE_API_Access_ControlAudit All Advanced Hunting Activity: Enable Purview audit logging and alert on every query/export.
Manual Steps:
Disable Advanced Hunting for Non-SOC Users: Enforce organization-wide policy prohibiting non-security personnel from accessing Advanced Hunting.
Manual Steps:
Implement Data Loss Prevention (DLP) for Advanced Hunting Exports: Prevent CSV exports containing credentials, PII, or sensitive patterns.
Manual Steps:
Prevent_MDE_Export_With_Credentials# Verify Conditional Access policy is in place
Get-AzADConditionalAccessPolicy | Where-Object {$_.DisplayName -like "*MDE*"}
# Verify audit retention
Get-AzPrivateEndpoint -ResourceGroupName "RG" | Where-Object {$_.Name -like "*audit*"}
# Verify Advanced Hunting access is restricted
$nonSOCUsers = Get-AzADGroupMember -GroupObjectId (Get-AzADGroup -DisplayName "SOC_Team").Id
Get-AzRoleAssignment -RoleDefinitionName "Security Reader" | Where-Object { $_.ObjectId -notin $nonSOCUsers.Id }
# Should return 0 if properly configured (no non-SOC users have Security Reader role)
Expected Output (If Secure):
# No results returned = Secure configuration
What to Look For:
C:\Users\[Username]\Downloads\export_*.csv (MDE export files from portal)C:\temp\mde_telemetry.csv (programmatic API export destination)$apiUrl = "https://api.securitycenter..."HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU (Most recently used paths; check for “export_” entries)api.securitycenter.microsoft.com/api/advancedhunting/run from non-SOC machinesPowerShell or curl to *.securitycenter.microsoft.com from unexpected IP addressesC:\Users\[Username]\AppData\Roaming\Microsoft\Windows\Recent\export*.csv (Recent files metadata)C:\Windows\Prefetch\POWERSHELL.EXE-*.pf shows last execution time and file paths accessed$token = "eyJ..." variable contains JWT tokenLAQueryLogs table: Records every query with QueryText, UserId, Timestamp, ResultCountAuditLogs table: Operation=”Run query”, Operation=”Export”, showing User, Timestamp, IpAddressSentinelAudit table: “AdvancedHuntingQueryRun” events if using Sentinel integrationCloudAppEvents table: ActionType=”ExportAdvancedHuntingResults” shows who exported, when, and result count# Disable the compromised user account immediately
Disable-AzADUser -ObjectId "compromised.user@company.com"
# Revoke all active sessions
Revoke-AzAccessToken
Manual (Azure Portal):
# Export Advanced Hunting queries executed by compromised user
Search-AzLog -StartTime (Get-Date).AddDays(-7) -EventInitiator "compromised.user@company.com" -MaxResult 10000 | Export-Csv -Path "C:\Incident\user_queries.csv"
# Export audit logs for API token generation
Search-UnifiedAuditLog -UserIds "compromised.user@company.com" -Operations "Run query" -StartDate (Get-Date).AddDays(-7) | Export-Csv -Path "C:\Incident\api_activity.csv"
Manual (Purview):
# Revoke all app registrations created by compromised account
Get-AzADApplication | Where-Object {$_.CreatedDateTime -gt (Get-Date).AddDays(-7) -and $_.CreatedOnBehalfOfRef -like "*compromised*"} | Remove-AzADApplication
# Rotate all API secrets created in past 7 days (except active SOC ones)
Get-AzADAppCredential | Where-Object {$_.StartDate -gt (Get-Date).AddDays(-7)} | Remove-AzADAppCredential
Manual:
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | T1190 - Exploit Public-Facing Application | Attacker gains initial foothold via phishing or credential compromise |
| 2 | Privilege Escalation | T1548 - Abuse Elevation Control Mechanism | Escalate to Security Admin role via compromised account or Group Policy |
| 3 | Collection | [COLLECT-DEFENDER-001] | Extract weeks of endpoint telemetry via Advanced Hunting API |
| 4 | Exfiltration | T1041 - Exfiltration Over C2 Channel | Send extracted CSV to attacker-controlled server via HTTPS |
| 5 | Impact | T1529 - System Shutdown/Reboot | (Optional) Disable MDE agents after intelligence collection |
Primary References:
Secondary References: