Private/Test/Test-IsLowPrivilegePrincipal.ps1
|
function Test-IsLowPrivilegePrincipal { <# .SYNOPSIS Tests if a security principal is a low-privilege (custom) principal. .DESCRIPTION Examines security principal identifiers (SIDs or NTAccount names) to determine if they represent low-privilege or custom principals - those that are neither high-privilege administrative principals nor overly broad dangerous principals. This function identifies "middle ground" principals by excluding two categories: 1. Safe/Administrative principals: Domain Admins, Enterprise Admins, SYSTEM, Domain Controllers, etc. 2. Dangerous principals: Everyone, Authenticated Users, Domain Users, Domain Computers, etc. Principals that don't match either category represent custom security configurations such as specific service accounts, security groups, or users that have been granted permissions outside the standard administrative model. These should be reviewed to ensure they align with security policies and least privilege principles. .PARAMETER IdentityReference One or more security principal identifiers to test. Can be SIDs, NTAccount names, or any string representation of a security principal. .PARAMETER SafeEnrollee An array of SIDs and regex patterns identifying high-privilege administrative principals that are considered safe/expected to have enrollment permissions. Default includes Domain Admins, Enterprise Admins, SYSTEM, Domain Controllers, Key Admins, etc. .PARAMETER DangerousEnrollee An array of SIDs, NTAccount names, and regex patterns identifying overly broad dangerous principals. Default includes NULL SID, Everyone, Anonymous Logon, Authenticated Users, Domain Users, etc. .INPUTS System.String[] You can pipe security principal identifiers to this function. .OUTPUTS System.Boolean Returns $true if the principal is low-privilege (neither safe nor dangerous), $false otherwise. When multiple principals are provided, returns one boolean per principal. .EXAMPLE Test-IsLowPrivilegePrincipal -IdentityReference 'CONTOSO\WebServerAdmins' Returns $true if 'CONTOSO\WebServerAdmins' is neither an admin group nor a dangerous principal. .EXAMPLE Test-IsLowPrivilegePrincipal -IdentityReference 'Everyone' Returns $false as 'Everyone' is a dangerous principal. .EXAMPLE Test-IsLowPrivilegePrincipal -IdentityReference 'S-1-5-21-1234567890-1234567890-1234567890-512' Returns $false as SIDs ending in -512 represent Domain Admins groups (safe principal). .EXAMPLE $ace.IdentityReference | Test-IsLowPrivilegePrincipal Tests an ACE's identity reference to determine if it's a custom/low-privilege principal. .EXAMPLE 'CONTOSO\ServiceAccount' | Test-IsLowPrivilegePrincipal Returns $true for a custom service account that isn't in safe or dangerous lists. .EXAMPLE $customSafe = @('-512$', '-519$', '-544$') Test-IsLowPrivilegePrincipal -IdentityReference 'CONTOSO\CustomGroup' -SafeEnrollee $customSafe Uses a custom list of safe administrative principals. .NOTES Safe/Administrative principals excluded by default: - Domain Admins (-512), Enterprise Admins (-519), Builtin Administrators (-544) - SYSTEM (-18), Builtin Administrator (-500) - Cert Publishers (-517) - Domain Controllers (-516), Read-Only Domain Controllers (-521) - Enterprise Domain Controllers (-498), Enterprise Read-Only Domain Controllers (-9) - Key Admins (-526), Enterprise Key Admins (-527) - SELF (S-1-5-10) Dangerous principals excluded by default: - NULL SID, Everyone, Anonymous Logon, BUILTIN\Users - Authenticated Users, Domain Users, Domain Computers The function uses regex matching to support both exact matches (SIDs, NTAccount names) and pattern matches (SID suffixes). .LINK https://posts.specterops.io/certified-pre-owned-d95910965cd2 #> [CmdletBinding()] [OutputType([bool])] param ( [Parameter(Mandatory, ValueFromPipeline)] [string[]]$IdentityReference, [Parameter()] [string[]]$SafeEnrollee, [Parameter()] [string[]]$DangerousEnrollee ) #requires -Version 5.1 begin { # Load principal patterns from data file if not provided if (-not $SafeEnrollee -or -not $DangerousEnrollee) { $principalDefinitions = Import-PowerShellDataFile -Path "$PSScriptRoot\..\Data\PrincipalDefinitions.psd1" if (-not $SafeEnrollee) { $SafeEnrollee = $principalDefinitions.SafePrincipals Write-Verbose "Loaded $($SafeEnrollee.Count) safe principal patterns from PrincipalDefinitions.psd1" } if (-not $DangerousEnrollee) { $DangerousEnrollee = $principalDefinitions.DangerousPrincipals Write-Verbose "Loaded $($DangerousEnrollee.Count) dangerous principal patterns from PrincipalDefinitions.psd1" } } $safeEnrolleePattern = $SafeEnrollee -join '|' $dangerousEnrolleePattern = $DangerousEnrollee -join '|' } process { $IdentityReference | ForEach-Object { Write-Verbose "Testing if '$_' is a low-privilege principal..." $isDangerous = Test-IsDangerousPrincipal -IdentityReference $_ -DangerousEnrollee $DangerousEnrollee $isSafe = $_ -match $safeEnrolleePattern $isLowPrivilege = (-not $isSafe) -and (-not $isDangerous) if ($isDangerous) { Write-Verbose "'$_' is a dangerous principal (not low-privilege)" } elseif ($isSafe) { Write-Verbose "'$_' is a safe/administrative principal (not low-privilege)" } else { Write-Verbose "'$_' is a low-privilege/custom principal" } $isLowPrivilege } } end { } } |