LDAP Verbindungen auf DCs finden

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?

Application and Service Logs -> Directory Service-> Event ID 2889

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.

Was this article helpful?
YesNo
5 3 votes
Article Rating
Abonnieren
Benachrichtige mich bei
guest
6 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Valerij
Valerij
7 Monate zuvor

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

Andreas
Andreas
1 Jahr zuvor

in der „cleared CSV“ tauchen Einträge mit „0“ und mit „1“ am Ende auf;
wo liegt der Unterschied?

Simon Wolf
Simon Wolf
1 Jahr zuvor

Da zieht man extra nach Österreich um, nur um heute beim googlen über deinen Blog zu stolpern 😉