ADACLScanner est un outil en PowerShell, créé par Robin Granberg (ex PFE Microsoft et aujourd'hui chez Semperis). L'outil est disponible sur https://github.com/canix1/ADACLScanner
Cet outil est utilisé pour auditer les autorisations Active Directory, les exporter au format CSV/HTML, les comparer avec une exportation précédente ou avec un autre domaine ou avec les autorisations par défaut.Il permet aussi de connaître les autorisations d'un utilisateur ou d'un groupe, le propriétaire d'un objet, la date de dernière modification des droits, les droits hérités, etc.
Il peut être exécuté avec une interface graphique (GUI) ou en ligne de commande.Le projet est toujours actif, et Robin prend le temps de corriger les erreurs signalées via Github.
Utilisation ligne de commandes
-RecursiveFind
: This parameter will search any nested groups to show all security prinicpals that have access.
-RecursiveObjectType
: Ce paramètre filtre les groupes imbriqués pour n'afficher que les utilisateurs qui y ont accès.
-FilterTrustee
: Filtre ACL pour les chaînes de caractères correspondantes dans Trustee Exemple 1 -FilterTrustee "user1" (domaine\ est nécessaire)
-Owner
: Obtenir les propriétaires
Exemples de recherche
ACL personnalisés sur toutes les OU et tous les conteneurs
$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 et date de dernière modification
.\ADACLScan.ps1 -Base "CN=AdminSDHolder,CN=System,$((Get-ADRootDSE).defaultNamingContext)" -SDDate -ShowCriticalityColor -Show -Output HTML
Obtenir les droits d'un utilisateur/groupe spécifique
.\ADACLScan.ps1 -Base (Get-ADRootDSE).defaultNamingContext -Scope subtree -Filter "(|(objectClass=organizationalUnit)(objectClass=container))" -SkipDefaults -SkipBuiltIn -ShowCriticalityColor -Output HTML -Show -EffectiveRightsPrincipal domain\user
Droits sur les GPOs
.\ADACLScan.ps1 -GPO -Scope subtree -ShowCriticalityColor -Output HTML -Show -RecursiveFind
Obtenir les propriétaires
.\ADACLScan.ps1 -Base (Get-ADRootDSE).defaultNamingContext -Owner -Scope subtree -Filter '(objectClass=*)' | Where-Object {$_.Access -eq 'Owner'}
Obtenir les permissions Deny
Rechercher les permissions autorisation Deny
(peut être utile pour vérifier ManagedBy ou d'autres attributs utiles cachés).
.\ADACLScan.ps1 -Base (Get-ADRootDSE).defaultNamingContext -Scope subtree -Filter '(objectClass=*)' | Where-Object {$_.Access -eq 'Deny'}
SID non résolus - tous les naming context et propriétaires
⚠️ Le temps d'exécution dépend du nombre d'objets⚠️
[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($_)
}
}
SID non résolus - tous les objets de la partition domaine
⚠️ Le temps d'exécution dépend du nombre d'objets⚠️
$unresolvedSIDArray = .\ADACLScan.ps1 -Base (Get-ADRootDSE).defaultNamingContext -Scope subtree -Filter '(objectClass=*)' -Owner -FilterTrustee 'S-1-5*'
SID non résolus sur OUs et conteneurs uniquement
⚠️ Le temps d'exécution dépend du nombre d'objets⚠️
$unresolvedSIDArray= .\ADACLScan.ps1 -Base (Get-ADRootDSE).defaultNamingContext -Scope subtree -Filter '(|(objectClass=organizationalUnit)(objectClass=container))' -Owner -FilterTrustee 'S-1-5*'
SID non résolus sur GPO uniquement
⚠️ Le temps d'exécution dépend du nombre d'objets⚠️
[System.Collections.Generic.List[PSObject]]$unresolvedSIDArray = @()
$unresolvedSID = .\ADACLScan.ps1 -GPO -Scope subtree -Owner -FilterTrustee 'S-1-5*'
$unresolvedSID | ForEach-Object {
$unresolvedSIDArray.add($_)
}
SID non résolus sur partitions DNS intégrées à AD
$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($_)
}
}
Permissions DCSync
Le paramètre RecursiveFind
recherche récursivement les membres du groupe.
.\ADACLScan.ps1 -Base (Get-ADRootDSE).defaultNamingContext -RecursiveFind | Where-Object {$_.Permission -eq 'ExtendedRight Replicating Directory Changes' -or $_.Permission -eq 'ExtendedRight Replicating Directory Changes All' -or $_.Permission -eq 'ExtendedRight Replicating Directory Changes in Filtered Set'}
Utilisation avancées des résultats
Si vous souhaitez supprimer les SID inconnus ou modifier le propriétaire , vous pouvez utilisez les commandes suivantes.
Supprimer SID inconnus
Quelques informations utiles :
Import-Module ActiveDirectory
est nécessaire pour accéder au lecteurAD:
.- Il faut utiliser
RemoveAccessRuleSpecific()
à la place deRemoveAccessRule()
.
La raison est qu’avec la méthode initiale, l’algorithme ne parvient pas toujours à identifier avec précision l’ACE à supprimer.RemoveAccessRuleSpecific()
parcourt l’ACL pour trouver l’ACE exact et supprime uniquement celui-ci.
Explications détaillées ici :
https://forums.powershell.org/t/weird-issue-when-removing-permissions/5066
Import-Module ActiveDirectory
$DNWithUnresolvedSID = ($unresolvedSIDArray | Where-Object {$_.Access -ne 'Owner'}).Object | Select-Object -Unique
foreach ($dn in $DNWithUnresolvedSID){
$acl = Get-Acl "AD:\$dn"
$aclsToDelete = $acl.Access | Where-Object {$_.IdentityReference -like 'S-1-5*'}
foreach($aclToDelete in $aclsToDelete){
$acl.RemoveAccessRuleSpecific($aclToDelete)
}
Set-Acl -Path "AD:\$dn" -AclObject $acl
}
Pour l’historique, voici la méthode avec RemoveAccessRule
, mais comme indiqué précédemment, elle ne fonctionne pas toujours. Je la laisse néanmoins à titre de référence.
Import-Module ActiveDirectory
$DNWithUnresolvedSID = ($unresolvedSIDArray | Where-Object {$_.Access -ne 'Owner'} | Select-Object -Unique).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
}
Modifier propriétaire
$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
}
Supprimer les ACL correspondant à une classe d'objet ou un attribut
Parfois, il est nécessaire de supprimer tous les droits associés à une classe d’objet ou un attribut, par exemple après avoir supprimé cette classe/attribut. Dans le script ci-dessous, remplacez xxx
par le nom de votre classe. À la fin, deux nombres s’affichent : le nombre d’ACE au début et le nombre d’ACE restants.
Import-Module ActiveDirectory
$rootdse = Get-ADRootDSE
$global:guidmap = @{}
Get-ADObject -SearchBase ($rootdse.SchemaNamingContext) -LDAPFilter '(schemaidguid=*)' -Properties lDAPDisplayName, schemaIDGUID | ForEach-Object {
$guidmap[$_.lDAPDisplayName] = [System.GUID]$_.schemaIDGUID
}
$acl = Get-Acl 'AD:\xxx'
$oldacl = Get-Acl 'AD:\xxx'
foreach ($access in $acl.Access) {
$found = $false
# search in guidmap by value is name
$found = $guidmap.GetEnumerator() | Where-Object { $_.Value -eq $access.ObjectType } | Where-Object { $_.Name -like 'xxx*' }
if ($found) {
Write-Host -ForegroundColor Cyan "$($found.Name) for $($access.IdentityReference) on $($access.ObjectType) with inherited object type $($access.InheritedObjectType)"
# remove the access rule
$acl.RemoveAccessRuleSpecific($access)
continue
}
elseif ($access.InheritedObjectType.Guid -eq '00000000-0000-0000-0000-000000000000') {
continue
}
else {
$found = $guidmap.GetEnumerator() | Where-Object { $_.Value -eq $access.InheritedObjectType } | Where-Object { $_.Name -like 'xxx*' }
if ($found) {
$found = $guidmap.GetEnumerator() | Where-Object { $_.Value -eq $access.ObjectType } | Where-Object { $_.Name -like '*xxx*' }
Write-Host -ForegroundColor Cyan "$($found.Name) for $($access.IdentityReference) on $($access.ObjectType) with inherited object type $($access.InheritedObjectType)"
# remove the access rule
$acl.RemoveAccessRuleSpecific($access)
}
}
}
$oldacl.Access.count
$acl.Access.count
#Set-Acl -Path "AD:\xxx" -AclObject $acl
Comments