1
1
# File name : psLDAPmonitor.ps1
2
2
# Author : Podalirius (@podalirius_)
3
- # Date created : 17 Oct 2021
3
+ # Date created : 3 Jan 2022
4
4
5
5
Param (
6
6
[parameter (Mandatory = $true )][string ]$dcip = $null ,
7
7
[parameter (Mandatory = $false )][string ]$Username = $null ,
8
8
[parameter (Mandatory = $false )][string ]$Password = $null ,
9
9
[parameter (Mandatory = $false )][string ]$LogFile = $null ,
10
10
[parameter (Mandatory = $false )][int ]$PageSize = 5000 ,
11
+ [parameter (Mandatory = $false )][string ]$SearchBase = $null ,
11
12
[parameter (Mandatory = $false )][int ]$Delay = 1 ,
12
13
[parameter (Mandatory = $false )][switch ]$LDAPS ,
13
14
[parameter (Mandatory = $false )][switch ]$Randomize ,
@@ -17,7 +18,7 @@ Param (
17
18
18
19
If ($Help ) {
19
20
Write-Host " [+]======================================================"
20
- Write-Host " [+] Powershell LDAP live monitor v1.1 @podalirius_ "
21
+ Write-Host " [+] Powershell LDAP live monitor v1.3 @podalirius_ "
21
22
Write-Host " [+]======================================================"
22
23
Write-Host " "
23
24
@@ -29,6 +30,7 @@ If ($Help) {
29
30
Write-Host " -Username : User to authenticate as."
30
31
Write-Host " -Password : Password for authentication."
31
32
Write-Host " -PageSize : Sets the LDAP page size to use in queries (default: 5000)."
33
+ Write-Host " -SearchBase : Sets the LDAP search base."
32
34
Write-Host " -LDAPS : Use LDAPS instead of LDAP."
33
35
Write-Host " -LogFile : Log file to save output to."
34
36
Write-Host " -Delay : Delay between two queries in seconds (default: 1)."
@@ -71,13 +73,96 @@ Function Write-Logger {
71
73
}
72
74
}
73
75
76
+ Function Init-LdapConnection {
77
+ [CmdletBinding ()]
78
+ [OutputType ([Nullable ])]
79
+ Param
80
+ (
81
+ [Parameter (Mandatory = $true )] $connectionString ,
82
+ [Parameter (Mandatory = $false )] $SearchBase ,
83
+ [Parameter (Mandatory = $false )] $Username ,
84
+ [Parameter (Mandatory = $false )] $Password ,
85
+ [Parameter (Mandatory = $false )] $PageSize
86
+ )
87
+ Begin
88
+ {
89
+ $ldapSearcher = New-Object System.DirectoryServices.DirectorySearcher
90
+ if ($Username ) {
91
+ if ($SearchBase.Length -ne 0 ) {
92
+ # Connect to Domain with credentials
93
+ $ldapSearcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry((" {0}/{1}" -f $connectionString , $SearchBase ), $Username , $Password )
94
+ } else {
95
+ # Connect to Domain with current session
96
+ $ldapSearcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry(" $connectionString " , $Username , $Password )
97
+ }
98
+ } else {
99
+ if ($SearchBase.Length -ne 0 ) {
100
+ # Connect to Domain with credentials
101
+ $ldapSearcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry((" {0}/{1}" -f $connectionString , $SearchBase ))
102
+ } else {
103
+ # Connect to Domain with current session
104
+ $ldapSearcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry(" $connectionString " )
105
+ }
106
+ }
107
+ $ldapSearcher.SearchScope = " Subtree"
108
+ if ($PageSize ) {
109
+ $ldapSearcher.PageSize = $PageSize
110
+ } else {
111
+ Write-Verbose (" Setting PageSize to $PageSize " );
112
+ $ldapSearcher.PageSize = 5000
113
+ }
114
+ return $ldapSearcher ;
115
+ }
116
+ }
117
+
118
+
119
+ Function Query-AllNamingContextsOrSearchBase {
120
+ [CmdletBinding ()]
121
+ [OutputType ([Nullable ])]
122
+ Param
123
+ (
124
+ [Parameter (Mandatory = $true )] $namingContexts ,
125
+ [Parameter (Mandatory = $true )] $connectionString ,
126
+ [Parameter (Mandatory = $false )] $SearchBase ,
127
+ [Parameter (Mandatory = $false )] $Username ,
128
+ [Parameter (Mandatory = $false )] $Password ,
129
+ [Parameter (Mandatory = $false )] $PageSize
130
+ )
131
+ Begin
132
+ {
133
+ if ($SearchBase.Length -ne 0 ) {
134
+ Write-Verbose " Using SearchBase: $nc "
135
+ $ldapSearcher = Init- LdapConnection - connectionString $connectionString - SearchBase $SearchBase - Username $Username - Password $Password - PageSize $PageSize
136
+ $ldapSearcher.Filter = " (objectClass=*)"
137
+ return $ldapSearcher.FindAll ();
138
+ } else {
139
+ $results = [ordered ]@ {};
140
+ foreach ($nc in $namingContexts ) {
141
+ Write-Verbose " Using namingContext as search base: $nc "
142
+ $ldapSearcher = Init- LdapConnection - connectionString $connectionString - SearchBase $nc - Username $Username - Password $Password - PageSize $PageSize
143
+ $ldapSearcher.Filter = " (objectClass=*)"
144
+
145
+ Foreach ($item in $ldapSearcher.FindAll ()) {
146
+ if (! ($results.Keys -contains $item.Path )) {
147
+ $results [$item.Path ] = $item.Properties ;
148
+ } else {
149
+ Write-Host " [debug] key already exists: $key (this shouldn't be possible)"
150
+ }
151
+ }
152
+ }
153
+ return $results ;
154
+ }
155
+ }
156
+ }
157
+
158
+
74
159
Function ResultsDiff {
75
160
[CmdletBinding ()]
76
161
[OutputType ([Nullable ])]
77
162
Param
78
163
(
79
- [Parameter (Mandatory = $true )] $Before ,
80
- [Parameter (Mandatory = $true )] $After ,
164
+ [Parameter (Mandatory = $true )] $ResultsBefore ,
165
+ [Parameter (Mandatory = $true )] $ResultsAfter ,
81
166
[Parameter (Mandatory = $true )] $connectionString ,
82
167
[Parameter (Mandatory = $true )] $Logfile ,
83
168
[parameter (Mandatory = $false )][switch ]$IgnoreUserLogons
@@ -90,23 +175,18 @@ Function ResultsDiff {
90
175
}
91
176
92
177
$dateprompt = (" [{0}] " -f (Get-Date - Format " yyyy/MM/dd hh:mm:ss" ));
93
- # Get Keys
94
- $dict_results_before = [ordered ]@ {};
95
- $dict_results_after = [ordered ]@ {};
96
- Foreach ($itemBefore in $Before ) { $dict_results_before [$itemBefore.Path ] = $itemBefore.Properties ; }
97
- Foreach ($itemAfter in $After ) { $dict_results_after [$itemAfter.Path ] = $itemAfter.Properties ; }
98
178
99
179
# Get created and deleted entries, and common_keys
100
180
[System.Collections.ArrayList ]$commonPaths = @ ();
101
- Foreach ($bpath in $dict_results_before .Keys ) {
102
- if (! ($dict_results_after .Keys -contains $bpath )) {
181
+ Foreach ($bpath in $ResultsBefore .Keys ) {
182
+ if (! ($ResultsAfter .Keys -contains $bpath )) {
103
183
Write-Logger - Logfile $Logfile - Message (" {0}'{1}' was deleted." -f $dateprompt , $bpath.replace ($connectionString + " /" , " " ))
104
184
} else {
105
185
$commonPaths.Add ($bpath ) | Out-Null
106
186
}
107
187
}
108
- Foreach ($apath in $dict_results_after .Keys ) {
109
- if (! ($dict_results_before .Keys -contains $apath )) {
188
+ Foreach ($apath in $ResultsAfter .Keys ) {
189
+ if (! ($ResultsBefore .Keys -contains $apath )) {
110
190
Write-Logger - Logfile $Logfile - Message (" {0}'{1}' was created." -f $dateprompt , $apath.replace ($connectionString + " /" , " " ))
111
191
}
112
192
}
@@ -120,14 +200,14 @@ Function ResultsDiff {
120
200
$dict_direntry_before = [ordered ]@ {};
121
201
$dict_direntry_after = [ordered ]@ {};
122
202
123
- Foreach ($propkey in $dict_results_before [$path ].Keys) {
203
+ Foreach ($propkey in $ResultsBefore [$path ].Keys) {
124
204
if (! ($ignored_keys -Contains $propkey.ToLower ())) {
125
- $dict_direntry_before.Add ($propkey , $dict_results_before [$path ][$propkey ][0 ]);
205
+ $dict_direntry_before.Add ($propkey , $ResultsBefore [$path ][$propkey ][0 ]);
126
206
}
127
207
};
128
- Foreach ($propkey in $dict_results_after [$path ].Keys) {
208
+ Foreach ($propkey in $ResultsAfter [$path ].Keys) {
129
209
if (! ($ignored_keys -Contains $propkey.ToLower ())) {
130
- $dict_direntry_after.Add ($propkey , $dict_results_after [$path ][$propkey ][0 ]);
210
+ $dict_direntry_after.Add ($propkey , $ResultsAfter [$path ][$propkey ][0 ]);
131
211
}
132
212
};
133
213
@@ -165,7 +245,7 @@ Function ResultsDiff {
165
245
# ===============================================================================
166
246
167
247
Write-Logger - Logfile $Logfile - Message " [+]======================================================"
168
- Write-Logger - Logfile $Logfile - Message " [+] Powershell LDAP live monitor v1.1 @podalirius_ "
248
+ Write-Logger - Logfile $Logfile - Message " [+] Powershell LDAP live monitor v1.3 @podalirius_ "
169
249
Write-Logger - Logfile $Logfile - Message " [+]======================================================"
170
250
Write-Logger - Logfile $Logfile - Message " "
171
251
@@ -176,43 +256,32 @@ If ($LDAPS) {
176
256
} else {
177
257
$connectionString = ($connectionString -f $dcip , " 389" );
178
258
}
179
- Write-Verbose " $connectionString "
259
+ Write-Verbose " Using connectionString: $connectionString "
180
260
181
261
# Connect to LDAP
182
262
try {
183
- # Connect to Domain with credentials
184
- if ($Username ) {
185
- $objDomain = New-Object System.DirectoryServices.DirectoryEntry(" $connectionString " , $Username , $Password )
186
- } else {
187
- $objDomain = New-Object System.DirectoryServices.DirectoryEntry(" $connectionString " )
188
- }
189
- $searcher = New-Object System.DirectoryServices.DirectorySearcher
190
- $searcher.SearchRoot = $objDomain
191
- if ($PageSize ) {
192
- $searcher.PageSize = $PageSize
193
- } else {
194
- Write-Verbose (" Setting PageSize to $PageSize " );
195
- $searcher.PageSize = 5000
196
- }
263
+ $rootDSE = New-Object System.DirectoryServices.DirectoryEntry(" {0}/RootDSE" -f $connectionString );
264
+ $namingContexts = $rootDSE.Properties [" namingContexts" ];
197
265
198
266
Write-Verbose (" Authentication successful!" );
199
267
200
268
# First query
201
- $searcher.Filter = " (objectClass=*)"
202
- $results_before = $searcher.FindAll ();
269
+ $results_before = Query- AllNamingContextsOrSearchBase - connectionString $connectionString - SearchBase $SearchBase - namingContexts $namingContexts - Username $Username - Password $Password - PageSize $PageSize
203
270
204
271
Write-Logger - Logfile $Logfile - Message " [>] Listening for LDAP changes ..." ;
205
272
Write-Logger - Logfile $Logfile - Message " " ;
206
273
207
274
While ($true ) {
208
275
# Update query
209
- $results_after = $searcher.FindAll ();
276
+ $results_after = Query- AllNamingContextsOrSearchBase - connectionString $connectionString - SearchBase $SearchBase - namingContexts $namingContexts - Username $Username - Password $Password - PageSize $PageSize
277
+
210
278
# Diff
211
279
if ($IgnoreUserLogons ) {
212
- ResultsDiff - Before $results_before - After $results_after - connectionString $connectionString - Logfile $Logfile - IgnoreUserLogons
280
+ ResultsDiff - ResultsBefore $results_before - ResultsAfter $results_after - connectionString $connectionString - Logfile $Logfile - IgnoreUserLogons
213
281
} else {
214
- ResultsDiff - Before $results_before - After $results_after - connectionString $connectionString - Logfile $Logfile
282
+ ResultsDiff - ResultsBefore $results_before - ResultsAfter $results_after - connectionString $connectionString - Logfile $Logfile
215
283
}
284
+
216
285
$results_before = $results_after ;
217
286
if ($Randomize ) {
218
287
$DelayInSeconds = Get-Random - Minimum 1 - Maximum 5
0 commit comments