| Attribute | Details |
|---|---|
| Technique ID | COLLECT-SENTINEL-001 |
| MITRE ATT&CK v18.1 | T1123 - Audio Capture |
| Tactic | Collection |
| Platforms | Entra ID, Microsoft Sentinel, Azure Monitor |
| Severity | High |
| CVE | N/A |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | Sentinel workspace any version, Azure Monitor Log Analytics |
| Patched In | N/A (Operational Feature) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Microsoft Sentinel is a cloud-native SIEM platform that ingests security alerts, logs, and analytics from Microsoft 365, Azure, Entra ID, and third-party integrations. Sentinel stores triggered alerts in the SecurityAlert table and raw security events in tables like AuditLogs, SigninLogs, DeviceLogonEvents, and others. Red teams with access to Sentinel can extract weeks of alert history, detection logic, and alert evidence to understand which attack techniques triggered detections and which remained unnoticed. Blue teams use this data for incident investigation, threat hunting, and detection effectiveness validation. An attacker gaining access to Sentinel can determine exactly which tactics are detected and refine their operations accordingly, making Sentinel access a high-value target.
Attack Surface: Sentinel workspace portal (portal.azure.com), KQL query editor, alert tables (SecurityAlert, AlertInfo, AlertEvidence), Log Analytics workspace API, data connector configurations.
Business Impact: Complete visibility into security alert configuration, detection evasion guidance, incident investigation data exposure, and loss of threat intelligence. An attacker with Sentinel access can: (1) Read all triggered alerts to identify detected techniques, (2) Query raw logs to understand data collection capabilities, (3) Modify alert rules to disable detections, (4) Extract evidence data from past incidents revealing adversary tradecraft, (5) Identify gaps in logging and monitoring by analyzing missing data.
Technical Context: Sentinel retains alert data in SecurityAlert table with 30-day default retention (configurable up to 12 years). AlertInfo and AlertEvidence tables contain event details and supporting evidence. KQL queries execute with millisecond-to-second response times. No special licensing or tools required beyond valid Entra ID credentials with Sentinel Reader or higher role. All query execution is logged to LAQueryLogs and SentinelAudit tables.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 18.2 | Audit logging for SIEM access and query execution must be monitored |
| DISA STIG | IA-2 (SV-89853r1) | Authentication for privileged accounts accessing security monitoring systems |
| CISA SCuBA | ID.AM-1 | Asset Management – Sentinel workspace inventory and access controls |
| NIST 800-53 | SI-4(3) | System Monitoring – Analyze monitoring data for security alerts |
| GDPR | Art. 32 | Security of Processing – Confidentiality and integrity of security systems |
| DORA | Art. 23 | ICT-related incident handling and notification procedures |
| NIS2 | Art. 21 | Cybersecurity Risk Management Measures – Continuous monitoring |
| ISO 27001 | A.12.4.1 | Recording user activities and system events in SIEM |
| ISO 27005 | 8.3 | Risk Assessment – Unauthorized SIEM access scenarios |
Supported Versions:
Tools:
# Check if Sentinel is deployed and active
Connect-AzAccount
$workspaceName = "your-sentinel-workspace"
$resourceGroup = "your-resource-group"
$workspace = Get-AzOperationalInsightsWorkspace -ResourceGroupName $resourceGroup -Name $workspaceName
Write-Host "Workspace ID: $($workspace.ResourceId)"
Write-Host "Retention Days: $($workspace.RetentionInDays)"
What to Look For:
Version Note: Command output identical across all Azure regions and Sentinel versions.
Command (Server 2016-2025 - Terraform / Infrastructure-as-Code Discovery):
# Query Azure Resource Graph for all Sentinel workspaces (requires Reader role)
Search-AzGraph -Query "
resources
| where type == 'microsoft.operationalinsights/workspaces'
| where resourceGroup =~ 'your-rg'
| project name, resourceGroup, location, properties.retentionInDays
"
Supported Versions: All versions (cloud-native, version-agnostic)
Objective: Establish authenticated session to Sentinel workspace analytics interface
Command:
Expected Output:
What This Means:
OpSec & Evasion:
Troubleshooting:
References & Proofs:
Objective: Construct Kusto Query Language query to retrieve alert details, timelines, and evidence
Command (Example 1: All High-Severity Alerts for Last 30 Days):
SecurityAlert
| where TimeGenerated > ago(30d)
| where AlertSeverity == "High" or AlertSeverity == "Critical"
| project TimeGenerated, AlertName, AlertType, Description, Entities, Severity = AlertSeverity, Status = AlertStatus
| order by TimeGenerated desc
Command (Example 2: Alerts Involving Credential Access Techniques):
SecurityAlert
| where TimeGenerated > ago(30d)
| where AlertName has_any ("Credential", "Password", "Token", "Authentication", "LSASS", "Mimikatz", "Kerberos")
| project TimeGenerated, AlertName, AlertType, SourceIP, UserId, Description, AlertDetails = todynamic(Entities)
| order by TimeGenerated desc
Command (Example 3: Alerts Triggered by Specific User or IP):
AlertInfo
| join kind=inner AlertEvidence on AlertId
| where TimeGenerated > ago(7d)
| where SourceIP == "203.0.113.45" or InitiatingUser == "admin@company.com"
| project TimeGenerated, AlertId, AlertName, SourceIP, InitiatingUser, EventType, DetectionStatus
| order by TimeGenerated desc
Expected Output:
TimeGenerated | AlertName | AlertType | Severity | Status
2026-01-09T14:32:15Z | Suspected credential dumping | SecurityAlert | High | New
2026-01-09T13:22:08Z | Suspicious PowerShell activity | SecurityAlert | Medium | Resolved
2026-01-08T22:15:33Z | Brute force attack detected | SecurityAlert | Critical | Closed
What This Means:
OpSec & Evasion:
Troubleshooting:
where TimeGenerated > ago(1d) instead of ago(30d)References & Proofs:
Objective: Download query results in CSV format for offline analysis or exfiltration
Command:
query_yyyy-mm-dd_hhmm.csv to local Downloads folderExpected Output:
TimeGenerated,AlertName,AlertType,Description,Severity,Status,Entities,SourceIP
2026-01-09T14:32:15Z,Suspected credential dumping,SecurityAlert,"Mimikatz-like process behavior detected",High,New,"[{""Type"":""Account"",""Name"":""CORP\admin""},{""Type"":""Host"",""Name"":""DESKTOP-ABC123""}]","203.0.113.45"
2026-01-09T13:22:08Z,Suspicious PowerShell activity,SecurityAlert,"Obfuscated PowerShell script executed",Medium,Resolved,"[{""Type"":""Process"",""Name"":""powershell.exe""},{""Type"":""Account"",""Name"":""CORP\user1""}]",NULL
What This Means:
OpSec & Evasion:
Troubleshooting:
top 50000 in query or filter by date range furtherReferences & Proofs:
Supported Versions: All Sentinel versions (API available globally)
Objective: Create service principal with read access to Log Analytics workspace
Manual Steps (Azure Portal):
Sentinel-Alert-CollectionSentinel API Access, Expires: 6 monthsData.ReadExpected Output:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx~B........................... (64+ characters)OpSec & Evasion:
References & Proofs:
Objective: Authenticate service principal and obtain JWT token for API calls
Command (PowerShell):
$TenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$ClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$ClientSecret = "~B............................"
$uri = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token"
$body = @{
grant_type = "client_credentials"
client_id = $ClientId
client_secret = $ClientSecret
scope = "https://api.loganalytics.io/.default"
}
$response = Invoke-RestMethod -Method Post -Uri $uri -Body $body -ContentType "application/x-www-form-urlencoded"
$token = $response.access_token
Write-Host "Bearer Token obtained: $($token.Substring(0, 50))..."
Expected Output:
{
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs..."
}
What This Means:
OpSec & Evasion:
References & Proofs:
Objective: Call Log Analytics Query API with bearer token to fetch alerts
Command (PowerShell):
$token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs..."
$subscriptionId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$resourceGroup = "your-resource-group"
$workspaceName = "your-sentinel-workspace"
$headers = @{
"Authorization" = "Bearer $token"
"Content-Type" = "application/json"
}
# Get workspace ID
$workspaceUri = "https://management.azure.com/subscriptions/$subscriptionId/resourcegroups/$resourceGroup/providers/microsoft.operationalinsights/workspaces/$workspaceName/api/query?api-version=2020-08-01"
# KQL query to execute
$queryBody = @{
"query" = "SecurityAlert | where TimeGenerated > ago(30d) | where AlertSeverity =~ 'High' or AlertSeverity =~ 'Critical' | top 1000 by TimeGenerated desc"
} | ConvertTo-Json
$response = Invoke-RestMethod -Method Post -Uri $workspaceUri -Headers $headers -Body $queryBody
# Export to CSV
$response.tables[0].rows | ForEach-Object {
[PSCustomObject]@{
TimeGenerated = $_[0]
AlertName = $_[1]
AlertType = $_[2]
Severity = $_[3]
}
} | Export-Csv -Path "C:\temp\sentinel_alerts.csv" -NoTypeInformation
Write-Host "Alerts exported to C:\temp\sentinel_alerts.csv"
Command (Python):
#!/usr/bin/env python3
import requests
import json
import csv
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs..."
subscription_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
resource_group = "your-resource-group"
workspace_name = "your-sentinel-workspace"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
workspace_uri = f"https://management.azure.com/subscriptions/{subscription_id}/resourcegroups/{resource_group}/providers/microsoft.operationalinsights/workspaces/{workspace_name}/api/query?api-version=2020-08-01"
query = """
SecurityAlert
| where TimeGenerated > ago(30d)
| where AlertSeverity =~ 'High' or AlertSeverity =~ 'Critical'
| top 1000 by TimeGenerated desc
| project TimeGenerated, AlertName, AlertType, AlertSeverity, Entities
"""
payload = {"query": query}
response = requests.post(workspace_uri, headers=headers, json=payload)
if response.status_code == 200:
data = response.json()
results = data.get("tables", [{}])[0].get("rows", [])
# Save to CSV
with open("/tmp/sentinel_alerts.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["TimeGenerated", "AlertName", "AlertType", "Severity", "Entities"])
writer.writerows(results)
print(f"Alerts exported to /tmp/sentinel_alerts.csv - {len(results)} rows")
else:
print(f"Error: {response.status_code} - {response.text}")
Expected Output:
{
"tables": [
{
"name": "PrimaryResult",
"columns": [
{"name": "TimeGenerated", "type": "datetime"},
{"name": "AlertName", "type": "string"},
{"name": "AlertType", "type": "string"},
{"name": "AlertSeverity", "type": "string"}
],
"rows": [
["2026-01-09T14:32:15Z", "Suspected credential dumping", "SecurityAlert", "High"],
["2026-01-09T13:22:08Z", "Suspicious PowerShell activity", "SecurityAlert", "Medium"]
]
}
]
}
What This Means:
OpSec & Evasion:
Troubleshooting:
References & Proofs:
Version: v1 (stable, production-ready) Minimum Version: API GA since 2020 Supported Platforms: Windows, Linux, macOS (any HTTP client)
Installation:
# No installation required; uses standard HTTP/REST
# Optional: Install Azure CLI for easier authentication
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
# Optional: Install Azure PowerShell
Install-Module Az.OperationalInsights -Force
Usage:
# Use service principal with bearer token (see METHOD 2)
$headers = @{ "Authorization" = "Bearer $token" }
Invoke-RestMethod -Method Post -Uri $apiUrl -Headers $headers -Body $queryBody
Version: Latest (continuously updated) Platforms: All Cloud (Azure, AWS, GCP support KQL via Kusto)
Common Operators:
where – Filter rows by conditionproject – Select columns to displaysummarize – Aggregate data (count, sum, avg, max, min)join – Correlate data from multiple tablesorder by – Sort results ascending/descendingtop – Limit number of rows returnedExample Query - Extract All Alerts and Entities:
SecurityAlert
| where TimeGenerated > ago(30d)
| project TimeGenerated, AlertName, Entities = todynamic(Entities)
| mvexpand Entities
| project TimeGenerated, AlertName, EntityType = Entities.Type, EntityName = Entities.Name
| top 50000 by TimeGenerated desc
Rule Configuration:
KQL Query:
LAQueryLogs
| where QueryText has_any ("SecurityAlert", "AlertInfo", "AlertEvidence")
| where ResultCount > 5000 // Bulk export indicates suspicious activity
| where UserId !in ("AutomatedHunting", "SOC_Reports_Automation")
| summarize ExportCount = count(), TotalRows = sum(ResultCount) by UserId, tostring(QueryText)
| where ExportCount > 2 // Multiple high-volume exports in short timeframe
| project UserId, ExportCount, TotalRows, QueryText
What This Detects:
Manual Configuration Steps (Azure Portal):
Sentinel_Bulk_Alert_ExportHigh10 minutes1 hourUserIdEvent ID: 4688 (Process Creation)
Manual Configuration (Group Policy):
gpupdate /force and restartRestrict Sentinel Query Access: Limit KQL query editor access to approved SOC members only. Disable query access for analysts without need-to-know.
Manual Steps:
PowerShell:
# Remove user from Sentinel Reader
$user = Get-AzADUser -ObjectId "user@company.com"
Remove-AzRoleAssignment -ObjectId $user.Id -RoleDefinitionName "Sentinel Reader" `
-ResourceGroupName "your-rg" -ResourceName "your-workspace" -ResourceType "Microsoft.OperationalInsights/workspaces"
Enable Audit Logging for Query Execution: Ensure LAQueryLogs and SentinelAudit tables capture all KQL queries with full text and result counts.
Manual Steps:
Sentinel-Query-Audit-LogsConfigure Conditional Access for Sentinel Portal: Require MFA and compliant device for Sentinel access.
Manual Steps:
Sentinel_Portal_MFA_RequirementImplement Data Retention Limits: Set alert data retention to minimum required (default 30 days, can reduce to 7 days for sensitive environments).
Manual Steps:
Monitor Query Execution via Alerts: Create automated alerts for any query accessing SecurityAlert table with High severity filters.
This is covered in detection section above
Conditional Access: Enforce MFA + Device Compliance + IP restrictions for all Sentinel access
# Verify only SOC group has Sentinel access
Get-AzRoleAssignment -RoleDefinitionName "Sentinel Reader" | Where-Object {$_.Scope -like "*sentinel*"} | Select-Object DisplayName, RoleDefinitionName, Scope
Expected Output (If Secure):
C:\Users\[Username]\Downloads\query_*.csv (exported alert files)https://api.loganalytics.io or https://management.azure.comapi.loganalytics.io/v1/workspaces/*/query from non-SOC IP rangeseyJ0...)Disk: MFT entries for CSV export files showing creation time = query execution time
Memory: PowerShell process memory contains bearer tokens and query text
Cloud:
LAQueryLogs table: Every query with text, execution time, result countSentinelAudit table: “KQLQueryRun” and “ExportQueryResults” eventsAuditLogs table: “Run query” operations with timestamp and user# Disable compromised account immediately
Disable-AzADUser -ObjectId "compromised.user@company.com"
# Revoke all active sessions
Revoke-AzAccessToken
# Export all queries executed by compromised user (last 7 days)
Search-AzLog -StartTime (Get-Date).AddDays(-7) -EventInitiator "compromised@company.com" | Export-Csv "C:\Incident\queries.csv"
# Export all alert exports by same user
Search-UnifiedAuditLog -UserIds "compromised@company.com" -Operations "ExportQueryResults" -StartDate (Get-Date).AddDays(-7) | Export-Csv "C:\Incident\exports.csv"
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | T1078 - Valid Accounts | Compromise SOC user account via phishing or credential reuse |
| 2 | Privilege Escalation | T1547 - Boot or Logon Autostart Execution | Escalate to Sentinel Contributor role via compromised account |
| 3 | Collection | [COLLECT-SENTINEL-001] | Extract 30 days of Sentinel alerts and evidence |
| 4 | Exfiltration | T1020 - Automated Exfiltration | Send CSV files to attacker C2 infrastructure |
| 5 | Impact | T1531 - Account Access Removal | Delete alert rules to evade future detection |