Photo by Brands&People / Unsplash
Get AD permissions with ADACLScanner

Get AD permissions with ADACLScanner

Published on 11 Jul 2023

Bastien Perez
Bastien Perez

Clap

ADACLScanner is a PowerShell tool, created by Robin Granberg (formerly a PFE at Microsoft and now at Semperis). The tool can be accessed on https://github.com/canix1/ADACLScanner

It's designed to audit Active Directory permissions, export them in CSV/HTML formats, compare them to a previous export or another domain, or to default permissions. The tool also provides features to know the permissions of a user or group, identify the owner of an object, the date of the last rights modification, inherited rights, and much more.

It can be used via a graphical interface (GUI) or command line. The project is ongoing, and Robin is committed to fixing errors reported via Github.

Command Line Usage

-RecursiveFind This parameter searches all nested groups to display all security principals having access.

-RecursiveObjectType This parameter filters nested groups to only display users who have access.

-FilterTrustee ACL filter for matching strings in Trustee.

-Owner To obtain the owners.

Search examples

Custom ACLs on all OUs and containers

$res = .\ADACLScan.ps1 -Base (Get-ADRootDSE).defaultNamingContext -Scope subtree -Filter "(|(objectClass=organizationalUnit)(objectClass=container))" -SkipDefaults -ShowCriticalityColor -Output HTML -Show
# To view the result and exlucde groupPolicyContainer
$res | Where-Object ObjectClass -ne 'groupPolicyContainer'

AdminSDHolder ACL and last modified date

.\ADACLScan.ps1 -Base "CN=AdminSDHolder,CN=System,$((Get-ADRootDSE).defaultNamingContext)" -SDDate -ShowCriticalityColor -Show -Output HTML

Obtain rights for a specific user/group

.\ADACLScan.ps1 -Base (Get-ADRootDSE).defaultNamingContext -Scope subtree -Filter "(|(objectClass=organizationalUnit)(objectClass=container))" -SkipDefaults -SkipBuiltIn -ShowCriticalityColor -Output HTML -Show -EffectiveRightsPrincipal domain\user

GPO rights

.\ADACLScan.ps1 -GPO -Scope subtree -ShowCriticalityColor -Output HTML -Show -RecursiveFind

Get owners

.\ADACLScan.ps1 -Base (Get-ADRootDSE).defaultNamingContext -Owner -Scope subtree -Filter '(objectClass=*)' | Where-Object {$_.Access -eq 'Owner'}

Get Deny permissions

Search for permissions Deny (can be useful for checking ManagedBy or other useful hidden attributes).

.\ADACLScan.ps1 -Base (Get-ADRootDSE).defaultNamingContext -Scope subtree -Filter '(objectClass=*)' | Where-Object {$_.Access -eq 'Deny'}

Unresolved SIDs - all naming context and proprietary naming

⚠️ Execution time depends on number of objects⚠️

[System.Collections.Generic.List[PSObject]]$unresolvedSIDArray = @()
foreach($nc in (Get-ADRootDSE).namingcontexts){
    $unresolvedSID = .\ADACLScan.ps1 -Base $nc -Scope subtree -Filter '(objectClass=*)' -Owner -FilterTrustee 'S-1-5*'
    $unresolvedSID | ForEach-Object {
        $unresolvedSIDArray.add($_)
    }
}

Unresolved SIDs - all objects in the domain partition

⚠️ Execution time depends on number of objects⚠️

$unresolvedSIDArray = .\ADACLScan.ps1 -Base (Get-ADRootDSE).defaultNamingContext -Scope subtree -Filter '(objectClass=*)' -Owner -FilterTrustee 'S-1-5*'

Unresolved SIDs on OUs and containers only

⚠️ Execution time depends on number of objects⚠️

$unresolvedSIDArray= .\ADACLScan.ps1 -Base (Get-ADRootDSE).defaultNamingContext -Scope subtree -Filter '(|(objectClass=organizationalUnit)(objectClass=container))' -Owner -FilterTrustee 'S-1-5*'

Unresolved SIDs on GPO only

⚠️ Execution time depends on number of objects⚠️

[System.Collections.Generic.List[PSObject]]$unresolvedSIDArray = @()
$unresolvedSID = .\ADACLScan.ps1 -GPO -Scope subtree -Owner -FilterTrustee 'S-1-5*'
$unresolvedSID | ForEach-Object {
    $unresolvedSIDArray.add($_)
}

Unresolved SIDs on AD-integrated DNS partitions

$adIntegratedDNSNC = (Get-ADRootDSE).namingContexts | Where-Object {$_ -like 'DC=DomainDNSZones*' -or $_ -like 'DC=ForestDNSZones*'}
[System.Collections.Generic.List[PSObject]]$unresolvedSIDArray = @()
foreach($nc in $adIntegratedDNSNC){
    $unresolvedSID = .\ADACLScan.ps1 -Base $nc -Scope subtree -Filter '(objectClass=*)' -Owner  -FilterTrustee 'S-1-5*'
     $unresolvedSID | ForEach-Object {
        $unresolvedSIDArray.add($_)
    }
}

Advanced use of results

If you wish to delete unknown SIDs or modify the owner, you can use the following commands.

Delete unknown SIDs

Warning: only use the following commands if you know what you're doing! These commands are *not* ADACLScanner commands!
$DNWithUnresolvedSID = ($unresolvedSIDArray | Where-Object {$_.Access -ne 'Owner'}).Object
foreach ($dn in $DNWithUnresolvedSID){
    $acl = Get-Acl "AD:\$dn"
    $aclsToDelete = $acl.Access | Where-Object {$_.IdentityReference -like 'S-1-5*'}
    foreach($aclToDelete in $aclsToDelete){
        $acl.RemoveAccessRule($aclToDelete)
    }
    Set-Acl -Path "AD:\$dn" -AclObject $acl
}

Change owner

Warning: only use the following commands if you know what you're doing! These commands are *not* ADACLScanner commands!
$DNWithUnresolvedSIDOwner = ($unresolvedSIDArray | Where-Object {$_.Access -eq 'Owner'}).Object
$newOwner = New-Object System.Security.Principal.Ntaccount("Domain Admins")

foreach ($dn in $DNWithUnresolvedSIDOwner){
    $acl = Get-Acl "AD:\$dn"
    $acl.SetOwner($newOwner)
    Set-Acl -Path "AD:\$dn" -AclObject $acl
}

Comments

banner-Bastien Perez
Bastien Perez

Freelance Microsoft 365 - Active Directory - Modern Workplace

France