The estimated reading time 9 minutes
Vor einiger Zeit gab Microsoft das Aus für LDAP als Standard Konfiguration für Windows Domänen Controller bekannt. Siehe LINK.
Dies betrifft alle aktuell unterstützten Domänen Controller (von 2008R2 – 2019). Es gibt einen weiteren LINK ADV190023 mit einer detaillierten Erklärung hierzu.
Eigentlich sollte es hier auch keine große Diskussion geben und der unverschlüsselte LDAP Weg komplett abgeschaltet werden. Aber was ist irgendwelcher Drittanbietersoftware, welche eben kein LDAPS spricht, bzw. das ganze nicht konfiguriert ist? Wie kann ich solche Software finden, die sich per LDAP auf den DCs anmeldet?
Vorab Info: wenn man keine Zertifikate auf den Domänen Controllern installiert hat, funktioniert LDAP over SSL auch nicht. Hierzu gibt es eine Anleitung von Microsoft um ein SSL Zertifikat auf einem DC zu integrieren.
How to enable LDAP over SSL
Nun aber weiter im LDAP Logging. Ich würde es empfehlen auf allen Domänen Controllern das Logging zu aktivieren um sicherzugehen, dass keine LDAP Verbindungen unentdeckt bleiben. Wie sieht das im Log nun aus wenn eine LDAP Verbindung ankommt und was wird protokolliert?
Wie man sieht wird die IP Adresse des initiierenden Gerätes gezeigt und der Benutzername mit dem ein BIND durchgeführt wird.
Bevor dies aber geloggt wird muss die Protokollierung aktiviert werden.
Dies kann manuell, per Skript oder per GPO erfolgen. (der folgende Key ist auf einem DC bereits vorhanden und auf 0 gesetzt)
LDAP Logging:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics -> DWORD -> “16 LDAP Interface Events” -> Value “2”
Eventlog Größe anpassen 2GB (Wert ist in Bytes):
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Directory Service -> DWORD -> “MaxSize” -> Value= “2147483648”
Hier die passende GPO dazu:
Per Skript kann dies mit reg add erledigt werden:
Reg Add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics /v "16 LDAP Interface Events" /t REG_DWORD /d 2 Reg Add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Directory Service" /v "MaxSize" /t REG_DWORD /d "2147483648"
Nachdem die Registry koorekt bearbeitet wurde, muss einige Zeit vergehen, da ja möglichst viele LDAP Verbindungen aufgespürt werden sollen. Nicht jedes System baut permanent LDAP Verbindungen auf .
Wer die Protokollierung einmal testen möchte kann dies von einem anderen Server oder direkt vom DomänenController tun. Im System32 Ordner liegt die zugehörige LDP.EXE (C:\Windows\System32\=
Wenn dann noch ein Simplebind durchgeführt wird, wird die Verbindung im Eventlog protokolliert.
Man sollte auf jeden auch prüfen ob die Server LDAPS annehmen und richtig konfiguriert sind.
Wenn LDAPS Verbindungen angenommen werden kann die Umstellung getrost erfolgen und LDAP Verbindungen werden protokolliert. Wie schon erwähnt benötigen wir an diesem Schritt etwas Zeit um möglichst viele LDAP Verbindungen zu finden.
Da ich von Natur aus faul bin habe ich mir ein Skript erstellt, welches mit die richtigen Eventlogs herausfiltert und sogar Benutzer und IP Adresse aus diesen Eventlogs ausliest.
Hier nun das Skript:
<# #### requires ps-version 3.0 #### <# .SYNOPSIS Analyses Eventlog from DomainControllers and searches for LDAP access .DESCRIPTION Creates a working directory on your specified path. After creation it copies evtx files from all Domaincontrollers via robocopy into this working directory. Every domaincontroller eventlog creates four files (cleared and raw) .PARAMETER WorkDir Path where you want to have your logs and results (please check diskspace, evtx file may have some GBs) .INPUTS - .OUTPUTS YYYY-MM-DD-HH-MM-SS-NameofDC.evtx (copied but untouched) YYYY-MM-DD-HH-MM-SS-NameofDC-LDAP-cleared.csv YYYY-MM-DD-HH-MM-SS-NameofDC-LDAP-raw.csv YYYY-MM-DD-HH-MM-SS-NameofDC-LDAP-time.csv YYYY-MM-DD-HH-MM-SS-NameofDC-LDAP-time-cleared.csv .NOTES Version: 0.1 Author: Alexander Koehler Creation Date: Tuesday, March 10th 2020, 11:07:01 pm File: ad-ldap-audit-0-1.ps1 Copyright (c) 2020 blog.it-koehler.com HISTORY: Date By Comments ---------- --- ---------------------------------------------------------- .LINK blog.it-koehler.com/Archive/3010 .COMPONENT Required Modules: none .LICENSE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use copy, modify, merge, publish, distribute sublicense and /or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. .EXAMPLE C:\temp\ad-ldap-audit-0-1.ps1 -WorkDir "C:\temp\ldapaudit" # #> # define parameters (Working Directory) and check if it's a directory and if it does not exist, create it. [CmdletBinding()] Param( [Parameter(Mandatory = $True)] [String] $WorkDir) if (-not (Test-Path $WorkDir -PathType Container)) { try { New-Item -Path $WorkDir -ItemType Directory -ErrorAction Stop | Out-Null } catch { Write-Error -Message "Unable to create directory '$WorkDir'. Error was: $_" -ErrorAction Stop } "Successfully created directory '$WorkDir'." } else { "Directory already existes" } #get date as string (is needed for filenames) $date=((Get-Date).ToString('yyyy-MM-dd-HH-mm-ss')) #getting all DCs in environment without AD PowerSehll Module $DCs = ([System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites | % { $_.Servers } | select Name).Name #executing code for every domaincontroller foreach($DC in $DCs){ #copy evtx file in working dir with robocopy $path = "\\$DC\c$\Windows\System32\winevt\Logs\" robocopy "$path" "$workdir" "Directory Service.evtx" Rename-Item "$workdir\Directory Service.evtx" "$date-$DC.evtx" $evtxfile = "$workdir\$date-$DC.evtx" #define output file prefix $csvoutputpath = "$workdir\$date-$DC-LDAP" #searching for event 2889 $Events = Get-WinEvent @{Path=$evtxfile;Id=2889} #define array for outputs $eventarraytime = @() $eventarrayraw =@() ForEach ($Event in $Events) { # Convert the event to XML #see https://docs.microsoft.com/de-de/archive/blogs/ashleymcglone/powershell-get-winevent-xml-madness-getting-details-from-event-logs $eventXML = [xml]$Event.ToXml() #getting timestamp $time = ($Event.TimeCreated) #getting user from eventlog $ldapconn = ($eventXML.Event.EventData.Data) #creating custom object and put all together #customobject with timestamp $eventobj = New-Object System.Object $eventobj | Add-Member -type NoteProperty -name AccessTime -Value $time $eventobj | Add-Member -type NoteProperty -name LDAPAccess -Value (@($ldapconn) -join " ") #custom object witout timestamp $eventraw = New-Object System.Object $eventraw | Add-Member -type NoteProperty -name LDAPAccess -Value (@($ldapconn) -join " ") #appending content to array $eventarraytime += $eventobj $eventarrayraw += $eventraw } #output data in rawformat to csv $eventarraytime | Export-Csv -Path "$csvoutputpath-time.csv" -Encoding UTF8 -Delimiter ";" -NoTypeInformation $eventarrayraw | Export-Csv -Path "$csvoutputpath-raw.csv" -Encoding UTF8 -Delimiter ";" -NoTypeInformation #cleaning up double entries $eventstimecleared = $eventarraytime | Sort-Object -Unique LDAPAccess -Descending $eventstimecleared | Export-Csv -Path "$csvoutputpath-time-cleared.csv" -Encoding UTF8 -Delimiter ";" -NoTypeInformation $eventarraycleared = $eventarrayraw | Sort-Object -Unique LDAPAccess -Descending $eventarraycleared | Export-Csv -Path "$csvoutputpath-cleared.csv" -Encoding UTF8 -Delimiter ";" -NoTypeInformation }
Das Skript muss nur in eine PS1 Datei kopiert und abgespeichert werden. Danach kann es mit dem Parameter WorkDir gestartet werden.
Bitte beachten: eventuell sind die Eventlogs der DomänenController größer, deshalb Festplattenplatz beachten. Außerdem kann die Verarbeitung bei großen Dateien auch mal einige Stunden in Anspruch nehmen.
Kennt man sich mit PowerShell gut aus, braucht man ab hier nicht mehr weiterlesen. Es folgen noch Erklärungen zu einzelnen Skriptauszügen.
LINIE 82:
$DCs = ([System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites | % { $_.Servers } | select Name).Name
Dies ist eine integrierte .Net Funktion um alle DCs mit ihrem FQDN zu finden. Der nachfolgende Code wird dann in einer Foreach Schleife für jeden DC ausgeführt.
LINIE 93:
$Events = Get-WinEvent @{Path=$evtxfile;Id=2889}
Sucht innerhalb der EVTX Datei nach Ereignis mit der ID 2889 und schreibt diese in die Variable $Events
LINIE 100:
$eventXML = [xml]$Event.ToXml()
Die Ereignisse werden zu XML konvertiert um diese weiter bearbeiten zu können.
LINIE 104:
$ldapconn = ($eventXML.Event.EventData.Data)
Hier wird eine Variable mit der Ereignismeldung befüllt. Alles andere ist mehr oder weniger Code um das Ganze in vernünftige Form zu bringen und zu exportieren in CSVs.
Innerhalb meiner Testumgebung habe ich nur ein paar wenige Verbindungen erzeugt.
IP und Benutzer sind durch ein Semikolon, somit kann dies einfach in eine Excel Liste exportiert und gefiltert werden. Sollten dazu Fragen sein, lasst mir gerne einen Kommentar da. Wenn euch der Artikel gefallen hat, drückt bitte auf „Helpful“. Viel Glück beim Abschalten von LDAP.
Hallo Alexander,
danke für da Script. Sind gerade dabei auf ldaps umzustellen und sind über das Script gestolpert.
Alles hat soweit geklappt allerdings bekomme ich nur 125 Einträge und nur für Vorgestern ausgegeben. Kann man da steuern oder im Script anpassen?
VG
Valerij
Hallo Valerij,
eigentlich sollte das Script wirklich alle Einträge filtern und ausgeben, kannst du mal schauen und im Eventlog nach EventID 2889 suchen? Wieviele Einträge bekommst du da?
Viele Grüße
Alexander
in der „cleared CSV“ tauchen Einträge mit „0“ und mit „1“ am Ende auf;
wo liegt der Unterschied?
Hallo Andreas,
wenn ich es richtig im Kopf habe, ist das der Binding Type. Laut Reddit stehen die Werte für folgende Parameter:
Viele Grüße
Da zieht man extra nach Österreich um, nur um heute beim googlen über deinen Blog zu stolpern 😉
Hi,
schön, dass du über meinen Blog gestolpert bist. Meld dich mal bei mir wenn du an SIG vorbeifährst.
Viele Grüße nach Österreich.
Alex