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 Cause | Example |
|---|---|
| Functional delegation | "Grant user X password reset for the Help Desk group" → X can now reset any member's password |
| Delegated roles with hidden elevation | A custom Okta role with user.lifecycle.activate + user.userprofile.update effectively is admin |
| Group-based indirect admin | User added to a group that was granted admin rights → neither user nor group is in admin list |
| Inherited permissions from nested groups | AD nested groups pass rights without the individual being visible at the top level |
| Application-level admin | User 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 Right | Target | Effective Capability |
|---|---|---|
GenericAll on DA group | Domain Admins | Add anyone to DA |
WriteDacl on DA group | Domain Admins | Modify group ACL → add self |
WriteOwner on DA group | Domain Admins | Take ownership → modify ACL |
GenericAll on DC$ | DC machine account | RBCD, S4U, DCSync |
Replication-Get-Changes-All | Domain root | DCSync |
ForceChangePassword on DA | DA user account | Reset DA password |
GenericWrite on DA user | DA user account | Set 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:
| Path | Effective Capability |
|---|---|
| Owner of another privileged admin account | Can modify that account (reset creds) |
Application.ReadWrite.All graph permission | Add credentials to any app — SP hijacking |
RoleManagement.ReadWrite.Directory | Assign any Entra ID role |
| Owner of a service principal with Global Admin app roles | Inherit the SP's permissions |
| Member of a group with admin Entra ID role assignment | Group-based role assignment not visible in role member list |
AuthenticationAdministrator role | Reset MFA/credentials for non-admin users |
| Owner of a privileged group | Add self to that group → inherit all group rights |
Okta Shadow Admins
Okta custom roles with overlapping capabilities:
| Custom Role Combination | Equivalent Capability |
|---|---|
user.lifecycle.activate + user.userprofile.update + group.manage | Functionally near-superadmin for user management |
app.settings.manage | Modify app SCIM configs, client secrets |
Custom admin with policy.rule.manage | Modify auth/CA policies |
SaaS Platform Shadow Admins
| Platform | Shadow Admin Path |
|---|---|
| GitHub | Owner of a team that is org admin → can perform admin actions |
| Salesforce | Profile with "Modify All Data" → can access all records; not listed as admin |
| Slack | Workspace admin via third-party app with admin scope |
| Jira | Project 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:
- BloodHound / BloodHound CE — AD shadow admin paths
- Veza — cross-platform authorization graph
- Semperis Forest Druid — AD Tier 0 blast radius
- 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
| Control | Effect |
|---|---|
| Regular BloodHound runs | Find AD shadow admin paths proactively |
| Veza or Semperis continuous monitoring | Real-time shadow admin detection |
Restrict GenericAll/GenericWrite on privileged objects | Remove excess ACLs |
| Audit Entra ID app role assignments | Find SP-based shadow admins |
| Least privilege for Okta custom roles | Minimize role capability overlap |
| Periodic access certification for custom roles | IGA controls on non-standard roles |
Cross-Links
| Topic | Link |
|---|---|
| ACL Abuse | acl-abuse |
| RBCD | rbcd |
| Service Principal Abuse | service-principal-abuse |
| ITDR Vendors | itdr-vendor-landscape |