Skip to main content

Shadow Admin

ATT&CK: T1098 — Account Manipulation, T1098.003 — Additional Cloud Roles

A shadow admin is an account that has administrative capabilities in a system without appearing in the formal administrator list. The account may have been granted rights through a group, a custom role, or delegated permissions that are functionally equivalent to admin access but are not tracked as such.


Why Shadow Admins Exist

Root CauseExample
Functional delegation"Grant user X password reset for the Help Desk group" → X can now reset any member's password
Delegated roles with hidden elevationA custom Okta role with user.lifecycle.activate + user.userprofile.update effectively is admin
Group-based indirect adminUser added to a group that was granted admin rights → neither user nor group is in admin list
Inherited permissions from nested groupsAD nested groups pass rights without the individual being visible at the top level
Application-level adminUser is SharePoint Site Collection Admin but not M365 Global Admin — still destructive

Active Directory Shadow Admins

In AD, users with the following ACLs over Domain Admins or Domain Controllers are effectively shadow admins:

ACL RightTargetEffective Capability
GenericAll on DA groupDomain AdminsAdd anyone to DA
WriteDacl on DA groupDomain AdminsModify group ACL → add self
WriteOwner on DA groupDomain AdminsTake ownership → modify ACL
GenericAll on DC$DC machine accountRBCD, S4U, DCSync
Replication-Get-Changes-AllDomain rootDCSync
ForceChangePassword on DADA user accountReset DA password
GenericWrite on DA userDA user accountSet SPN → Kerberoast

These ACLs are shadow admin paths because the user does not appear in the DA group or any privileged group listing.

# BloodHound — find all paths to Domain Admin
# Queries:
MATCH p = allShortestPaths((u:User)-[*1..]->(g:Group {name: "DOMAIN ADMINS@DOMAIN.COM"})) RETURN p

# PowerView — find users with WriteDacl on Domain Admins
Get-DomainObjectACL -Identity "Domain Admins" -ResolveGUIDs |
Where-Object {$_.ActiveDirectoryRights -match "WriteDacl|GenericAll|GenericWrite"}

Entra ID Shadow Admins

In Entra ID, accounts can have administrative capabilities without being in a named admin role:

PathEffective Capability
Owner of another privileged admin accountCan modify that account (reset creds)
Application.ReadWrite.All graph permissionAdd credentials to any app — SP hijacking
RoleManagement.ReadWrite.DirectoryAssign any Entra ID role
Owner of a service principal with Global Admin app rolesInherit the SP's permissions
Member of a group with admin Entra ID role assignmentGroup-based role assignment not visible in role member list
AuthenticationAdministrator roleReset MFA/credentials for non-admin users
Owner of a privileged groupAdd self to that group → inherit all group rights

Okta Shadow Admins

Okta custom roles with overlapping capabilities:

Custom Role CombinationEquivalent Capability
user.lifecycle.activate + user.userprofile.update + group.manageFunctionally near-superadmin for user management
app.settings.manageModify app SCIM configs, client secrets
Custom admin with policy.rule.manageModify auth/CA policies

SaaS Platform Shadow Admins

PlatformShadow Admin Path
GitHubOwner of a team that is org admin → can perform admin actions
SalesforceProfile with "Modify All Data" → can access all records; not listed as admin
SlackWorkspace admin via third-party app with admin scope
JiraProject admin in all projects → can create new projects, workflows

Detection Approach

Traditional detection monitors for explicit admin role additions (Event 4728, Entra AuditLogs). Shadow admin detection requires access graph analysis:

  1. BloodHound / BloodHound CE — AD shadow admin paths
  2. Veza — cross-platform authorization graph
  3. Semperis Forest Druid — AD Tier 0 blast radius
  4. Manual LDAP query — enumerate ACLs on sensitive objects
# BloodHound Cypher — find shadow admins (users with GenericAll on DA group)
MATCH (u:User)-[:GenericAll|GenericWrite|WriteDacl|WriteOwner]->(g:Group {name: "DOMAIN ADMINS@DOMAIN.COM"})
RETURN u.name, type(r), g.name

Audit Script — Entra ID Shadow Admin Detection

# Find all users with RoleManagement.ReadWrite.Directory permission
$graphSP = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"
$sensitiveRoleId = ($graphSP.AppRoles | Where-Object Value -eq "RoleManagement.ReadWrite.Directory").Id

Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $graphSP.Id |
Where-Object AppRoleId -eq $sensitiveRoleId |
ForEach-Object {
[PSCustomObject]@{
PrincipalType = $_.PrincipalType
Principal = $_.PrincipalDisplayName
}
}

Mitigation

ControlEffect
Regular BloodHound runsFind AD shadow admin paths proactively
Veza or Semperis continuous monitoringReal-time shadow admin detection
Restrict GenericAll/GenericWrite on privileged objectsRemove excess ACLs
Audit Entra ID app role assignmentsFind SP-based shadow admins
Least privilege for Okta custom rolesMinimize role capability overlap
Periodic access certification for custom rolesIGA controls on non-standard roles

TopicLink
ACL Abuseacl-abuse
RBCDrbcd
Service Principal Abuseservice-principal-abuse
ITDR Vendorsitdr-vendor-landscape