ActiveDirectory/Find-ADBackdoors.ps1
|
function Find-ADBackdoors { <# .SYNOPSIS Detects potential backdoors and persistence mechanisms in Active Directory .DESCRIPTION Scans for AD backdoors including AdminSDHolder abuse, DCSync permissions, SIDHistory manipulation, and other persistence mechanisms .PARAMETER CheckAdminSDHolder Check for AdminSDHolder tampering .PARAMETER CheckDCSync Check for DCSync permissions .PARAMETER CheckSIDHistory Check for SIDHistory abuse .PARAMETER CheckDelegation Check for dangerous delegations .PARAMETER Domain Target domain .EXAMPLE Find-ADBackdoors -CheckAdminSDHolder -CheckDCSync -CheckSIDHistory Find-ADBackdoors -CheckDelegation -Domain "contoso.com" .OUTPUTS PSCustomObject with backdoor findings #> [CmdletBinding()] param( [Parameter()] [switch]$CheckAdminSDHolder, [Parameter()] [switch]$CheckDCSync, [Parameter()] [switch]$CheckSIDHistory, [Parameter()] [switch]$CheckDelegation, [Parameter()] [string]$Domain ) begin { Write-Host "=== AD BACKDOOR DETECTION ===" -ForegroundColor Cyan if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) { throw "ActiveDirectory module required" } Import-Module ActiveDirectory -ErrorAction Stop if (-not $Domain) { $Domain = (Get-ADDomain).DNSRoot } Write-Host "Domain: $Domain" -ForegroundColor Yellow Write-Host "Start Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Yellow $results = [PSCustomObject]@{ ScanDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" Domain = $Domain Backdoors = @{ AdminSDHolder = @() DCSync = @() SIDHistory = @() DangerousDelegations = @() } Summary = @{ TotalBackdoors = 0 CriticalFindings = 0 HighFindings = 0 } Recommendations = @() } } process { try { # 1. Check AdminSDHolder if ($CheckAdminSDHolder) { Write-Host "`n[1/4] Checking AdminSDHolder..." -ForegroundColor Cyan try { # Get AdminSDHolder object $adminSDHolder = Get-ADObject -Filter {Name -eq "AdminSDHolder"} -SearchBase "CN=System,$((Get-ADDomain -Server $Domain).DistinguishedName)" -Properties ntSecurityDescriptor -Server $Domain if ($adminSDHolder) { # Check users with AdminCount=1 who shouldn't have it $adminCountUsers = Get-ADUser -Filter {AdminCount -eq 1} -Properties AdminCount, MemberOf -Server $Domain # Get actual privileged group members $privilegedGroups = @( 'Domain Admins', 'Enterprise Admins', 'Schema Admins', 'Administrators', 'Account Operators', 'Backup Operators', 'Server Operators', 'Print Operators' ) $legitimatePrivilegedUsers = @() foreach ($groupName in $privilegedGroups) { try { $group = Get-ADGroup -Filter "Name -eq '$groupName'" -Server $Domain -ErrorAction SilentlyContinue if ($group) { $members = Get-ADGroupMember -Identity $group -Recursive -Server $Domain -ErrorAction SilentlyContinue if ($members) { $legitimatePrivilegedUsers += $members.SamAccountName } } } catch { Write-Verbose "Could not check group: $groupName" } } # Find suspicious AdminCount users foreach ($user in $adminCountUsers) { if ($user.SamAccountName -notin $legitimatePrivilegedUsers) { $results.Backdoors.AdminSDHolder += [PSCustomObject]@{ Severity = 'Critical' Username = $user.SamAccountName DisplayName = $user.Name Issue = 'AdminCount=1 but not in privileged groups' Description = 'Possible AdminSDHolder abuse for persistence' } $results.Summary.CriticalFindings++ Write-Host " [!] Suspicious: $($user.SamAccountName)" -ForegroundColor Red } } if ($results.Backdoors.AdminSDHolder.Count -eq 0) { Write-Host " No AdminSDHolder abuse detected" -ForegroundColor Green } } } catch { Write-Warning "Error checking AdminSDHolder: $_" } } # 2. Check DCSync Permissions if ($CheckDCSync) { Write-Host "`n[2/4] Checking DCSync permissions..." -ForegroundColor Cyan try { # Get domain root $domainDN = (Get-ADDomain -Server $Domain).DistinguishedName $domainObject = Get-ADObject -Identity $domainDN -Properties ntSecurityDescriptor -Server $Domain # DCSync requires these rights: # - DS-Replication-Get-Changes (GUID: 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2) # - DS-Replication-Get-Changes-All (GUID: 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2) $acl = $domainObject.ntSecurityDescriptor $dcsyncRights = @( '1131f6aa-9c07-11d1-f79f-00c04fc2dcd2', # DS-Replication-Get-Changes '1131f6ad-9c07-11d1-f79f-00c04fc2dcd2' # DS-Replication-Get-Changes-All ) foreach ($ace in $acl.Access) { if ($ace.ObjectType -in $dcsyncRights) { # Resolve SID to account try { $aceSid = $ace.IdentityReference if ($aceSid -isnot [System.Security.Principal.SecurityIdentifier]) { $aceSid = $aceSid.Translate([System.Security.Principal.SecurityIdentifier]) } $account = Get-ADObject -Filter "objectSid -eq '$aceSid'" -Server $Domain -ErrorAction SilentlyContinue if ($account) { # Check if it's a legitimate DC or admin account $isLegitimate = $account.Name -match "Domain Controllers|Enterprise Admins|Domain Admins|^Administrators$" if (-not $isLegitimate) { $results.Backdoors.DCSync += [PSCustomObject]@{ Severity = 'Critical' Account = $account.Name AccountType = $account.ObjectClass Permission = 'DCSync' Issue = 'Non-standard account has DCSync permissions' Description = 'Can replicate password hashes from domain' } $results.Summary.CriticalFindings++ Write-Host " [!] DCSync: $($account.Name)" -ForegroundColor Red } } } catch { Write-Verbose "Could not resolve SID: $($ace.IdentityReference)" } } } if ($results.Backdoors.DCSync.Count -eq 0) { Write-Host " No suspicious DCSync permissions found" -ForegroundColor Green } } catch { Write-Warning "Error checking DCSync permissions: $_" } } # 3. Check SIDHistory if ($CheckSIDHistory) { Write-Host "`n[3/4] Checking SIDHistory abuse..." -ForegroundColor Cyan try { # Find users with SIDHistory $usersWithSIDHistory = Get-ADUser -Filter {SIDHistory -like "*"} -Properties SIDHistory, MemberOf -Server $Domain foreach ($user in $usersWithSIDHistory) { # SIDHistory is legitimate in migration scenarios, but can be abused $sidHistoryCount = $user.SIDHistory.Count # Check if any SID in history is from a privileged group foreach ($sid in $user.SIDHistory) { try { $sidObject = Get-ADObject -Filter "objectSid -eq '$sid'" -Server $Domain -ErrorAction SilentlyContinue if ($sidObject) { $results.Backdoors.SIDHistory += [PSCustomObject]@{ Severity = 'High' Username = $user.SamAccountName SIDHistoryObject = $sidObject.Name SIDHistoryCount = $sidHistoryCount Issue = 'User has SIDHistory attribute set' Description = 'Can be used for privilege escalation' } $results.Summary.HighFindings++ Write-Host " [!] SIDHistory: $($user.SamAccountName) -> $($sidObject.Name)" -ForegroundColor Yellow } } catch { Write-Verbose "Could not resolve SID: $sid" } } } if ($results.Backdoors.SIDHistory.Count -eq 0) { Write-Host " No SIDHistory abuse detected" -ForegroundColor Green } } catch { Write-Warning "Error checking SIDHistory: $_" } } # 4. Check Dangerous Delegations if ($CheckDelegation) { Write-Host "`n[4/4] Checking dangerous delegations..." -ForegroundColor Cyan try { # Check for unconstrained delegation $unconstrainedDelegation = Get-ADComputer -Filter {TrustedForDelegation -eq $true} -Properties TrustedForDelegation, PrimaryGroupID -Server $Domain foreach ($computer in $unconstrainedDelegation) { # Domain Controllers are expected to have this (PrimaryGroupID 516 = RWDC, 521 = RODC); # matching on the computer name would miss DCs with arbitrary names and # falsely excuse non-DCs whose names merely contain "DC" if ($computer.PrimaryGroupID -notin @(516, 521)) { $results.Backdoors.DangerousDelegations += [PSCustomObject]@{ Severity = 'High' Object = $computer.Name ObjectType = 'Computer' DelegationType = 'Unconstrained' Issue = 'Computer has unconstrained delegation enabled' Description = 'Can impersonate any user to any service' } $results.Summary.HighFindings++ Write-Host " [!] Unconstrained Delegation: $($computer.Name)" -ForegroundColor Yellow } } # Check for constrained delegation with protocol transition $constrainedDelegation = Get-ADObject -Filter {msDS-AllowedToDelegateTo -like "*"} -Properties msDS-AllowedToDelegateTo, TrustedToAuthForDelegation -Server $Domain foreach ($object in $constrainedDelegation) { if ($object.TrustedToAuthForDelegation) { $results.Backdoors.DangerousDelegations += [PSCustomObject]@{ Severity = 'Medium' Object = $object.Name ObjectType = $object.ObjectClass DelegationType = 'Constrained with Protocol Transition' DelegateTo = $object.'msDS-AllowedToDelegateTo' -join ', ' Issue = 'Protocol transition enabled' Description = 'Can obtain service tickets without user credentials' } Write-Host " [!] Protocol Transition: $($object.Name)" -ForegroundColor Yellow } } if ($results.Backdoors.DangerousDelegations.Count -eq 0) { Write-Host " No dangerous delegations found" -ForegroundColor Green } } catch { Write-Warning "Error checking delegations: $_" } } # Calculate total backdoors $results.Summary.TotalBackdoors = $results.Backdoors.AdminSDHolder.Count + $results.Backdoors.DCSync.Count + $results.Backdoors.SIDHistory.Count + $results.Backdoors.DangerousDelegations.Count # Generate recommendations if ($results.Backdoors.AdminSDHolder.Count -gt 0) { $results.Recommendations += "Review and remove AdminCount attribute from non-privileged accounts" $results.Recommendations += "Investigate how AdminCount was set on these accounts" } if ($results.Backdoors.DCSync.Count -gt 0) { $results.Recommendations += "Remove DCSync permissions from non-administrative accounts immediately" $results.Recommendations += "Audit replication activities" } if ($results.Backdoors.SIDHistory.Count -gt 0) { $results.Recommendations += "Clear SIDHistory for accounts not in active migration" $results.Recommendations += "Monitor SIDHistory changes" } if ($results.Backdoors.DangerousDelegations.Count -gt 0) { $results.Recommendations += "Review and restrict delegation configurations" $results.Recommendations += "Use constrained delegation instead of unconstrained" } $results.Recommendations += "Implement regular AD security audits" $results.Recommendations += "Enable advanced audit policies for privilege use" $results.Recommendations += "Monitor for Golden Ticket indicators" } catch { Write-Error "Error during backdoor detection: $_" throw } } end { Write-Host "`n=== SCAN COMPLETE ===" -ForegroundColor Green Write-Host "`nBackdoor Summary:" -ForegroundColor Cyan Write-Host " Total Backdoors Found: $($results.Summary.TotalBackdoors)" -ForegroundColor $( if ($results.Summary.TotalBackdoors -gt 0) { 'Red' } else { 'Green' } ) Write-Host " Critical Findings: $($results.Summary.CriticalFindings)" -ForegroundColor Red Write-Host " High Findings: $($results.Summary.HighFindings)" -ForegroundColor Yellow if ($results.Backdoors.AdminSDHolder.Count -gt 0) { Write-Host "`nAdminSDHolder Abuse: $($results.Backdoors.AdminSDHolder.Count)" -ForegroundColor Red } if ($results.Backdoors.DCSync.Count -gt 0) { Write-Host "DCSync Permissions: $($results.Backdoors.DCSync.Count)" -ForegroundColor Red } if ($results.Backdoors.SIDHistory.Count -gt 0) { Write-Host "SIDHistory Abuse: $($results.Backdoors.SIDHistory.Count)" -ForegroundColor Yellow } if ($results.Backdoors.DangerousDelegations.Count -gt 0) { Write-Host "Dangerous Delegations: $($results.Backdoors.DangerousDelegations.Count)" -ForegroundColor Yellow } if ($results.Recommendations.Count -gt 0) { Write-Host "`nRecommendations:" -ForegroundColor Cyan $results.Recommendations | ForEach-Object { Write-Host " ! $_" -ForegroundColor Yellow } } if ($results.Summary.TotalBackdoors -gt 0) { Write-Host "`n[!!!] BACKDOORS DETECTED - IMMEDIATE ACTION REQUIRED [!!!]" -ForegroundColor Red -BackgroundColor Black } return $results } } |