Public/Get-InsecureLDAPBinds.ps1
|
function Get-InsecureLDAPBinds { <# .SYNOPSIS Detects insecure LDAP binds across all domain controllers. .DESCRIPTION Queries Event ID 2889 from the Directory Service log on every domain controller in the domain. Reports unsigned and simple LDAP binds which are a security risk and should be remediated before enforcing LDAP signing via Group Policy. Performs ping and RPC port 135 connectivity checks before querying each DC to avoid hanging on unreachable hosts. Output is saved as a dated CSV to the OutputFolder parameter path. .PARAMETER Hours How many hours back to search for events. Defaults to 24. .PARAMETER OutputPath Folder where the CSV output file is written. Defaults to C:\ADOpsKit\Reports\Get-InsecureLDAPBinds .EXAMPLE Get-InsecureLDAPBinds Checks the last 24 hours across all DCs. .EXAMPLE Get-InsecureLDAPBinds -Hours 72 Checks the last 72 hours across all DCs. .EXAMPLE Get-InsecureLDAPBinds -Hours 48 -OutputFolder "C:\Reports\LDAP" Checks the last 48 hours and writes the CSV to a custom folder. .NOTES Author: K Shankar R Karanth Website: https://karanth.ovh Version: 1.0 Run as Domain Admin or equivalent with WMI access to all DCs. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $false, Position = 0)] [Int]$Hours = 24, [string]$OutputPath = 'C:\ADOpsKit\Reports\Get-InsecureLDAPBinds' ) $dateString = Get-Date -Format "yyyy-MM-dd" $since = (Get-Date).AddHours(-$Hours) if (-not (Test-Path -Path $OutputPath)) { New-Item -ItemType Directory -Path $OutputPath | Out-Null } $DomainControllers = Get-ADDomainController -Filter * $AllInsecureLDAPBinds = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($DC in $DomainControllers) { $ComputerName = $DC.HostName Write-Host "Checking connectivity to $ComputerName..." if (-not (Test-Connection -ComputerName $ComputerName -Count 1 -Quiet -ErrorAction SilentlyContinue)) { Write-Warning "$ComputerName is not responding to ping. Skipping." continue } if (-not (Test-ADOKTcpPort -ComputerName $ComputerName -Port 135 -TimeoutSeconds 15)) { Write-Warning "Cannot connect to RPC port 135 on $ComputerName. Skipping." continue } Write-Host "Connectivity OK for $ComputerName. Querying event logs via WMI..." try { $wmiQuery = "SELECT * FROM Win32_NTLogEvent WHERE Logfile='Directory Service' AND EventCode=2889" $Events = Get-WmiObject -ComputerName $ComputerName -Query $wmiQuery -ErrorAction Stop $FilteredEvents = $Events | Where-Object { $dt = [System.Management.ManagementDateTimeConverter]::ToDateTime($_.TimeGenerated) $dt -ge $since } } catch { Write-Warning "WMI query failed for $ComputerName : $_" continue } foreach ($LdapEvent in $FilteredEvents) { $Client = $LdapEvent.InsertionStrings[0] $User = $LdapEvent.InsertionStrings[1] $BindTypeRaw = $LdapEvent.InsertionStrings[2] $IPAddress = $null $Port = $null if ($Client -and $Client.LastIndexOf(":") -gt 0) { $IPAddress = $Client.Substring(0, $Client.LastIndexOf(":")) $Port = $Client.Substring($Client.LastIndexOf(":") + 1) } $BindType = switch ($BindTypeRaw) { "0" { "Unsigned" } "1" { "Simple" } default { "Unknown" } } $ClientName = $null if ($IPAddress) { try { $ClientName = ([System.Net.Dns]::GetHostEntry($IPAddress)).HostName } catch { $ClientName = $null } } $AllInsecureLDAPBinds.Add([PSCustomObject]@{ DCName = $ComputerName ClientName = $ClientName IPAddress = $IPAddress Port = $Port User = $User BindType = $BindType }) } Write-Host "Events collected from $ComputerName." } $outputFile = Join-Path $OutputPath "${dateString}_InsecureLDAPBinds.csv" if ($AllInsecureLDAPBinds.Count -gt 0) { $AllInsecureLDAPBinds | Export-Csv -NoTypeInformation -Path $outputFile Write-Host "$($AllInsecureLDAPBinds.Count) record(s) saved to $outputFile" } else { Write-Host "No insecure LDAP binds found in the last $Hours hours." } } |