· 7 years ago · Dec 21, 2018, 07:38 PM
1 $worksheet = $workbook.Worksheets.Item(1)
2 # Freeze First Row and Column
3 $worksheet.Select()
4 $worksheet.Application.ActiveWindow.splitcolumn = 1
5 $worksheet.Application.ActiveWindow.splitrow = 1
6 $worksheet.Application.ActiveWindow.FreezePanes = $true
7
8 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
9 Remove-Variable worksheet
10 }
11
12 $ADFileName = -join($ReportPath,'\','OUs.csv')
13 If (Test-Path $ADFileName)
14 {
15 Get-ADRExcelWorkbook -Name "OUs"
16 Get-ADRExcelImport -ADFileName $ADFileName
17 Remove-Variable ADFileName
18 }
19
20 $ADFileName = -join($ReportPath,'\','UserSPNs.csv')
21 If (Test-Path $ADFileName)
22 {
23 Get-ADRExcelWorkbook -Name "User SPNs"
24 Get-ADRExcelImport -ADFileName $ADFileName
25 Remove-Variable ADFileName
26 }
27
28 $ADFileName = -join($ReportPath,'\','Groups.csv')
29 If (Test-Path $ADFileName)
30 {
31 Get-ADRExcelWorkbook -Name "Groups"
32 Get-ADRExcelImport -ADFileName $ADFileName
33 Remove-Variable ADFileName
34
35 Get-ADRExcelSort -ColumnName "DistinguishedName"
36 }
37
38 $ADFileName = -join($ReportPath,'\','GroupMembers.csv')
39 If (Test-Path $ADFileName)
40 {
41 Get-ADRExcelWorkbook -Name "Group Members"
42 Get-ADRExcelImport -ADFileName $ADFileName
43 Remove-Variable ADFileName
44
45 Get-ADRExcelSort -ColumnName "Group Name"
46 }
47
48 $ADFileName = -join($ReportPath,'\','Users.csv')
49 If (Test-Path $ADFileName)
50 {
51 Get-ADRExcelWorkbook -Name "Users"
52 Get-ADRExcelImport -ADFileName $ADFileName
53 Remove-Variable ADFileName
54
55 Get-ADRExcelSort -ColumnName "UserName"
56
57 $worksheet = $workbook.Worksheets.Item(1)
58
59 # Freeze First Row and Column
60 $worksheet.Select()
61 $worksheet.Application.ActiveWindow.splitcolumn = 1
62 $worksheet.Application.ActiveWindow.splitrow = 1
63 $worksheet.Application.ActiveWindow.FreezePanes = $true
64
65 $worksheet.Cells.Item(1,3).Interior.ColorIndex = 5
66 $worksheet.Cells.Item(1,3).font.ColorIndex = 2
67 # Set Filter to Enabled Accounts only
68 $worksheet.UsedRange.Select() | Out-Null
69 $excel.Selection.AutoFilter(3,$true) | Out-Null
70 $worksheet.Cells.Item(1,1).Select() | Out-Null
71 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
72 Remove-Variable worksheet
73 }
74
75 # Computer Role Stats
76 $ADFileName = -join($ReportPath,'\','ComputerSPNs.csv')
77 If (Test-Path $ADFileName)
78 {
79 Get-ADRExcelWorkbook -Name "Computer Role Stats"
80 Remove-Variable ADFileName
81
82 $worksheet = $workbook.Worksheets.Item(1)
83 $PivotTableName = "Computer SPNs"
84 Get-ADRExcelPivotTable -SrcSheetName "Computer SPNs" -PivotTableName $PivotTableName -PivotRows @("Service") -PivotValues @("Service")
85
86 $worksheet.Cells.Item(1,1) = "Computer Role"
87 $worksheet.Cells.Item(1,2) = "Count"
88
89 # https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlsortorder-enumeration-excel
90 $worksheet.PivotTables($PivotTableName).PivotFields("Service").AutoSort([Microsoft.Office.Interop.Excel.XlSortOrder]::xlDescending,"Count")
91
92 Get-ADRExcelChart -ChartType "xlColumnClustered" -ChartLayout 10 -ChartTitle "Computer Roles in AD" -RangetoCover "D2:U16"
93 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,4) , "" , "'Computer SPNs'!A1", "", "Raw Data") | Out-Null
94 $excel.Windows.Item(1).Displaygridlines = $false
95 Remove-Variable PivotTableName
96
97 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
98 Remove-Variable worksheet
99 }
100
101 # Operating System Stats
102 $ADFileName = -join($ReportPath,'\','Computers.csv')
103 If (Test-Path $ADFileName)
104 {
105 Get-ADRExcelWorkbook -Name "Operating System Stats"
106 Remove-Variable ADFileName
107
108 $worksheet = $workbook.Worksheets.Item(1)
109 $PivotTableName = "Operating Systems"
110 Get-ADRExcelPivotTable -SrcSheetName "Computers" -PivotTableName $PivotTableName -PivotRows @("Operating System") -PivotValues @("Operating System")
111
112 $worksheet.Cells.Item(1,1) = "Operating System"
113 $worksheet.Cells.Item(1,2) = "Count"
114
115 # https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlsortorder-enumeration-excel
116 $worksheet.PivotTables($PivotTableName).PivotFields("Operating System").AutoSort([Microsoft.Office.Interop.Excel.XlSortOrder]::xlDescending,"Count")
117
118 Get-ADRExcelChart -ChartType "xlColumnClustered" -ChartLayout 10 -ChartTitle "Operating Systems in AD" -RangetoCover "D2:S16"
119 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,4) , "" , "Computers!A1", "", "Raw Data") | Out-Null
120 $excel.Windows.Item(1).Displaygridlines = $false
121 Remove-Variable PivotTableName
122
123 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
124 Remove-Variable worksheet
125 }
126
127 # Group Stats
128 $ADFileName = -join($ReportPath,'\','GroupMembers.csv')
129 If (Test-Path $ADFileName)
130 {
131 Get-ADRExcelWorkbook -Name "Privileged Group Stats"
132 Remove-Variable ADFileName
133
134 $worksheet = $workbook.Worksheets.Item(1)
135 $PivotTableName = "Group Members"
136 Get-ADRExcelPivotTable -SrcSheetName "Group Members" -PivotTableName $PivotTableName -PivotRows @("Group Name")-PivotFilters @("AccountType") -PivotValues @("AccountType")
137
138 # Set the filter
139 $worksheet.PivotTables($PivotTableName).PivotFields("AccountType").CurrentPage = "user"
140
141 $worksheet.Cells.Item(1,2).Interior.ColorIndex = 5
142 $worksheet.Cells.Item(1,2).font.ColorIndex = 2
143
144 $worksheet.Cells.Item(3,1) = "Group Name"
145 $worksheet.Cells.Item(3,2) = "Count (Not-Recursive)"
146
147 $excel.ScreenUpdating = $false
148 # Create a copy of the Pivot Table
149 $PivotTableTemp = ($workbook.PivotCaches().Item($workbook.PivotCaches().Count)).CreatePivotTable("R1C5","PivotTableTemp")
150 $PivotFieldTemp = $PivotTableTemp.PivotFields("Group Name")
151 # Set a filter
152 $PivotFieldTemp.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlPageField
153 Try
154 {
155 $PivotFieldTemp.CurrentPage = "Domain Admins"
156 }
157 Catch
158 {
159 # No Direct Domain Admins. Good Job!
160 $NoDA = $true
161 }
162 If ($NoDA)
163 {
164 Try
165 {
166 $PivotFieldTemp.CurrentPage = "Administrators"
167 }
168 Catch
169 {
170 # No Direct Administrators
171 }
172 }
173 # Create a Slicer
174 $PivotSlicer = $workbook.SlicerCaches.Add($PivotTableTemp,$PivotFieldTemp)
175 # Add Original Pivot Table to the Slicer
176 $PivotSlicer.PivotTables.AddPivotTable($worksheet.PivotTables($PivotTableName))
177 # Delete the Slicer
178 $PivotSlicer.Delete()
179 # Delete the Pivot Table Copy
180 $PivotTableTemp.TableRange2.Delete() | Out-Null
181
182 Get-ADRExcelComObjRelease -ComObjtoRelease $PivotFieldTemp
183 Get-ADRExcelComObjRelease -ComObjtoRelease $PivotSlicer
184 Get-ADRExcelComObjRelease -ComObjtoRelease $PivotTableTemp
185
186 Remove-Variable PivotFieldTemp
187 Remove-Variable PivotSlicer
188 Remove-Variable PivotTableTemp
189
190 "Account Operators","Administrators","Backup Operators","Cert Publishers","Crypto Operators","DnsAdmins","Domain Admins","Enterprise Admins","Enterprise Key Admins","Incoming Forest Trust Builders","Key Admins","Microsoft Advanced Threat Analytics Administrators","Network Operators","Print Operators","Remote Desktop Users","Schema Admins","Server Operators" | ForEach-Object {
191 Try
192 {
193 $worksheet.PivotTables($PivotTableName).PivotFields("Group Name").PivotItems($_).Visible = $true
194 }
195 Catch
196 {
197 # when PivotItem is not found
198 }
199 }
200
201 # https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlsortorder-enumeration-excel
202 $worksheet.PivotTables($PivotTableName).PivotFields("Group Name").AutoSort([Microsoft.Office.Interop.Excel.XlSortOrder]::xlDescending,"Count (Not-Recursive)")
203
204 $worksheet.Cells.Item(3,1).Interior.ColorIndex = 5
205 $worksheet.Cells.Item(3,1).font.ColorIndex = 2
206
207 $excel.ScreenUpdating = $true
208
209 Get-ADRExcelChart -ChartType "xlColumnClustered" -ChartLayout 10 -ChartTitle "Privileged Groups in AD" -RangetoCover "D2:P16" -StartRow "A3" -StartColumn "B3"
210 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,4) , "" , "'Group Members'!A1", "", "Raw Data") | Out-Null
211 $excel.Windows.Item(1).Displaygridlines = $false
212
213 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
214 Remove-Variable worksheet
215 }
216
217 # Computer Stats
218 $ADFileName = -join($ReportPath,'\','Computers.csv')
219 If (Test-Path $ADFileName)
220 {
221 Get-ADRExcelWorkbook -Name "Computer Stats"
222 Remove-Variable ADFileName
223
224 $ObjAttributes = New-Object System.Collections.Specialized.OrderedDictionary
225 $ObjAttributes.Add("Delegation Typ",'"Unconstrained"')
226 $ObjAttributes.Add("Delegation Type",'"Constrained"')
227 $ObjAttributes.Add("SIDHistory",'"*"')
228 $ObjAttributes.Add("Dormant",'"TRUE"')
229 $ObjAttributes.Add("Password Age (> ",'"TRUE"')
230 $ObjAttributes.Add("ms-ds-CreatorSid",'"*"')
231
232 Get-ADRExcelAttributeStats -SrcSheetName "Computers" -Title1 "Computer Accounts in AD" -Title2 "Status of Computer Accounts" -ObjAttributes $ObjAttributes
233 Remove-Variable ObjAttributes
234
235 Get-ADRExcelChart -ChartType "xlPie" -ChartLayout 3 -ChartTitle "Computer Accounts in AD" -RangetoCover "A11:D23" -ChartData $workbook.Worksheets.Item(1).Range("A3:A4,B3:B4")
236 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(10,1) , "" , "Computers!A1", "", "Raw Data") | Out-Null
237
238 Get-ADRExcelChart -ChartType "xlBarClustered" -ChartLayout 1 -ChartTitle "Status of Computer Accounts" -RangetoCover "F11:L23" -ChartData $workbook.Worksheets.Item(1).Range("F2:F8,G2:G8")
239 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(10,6) , "" , "Computers!A1", "", "Raw Data") | Out-Null
240
241 $workbook.Worksheets.Item(1).UsedRange.EntireColumn.AutoFit() | Out-Null
242 $excel.Windows.Item(1).Displaygridlines = $false
243 }
244
245 # User Stats
246 $ADFileName = -join($ReportPath,'\','Users.csv')
247 If (Test-Path $ADFileName)
248 {
249 Get-ADRExcelWorkbook -Name "User Stats"
250 Remove-Variable ADFileName
251
252 $ObjAttributes = New-Object System.Collections.Specialized.OrderedDictionary
253 $ObjAttributes.Add("Must Change Password at Logon",'"TRUE"')
254 $ObjAttributes.Add("Cannot Change Password",'"TRUE"')
255 $ObjAttributes.Add("Password Never Expires",'"TRUE"')
256 $ObjAttributes.Add("Reversible Password Encryption",'"TRUE"')
257 $ObjAttributes.Add("Smartcard Logon Required",'"TRUE"')
258 $ObjAttributes.Add("Delegation Permitted",'"TRUE"')
259 $ObjAttributes.Add("Kerberos DES Only",'"TRUE"')
260 $ObjAttributes.Add("Kerberos RC4",'"TRUE"')
261 $ObjAttributes.Add("Does Not Require Pre Auth",'"TRUE"')
262 $ObjAttributes.Add("Password Age (> ",'"TRUE"')
263 $ObjAttributes.Add("Account Locked Out",'"TRUE"')
264 $ObjAttributes.Add("Never Logged in",'"TRUE"')
265 $ObjAttributes.Add("Dormant",'"TRUE"')
266 $ObjAttributes.Add("Password Not Required",'"TRUE"')
267 $ObjAttributes.Add("Delegation Typ",'"Unconstrained"')
268 $ObjAttributes.Add("SIDHistory",'"*"')
269
270 Get-ADRExcelAttributeStats -SrcSheetName "Users" -Title1 "User Accounts in AD" -Title2 "Status of User Accounts" -ObjAttributes $ObjAttributes
271 Remove-Variable ObjAttributes
272
273 Get-ADRExcelChart -ChartType "xlPie" -ChartLayout 3 -ChartTitle "User Accounts in AD" -RangetoCover "A21:D33" -ChartData $workbook.Worksheets.Item(1).Range("A3:A4,B3:B4")
274 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(20,1) , "" , "Users!A1", "", "Raw Data") | Out-Null
275
276 Get-ADRExcelChart -ChartType "xlBarClustered" -ChartLayout 1 -ChartTitle "Status of User Accounts" -RangetoCover "F21:L43" -ChartData $workbook.Worksheets.Item(1).Range("F2:F18,G2:G18")
277 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(20,6) , "" , "Users!A1", "", "Raw Data") | Out-Null
278
279 $workbook.Worksheets.Item(1).UsedRange.EntireColumn.AutoFit() | Out-Null
280 $excel.Windows.Item(1).Displaygridlines = $false
281 }
282
283 # Create Table of Contents
284 Get-ADRExcelWorkbook -Name "Table of Contents"
285 $worksheet = $workbook.Worksheets.Item(1)
286
287 $excel.ScreenUpdating = $false
288 # Image format and properties
289 # $path = "C:\SOS_Logo.jpg"
290 # $base64sos = [convert]::ToBase64String((Get-Content $path -Encoding byte))
291
292 $base64sos = "/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEAAQBIAAAAAQAB/+Fik2h0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNC4yLjItYzA2MyA1My4zNTE3MzUsIDIwMDgvMDcvMjItMTg6MTE6MTIgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyI+CiAgICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2UvanBlZzwvZGM6Zm9ybWF0PgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp4bXBHSW1nPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvZy9pbWcvIj4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxMy0xMC0wM1QxMToyNjoyNSsxMDowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTMtMTAtMDNUMDE6MjY6MzBaPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcDpDcmVhdGVEYXRlPjIwMTMtMTAtMDNUMTE6MjY6MjUrMTA6MDA8L3htcDpDcmVhdGVEYXRlPgogICAgICAgICA8eG1wOkNyZWF0b3JUb29sPkFkb2JlIElsbHVzdHJhdG9yIENTNDwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8eG1wOlRodW1ibmFpbHM+CiAgICAgICAgICAgIDxyZGY6QWx0PgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHhtcEdJbWc6d2lkdGg+MjU2PC94bXBHSW1nOndpZHRoPgogICAgICAgICAgICAgICAgICA8eG1wR0ltZzpoZWlnaHQ+OTY8L3htcEdJbWc6aGVpZ2h0PgogICAgICAgICAgICAgICAgICA8eG1wR0ltZzpmb3JtYXQ+SlBFRzwveG1wR0ltZzpmb3JtYXQ+CiAgICAgICAgICAgICAgICAgIDx4bXBHSW1nOmltYWdlPi85ai80QUFRU2taSlJnQUJBZ0VCTEFFc0FBRC83UUFzVUdodmRHOXphRzl3SURNdU1BQTRRa2xOQSswQUFBQUFBQkFCTEFBQUFBRUEmI3hBO0FRRXNBQUFBQVFBQi8rNEFEa0ZrYjJKbEFHVEFBQUFBQWYvYkFJUUFCZ1FFQkFVRUJnVUZCZ2tHQlFZSkN3Z0dCZ2dMREFvS0N3b0smI3hBO0RCQU1EQXdNREF3UURBNFBFQThPREJNVEZCUVRFeHdiR3hzY0h4OGZIeDhmSHg4Zkh3RUhCd2NOREEwWUVCQVlHaFVSRlJvZkh4OGYmI3hBO0h4OGZIeDhmSHg4Zkh4OGZIeDhmSHg4Zkh4OGZIeDhmSHg4Zkh4OGZIeDhmSHg4Zkh4OGZIeDhmSHg4Zi84QUFFUWdBWUFFQUF3RVImI3hBO0FBSVJBUU1SQWYvRUFhSUFBQUFIQVFFQkFRRUFBQUFBQUFBQUFBUUZBd0lHQVFBSENBa0tDd0VBQWdJREFRRUJBUUVBQUFBQUFBQUEmI3hBO0FRQUNBd1FGQmdjSUNRb0xFQUFDQVFNREFnUUNCZ2NEQkFJR0FuTUJBZ01SQkFBRklSSXhRVkVHRTJFaWNZRVVNcEdoQnhXeFFpUEImI3hBO1V0SGhNeFppOENSeWd2RWxRelJUa3FLeVkzUENOVVFuazZPek5oZFVaSFREMHVJSUpvTUpDaGdaaEpSRlJxUzBWdE5WS0JyeTQvUEUmI3hBOzFPVDBaWFdGbGFXMXhkWGw5V1oyaHBhbXRzYlc1dlkzUjFkbmQ0ZVhwN2ZIMStmM09FaFlhSGlJbUtpNHlOam8rQ2s1U1ZscGVZbVomI3hBO3FibkoyZW41S2pwS1dtcDZpcHFxdXNyYTZ2b1JBQUlDQVFJREJRVUVCUVlFQ0FNRGJRRUFBaEVEQkNFU01VRUZVUk5oSWdaeGdaRXkmI3hBO29iSHdGTUhSNFNOQ0ZWSmljdkV6SkRSRGdoYVNVeVdpWTdMQ0IzUFNOZUpFZ3hkVWt3Z0pDaGdaSmpaRkdpZGtkRlUzOHFPend5Z3AmI3hBOzArUHpoSlNrdE1UVTVQUmxkWVdWcGJYRjFlWDFSbFptZG9hV3ByYkcxdWIyUjFkbmQ0ZVhwN2ZIMStmM09FaFlhSGlJbUtpNHlOam8mI3hBOytEbEpXV2w1aVptcHVjblo2ZmtxT2twYWFucUttcXE2eXRycSt2L2FBQXdEQVFBQ0VRTVJBRDhBOVU0cTdGVXQ4eUN5ZlJMcUMrWGwmI3hBO1ozU3JhejlQaFM1WVFsOS81ZWZMNk1qT0hFQ085dHdacFlweG5IbkVnL0o4b1hWMzVrOHY2bmM2Y2wvZFdzOWpLOERDR2FTT2hqYmomI3hBO1VjU052aEZNNS9lSjdpK3lZOGVEVVl4TXhqSVNGN2dIbTk5L0pIemJkYTc1WGt0Nys0ZTUxSFRwVEhKTkt4ZVI0cFBpalptSkpQN1MmI3hBOzcrR2JYUlpUS0pCNWg4NzlxT3o0NmZVQ1VBSXdtT1E1V09mNkM5RXpOZWFkaXJzVmRpcnNWZGlyc1ZkaXJzVmRpcnNWZGlyc1ZkaXImI3hBO3NWZGlyc1ZkaXJzVmRpcnNWZGlyc1ZkaXJzVmRpcnNWZGlyc1ZRT3U2WU5WMFRVTk1MY0JmVzB0dnpxUVY5VkNuSUViaWxhN1lxK1kmI3hBOy9QNWZVNGRGODJGQXNtdDJnWFVWVUNpYWhaL3VMcE50dnRybW4xdVBobmZlK2wreU9zOFRUbkdlZU0vWWR4K2xNZnlROHhmb256dkQmI3hBO2F5TlMyMVZEYXVPM3FING9qL3dRNC9Ua05KazRjZzg5bkk5cU5INDJrTWg5V1AxZkRyK3Y0UHBuTjIrV094VjJLdXhWMkt1eFYyS3UmI3hBO3hWMkt1eFYyS3V4VjJLdXhWMkt1eFYyS3V4VjJLdXhWMkt1eFYyS3V4VjJLdXhWMkt1eFYyS3V4Vjg0YXRwdk81L01YeWF5VW4wYSsmI3hBOy93QVQ2U1ArWGE3Vld1bFgvSlFTQS9QTVRXNCtLRjl6MEhzenJQQjFjUWZwbjZUK2o3WG5GdGNUVzF4RmNRTVVtaGRaSW5IVU1ocXAmI3hBOytnak5NK3FUZ0pSTVR5TDdFOHQ2MURyZWcyR3JRMENYa0tTbFIreTVIeHIvQUxGcWpPZ3haT09JTDRycmRNY0dhV00vd212MUpsbGomI3hBO2l1SkFCSk5BT3B4VkFycm1rU1QvQUZlRzZqdUp3YU5GQWZXWmEvemlQa1ZIdTJWZU5DNkJ2M2J1UWRMa0E0akVnZWUzeXZtdnM5WDAmI3hBO3ErbnVMZXl2SWJtZTBLaTZqaGtXUm9pOWVJY0tUeEo0blk1YTQ2M1d0YTB2Uk5MdU5WMVM0VzFzTFJPYzg3MW9CMDZDcEpKMkFHNU8mI3hBO0t2QjljLzV5MGdTNmVQUTlCTTFzcG9seGR6ZW16YjlmU1JXNC93REI0cHBHZVYvK2NxYkhVZFRnc05WMENXMitzeUpGRk5hVEM0UEsmI3hBO1JncWd4c2taNm5zeCtXS0tlODRxN0ZYWXE3RlhZcTdGWFlxN0ZYWXE3RlhZcTdGWFlxN0ZYWXE3RlhZcTdGWFlxN0ZYWXE3RlhnZjUmI3hBO3IzU2VWUHozOHBlWXBSVFR0Y3RtMHZVbHBWWFF1WXBDM2lGVzRqYi9BR09BaXhSWlJrUWJITU1SOHcvbGQ1eDA3Vkx5RzIwaTd1N0smI3hBO0taMXRwNFltbDV4aHZnYWlBbmRhZHMwVXNFd2FvL0o5YTBmYnVseTQ0bVdTTVpFQ3dUVkhyelpWNUM4OStjL0xlaWp5N0I1YXVyKzYmI3hBOzlaNUxYbWtxY0VlaEtsQkdTUnlxMWFqcmx1SFVUZ09FQ3orT2pxZTF1eXRMcXN2am5OR0VhMzVHNjg3N2syMXp6djU1czRqTjVyOHgmI3hBO2FYNUx0MkZmcXNhcmMzeFhyOEVBTThoMjhDTXlnTTgrWjRRNkxKUHNyVDdRalBQTHpORDlIM0Y1bjVoL1BMeXREelN3dGRRODEzbmEmI3hBOzkxKzRkYlFIL0lzb21veWV6RVpaSFNSL2lKa2ZOd01uYldUbGlqRENQNklGL3dDbTV2UGZNbjV1ZWZkZmhOcGNhazFucHZSTk0wOVImI3hBO2FXeXIvTDZjUEhrUDljbk1xTVFCUTJkVE9jcG01RWsrYjZEL0FPY1JOUE1Ya1hWcjVoUTNXcE5HcDhWaGhqb2YrQ2tiQ3dMWC9PV1YmI3hBOzllUmVXOUVzNDJaYlc1dTVIdUFLMExSUmowd2YrRFkweFFFcC93Q2NiZklYa1BYZkxtb2FocTFuQnFtcXBkR0Y0TGdCeERDRVZrSWomI3hBO08zeGt0OFZPMU94eFNYcC8vS2tQeThpOHg2YnI5aHAvNlB2Tk9tRndzVnN4V0dSa0I0Y296eVVjV293NFU2WW9aNDdwR2pTU01FUkEmI3hBO1dabU5BQU55U1RpcnpQVy8rY2l2eXcwcThhMEY3TnFEeG5qSTlsRVpJd1I0U01VVnZtcEl4VkhlVlB6MC9ManpMZXBZMm1vTmFYMHAmI3hBOzR3Mjk2aGhMazlBcjFhTWs5aHlxZTJLc244MithOUk4cTZKTnJXcnM2V01ESXNqUnFYYXNqQkYrRWU1eFZpeC9QbjhzbDh2cHJiYW0mI3hBO1Zna2tlR08xOU5qY3M4ZEN3OUlWSUZHQjVIYmZyaXFWMkgvT1MvNVgzVnlrRWs5M1pxNXA2ODl1ZlRIejlOcEcvREZYcUZwZDJ0NWEmI3hBO3hYVnBLazl0T29raG1qWU1qb3dxR1Zoc1FjVmVlZVl2K2NndnkwME8rZXhlK2t2N2lJOFp2cU1mcW9yRHFQVUpSRFQvQUNTY1ZWUEwmI3hBO0g1OS9sdjVoMUNIVHJhOWx0YjI1ZFlyYUc3aWFQMUhjOFZWWFhtbFNUUUFzTVZaN2ZYOWxwOW5MZTMwOGRyYVFLWG11SldDSWlqdXomI3hBO05RREZYbU4vL3dBNUwvbGZhWEx3UnozZDRxR25yMjl1ZlRKSGdaR2pKKzdGV1NlVFB6YjhpZWI1dnEya2FnUHI5QzMxRzRVd3pFRGMmI3hBOzhRMnowNzhDY1Zaamlyc1ZZLzUwODkrVy9KdW1McU91M0JoaWtmMDRJa1V2TEs5SzhVVWVBNms3WXFyK1V2TlZoNW8wbjlLV01GekImI3hBO2JtUm9nbDNFWVpLcUFTZUpKMjM2NHFuT0t1eFYyS3RNeXFwWmlBb0ZTVHNBQmlyejN6ZCtmbjVaZVdlY1UycXJxRjZsZjlEMDhDNGYmI3hBO2tQMlM2a1JLZlpuR0t2RmZOMy9PVzNtaTk1d2VXTk9oMG1FN0xkWEZMbTQrWVVnUkw4aXJZcHA1NzVZMXp6UDUxL012eTJtdTZsY2EmI3hBO2xKTHFkclgxNUdaVVQxbE1uQlBzSU9LblpRTVZmZCtLSGtIL0FEazk1cDFiUVBJRnQraWJ5YXh2TDYvamdhYTNkb3BQUkVVcnVBNjAmI3hBO081VlIxeFVQamlhYWFlVnBabmFXVnpWNUhKWm1KN2tuYzRzbVNlVmZ5MDg5K2FtWDlCNk5jWFVKL3dDUG9yNlZ1UDhBbnRKd2oraXQmI3hBO2NWdDdUNVIvNXhDbmJoUDVzMWdSall0WTZjT1RVOERQS0FBZmxHZm5paTN2L2sveWRvUGxIUkl0RjBPRm9MR05ta283dEl6Tys3TXomI3hBO01UdWZ1eFFsZjVvZmw5YWVlZkswdWtTeUNDN2pZVDZmZEVWRWN5Z2djcWI4V0JLdDkvYkZYeWpjNmIrWlg1VStZVnVTczJsM1ZTa2QmI3hBOzFIKzh0YmhBYThhN3h5S2FWNHR1UEFIQ2w3aitWLzhBemtmcDNtQzd0OUc4elFwcHVxVGtSd1hrWnBheXVlaXR5Sk1UTWVtNUI4UmcmI3hBO1FrMy9BRGxINS92TGI2cDVPc0pURWx4RUxyVldRMExvekZZb1NSMnFoWmgzK0hGSVN6OHB2eUg4cWF2NWFnMXp6VmVzWmI5ZlV0YkcmI3hBO0daWWdrVmZoYVE3c1dicUJ0UVlVV2tINTNmbERvZmsrM3ROWjh1WGpUNmRQTDlYdUxXU1JaWGhrS2xrWldXaDROd1BYb2UrK0tVNnUmI3hBOy9PdDk1by81eHMxSmRSbE0yb2FWZVcxbkxPNXE4a1lsamVKMkozSjR0eHIzNDRxeG44ai9BTW9iSHo3YzM5enFsNUpiNmJwcGpWNGImI3hBO2VnbWtlVU1SOGJCbFZRRTMySitXS3NyL0FEcS9JZnkzNVk4cHY1aTh2UFBHTE9TTmJ5Mm1mMVZhT1Z4R3JxU0F5a093OGExd0todnkmI3hBO2U4ejY3TitVZm5yUmJXUjJuMHUwTnhwL0dwZEV1RWtFd1FqcHg5UGtQYzdZVUY1NStWT24rUnRRODNSV25uT2MyK2xTeE9JcFBVOUcmI3hBO1A2eFVjQkxJS2NVSzh0NmplbmJGSmZTMmcva04rWHVsK1k5TDh6YUlaVkZrV2xpZzlYMTRKQ3lGVWNNMVdxcFBJVWFtQkR4Ly9uSlQmI3hBOzh3TlExVHpYTDVXdDVtVFNOSjRDYUZUUVMzUlhrelA0OEF3VlIyTlQzd3BETWZKbi9PT2ZrUmRCdHB2TTk2OXhxODZMSmNSUlhDeFImI3hBO3dsaFgwd0I4Uksxb3hKNjRvdDVkK2JYNWZ4Zmw3NWxzWjlDMUY1ckc2Qm4wK2ZtcG5obGhaZVNsa29EeDVLUTFCMTlzVXZvdlFQelUmI3hBO3RXL0tDRHp6cW81UERiRVhjVWRGTWx6SEo2SEZhOVBVa0FwNFZ3SVkwZk92NThONWJQbk5kSzBoZEVFWDF3YU94bk43OVU0OHVmS28mI3hBO1d2RDR1dGY4bnRpckR2TkhtZnpoNXk4OGVSZFgwcTMwNXJhNWFlNDh0VzF5WmlGbGhWZnJBdk9MRDRvNW9pRUtVclFZVmZSbWpOcXomI3hBO2FWYU5yQ3dwcXBpWDY2dHJ5OUFTMCtQMCtaTGNhOUs0RlJtS3V4VjJLdmhYODJ2ekY4MStZUE5tdDJkMXF0eEpvOEY5Y3cyZGdybEkmI3hBO0ZoamxaWTZ4cFJXUEVENGpVNHBEQTRvcFpaRmlpUnBKSElWRVVFc1NlZ0FIWEZMMERRUHlLOCs2bGJyZmFqQkY1ZTBvMExYMnJ5QzEmI3hBO1hpZjVZMi9lazA2ZkR2NDVHVXhFV1RUWml3enlTNFlSTWo1QzJjZVU5Qi9LN3lIckZucXlhaGUrYXRmczM1d0MyUVd0a2toQlg5dmwmI3hBO0k1RmRxYkh3ekR5YStBK25mN0hvdEw3S2FySUxuV01lZTUrUS9XOVl0UDhBbklueXE3Y2J2VDcyM1Bpb2lrQStmeG9md3lNZTBJOVEmI3hBO1cvSjdHNmdmVE9CK1kvUVhuMy9PU3ZtUFR2TS9sTHl4cStubVI5RFhVSjRMMlNuQ1JKZlRRcW5FZ2lwakRrYjVsNGN3eUN3ODdydEImI3hBO2swdVR3OGc5Vlc5UDhtZmtOK1Z2bCtLRzZ0dE5HcVhKQ3ZIZmFnUmNNYWlxc3FFTEV2V3V5Vnkxd25vNklpSXFJb1ZGQUNxQlFBRFkmI3hBO0FBWXEzaXJzVmFkMFJlVHNGV29GU2FDcE5BTi9FbkZWRFVkTjAvVXJPV3kxQzJqdTdPWWNaYmVaUTZNUGRXcU1WZkdmNTNlUnRPOG0mI3hBO2VlWDAvUzJJMCs2Z2p2YldFc1dhRlpHZERIeU81bzBaSzEzcFRDa0lMODBiN1VkUzFIUk5Udnl6VDN1aTJNaGticS9CREV6Ky9KNDImI3hBO09LaG1ubG4vQUp4cTFUekQ1ZjAvVzdMWDdRVzJvUUpPaUdLUXNoWWZFalVOT1NOVlQ3akZiVFAvQUtGTDh3LzlYKzAvNUZTLzF4VzAmI3hBO1o1ci9BQ3h1dklINUhlWWJLNnZJNzJlOXZyU2IxSWxaVkNMSkdxaWpkNjF4UWp2K2NTUCtPWjVrL3dDTTlyL3hDVEFwWjMvemtMLzUmI3hBO0ovWC9BUG8wL3dDbzJERlhtUDhBemlRQWRROHpBaW9NTnFDRC9yUzRwTElmekEvNXhpMGpWSjU5UjhxM0s2VmR5VmM2ZEtDMXF6bmYmI3hBOzRHV3J4QStGR0hnQU1VUEd2TG5uTHoxK1YzbXFYVDJsa2pXeW40YWxwRHZ5Z2xVR3BvTjFCWlRWWFg5VzJGS1hmbWU4ZHgrWXV0ejEmI3hBO0tRWGQyYmlOMkcvcFhBRXFOUWY1RGc0cUhwY1AvT0tHdVR3cE5ENWhzNUlwVkR4dXNVaERLd3FDRFhvUml0ci9BUG9VdnpEL0FOWCsmI3hBOzAvNUZTLzF4VzJYK2JmeXgxVFJmK2NmTG55ekJMOWZ2dFBZM3NwaEJVU0tMa3pPRlU3L0RFYTA4UmdRbVRmbkY1Q1g4cC9yUzZwYm0mI3hBOzlPbWZWMTByMUYrdGZXUFI5UDB6Rjl1blA5dW5HbStGV0ErU05GdjlJMW44b29MNUdpbm4vU2wxNlRiRlVuQmVPb1BTcUVOOU9LdnAmI3hBO2ZBcnNWZGlyc1ZmT1dqZjg0cEtKVzFEelRxRDZsZTNEbVdTeDA4aUNBT3g1TjZseE55a1pTVCt6SFhJekpISVcyWXhFbmM4SStmNCsmI3hBO3g2Um9YNVZEUm8vVDBOTlA4dEpUaTB0aGIvVzcxZ2V2Szl1dmkvNUo1UVlaWmRSSDNidWZqejZYSC9CTElmNlI0Ui9wWS84QUZJOVAmI3hBO3loOHBTM1AxclZtdTlidSt2cmFoY1BJZC93REpUMDFwN1V5STBVTHMyWEtQdEZxUkhoeDhPS1BkQ0lIMzJ5WFRQTG1nYVVBTk4wNjImI3hBO3M2ZnRReElqSDVzQlU1ZkREQ1BJQjFlZlc1czM5NU9VdmVTK2FQemM4dS9vVHp6ZnhvbkMydlQ5Y3RxQ2c0ekVsZ1BsSnlHYWJVWSsmI3hBO0daRDZsN1Bheng5SkVuNm8ray9EOWxLSGx1MFBtTHlkNW84bkVjN2k1dHYwbnBLOS9ybGo4ZkJQOHFXT3EvTEw5QmtxUmozdW45c2QmI3hBO0h4WTQ1aC9DYVB1UDdmdmUrL2szcnY2Yy9MRHk1Zmx1Y2dzMHQ1bTdtUzFKZ1luM0pqcm0yZlBHWXN5cXBaaUFvRlNUc0FCaW9Gdm0mI3hBO1h6aitZWG1uenByNTAvU3BKbDArV1gwZFAwK0FsRElLMFZwS1VMRnVwcnN2NDVvODJhV1ErWGMrcTltOWo2ZlE0ZVBJQnhnWEtSNmUmI3hBOzc4YnRYSDVOL21UcHNJdkliVG02Q3BXMW5VeXI4Z0NDZjlqWEdXbHlBWFN3OXBORGxQQ1pmNlliZmozcHg1cTByODA5VS9KV3p0SUkmI3hBO2J2VkxpNnZHbXZZMlBLNmp0WVQrNmpDSDk0OVpVNTkyRzNiTmxvK0xnc3ZEZTBKd2ZtaU1JQWlBT1hJbm4rS2ViYWYrYW41MjZIQismI3hBO2pvdFF2a1dJQkJIZDJ5VHlKVHR5dUlwSDlxRTVsdWtSUGx6OHIvek0vTWZ6RCtrZGJTNmh0NW1YNjdyRityUi9BdTNHRkdDbHpUWlEmI3hBO280anZURlh0WDV1ZmtqQjVrOHI2WmIrWGdsdnFlZ1FDMnNJNURSWnJaVkE5Rm43TjhOVlk3VnJYclVCRHd6UXZNbjV5Zmx1MHVuVzkmI3hBO3ZlV05zejhtczdxMk1zQmMvdFJsbEkzcDFSdDhLVTd0UFBQL0FEa0Q1dTFqVDViT0c4bFMwbmpuamdndC9xMW9XUnEvdnBLSXBVMG8mI3hBO1E3MHhROXAvUDJ5MUxVZnlxdm9MVzBrbnZaSkxWamF3SzB6MUV5RmdBZ3EzSHhwZ1Zpbi9BRGl6bzJyNlpwM21GZFNzYml5YVdhMk0mI3hBO2EzTVR4RmdGa3J4NWhhMHhWbTM1OFdWN2ZmbFJybHJaVzhsMWN5ZlZmVGdoUnBKRzQza0xHaXFDVFFBbkZYbGYvT09tbGViTkV0dk4mI3hBOzl5Tkd1VjFENm5DZE90cm1OcmNUVEw2cFZBMDNCZnRFVjN3cExHN0w4MmZ6MThwdEpZYWpEY1ROeUpFZXAycnV3TEdwNHVPREZmRDQmI3hBO2lQREZVcjAveVQrWmY1cGViWDFTL3M1b2x2SGpONXFrMFJndDQ0bEFRZW1HQURjVVhaVnFmSHh4VjZwK2QzNUUzZXRMYmExNVZpRWwmI3hBOy9hVzhkcmRXQlpWYWFLQlFrVG9UUmVhb09KQk80QXB2MUNIbDJoZm1SK2Mza20xR2pJbDFIYlFmREZaMzlveitrQjJRdW9jTDdWcDQmI3hBO1lVc3UvTDN6Uitldm1Mei9BS1ZxMTNiM2x6cGtEbU83amVMNnBaQzNsK0dRaW9qUm5VZkV2VnFnWW9mUjJzYWxIcGVrWDJweUlaSTcmI3hBO0czbHVYaldnWmxoUXVRSzl6eHlFNWNNU2U1dTAyRTVja2NZMk01QWZNMDhjL0xuVmZLdm5EejVjc2ZLV2xXVWNOckpkeHlDM2plY3omI3hBO0NXSkE3UHhWYTBjblphMTc1aDZmVlN5VG83Q25wTzJmWitHaTB3bnhHVXpNRHVIS1I1ZkR2ZXlYR2thVmMzdHRmM05sQlBmV2ZMNnAmI3hBO2RTUkk4c1BNVWIwM1lGazVEcnhPWnp5cUt4VjJLdXhWMkt1eFYyS3V4VjJLdkp2K2NoUEx2MXZRTFRXNGxyTHAwbnBUa2RmUm1vQVQmI3hBOy9xeUFmZm12MStQWVNldzlqOVp3WnBZanltTEh2SDdQdWVKZVY5YmwwUHpEcCtyUjFyWnpMSTZqcXlWcEl2OEFza0pHYTJFakVnam8mI3hBOzk1cjlLTlJnbmpQOFErM3A5cjZDL0tLMWkwZWZ6UjVhaE5iT3kxSDYvcFpIMmZxT3B4aWVIajdLNnlMOUdkQ0NDTEQ0cktKaVNEekQmI3hBO1A3dUFYRnJOYms4Uk5HMGZJZHVRSXIrT0NjYkJDY2MrR1FsM0Y4bDZKcUYvNUs4NlEzVnhiOHJ2U3Azam50MjI1QWhvM0FKSGRXUEUmI3hBOzA5ODBNSkdFcjZoOWkxV0dHdTBwakUrbkpFVWZ0SDdYMDU1Vzg3ZVhQTTlxSnRLdTFlUUNzdHE1Q3p4LzZ5SGY2UnQ3NXVzV2VNK1QmI3hBOzVWcit5OCtsbFdTTzNmMFB4VDNMblh2TXZ6US9OOVBMVXgwalIwUzQxamlEUEpKdkhiaGhVQWdmYWNnMXAwSGZ3ekIxT3I0VHd4NXYmI3hBO1ZkaGV6cDFROFhMY2NYVHZsK3g1dkQ1cC9PN1VZRHFscytwUzJwK05aWWJmOTBSMStGVlRpdytXWVBpWlR2Y25wNWFEc3JHZkRsNFkmI3hBO2w1eTMrOWszNWZmbmpxTDZsRHBQbXJneVNzSWsxRUtJM1NRbWdFeWlpOGE3VkFGTytYNE5hUWFueTczVjlyK3kwQkE1TlAwMzRlZGomI3hBO3lldjYvcnVuYURwRnhxdW95ZW5hMnk4bXA5cGlkbFJSM1pqc00yT1RJSVJzdkY2VFNUMUdRWTREMVNlQjZ4K2MvbjdYOVJOcjVmUnImI3hBO09KeVJCYTJzUW51R1h0eVlxNXIvQUtnR2FxZXJ5U05EYjNQb2VtOW1kSHA0Y1dZOFI2bVJxUDZQdFVaUE9mNTArWDVZNTlRYTlTSnkmI3hBO0FGdkxmbEU1Sm9GNUZPcDlpRGtQR3l3M0pJOS83V3lQWm5aV29CRU9DLzZNdC92ZXgrYTlkOHhhZitXdHpyRGNMRFhJN2FLVjFqQWQmI3hBO1lwWGRReWdTQmhzR3B2WE5qbHlUR0xpNVMyKzk0blFhWEJrMXd4ZlhpTWlOOXJHL2N4UDhsUFBmbXJ6THFlcFFhMWZmVzRyZUJIaFgmI3hBOzBvWTZNWG9UV0pFSjI4Y3AwbWVjNVZJOUhjZTAvWlduMHVPQnhSNFNTYjNKKzhsNkY1MDFDODAzeW5xOS9aU2VsZDJ0ckxMQkpSVzQmI3hBO3Vxa2cwWUZUOUl6TXp5TVlFam04MzJaaGpsMU9PRXhjWlNBTHpqOGx2UDNtenpKcmwvYTYxZmZXNEliWDFZazlLR09qK29xMXJHaUgmI3hBO29jd3RKbm5PZEU3VitwNmIybTdKMDJseFJsaWp3a3lybkk5UE1sUi9PZjhBTUx6ZjVjODBXdGpvMS84QVZiV1N4am5lUDBZSkt5Tk4mI3hBO0twYXNpT2VpRHZoMWVlY0pWRTlHejJhN0gwMnAwOHA1WThVaE1qbkliVkh1STcyTGo4eVB6Yjh6S2lhTXR3VXRZa1c0YXhnREY1QW8mI3hBO0RTU09FMloyQlBGYURzQm1NZFJsbnl2NE8yL2tYczNTNzVlSDFFMXhTNmR3RjlPODJ5M1FOVC9OVWZscnJHclN6M1Urc2V0RkhwTnMmI3hBO2JaWkxoUWs2Sk8zcG1ObWFvTENqQTA0azVrWXBaZkRKcytYNlhUYXZCMmQrZXg0d0lqSFI0enhWSDZTWTczN3ZmYkRMTDgyZnpQWFgmI3hBO0xhd3Y5U2VKL3JNY056YnlXdHRHNHE0VmxZR0lNcHpIT3J5OS93QmdkNWw5bit6emlNNFF2MGtnaVVqMC9yUFJ2enkxTHpoYWFYYXcmI3hBOzZFazdhZGRRWGlhMFliY1RJSWVFWS9lT1VmMGh4Wjk2ajhNemRaS1lIcDViMjh6N0xZTk5QSkk1cTQ0bUhCY3EzczhoWXZldTk0bDUmI3hBO00xTHpocCtxU3plVkVuZlVXZ1pKUmJXNHVYOUV1aGFxRkpLRGtGM3BtdHd5bUQ2T2IzZmFlRFRaTVlHcHJndnJMaDNvOWJIUzNzM2smI3hBO1RWUHpXMVhRL01hNnU5MWFhcEZEQ2RGbHViT08zL2UwbExoVmFKRmZrVlFHb05LNXNNVXNzb3l1NzZiUEQ5cTRPenNPWEQ0WERMR1MmI3hBO2VPcG1XM3AvcGJkZmVrUDVXL216NW8xRHpkRnBYbUs5RnhiM2l0RkNHaGlpS1RqNGwzalJEOFhFclE5emxPbjFjak1DUjJMc2UzdlomI3hBOy9UNDlNY21DTlNqdWR5Ykh4Sjk3M0dTU09LTnBKR0NSb0N6c2RnQUJVazV0Q2FGdkF4aVNhSE40Qm92NW4vbUQ1bDg4dzZicG1wRzMmI3hBOzArOHV6NlVJdDdkakhhaGl4K0pvMllsWWgzUFhOVEhVNUp5b0htZko5RjFQWVdpMHVrT1RKQzV4ano0cGJ5K2ZlOVV2dnpFc2JUVjMmI3hBO3NXdFhhR1BueW5CUElyQ1pCTTZJRklLeC9WNWVYSmdmaFBFSGF1VlBXQVNxdngxKzU1TEYyTk9lTGo0aFpyYjMxUUp2bWVLUFFqZmMmI3hBO2pkbHVacnBuWXE3RlhZcWdOZjBpRFdkRnZkTG4vdTd5RjRpZkFzUGhiL1ltaHl2TERqaVIzdVJwTlFjT1dPUWM0bTN4MWVXczluZHomI3hBOzJrNjhKN2VSb3BWOEhSaXJEN3htZ2ZiTWVRVGlKRGtSZnplOS9rM3FQNlF0OU92eVFiaUcxazBTK3A5cHZxN2ZXYkpqMzRyRTh5MTgmI3hBO2MyK2l5Y1VLN255ejJuMGZnNnNrZlRrOVg2L3RlczVtUFBNUjg3L2xqNWM4MktacnBEYTZrRkNwZncwNTBIUU9wMmNmUGZ3T1kyZlMmI3hBO3h5YjhpN25zdnR6UG85bytxSDgwL283bmd2bTd5RjVxOGlYMFYzNnJHMzUwdE5VdGl5ZkgxNG1ueEkzdDl4T2FyTGhsak8vemZRK3omI3hBO3UxdFAyaEF4cjFkWXkvRzRleS9rNytZVjE1cDB1ZTAxTmcycTZmeDlTVUFEMW9ucnhjZ0FEa0NLTlQyelk2UFVHWW84dzhSN1NkangmI3hBOzBtUVN4LzNjL3NQZCtwODl5YXdsMTVqYldOU2hONGsxMGJxNnRpL0QxQVg1dEh5bzFBZW5UTlZkbXkrangweGhnOExHZUdvOElQZHQmI3hBO1Z2V1Uvd0Nja2xSUWllV3dxS0FGVVhsQUFPZ0ErcjVzQjJoWDhQMi9zZVBQc1ZlNXpmN0Qvanp6RHpyNWt0Zk1ubUdmV0lMQWFjYmsmI3hBO0tab0JKNm9NaWloZmx3aisxdFhiTUhMTVNrU0JWdlY5bWFLV213akVaY2ZEeU5WdDNjeTlCL09UV3IyYnlYNU10Sm1ZUGVXaVhsMkQmI3hBO1VWa1dDSUNvUHZJL1hNblZTOUVCNVBPZXplbWdOVnFKRCtHUmlQZHhIOVFaWitRR2hXZHI1U2ZWZ2ltOTFDYVFOTlQ0aEZFZUN4MTgmI3hBO09TbHN5TkJBQ0psMWRQN1hhdVU5VDRmOE1BTnZNNzI5T2tqamxRcElnZERTcXNBUnNhalk1bkVBODNsQklnMkdKL20zL3dDUzYxdi8mI3hBO0FJeEovd0FuVXpHMXY5MGZoOTRkeDdQZjQ3ajkvd0Nndk0vK2NjUCtPMXJIL01OSC93QW5NdzlCOVo5ejFYdHAvZFkvNngrNTZ4K1kmI3hBO3YvS0NhOS96QXpmOFFPWitwL3V5OGYyTi9qbUwrdVB2ZVEvODQ1LzhwTnFuL01GL3pOVE5mb1A3ejRmcEQyZnRuL2NRL3Ivb0tILzUmI3hBO3lKLzVUV3kvN1pzWC9KK2ZEci9ySHUvVzJleHYrS3kvNFlmOXpGN0QrV2VsV21tK1JORmp0a0MvV0xXSzZtWUNoYVM0UVNNVDQvYXAmI3hBOzhobWZwb0NPTWVlN3hYYm1lV1hWNURMcEl4SHVpYVpQbDdxbnl2NXEvd0RKcjN2L0FHMXYrWnd6bjh2MXk5NSs5OWMwSC9HZEgvaFgmI3hBOzZIMGI1NS81UXJ6Qi93QnMyOC81TVBtOHpmUkwzRjh5N0sveHJGL3d5SCs2RHhML0FKeDIvd0NVMXZmKzJiTC9BTW40TTF1ZytzKzcmI3hBOzlUM2Z0bC9pc2Y4QWhnLzNNbjBSbTJmTlh6RithK2h6ZVYvekFrdTdPc1VWeTY2alpPT2l1VzVNQi9xeXFUVHdwbWoxT1BnbWE5NzYmI3hBO3I3UDZvYXJSaU10ekVjRXZkL1k5Uy9NRHo1QkorVTZhcGFzRW0xMkpMYUpBZDFhVUVUanY5aFZkZm5tYm56M2hCL25mZy9xZVQ3STcmI3hBO0pJN1I4T1hMRWIrWDAvUFlzWi81eDI4dDg1dFI4eFRMdEdCWjJoUDh6VWVVajVEaVBwT1ZhREhaTXU3OGZqM3UxOXN0YlFoZ0hYMUgmI3hBOzdoK2w2VmQrUWRNdWRRbHVtdUpsaG5ZdE5iTHdwUitabGpWeXZOWTVUTTVkYTdsajB6SmxvNG1WMmZ4K2pkNWJIMnRrakFSb1dPUjMmI3hBOzhxTmNyalFvK1RKOHkzVk94VjJLdXhWMkt2UHZNMzVKZVZkZTFTNTFTU2U3dGJ1NmJuS0lXajlNdFFBbml5TWQ2VisxbUhrMFVaRW0mI3hBO3p1OUpvZmFmVWFmR01ZRVpSajMzZjNvcnlCK1dhK1RMMjhsdGRUa3VyUzhqVlpMYVNNS1E2R3FQekRkZ1dGT1BmMnc2ZlRIR1NidHAmI3hBOzdYN2MvT3dpSlFFWlJQTUhwM2N2ZDFadm1XNkY1M2Fmbmo1TmZWcjNUNzUzczB0cG1pZ3ZDclNRektwSTVmQUN5MXAzSDA1Z3gxMEMmI3hBO1NEeWVseWV5MnFHT000VkxpRmtjaVBuelNuODF2eks4azMvbEM5MHF5dTAxRzl1d2doU0pXS29WZFc1czVBQXBUYW0rVjZyVXdsQ2gmI3hBO3VYTTdBN0UxV1BVeHlUandSanp2cnR5cEl2OEFuSEhUcm82bnEycGNDTFZJRXQrWjZHUm5EMEh5VmQvbU1yMEVUeGsrVHNQYlBOSHcmI3hBOzhjUDRydjRjbUJYdHBMNU04OXRGZDJpeng2ZGRjdnE4b3FrMXZ5cXYyZ2FoNHpzY3hESGdsUjNvdlE0c2cxdWp1TXFNNDh4MGwrd3YmI3hBO2RkTjgyL2s1ZldhWElPbDIvTVZhRzRoaGlrVTkxS3N2YjIyelpSeWFjamtQazhCbTdQN1R4eU1mM2g4d1NSOTZ0NWYxejhzUE1HdFgmI3hBO09rNlZaV2R4TmJSQ1gxZnFzYXh1SzBZUmxsQmJqVVYyNzdaTEhMRE9YQ0lqNU5lczB1djArSVpNa3BnU05mVWJIdjM2c2QvNXlEOHUmI3hBO3kzSGwvVGRVdFl2M2VsTzBVeW90QWtNd1VLZHYyVmFNRDZjcjErUFlFY2c3UDJRMWdqbW5qa2Q4Z3NlOFgrdjdFbC9KZjh6ZEUwalMmI3hBOzMwRFc1L3FpTEswdGxkT0NZNlBRdEd4RmVQeFZJSjIzeXZTYWtRSERKenZhYnNQTG15ZU5pSEZ0VWgxMjZ2UTlhL04zeUZwZG9aLzAmI3hBO25IZlNmc1c5bVJMSXgrZzhWLzJSR1pjOVpqQTUyODFwdlozV1paVndHUG5MWWZqM0tuNWpsZFUvTFRWcHJGdldpbXRCY1JPdTRhTlMmI3hBO3N2SWY3QVZ4MVhxeEVqeUxIc1VlRnI0Q2V4RTYrUEw3M2pmNUgrYk5GMER6QmVMcTA0dFliMkFSeDNEL0FHRmRHNVVjOXFqdm12MG0mI3hBO1VRblo3bnQvYWpzL0xxTU1mREhFWXk1ZFhvSDVuZm1wNVMvd3hmYVhwMTJtcFh1b1F0QXEyNTVJaXVLRjNmN093N0RmTXJVNnFCaVkmI3hBO2pjbDV6c1BzSFUvbUk1Sng0SXdONzlhNlV4SC9BSnh6L3dDVW0xVC9BSmd2K1pxWlJvUDd6NGZwRHVmYlArNGgvWC9RVVA4QTg1RS8mI3hBOzhwclpmOXMyTC9rL1BoMS8xajNmcmJQWTMvRlpmOE1QKzVpOXQ4amY4b1Y1Zi83WnRuL3lZVE5saCtpUHVEd25hdjhBaldYL0FJWlAmI3hBOy9kRk84c2NCOHIrYXYvSnIzdjhBMjF2K1p3em44djF5OTUrOTljMEgvR2RIL2hYNkgwaDV6aGxtOG42N0RFcGVXWFQ3cEkwSFVzMEQmI3hBO2dENzgzZWI2SmU0dm1IWnNoSFU0aWVReVIvM1FmUFA1TCtaZE0wRHppWjlTbVczdGJxMWt0ak85ZUtNekpJcFlqb0NZNlZ6VTZYS0kmI3hBO1RzOG4wbjJtMFdUVWFXc1k0cFJrSlY4eCtsOUNhVjUwOHI2dnFSMDNTOVJpdmJ0WVd1SFdFbDFFYXNxRWx3T05hdU5xMXpiUXp3a2EmI3hBO0JmTjlSMlpxTU9QanlRTVkzVy9mdjArREQvejQ4dGZwVHlrTlRoV3QxcEQrcVQzTUVsRmxIMGZDMzBaamE3SGNlTHVkMTdKNjd3dFQmI3hBOzRaK25JSytJNWZwSHhmUHMycjZqYzZYWjZRN2w3U3psbGt0b2hXb2FmanlIdnVtM3pPYW95MnA5SGpwNFJ5U3lnZXFRQVB3djliNnQmI3hBOzhoK1hSNWU4cDZkcFpVQ2VLSVBkVTd6U2ZISnYzb3pVSHRtOTArUGdnQStROXJhejh6cVo1T2hPM3VHd1QvTG5YT3hWMkt1eFYyS3UmI3hBO3hWMkt1eFZxUkZrUmthdkZ3VmFoSU5EdDFHK0FpMGcwYmVSNjEvempyb3R4SzBta2FuTllxYWtRVElMaFI3SzNLTmdQbnl6WHk3UEgmI3hBO1F2WmFiMnl5eEZaWUNmbUR3L3IvQUVLR2wvOEFPT0dueHlxK3A2ekpjUmpkb2JlRVFrKzNObWwvNGpnajJmM2xzeisya3lLeDR3RDMmI3hBO2szOWxENzNxK2phTHBtaTZkRHAybVc2MjFuQUtKR3RlL1VrbXBZbnVUbWZER0lpZzhmcWRUa3p6TThoNHBGSi9PZjVlK1hmTnNDalUmI3hBO1ltanVvaFNDOWhJV1ZSL0xVZ2hscjJJeXJOcDQ1T2ZOenV6ZTJNK2pQb1BwUE9KNVBPWlArY2JJeTVNZm1FckgreXJXZ1lqNWtUTFgmI3hBOzdzeGY1UDhBNlgyZnRlbGo3YW10OFgrei93Q09zdThrZms5b1BsYlVFMU5ibWU4MUNNTXNjamtSeHFISEUwUk91eC9hSnk3RG94QTImI3hBO1RaZE4ycDdSNXRYRHd5SXhnZmlmbitwblUwTU04THd6SXNzTXFsSkkzQVpXVmhRZ2c5UWN5eUFSUmRCR1JpUVFhSWVWNjkvemoxNWUmI3hBO3ZibDU5S3ZwZExEbXBnS0M0alhmOWdGbzJBK2JITUNlZ0JPeHA2M1NlMkdhRWF5UkdUenZoUDNFZllnckgvbkcvVDQ1dzE5cmt0eEMmI3hBO0NLeHd3TEN4SGNjbWViOVdBZG4vQU5MN0hJeSsya3lQUmpBUG5LLzBCNnZwZWphZnBta3dhVGF4L3dDZ3dSK2lrVWhNbFU3aGk5YTEmI3hBO3JtZERHSXg0UnllUHo2bWVYSWNrajZ5YjdubU91LzhBT08raVhsNDgrbGFsSnBzVWpGamJORUxoRnIyUTg0MkErWk9ZVXV6d1RzYUQmI3hBOzFXbDlzY3NJZ1pJQ1pIVytINTdIOUNwcHYvT1BIbHlDMm1XK3Y1N3k2a2pLUlNoUkVrYkVVNWlNRmlTTzFXcGlPengxS00zdGpubEkmI3hBO2NFWXhpRDc3OHIvWW4va0w4cTlQOG5hamNYdHRmUzNUWE1Qb3NrcXFvQTVCcWpqL0FLdVc0Tkw0Y3J1OW5XOXJkdlQxc0JDVVJIaE4mI3hBOzdMUFBuNVRhZDV3MWVIVTdtL210WkliZGJZUnhxckFoWGQ2L0YveGt4ejZYeEpYZE11eWZhQ2VpeEhIR0lsY3IzOXdINkdYNk5wcWEmI3hBO1hwRmpwa2JtU094dDRyWkpHMkxDRkFnSnA0OGN5WVI0WWdkenBkVG1PWExMSWR1T1JQek5vekpOTHpiVXZ5UjBxLzhBTTAydlBxVTYmI3hBO1RUWFgxc3doRUtodWZQalhyVE5mTFEzSW0rWmVvd2UxR1RIZ0dFUWpRanczWmVrOWRqbXdlWGVUZVlmK2NlOUZ2OVFsdXRMMUY5TWomI3hBO21ZdTFzWVJQR3BQVVIvSEVWSHNTYzE4OUFDZGpRZXcwZnRobHh3RWNrQk1qcmRINDdGT2Z5Ny9LU0R5YnFrK3BmcE5yNmVhM2EyNCsmI3hBO2lJVkNzNk9UOXVRMS9kanZsdW4wdmh5dTdjTHRuMmhPdHhqSHdjQUVyNTMwSTdoM3N0OHkzbWoyMmlYdjZYdUk3ZXhsZ2tqbWFRZ1YmI3hBO1JsS3NBRDlvMFBRWmRtbEVSUEVhMmRQb3NlV1dXUGhBbVlrS2ZOdjVRZVcvMDU1NHNsa1RsYTJIK20zSGgrNkk0QS9PUXI5R2FmVFkmI3hBOytLWUQ2ZjdSNjN3TkpLdnFuNlI4ZWYyVytwYzNyNUs3RlhZcTdGWFlxN0ZYWXE3RlhZcTdGWFlxN0ZYWXE3RlhZcTdGWFlxN0ZYWXEmI3hBOzdGWFlxN0ZYWXE3RlhZcTdGWFlxN0ZYWXE3RlhnbXMva2Q1djFmelpxdDc2dHJiV041ZTNGeEhLN2xtOU9XVm5YNEZVNzBQUWtacVomI3hBOzZTY3BrOUxmUTlON1U2YkRwb1FxVXB4aEVWWFVBRG05VDhoZmwvcFBrN1QzZ3RXTnhlWEZEZDNyaWpPVjZBS0s4VkZUUVpuWU5PTVkmI3hBOzgza3UxdTE4bXRuY3RvamxIdS9heWpNaDFMc1ZmLy9aPC94bXBHSW1nOmltYWdlPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6QWx0PgogICAgICAgICA8L3htcDpUaHVtYm5haWxzPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iCiAgICAgICAgICAgIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIgogICAgICAgICAgICB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIj4KICAgICAgICAgPHhtcE1NOkRlcml2ZWRGcm9tIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgPHN0UmVmOmluc3RhbmNlSUQ+eG1wLmlpZDo0RDFEMTFCN0NBMkJFMzExQjI4QUY0ODQzMDE2MTY1OTwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SUQ+eG1wLmRpZDo0RDFEMTFCN0NBMkJFMzExQjI4QUY0ODQzMDE2MTY1OTwvc3RSZWY6ZG9jdW1lbnRJRD4KICAgICAgICAgICAgPHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD51dWlkOkQ1MkU0NzFBRThFMERCMTE4OUQ0RUM1M0VCQ0ZGRUQ3PC9zdFJlZjpvcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgICAgIDxzdFJlZjpyZW5kaXRpb25DbGFzcz5wcm9vZjpwZGY8L3N0UmVmOnJlbmRpdGlvbkNsYXNzPgogICAgICAgICA8L3htcE1NOkRlcml2ZWRGcm9tPgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAuaWlkOjRGMUQxMUI3Q0EyQkUzMTFCMjhBRjQ4NDMwMTYxNjU5PC94bXBNTTpJbnN0YW5jZUlEPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD54bXAuZGlkOjRGMUQxMUI3Q0EyQkUzMTFCMjhBRjQ4NDMwMTYxNjU5PC94bXBNTTpEb2N1bWVudElEPgogICAgICAgICA8eG1wTU06SGlzdG9yeT4KICAgICAgICAgICAgPHJkZjpTZXE+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmNvbnZlcnRlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6cGFyYW1ldGVycz5mcm9tIGFwcGxpY2F0aW9uL3Bvc3RzY3JpcHQgdG8gYXBwbGljYXRpb24vdm5kLmFkb2JlLmlsbHVzdHJhdG9yPC9zdEV2dDpwYXJhbWV0ZXJzPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+c2F2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDpCMzI2QzE1RjcxMUVFMzExQTNBNUI2MDA1RjMzNDREMzwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxMy0wOS0xNlQxMTo0MzoyMysxMDowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgSWxsdXN0cmF0b3IgQ1M0PC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+Y29udmVydGVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpwYXJhbWV0ZXJzPmZyb20gYXBwbGljYXRpb24vcG9zdHNjcmlwdCB0byBhcHBsaWNhdGlvbi92bmQuYWRvYmUuaWxsdXN0cmF0b3I8L3N0RXZ0OnBhcmFtZXRlcnM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjM2NDMwOEE4QkMyMEUzMTE5NjgzOUExODdDMjM1OUVGPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDEzLTA5LTE5VDA5OjQ3OjE5KzEwOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBJbGx1c3RyYXRvciBDUzQ8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jb252ZXJ0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+ZnJvbSBhcHBsaWNhdGlvbi9wb3N0c2NyaXB0IHRvIGFwcGxpY2F0aW9uL3ZuZC5hZG9iZS5pbGx1c3RyYXRvcjwvc3RFdnQ6cGFyYW1ldGVycz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6NEMxRDExQjdDQTJCRTMxMUIyOEFGNDg0MzAxNjE2NTk8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTMtMTAtMDNUMTE6MjU6NDArMTA6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIElsbHVzdHJhdG9yIENTNDwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6NEQxRDExQjdDQTJCRTMxMUIyOEFGNDg0MzAxNjE2NTk8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTMtMTAtMDNUMTE6MjU6NDgrMTA6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIElsbHVzdHJhdG9yIENTNDwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6NEYxRDExQjdDQTJCRTMxMUIyOEFGNDg0MzAxNjE2NTk8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTMtMTAtMDNUMTE6MjY6MjUrMTA6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIElsbHVzdHJhdG9yIENTNDwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8eG1wTU06T3JpZ2luYWxEb2N1bWVudElEPnV1aWQ6RDUyRTQ3MUFFOEUwREIxMTg5RDRFQzUzRUJDRkZFRDc8L3htcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOlJlbmRpdGlvbkNsYXNzPnByb29mOnBkZjwveG1wTU06UmVuZGl0aW9uQ2xhc3M+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4bXBUUGc9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC90L3BnLyIKICAgICAgICAgICAgeG1sbnM6c3REaW09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9EaW1lbnNpb25zIyIKICAgICAgICAgICAgeG1sbnM6eG1wRz0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL2cvIj4KICAgICAgICAgPHhtcFRQZzpNYXhQYWdlU2l6ZSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgIDxzdERpbTp3PjI5Ni45OTk5NTk8L3N0RGltOnc+CiAgICAgICAgICAgIDxzdERpbTpoPjIwOS45OTk5Mjk8L3N0RGltOmg+CiAgICAgICAgICAgIDxzdERpbTp1bml0Pk1pbGxpbWV0ZXJzPC9zdERpbTp1bml0PgogICAgICAgICA8L3htcFRQZzpNYXhQYWdlU2l6ZT4KICAgICAgICAgPHhtcFRQZzpOUGFnZXM+MTwveG1wVFBnOk5QYWdlcz4KICAgICAgICAgPHhtcFRQZzpIYXNWaXNpYmxlVHJhbnNwYXJlbmN5PkZhbHNlPC94bXBUUGc6SGFzVmlzaWJsZVRyYW5zcGFyZW5jeT4KICAgICAgICAgPHhtcFRQZzpIYXNWaXNpYmxlT3ZlcnByaW50PkZhbHNlPC94bXBUUGc6SGFzVmlzaWJsZU92ZXJwcmludD4KICAgICAgICAgPHhtcFRQZzpQbGF0ZU5hbWVzPgogICAgICAgICAgICA8cmRmOlNlcT4KICAgICAgICAgICAgICAgPHJkZjpsaT5NYWdlbnRhPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGk+WWVsbG93PC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGk+QmxhY2s8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6U2VxPgogICAgICAgICA8L3htcFRQZzpQbGF0ZU5hbWVzPgogICAgICAgICA8eG1wVFBnOlN3YXRjaEdyb3Vwcz4KICAgICAgICAgICAgPHJkZjpTZXE+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8eG1wRzpncm91cE5hbWU+RGVmYXVsdCBTd2F0Y2ggR3JvdXA8L3htcEc6Z3JvdXBOYW1lPgogICAgICAgICAgICAgICAgICA8eG1wRzpncm91cFR5cGU+MDwveG1wRzpncm91cFR5cGU+CiAgICAgICAgICAgICAgICAgIDx4bXBHOkNvbG9yYW50cz4KICAgICAgICAgICAgICAgICAgICAgPHJkZjpTZXE+CiAgICAgICAgICAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8eG1wRzpzd2F0Y2hOYW1lPldoaXRlPC94bXBHOnN3YXRjaE5hbWU+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDx4bXBHOm1vZGU+Q01ZSzwveG1wRzptb2RlPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8eG1wRzp0eXBlPlBST0NFU1M8L3htcEc6dHlwZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHhtcEc6Y3lhbj4wLjAwMDAwMDwveG1wRzpjeWFuPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8eG1wRzptYWdlbnRhPjAuMDAwMDAwPC94bXBHOm1hZ2VudGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDx4bXBHOnllbGxvdz4wLjAwMDAwMDwveG1wRzp5ZWxsb3c+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDx4bXBHOmJsYWNrPjAuMDAwMDAwPC94bXBHOmJsYWNrPgogICAgICAgICAgICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDx4bXBHOnN3YXRjaE5hbWU+QmxhY2s8L3htcEc6c3dhdGNoTmFtZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHhtcEc6bW9kZT5DTVlLPC94bXBHOm1vZGU+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDx4bXBHOnR5cGU+UFJPQ0VTUzwveG1wRzp0eXBlPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8eG1wRzpjeWFuPjAuMDAwMDAwPC94bXBHOmN5YW4+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDx4bXBHOm1hZ2VudGE+MC4wMDAwMDA8L3htcEc6bWFnZW50YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHhtcEc6eWVsbG93PjAuMDAwMDAwPC94bXBHOnllbGxvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHhtcEc6YmxhY2s+MTAwLjAwMDAwMDwveG1wRzpibGFjaz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8eG1wRzpzd2F0Y2hOYW1lPlNtb2tlPC94bXBHOnN3YXRjaE5hbWU+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDx4bXBHOm1vZGU+Q01ZSzwveG1wRzptb2RlPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8eG1wRzp0eXBlPlBST0NFU1M8L3htcEc6dHlwZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHhtcEc6Y3lhbj4wLjAwMDAwMDwveG1wRzpjeWFuPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8eG1wRzptYWdlbnRhPjAuMDAwMDAwPC94bXBHOm1hZ2VudGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDx4bXBHOnllbGxvdz4wLjAwMDAwMDwveG1wRzp5ZWxsb3c+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDx4bXBHOmJsYWNrPjMwLjAwMDAwMTwveG1wRzpibGFjaz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8eG1wRzpzd2F0Y2hOYW1lPlJlZDwveG1wRzpzd2F0Y2hOYW1lPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8eG1wRzptb2RlPkNNWUs8L3htcEc6bW9kZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHhtcEc6dHlwZT5QUk9DRVNTPC94bXBHOnR5cGU+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDx4bXBHOmN5YW4+MC4wMDAwMDA8L3htcEc6Y3lhbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHhtcEc6bWFnZW50YT4xMDAuMDAwMDAwPC94bXBHOm1hZ2VudGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDx4bXBHOnllbGxvdz4xMDAuMDAwMDAwPC94bXBHOnllbGxvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHhtcEc6YmxhY2s+MC4wMDAwMDA8L3htcEc6YmxhY2s+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICAgICAgICA8L3JkZjpTZXE+CiAgICAgICAgICAgICAgICAgIDwveG1wRzpDb2xvcmFudHM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICA8L3JkZjpTZXE+CiAgICAgICAgIDwveG1wVFBnOlN3YXRjaEdyb3Vwcz4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/Pv/uAA5BZG9iZQBkwAAAAAH/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQECAgICAgICAgICAgMDAwMDAwMDAwMBAQEBAQEBAgEBAgICAQICAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA//AABEIANkCTgMBEQACEQEDEQH/xAGiAAAABgIDAQAAAAAAAAAAAAAHCAYFBAkDCgIBAAsBAAAGAwEBAQAAAAAAAAAAAAYFBAMHAggBCQAKCxAAAgEDBAEDAwIDAwMCBgl1AQIDBBEFEgYhBxMiAAgxFEEyIxUJUUIWYSQzF1JxgRhikSVDobHwJjRyChnB0TUn4VM2gvGSokRUc0VGN0djKFVWVxqywtLi8mSDdJOEZaOzw9PjKThm83UqOTpISUpYWVpnaGlqdnd4eXqFhoeIiYqUlZaXmJmapKWmp6ipqrS1tre4ubrExcbHyMnK1NXW19jZ2uTl5ufo6er09fb3+Pn6EQACAQMCBAQDBQQEBAYGBW0BAgMRBCESBTEGACITQVEHMmEUcQhCgSORFVKhYhYzCbEkwdFDcvAX4YI0JZJTGGNE8aKyJjUZVDZFZCcKc4OTRnTC0uLyVWV1VjeEhaOzw9Pj8ykalKS0xNTk9JWltcXV5fUoR1dmOHaGlqa2xtbm9md3h5ent8fX5/dIWGh4iJiouMjY6Pg5SVlpeYmZqbnJ2en5KjpKWmp6ipqqusra6vr/2gAMAwEAAhEDEQA/AN/j37r3Xvfuvde9+691737r3Xvfuvde9+691WV/Nw2L2Vuf4W7+3p09vPfOxuxOkqzH9vYzKbA3RndpZeswG2Uqqfe9DVZLb9ZRVsmOo9n5Ksyfi1Waqx0J4tcAT3GtL6flea622WWG9tSJgY3ZCVWocEqQaBCzU9VHWYn3FuZeUNn+8LtfL3Pu37buXKnMUb7XJHe20N1Es1wVazdY50dBI11HFb6qVEc8gzWnWoPtD+Z78/8AY/h/gvyr7XrfBo0f3vy9J2Fq8fi0+b+/1BuX7i/hGryatV2vfU18brbn3nG0p4W4XBp/GRJ/1cDV/PrvFv33Nvuu8yav3hyTsceqtfpYmseNeH0T2+niaaaUxT4RQz+0f57f8wjbXi/jO8euOwPHbX/e7rDb9F57fXy/3C/uRbV+dGj/AAt7Prb3b5yg/tZYJv8ATxKP+rejqG9+/u0fusbvq/d9hu+1V4fS7jO9Ps+t+s/nXqxL4e/z6uze3O+OqOpO8+reqdvbe7H3bjtmVe9dlVO6sIcJlNwCTG7eqXxm5M9uanahn3HPSQ1DSVSCGnkeTUSliNOWvd6/3Ld7fbt2t7dIZ5AhdC66S2FNGZhTVQHOASesUPfv+7I5O5E9s98569tt63u73XaLF7tbS7W2m8aOCkk6+Jbw27axbrK6BYzqdVWndUbPnueuuNnXvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3RetwdgY6fv9fjrvamx1bgO2+gtzb12XiqmkaePPwddbuxuzu9MbmJXT7ZqZcV3JsoUtMWLzxzVrBdMTn3SSOOaNoZQGidSCDwIIoQfkR0rsL682y+h3Lb5Hhv7eVJYpENGSSNg6OpGQysAwPkQD18+T5TdH5T42/InuHo7KioZ+ut8ZjC4yqqk8U+U2xLIuT2fnHju3j/j21K+irQLmyzgXP194QcwbTJse9XO0yVrBKVBPmvFG/2yFW/Pr65PZX3Hs/d32o2D3IstIXdttilkVTVY7gDw7qEHz8G5SWKvqnAdAF7J+pQ6k0dZV46spchQVM1HXUNTBWUVXTSNDUUtXTSrPT1NPMhV4poJkDIwIKsAR7srMjB0JDg1BHEEcD0zcW8F3A9rcosltKhR1YAqysCGVgcEEEgg4IPX0evhr33S/J34v9Ld4RSxSZDe+ycfLueOFY44qTe+Gabb2+aKKKJY0jp6Td2JrUh9CaoQjBVBA95t8s7uu/bDa7qPjliGr5SL2yD8nDU+XXyT/eA9sZvZv3l5h9uJFYWu27i4tyaktZygT2bkmpLNayxFsmjlhUkV6M17Peoe697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuqe/wCbr2TL8XqD4R/OcVEeO2z8ZPmf1ptvvHLVLMtBRfGn5TY/NfGztGoyKRFJZ6fAbo39tfcEKFxGa7B07MrFVt7r3VVv/CiL48rt/s3qD5M4Wh8dB2Hg6jrPe08Mb+Jd2bPD5Pa9fWStdDW53a1dPSoqkDw4McXuTjp70bN4N/bb7EOyZDE/+nTKE/NkJH2R9dzf7qT3VO6cnb97PbhJW62q5XcLMEiv011SO5RRx0Q3KJISfxXnGlANbj3CHXXLr3v3XutrH/hOr8hVr9td1/F7M14NXgK6k7j2JSSMGmkw+X+x2vv2nhLMDHSYvLQYaZEUEGXJTNwb6shPZXedcF1sEp7kImjH9E0SQfYDoP2seuJP96/7VG13jl33m2+L9C6jbarxgMCWLXc2TH1aSI3aEmnbbxrny2bPc7dcduve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917qvL+bR8fn+Un8tD5wdGUtA2Uze8fjj2TWbQxyRmV6zsDZeDm3713TqisrapN9bYxwBFyp5ANrH3Xuqkfi52Gn83v/AITtdcbmkds53T1p1VBt3OeQxz5lu8/izG2BrquclqmNcz2rs/ELWjSV/Z3MOIiSiA/nzZf37ytdWiCtwieLH664+6g+bLqT/bdZQ/c291T7P/eI5e5kuZPD2O7uf3fe1NF+mvSIS70I7YJTFcnjmEYPDrVd94a9fU91737r3R2f5dfyGPxh+Y3SXaVZkP4ftUbpg2hv+SSZoaMbE3up21uKsr1UgT0+34MguVRG9P3FBGfwPYp5K3r9w8zWu4MdNv4miT08N+1if9LXX9qjrHX71/tUPeT2B5j5Lt4vF3v6I3VkAKv9bZ/4xAiejTlDbEjOiZx59fRE95o9fKV1737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3XvfuvdcXRJEaORVdHVkdHUMjowKsrKwIZWBsQeCPfuvdaOn/CZzs9vhx/Mu/mtfygdy1/8M27ge5+x+1ehsPWNFR0ss3WW9J9gblbERyLBNWZDffUtftPKwxKl1x235ZQoVXt7r3RS/5i3x3/ANlf+Y3dfV1DQ/Y7UO55d49fxxw+GkGxd7qNyYChofURLT7ejr3xTPxqnoJOB9PeF/Ouy/uDma629BS38TXH6eG/coH+lro+1T19Wf3Tvdb/AF5fYHl3nO5l8Xe/oxa3xJq31ln/AIvO7+jTlBcgeSTLk9Ej9hXrIzr3v3XuvoO/yv8A5DN8lfhN0vvfIV3327duYP8A0Zb8keRJKpt19frFgpa+vMdkWt3HhYqLLOAFAFeLADj3mTyFvP785WtbtzW5RPCk9dcfbU/Nl0v/ALbr5Yvvl+1Q9oPvE8w8uWsfh7Fd3P7wsgAQv019WYIlc6IJjNbA5/sDk8erAPYx6xd697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3XuvnAfzrt/Vn8q3/hT38f/nhQQ1VBs7sjC9E9y77OLhYQ5DY1Xh8v8Xe9cLRxRwCnfNZDrvZVXVPHaRjV5CKoe7yg+/de62Df+FDfQ1FuTZPRXyz2mlFkoMZK/Vm7czjGirosjtjcsVVu3rjLx1tIrwy4aiyMeViE/kaN5MtAE/VdoJ96tl129rv8Q7kJhkPnparRn7A2sfa467D/AN1B7q/Sb1zD7M7hKRDdxLulkpNFE0Oi3vVUE5eWJrVwFFdFtIxqBjVU9499duOve/de62RP+E73yGO3u0O3PjNma3RjexMHB2XsuCV28ce7dnomN3NR0iB9P3Wc2tWQ1EhKn9rC/UWsZv8AZfevBv7nYpT2TJ4qf6dMMB82Qg/YnXI7+9a9qhuvJuxe8O3x1u9quTt92wGTbXRMlu7GldMNyroufiu+B4jbZ95F9cMeve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de6r970/mr/y5PjZlq3b3cnzJ6L2xubGTmlyu08ZvGm3vvDD1KzJTtT5naWwY90bkxE6ySC6VNLEwUMxGlWI3Q9e6KfRf8KLv5M9fklxUHzQx0dS8k8Ylrej/AJLY3GhqdJZJC2YyPTNLiEjZYjoYzhZWKqhZmUH1D17o0nU/82n+Wh3bWUWM66+b/wAdMhmMk0ceNwW4exsNsHcGSmlRJEpcft/sCXa+ZrqwpJcwRQPKNLXUaGt6h691YTBPBVQQ1VLNFU01TFHPT1EEiTQTwTIJIZoZoy0csUsbBlZSQwNxx7117rL7917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de60UP8Ahbz8fkyfTnwe+U9DQLHLsrsnsfoPc+SijBkrafsvbNB2DsmlrJCxZYcPN1VnmgCqF1ZCXUblB7917qzn+T/2jSfzbP8AhPztzqjcOUp812t1519nvi5uaevqhLNjuzujIsXkukM5lKuQw1E0ldtFNoZOtmcqZpZqhGd/U7EXM20Lv2w3W0mmuWI6a+Ug7oz+ThSfl1MHsD7nT+zfvJy97kRFhbbbuMbXAXLPZy1gvIwM1Z7WSVVwaMVIFQOtWGso6vHVlVj6+mmo66hqZ6OtpKmNoailq6aVoKimqIXCvFNBMhV1IBVgQfeEjKyMUcEODQg8QRxHX1s29xBdwJdWzrJbSoHRlIKsrAFWUjBBBBBGCD1G916e6Hz4t93ZL43/ACI6e7wxgmkbrnfWGzmTpKdtE2T208xx+7sLHIQ3jOc2tW1lHqsdInvY29nHL+6vse9W27R1/QlViB5rwdf9shYfn1GHvT7c2nu57Ub/AO3F5pA3bbZYY2bIjuAPEtZiPPwblIpaeeinX0j8NmMXuHEYrP4Sup8nhc5jaHMYjJUj+SlyGLydLFW0FdTSWGunq6SdJEP5Vgfeb8Us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AiVUSqwjkQsT7jf3R3zdNh5djudplMNxJdrGWAUnSY5WIGoGhqoyMjyI6zv/ALvL2i9vfeX3xvdg9ydvXc9kseXLi+jgeSVIzcRXu3wI0gieMyIEuJaxOTGxI1qwFOtIvfXYe/ez9w1e7eyd67r3/umut95uLee4ctubN1CrfRHLk8zV1lY0UYNkTXpQcAAe8Vru9vL+Y3N9LJNcHizsWY/mxJ6+jHlrlXljk3ak2LlHbrHa9li+GC0git4V9SI4lRKnzNKk5JJ63Af+E7f/AGRT2h/4tJvX/wB9N0l7yT9lv+VWuP8ApYP/ANWYOuC/965/4kRs3/il2n/dz3fq+v3L3XMfr3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917rSU/nv/Hf/RD8x/8AShiaH7banyI2zT7yjkih8NJHvrba0m2990MPqby1EyJjctUPxqmy7cfk4se7my/u3mb94Rilvex6/l4i0WQf8dc/N+vor/uz/db+vnsD/Uy+l175ypeNakE1Y2dxquLNz6KCbi2QeSWoz6Um+4s66Kde9+691tw/yq67aHz3/ld/IT4H9o1K1UGL2r2F0hl/IiVNZRdad24HcFRtDcEBMyVC5Lb2eq8slCy6DTfwumMcgZRoyj9oN6/eHLbbbIaz2Umn/m3JVkP+9a1+xR188X9517VHkr32h59sY9Ozc12ImJAoPrbMJb3SgUpmI2kzGtWkmckDiaEv+Ed2792fG750fzH/AOXr2eP4Pveh2/BnspgKp3iWg358Ye0cz1Nv2gx61EUTyVlS/aEbunplkp8b5NBWJykr9c3evoU+/de697917r3v3XutNr/hRPkK2X5i9S4mSodsdRfGjbOQpaUhdENbk+0u26avqFIUOXqYMRTK1yQBELAc3xl96XY8zW0ZPYLFSB8zLMD+2g/Z13//ALqG0t09gt8vlUC7k5wuI2bNSke3bYyL6UUyyEYr3GvlSgf3D/XUDrf3/lD4PaOE/l4/HH+58dEIMvt7P5zPVNI6TSVm7q/eG4P7yyV86+uWtpMlC1JpcloIqdIRZY1AzC9t4raLkux+mpRkZmI83LtqqfUHHyAA8uvl7+/duW+7j96zm39/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7917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3XugS+SXbdT0L0F3B3TSYSDctV1f19ube0G36mukxkGYl2/jZ69MfNkIqatkoo6ow6TIsUhS99J+nsq3zcW2jZ7ndFQSNbws+kmmrSK0rQ0r60PUi+0XIsXud7n7B7eT3LWcO9brb2bTqgkaITyBDIELIHK1rpLLXhUdav8Au7/hRr31X0s8Wxvj31LtmqdFWCq3Pnt37zSAmORZJDTY2fZIlfylWS7hVCkMGvcQLc+9m7upFpZW0berM7/yGjrsvsX90x7ZW0yvzJzVvl5ADlbeG1tC2RQapBeUFKg4qaggrTNQXyk+b3yU+Y2Xoch3p2HV57E4aokqdu7KxFJTbf2Nt2eSN4XqcbtzGpFTT5IwyvGa6saqrzExjM5jsojbf+a985mkD7tMXjU1VAAsa/MKMV/pNVqYrTrPH2X+7l7Q+wNhLa+221JbX1woWe7lZp7ycAghZJ5CWEdQG8GIRw6gGEeqpIGdV9Xb67q7D2j1X1pt+t3Rvje+apMFt/DUKFnnqqp/3KmqmP7NBi8dTq9RWVcxSnpKWKSaVkjRmBTt9hd7pex7fYoZLuVwqqPU+Z9ABlicAAk4HUk8686cte3fKl/zrzhdR2XLe227TTyucBVGFUcXkkakcUSgvLIyxorOwB+jD8W+iML8ZPj51N0VgpYKun662hj8PkcnTwGmizm5J/Jkt2bhWnYs8Az+566rrBGxZoxMFJNr+81tg2iLYdmttpiIKwRBSRjU3F2/2zkt+fXyce9HuZuHvF7p757lbkrRy7tfvKkbHUYbcUjtoC3A+BbpFFUABtFaCtOh89nHUYde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3RMv5if8A2Qn8tf8AxAvY/wD7zlb7DHOv/Kpbj/zxy/8AHT1kF91H/wASW5G/8Wew/wCr6dfPV2xsreW9q5MZszaW5t3ZKWWOCLH7YwOVz9dJPNq8UKUmKpauoeWXSdKhbtY294Z29rdXT+HaxySP6IpY/sAPX1S7zzFy/wAuWxvOYb6zsLRVLF7iaOBABxJaVlUAeZrQdWZfHX+TV84+/K2iqMr1xJ0bs+Yo9XurucVW1auKC4aRKLYwp6nfVXXNCCYVmoKWldrB6iIHUB3svtjzXvDgyQfSWx4vPVD+UdDIT6VUD1YdYe+7H94F92/2wt5IrHdxzJv61C2206blSfIveals1SvxFZpJAKlYnIodrj4K/wAtnof4KYWpqtnpVb57XztAuP3X25uakpoM3W0ReGabC7axUElTS7Q2zLVQLK1LFLPUTuqfc1NR4ofHkLylyPtHKURa2rLuLijzMBqI/hUZCLXNASTjUzUFOIn3lfvd+5n3ldxSHfym28kW0uu22y3ZjCj0IE1xIQrXVwFJUSMqIgLeDDFrk12G+xn1ir1737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+691737r3Xvfuvde9+690n91f8AHu5b/qFP/Q6e2Z/7Fvs6M9m/5KkH+n6UHt7os697917r3v3Xuve/de697917r3v3Xuve/de697917r3v3Xuv/9k="
293
294 $bytes = [System.Convert]::FromBase64String($base64sos)
295 Remove-Variable base64sos
296
297 $CompanyLogo = -join($ReportPath,'\','SOS_Logo.jpg')
298 $p = New-Object IO.MemoryStream($bytes, 0, $bytes.length)
299 $p.Write($bytes, 0, $bytes.length)
300 Add-Type -AssemblyName System.Drawing
301 $picture = [System.Drawing.Image]::FromStream($p, $true)
302 $picture.Save($CompanyLogo)
303
304 Remove-Variable bytes
305 Remove-Variable p
306 Remove-Variable picture
307
308 $LinkToFile = $false
309 $SaveWithDocument = $true
310 $Left = 0
311 $Top = 0
312 $Width = 135
313 $Height = 50
314
315 # Add image to the Sheet
316 $worksheet.Shapes.AddPicture($CompanyLogo, $LinkToFile, $SaveWithDocument, $Left, $Top, $Width, $Height) | Out-Null
317
318 Remove-Variable LinkToFile
319 Remove-Variable SaveWithDocument
320 Remove-Variable Left
321 Remove-Variable Top
322 Remove-Variable Width
323 Remove-Variable Height
324
325 If (Test-Path -Path $CompanyLogo)
326 {
327 Remove-Item $CompanyLogo
328 }
329 Remove-Variable CompanyLogo
330
331 $row = 5
332 $column = 1
333 $worksheet.Cells.Item($row,$column)= "Table of Contents"
334 $worksheet.Cells.Item($row,$column).Style = "Heading 2"
335 $row++
336
337 For($i=2; $i -le $workbook.Worksheets.Count; $i++)
338 {
339 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item($row,$column) , "" , "'$($workbook.Worksheets.Item($i).Name)'!A1", "", $workbook.Worksheets.Item($i).Name) | Out-Null
340 $row++
341 }
342
343 $row++
344 $worksheet.Cells.Item($row, 1) = "© Sense of Security 2018"
345 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item($row,2) , "https://www.senseofsecurity.com.au", "" , "", "www.senseofsecurity.com.au") | Out-Null
346
347 $worksheet.UsedRange.EntireColumn.AutoFit() | Out-Null
348
349 $excel.Windows.Item(1).Displaygridlines = $false
350 $excel.ScreenUpdating = $true
351 $ADStatFileName = -join($ExcelPath,'\',$DomainName,'ADRecon-Report.xlsx')
352 Try
353 {
354 # Disable prompt if file exists
355 $excel.DisplayAlerts = $False
356 $workbook.SaveAs($ADStatFileName)
357 Write-Output "[+] Excelsheet Saved to: $ADStatFileName"
358 }
359 Catch
360 {
361 Write-Error "[EXCEPTION] $($_.Exception.Message)"
362 }
363 $excel.Quit()
364 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet -Final $true
365 Remove-Variable worksheet
366 Get-ADRExcelComObjRelease -ComObjtoRelease $workbook -Final $true
367 Remove-Variable -Name workbook -Scope Global
368 Get-ADRExcelComObjRelease -ComObjtoRelease $excel -Final $true
369 Remove-Variable -Name excel -Scope Global
370 }
371}
372
373Function Get-ADRDomain
374{
375<#
376.SYNOPSIS
377 Returns information of the current (or specified) domain.
378
379.DESCRIPTION
380 Returns information of the current (or specified) domain.
381
382.PARAMETER Protocol
383 [string]
384 Which protocol to use; ADWS (default) or LDAP.
385
386.PARAMETER objDomain
387 [DirectoryServices.DirectoryEntry]
388 Domain Directory Entry object.
389
390.PARAMETER objDomainRootDSE
391 [DirectoryServices.DirectoryEntry]
392 RootDSE Directory Entry object.
393
394.PARAMETER DomainController
395 [string]
396 IP Address of the Domain Controller.
397
398.PARAMETER Credential
399 [Management.Automation.PSCredential]
400 Credentials.
401
402.OUTPUTS
403 PSObject.
404#>
405 param(
406 [Parameter(Mandatory = $true)]
407 [string] $Protocol,
408
409 [Parameter(Mandatory = $false)]
410 [DirectoryServices.DirectoryEntry] $objDomain,
411
412 [Parameter(Mandatory = $false)]
413 [DirectoryServices.DirectoryEntry] $objDomainRootDSE,
414
415 [Parameter(Mandatory = $false)]
416 [string] $DomainController,
417
418 [Parameter(Mandatory = $false)]
419 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty
420 )
421
422 If ($Protocol -eq 'ADWS')
423 {
424 Try
425 {
426 $ADDomain = Get-ADDomain
427 }
428 Catch
429 {
430 Write-Warning "[Get-ADRDomain] Error getting Domain Context"
431 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
432 Return $null
433 }
434 If ($ADDomain)
435 {
436 $DomainObj = @()
437
438 # Values taken from https://technet.microsoft.com/en-us/library/hh852281(v=wps.630).aspx
439 $FLAD = @{
440 0 = "Windows2000";
441 1 = "Windows2003/Interim";
442 2 = "Windows2003";
443 3 = "Windows2008";
444 4 = "Windows2008R2";
445 5 = "Windows2012";
446 6 = "Windows2012R2";
447 7 = "Windows2016"
448 }
449 $DomainMode = $FLAD[[convert]::ToInt32($ADDomain.DomainMode)] + "Domain"
450 Remove-Variable FLAD
451 If (-Not $DomainMode)
452 {
453 $DomainMode = $ADDomain.DomainMode
454 }
455
456 $ObjValues = @("Name", $ADDomain.DNSRoot, "NetBIOS", $ADDomain.NetBIOSName, "Functional Level", $DomainMode, "DomainSID", $ADDomain.DomainSID.Value)
457
458 For ($i = 0; $i -lt $($ObjValues.Count); $i++)
459 {
460 $Obj = New-Object PSObject
461 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value $ObjValues[$i]
462 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ObjValues[$i+1]
463 $i++
464 $DomainObj += $Obj
465 }
466 Remove-Variable DomainMode
467
468 For($i=0; $i -lt $ADDomain.ReplicaDirectoryServers.Count; $i++)
469 {
470 $Obj = New-Object PSObject
471 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Domain Controller"
472 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADDomain.ReplicaDirectoryServers[$i]
473 $DomainObj += $Obj
474 }
475 For($i=0; $i -lt $ADDomain.ReadOnlyReplicaDirectoryServers.Count; $i++)
476 {
477 $Obj = New-Object PSObject
478 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Read Only Domain Controller"
479 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADDomain.ReadOnlyReplicaDirectoryServers[$i]
480 $DomainObj += $Obj
481 }
482
483 Try
484 {
485 $ADForest = Get-ADForest $ADDomain.Forest
486 }
487 Catch
488 {
489 Write-Verbose "[Get-ADRDomain] Error getting Forest Context"
490 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
491 }
492
493 If (-Not $ADForest)
494 {
495 Try
496 {
497 $ADForest = Get-ADForest -Server $DomainController
498 }
499 Catch
500 {
501 Write-Warning "[Get-ADRDomain] Error getting Forest Context"
502 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
503 }
504 }
505 If ($ADForest)
506 {
507 $DomainCreation = Get-ADObject -SearchBase "$($ADForest.PartitionsContainer)" -LDAPFilter "(&(objectClass=crossRef)(systemFlags=3)(Name=$($ADDomain.Name)))" -Properties whenCreated
508 If (-Not $DomainCreation)
509 {
510 $DomainCreation = Get-ADObject -SearchBase "$($ADForest.PartitionsContainer)" -LDAPFilter "(&(objectClass=crossRef)(systemFlags=3)(Name=$($ADDomain.NetBIOSName)))" -Properties whenCreated
511 }
512 Remove-Variable ADForest
513 }
514 # Get RIDAvailablePool
515 Try
516 {
517 $RIDManager = Get-ADObject -Identity "CN=RID Manager$,CN=System,$($ADDomain.DistinguishedName)" -Properties rIDAvailablePool
518 $RIDproperty = $RIDManager.rIDAvailablePool
519 [int32] $totalSIDS = $($RIDproperty) / ([math]::Pow(2,32))
520 [int64] $temp64val = $totalSIDS * ([math]::Pow(2,32))
521 $RIDsIssued = [int32]($($RIDproperty) - $temp64val)
522 $RIDsRemaining = $totalSIDS - $RIDsIssued
523 Remove-Variable RIDManager
524 Remove-Variable RIDproperty
525 Remove-Variable totalSIDS
526 Remove-Variable temp64val
527 }
528 Catch
529 {
530 Write-Warning "[Get-ADRDomain] Error accessing CN=RID Manager$,CN=System,$($ADDomain.DistinguishedName)"
531 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
532 }
533 If ($DomainCreation)
534 {
535 $Obj = New-Object PSObject
536 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Creation Date"
537 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $DomainCreation.whenCreated
538 $DomainObj += $Obj
539 Remove-Variable DomainCreation
540 }
541
542 $Obj = New-Object PSObject
543 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "ms-DS-MachineAccountQuota"
544 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $((Get-ADObject -Identity ($ADDomain.DistinguishedName) -Properties ms-DS-MachineAccountQuota).'ms-DS-MachineAccountQuota')
545 $DomainObj += $Obj
546
547 If ($RIDsIssued)
548 {
549 $Obj = New-Object PSObject
550 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "RIDs Issued"
551 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $RIDsIssued
552 $DomainObj += $Obj
553 Remove-Variable RIDsIssued
554 }
555 If ($RIDsRemaining)
556 {
557 $Obj = New-Object PSObject
558 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "RIDs Remaining"
559 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $RIDsRemaining
560 $DomainObj += $Obj
561 Remove-Variable RIDsRemaining
562 }
563 }
564 }
565
566 If ($Protocol -eq 'LDAP')
567 {
568 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
569 {
570 $DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
571 $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$($DomainFQDN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
572 Try
573 {
574 $ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
575 }
576 Catch
577 {
578 Write-Warning "[Get-ADRDomain] Error getting Domain Context"
579 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
580 Return $null
581 }
582 Remove-Variable DomainContext
583 # Get RIDAvailablePool
584 Try
585 {
586 $SearchPath = "CN=RID Manager$,CN=System"
587 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$SearchPath,$($objDomain.distinguishedName)", $Credential.UserName,$Credential.GetNetworkCredential().Password
588 $objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
589 $objSearcherPath.PropertiesToLoad.AddRange(("ridavailablepool"))
590 $objSearcherResult = $objSearcherPath.FindAll()
591 $RIDproperty = $objSearcherResult.Properties.ridavailablepool
592 [int32] $totalSIDS = $($RIDproperty) / ([math]::Pow(2,32))
593 [int64] $temp64val = $totalSIDS * ([math]::Pow(2,32))
594 $RIDsIssued = [int32]($($RIDproperty) - $temp64val)
595 $RIDsRemaining = $totalSIDS - $RIDsIssued
596 Remove-Variable SearchPath
597 $objSearchPath.Dispose()
598 $objSearcherPath.Dispose()
599 $objSearcherResult.Dispose()
600 Remove-Variable RIDproperty
601 Remove-Variable totalSIDS
602 Remove-Variable temp64val
603 }
604 Catch
605 {
606 Write-Warning "[Get-ADRDomain] Error accessing CN=RID Manager$,CN=System,$($SearchPath),$($objDomain.distinguishedName)"
607 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
608 }
609 Try
610 {
611 $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Forest",$($ADDomain.Forest),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
612 $ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
613 }
614 Catch
615 {
616 Write-Warning "[Get-ADRDomain] Error getting Forest Context"
617 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
618 }
619 If ($ForestContext)
620 {
621 Remove-Variable ForestContext
622 }
623 If ($ADForest)
624 {
625 $GlobalCatalog = $ADForest.FindGlobalCatalog()
626 }
627 If ($GlobalCatalog)
628 {
629 $DN = "GC://$($GlobalCatalog.IPAddress)/$($objDomain.distinguishedname)"
630 Try
631 {
632 $ADObject = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList ($($DN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
633 $ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($ADObject.objectSid[0], 0)
634 $ADObject.Dispose()
635 }
636 Catch
637 {
638 Write-Warning "[Get-ADRDomain] Error retrieving Domain SID using the GlobalCatalog $($GlobalCatalog.IPAddress). Using SID from the ObjDomain."
639 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
640 $ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objectSid[0], 0)
641 }
642 }
643 Else
644 {
645 $ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objectSid[0], 0)
646 }
647 }
648 Else
649 {
650 $ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
651 $ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
652 Try
653 {
654 $GlobalCatalog = $ADForest.FindGlobalCatalog()
655 $DN = "GC://$($GlobalCatalog)/$($objDomain.distinguishedname)"
656 $ADObject = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList ($DN)
657 $ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($ADObject.objectSid[0], 0)
658 $ADObject.dispose()
659 }
660 Catch
661 {
662 Write-Warning "[Get-ADRDomain] Error retrieving Domain SID using the GlobalCatalog $($GlobalCatalog.IPAddress). Using SID from the ObjDomain."
663 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
664 $ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objectSid[0], 0)
665 }
666 # Get RIDAvailablePool
667 Try
668 {
669 $RIDManager = ([ADSI]"LDAP://CN=RID Manager$,CN=System,$($objDomain.distinguishedName)")
670 $RIDproperty = $ObjDomain.ConvertLargeIntegerToInt64($RIDManager.Properties.rIDAvailablePool.value)
671 [int32] $totalSIDS = $($RIDproperty) / ([math]::Pow(2,32))
672 [int64] $temp64val = $totalSIDS * ([math]::Pow(2,32))
673 $RIDsIssued = [int32]($($RIDproperty) - $temp64val)
674 $RIDsRemaining = $totalSIDS - $RIDsIssued
675 Remove-Variable RIDManager
676 Remove-Variable RIDproperty
677 Remove-Variable totalSIDS
678 Remove-Variable temp64val
679 }
680 Catch
681 {
682 Write-Warning "[Get-ADRDomain] Error accessing CN=RID Manager$,CN=System,$($SearchPath),$($objDomain.distinguishedName)"
683 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
684 }
685 }
686
687 If ($ADDomain)
688 {
689 $DomainObj = @()
690
691 # Values taken from https://technet.microsoft.com/en-us/library/hh852281(v=wps.630).aspx
692 $FLAD = @{
693 0 = "Windows2000";
694 1 = "Windows2003/Interim";
695 2 = "Windows2003";
696 3 = "Windows2008";
697 4 = "Windows2008R2";
698 5 = "Windows2012";
699 6 = "Windows2012R2";
700 7 = "Windows2016"
701 }
702 $DomainMode = $FLAD[[convert]::ToInt32($objDomainRootDSE.domainFunctionality,10)] + "Domain"
703 Remove-Variable FLAD
704
705 $ObjValues = @("Name", $ADDomain.Name, "NetBIOS", $objDomain.dc.value, "Functional Level", $DomainMode, "DomainSID", $ADDomainSID.Value)
706
707 For ($i = 0; $i -lt $($ObjValues.Count); $i++)
708 {
709 $Obj = New-Object PSObject
710 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value $ObjValues[$i]
711 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ObjValues[$i+1]
712 $i++
713 $DomainObj += $Obj
714 }
715 Remove-Variable DomainMode
716
717 For($i=0; $i -lt $ADDomain.DomainControllers.Count; $i++)
718 {
719 $Obj = New-Object PSObject
720 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Domain Controller"
721 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADDomain.DomainControllers[$i]
722 $DomainObj += $Obj
723 }
724
725 $Obj = New-Object PSObject
726 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Creation Date"
727 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $objDomain.whencreated.value
728 $DomainObj += $Obj
729
730 $Obj = New-Object PSObject
731 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "ms-DS-MachineAccountQuota"
732 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $objDomain.'ms-DS-MachineAccountQuota'.value
733 $DomainObj += $Obj
734
735 If ($RIDsIssued)
736 {
737 $Obj = New-Object PSObject
738 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "RIDs Issued"
739 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $RIDsIssued
740 $DomainObj += $Obj
741 Remove-Variable RIDsIssued
742 }
743 If ($RIDsRemaining)
744 {
745 $Obj = New-Object PSObject
746 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "RIDs Remaining"
747 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $RIDsRemaining
748 $DomainObj += $Obj
749 Remove-Variable RIDsRemaining
750 }
751 }
752 }
753
754 If ($DomainObj)
755 {
756 Return $DomainObj
757 }
758 Else
759 {
760 Return $null
761 }
762}
763
764Function Get-ADRForest
765{
766<#
767.SYNOPSIS
768 Returns information of the current (or specified) forest.
769
770.DESCRIPTION
771 Returns information of the current (or specified) forest.
772
773.PARAMETER Protocol
774 [string]
775 Which protocol to use; ADWS (default) or LDAP.
776
777.PARAMETER objDomain
778 [DirectoryServices.DirectoryEntry]
779 Domain Directory Entry object.
780
781.PARAMETER objDomainRootDSE
782 [DirectoryServices.DirectoryEntry]
783 RootDSE Directory Entry object.
784
785.PARAMETER DomainController
786 [string]
787 IP Address of the Domain Controller.
788
789.PARAMETER Credential
790 [Management.Automation.PSCredential]
791 Credentials.
792
793.OUTPUTS
794 PSObject.
795#>
796 param(
797 [Parameter(Mandatory = $true)]
798 [string] $Protocol,
799
800 [Parameter(Mandatory = $false)]
801 [DirectoryServices.DirectoryEntry] $objDomain,
802
803 [Parameter(Mandatory = $false)]
804 [DirectoryServices.DirectoryEntry] $objDomainRootDSE,
805
806 [Parameter(Mandatory = $false)]
807 [string] $DomainController,
808
809 [Parameter(Mandatory = $false)]
810 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty
811 )
812
813 If ($Protocol -eq 'ADWS')
814 {
815 Try
816 {
817 $ADDomain = Get-ADDomain
818 }
819 Catch
820 {
821 Write-Warning "[Get-ADRForest] Error getting Domain Context"
822 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
823 Return $null
824 }
825
826 Try
827 {
828 $ADForest = Get-ADForest $ADDomain.Forest
829 }
830 Catch
831 {
832 Write-Verbose "[Get-ADRForest] Error getting Forest Context"
833 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
834 }
835 Remove-Variable ADDomain
836
837 If (-Not $ADForest)
838 {
839 Try
840 {
841 $ADForest = Get-ADForest -Server $DomainController
842 }
843 Catch
844 {
845 Write-Warning "[Get-ADRForest] Error getting Forest Context"
846 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
847 Return $null
848 }
849 }
850
851 If ($ADForest)
852 {
853 # Get Tombstone Lifetime
854 Try
855 {
856 $ADForestCNC = (Get-ADRootDSE).configurationNamingContext
857 $ADForestDSCP = Get-ADObject -Identity "CN=Directory Service,CN=Windows NT,CN=Services,$($ADForestCNC)" -Partition $ADForestCNC -Properties *
858 $ADForestTombstoneLifetime = $ADForestDSCP.tombstoneLifetime
859 Remove-Variable ADForestCNC
860 Remove-Variable ADForestDSCP
861 }
862 Catch
863 {
864 Write-Warning "[Get-ADRForest] Error retrieving Tombstone Lifetime"
865 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
866 }
867
868 # Check Recycle Bin Feature Status
869 If ([convert]::ToInt32($ADForest.ForestMode) -ge 6)
870 {
871 Try
872 {
873 $ADRecycleBin = Get-ADOptionalFeature -Identity "Recycle Bin Feature"
874 }
875 Catch
876 {
877 Write-Warning "[Get-ADRForest] Error retrieving Recycle Bin Feature"
878 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
879 }
880 }
881
882 # Check Privileged Access Management Feature status
883 If ([convert]::ToInt32($ADForest.ForestMode) -ge 7)
884 {
885 Try
886 {
887 $PrivilegedAccessManagement = Get-ADOptionalFeature -Identity "Privileged Access Management Feature"
888 }
889 Catch
890 {
891 Write-Warning "[Get-ADRForest] Error retrieving Privileged Acceess Management Feature"
892 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
893 }
894 }
895
896 $ForestObj = @()
897
898 # Values taken from https://technet.microsoft.com/en-us/library/hh852281(v=wps.630).aspx
899 $FLAD = @{
900 0 = "Windows2000";
901 1 = "Windows2003/Interim";
902 2 = "Windows2003";
903 3 = "Windows2008";
904 4 = "Windows2008R2";
905 5 = "Windows2012";
906 6 = "Windows2012R2";
907 7 = "Windows2016"
908 }
909 $ForestMode = $FLAD[[convert]::ToInt32($ADForest.ForestMode)] + "Forest"
910 Remove-Variable FLAD
911
912 If (-Not $ForestMode)
913 {
914 $ForestMode = $ADForest.ForestMode
915 }
916
917 $ObjValues = @("Name", $ADForest.Name, "Functional Level", $ForestMode, "Domain Naming Master", $ADForest.DomainNamingMaster, "Schema Master", $ADForest.SchemaMaster, "RootDomain", $ADForest.RootDomain, "Domain Count", $ADForest.Domains.Count, "Site Count", $ADForest.Sites.Count, "Global Catalog Count", $ADForest.GlobalCatalogs.Count)
918
919 For ($i = 0; $i -lt $($ObjValues.Count); $i++)
920 {
921 $Obj = New-Object PSObject
922 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value $ObjValues[$i]
923 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ObjValues[$i+1]
924 $i++
925 $ForestObj += $Obj
926 }
927 Remove-Variable ForestMode
928
929 For($i=0; $i -lt $ADForest.Domains.Count; $i++)
930 {
931 $Obj = New-Object PSObject
932 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Domain"
933 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForest.Domains[$i]
934 $ForestObj += $Obj
935 }
936 For($i=0; $i -lt $ADForest.Sites.Count; $i++)
937 {
938 $Obj = New-Object PSObject
939 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Site"
940 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForest.Sites[$i]
941 $ForestObj += $Obj
942 }
943 For($i=0; $i -lt $ADForest.GlobalCatalogs.Count; $i++)
944 {
945 $Obj = New-Object PSObject
946 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "GlobalCatalog"
947 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForest.GlobalCatalogs[$i]
948 $ForestObj += $Obj
949 }
950
951 $Obj = New-Object PSObject
952 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Tombstone Lifetime"
953 If ($ADForestTombstoneLifetime)
954 {
955 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForestTombstoneLifetime
956 Remove-Variable ADForestTombstoneLifetime
957 }
958 Else
959 {
960 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Not Retrieved"
961 }
962 $ForestObj += $Obj
963
964 $Obj = New-Object PSObject
965 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Recycle Bin (2008 R2 onwards)"
966 If ($ADRecycleBin)
967 {
968 If ($ADRecycleBin.EnabledScopes.Count -gt 0)
969 {
970 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Enabled"
971 $ForestObj += $Obj
972 For($i=0; $i -lt $($ADRecycleBin.EnabledScopes.Count); $i++)
973 {
974 $Obj = New-Object PSObject
975 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Enabled Scope"
976 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADRecycleBin.EnabledScopes[$i]
977 $ForestObj += $Obj
978 }
979 }
980 Else
981 {
982 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
983 $ForestObj += $Obj
984 }
985 Remove-Variable ADRecycleBin
986 }
987 Else
988 {
989 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
990 $ForestObj += $Obj
991 }
992
993 $Obj = New-Object PSObject
994 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Privileged Access Management (2016 onwards)"
995 If ($PrivilegedAccessManagement)
996 {
997 If ($PrivilegedAccessManagement.EnabledScopes.Count -gt 0)
998 {
999 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Enabled"
1000 $ForestObj += $Obj
1001 For($i=0; $i -lt $($PrivilegedAccessManagement.EnabledScopes.Count); $i++)
1002 {
1003 $Obj = New-Object PSObject
1004 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Enabled Scope"
1005 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $PrivilegedAccessManagement.EnabledScopes[$i]
1006 $ForestObj += $Obj
1007 }
1008 }
1009 Else
1010 {
1011 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
1012 $ForestObj += $Obj
1013 }
1014 Remove-Variable PrivilegedAccessManagement
1015 }
1016 Else
1017 {
1018 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
1019 $ForestObj += $Obj
1020 }
1021 Remove-Variable ADForest
1022 }
1023 }
1024
1025 If ($Protocol -eq 'LDAP')
1026 {
1027 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
1028 {
1029 $DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
1030 $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$($DomainFQDN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
1031 Try
1032 {
1033 $ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
1034 }
1035 Catch
1036 {
1037 Write-Warning "[Get-ADRForest] Error getting Domain Context"
1038 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1039 Return $null
1040 }
1041 Remove-Variable DomainContext
1042
1043 $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Forest",$($ADDomain.Forest),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
1044 Remove-Variable ADDomain
1045 Try
1046 {
1047 $ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
1048 }
1049 Catch
1050 {
1051 Write-Warning "[Get-ADRForest] Error getting Forest Context"
1052 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1053 Return $null
1054 }
1055 Remove-Variable ForestContext
1056
1057 # Get Tombstone Lifetime
1058 Try
1059 {
1060 $SearchPath = "CN=Directory Service,CN=Windows NT,CN=Services"
1061 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$SearchPath,$($objDomainRootDSE.configurationNamingContext)", $Credential.UserName,$Credential.GetNetworkCredential().Password
1062 $objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
1063 $objSearcherPath.Filter="(name=Directory Service)"
1064 $objSearcherResult = $objSearcherPath.FindAll()
1065 $ADForestTombstoneLifetime = $objSearcherResult.Properties.tombstoneLifetime
1066 Remove-Variable SearchPath
1067 $objSearchPath.Dispose()
1068 $objSearcherPath.Dispose()
1069 $objSearcherResult.Dispose()
1070 }
1071 Catch
1072 {
1073 Write-Warning "[Get-ADRForest] Error retrieving Tombstone Lifetime"
1074 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1075 }
1076 # Check Recycle Bin Feature Status
1077 If ([convert]::ToInt32($objDomainRootDSE.forestFunctionality,10) -ge 6)
1078 {
1079 Try
1080 {
1081 $SearchPath = "CN=Recycle Bin Feature,CN=Optional Features,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration"
1082 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$($SearchPath),$($objDomain.distinguishedName)", $Credential.UserName,$Credential.GetNetworkCredential().Password
1083 $objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
1084 $ADRecycleBin = $objSearcherPath.FindAll()
1085 Remove-Variable SearchPath
1086 $objSearchPath.Dispose()
1087 $objSearcherPath.Dispose()
1088 }
1089 Catch
1090 {
1091 Write-Warning "[Get-ADRForest] Error retrieving Recycle Bin Feature"
1092 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1093 }
1094 }
1095 # Check Privileged Access Management Feature status
1096 If ([convert]::ToInt32($objDomainRootDSE.forestFunctionality,10) -ge 7)
1097 {
1098 Try
1099 {
1100 $SearchPath = "CN=Privileged Access Management Feature,CN=Optional Features,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration"
1101 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$($SearchPath),$($objDomain.distinguishedName)", $Credential.UserName,$Credential.GetNetworkCredential().Password
1102 $objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
1103 $PrivilegedAccessManagement = $objSearcherPath.FindAll()
1104 Remove-Variable SearchPath
1105 $objSearchPath.Dispose()
1106 $objSearcherPath.Dispose()
1107 }
1108 Catch
1109 {
1110 Write-Warning "[Get-ADRForest] Error retrieving Privileged Access Management Feature"
1111 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1112 }
1113 }
1114 }
1115 Else
1116 {
1117 $ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
1118 $ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
1119
1120 # Get Tombstone Lifetime
1121 $ADForestTombstoneLifetime = ([ADSI]"LDAP://CN=Directory Service,CN=Windows NT,CN=Services,$($objDomainRootDSE.configurationNamingContext)").tombstoneLifetime.value
1122
1123 # Check Recycle Bin Feature Status
1124 If ([convert]::ToInt32($objDomainRootDSE.forestFunctionality,10) -ge 6)
1125 {
1126 $ADRecycleBin = ([ADSI]"LDAP://CN=Recycle Bin Feature,CN=Optional Features,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,$($objDomain.distinguishedName)")
1127 }
1128 # Check Privileged Access Management Feature Status
1129 If ([convert]::ToInt32($objDomainRootDSE.forestFunctionality,10) -ge 7)
1130 {
1131 $PrivilegedAccessManagement = ([ADSI]"LDAP://CN=Privileged Access Management Feature,CN=Optional Features,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,$($objDomain.distinguishedName)")
1132 }
1133 }
1134
1135 If ($ADForest)
1136 {
1137 $ForestObj = @()
1138
1139 # Values taken from https://technet.microsoft.com/en-us/library/hh852281(v=wps.630).aspx
1140 $FLAD = @{
1141 0 = "Windows2000";
1142 1 = "Windows2003/Interim";
1143 2 = "Windows2003";
1144 3 = "Windows2008";
1145 4 = "Windows2008R2";
1146 5 = "Windows2012";
1147 6 = "Windows2012R2";
1148 7 = "Windows2016"
1149 }
1150 $ForestMode = $FLAD[[convert]::ToInt32($objDomainRootDSE.forestFunctionality,10)] + "Forest"
1151 Remove-Variable FLAD
1152
1153 $ObjValues = @("Name", $ADForest.Name, "Functional Level", $ForestMode, "Domain Naming Master", $ADForest.NamingRoleOwner, "Schema Master", $ADForest.SchemaRoleOwner, "RootDomain", $ADForest.RootDomain, "Domain Count", $ADForest.Domains.Count, "Site Count", $ADForest.Sites.Count, "Global Catalog Count", $ADForest.GlobalCatalogs.Count)
1154
1155 For ($i = 0; $i -lt $($ObjValues.Count); $i++)
1156 {
1157 $Obj = New-Object PSObject
1158 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value $ObjValues[$i]
1159 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ObjValues[$i+1]
1160 $i++
1161 $ForestObj += $Obj
1162 }
1163 Remove-Variable ForestMode
1164
1165 For($i=0; $i -lt $ADForest.Domains.Count; $i++)
1166 {
1167 $Obj = New-Object PSObject
1168 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Domain"
1169 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForest.Domains[$i]
1170 $ForestObj += $Obj
1171 }
1172 For($i=0; $i -lt $ADForest.Sites.Count; $i++)
1173 {
1174 $Obj = New-Object PSObject
1175 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Site"
1176 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForest.Sites[$i]
1177 $ForestObj += $Obj
1178 }
1179 For($i=0; $i -lt $ADForest.GlobalCatalogs.Count; $i++)
1180 {
1181 $Obj = New-Object PSObject
1182 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "GlobalCatalog"
1183 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForest.GlobalCatalogs[$i]
1184 $ForestObj += $Obj
1185 }
1186
1187 $Obj = New-Object PSObject
1188 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Tombstone Lifetime"
1189 If ($ADForestTombstoneLifetime)
1190 {
1191 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForestTombstoneLifetime
1192 Remove-Variable ADForestTombstoneLifetime
1193 }
1194 Else
1195 {
1196 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Not Retrieved"
1197 }
1198 $ForestObj += $Obj
1199
1200 $Obj = New-Object PSObject
1201 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Recycle Bin (2008 R2 onwards)"
1202 If ($ADRecycleBin)
1203 {
1204 If ($ADRecycleBin.Properties.'msDS-EnabledFeatureBL'.Count -gt 0)
1205 {
1206 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Enabled"
1207 $ForestObj += $Obj
1208 For($i=0; $i -lt $($ADRecycleBin.Properties.'msDS-EnabledFeatureBL'.Count); $i++)
1209 {
1210 $Obj = New-Object PSObject
1211 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Enabled Scope"
1212 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADRecycleBin.Properties.'msDS-EnabledFeatureBL'[$i]
1213 $ForestObj += $Obj
1214 }
1215 }
1216 Else
1217 {
1218 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
1219 $ForestObj += $Obj
1220 }
1221 Remove-Variable ADRecycleBin
1222 }
1223 Else
1224 {
1225 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
1226 $ForestObj += $Obj
1227 }
1228
1229 $Obj = New-Object PSObject
1230 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Privileged Access Management (2016 onwards)"
1231 If ($PrivilegedAccessManagement)
1232 {
1233 If ($PrivilegedAccessManagement.Properties.'msDS-EnabledFeatureBL'.Count -gt 0)
1234 {
1235 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Enabled"
1236 $ForestObj += $Obj
1237 For($i=0; $i -lt $($PrivilegedAccessManagement.Properties.'msDS-EnabledFeatureBL'.Count); $i++)
1238 {
1239 $Obj = New-Object PSObject
1240 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Enabled Scope"
1241 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $PrivilegedAccessManagement.Properties.'msDS-EnabledFeatureBL'[$i]
1242 $ForestObj += $Obj
1243 }
1244 }
1245 Else
1246 {
1247 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
1248 $ForestObj += $Obj
1249 }
1250 Remove-Variable PrivilegedAccessManagement
1251 }
1252 Else
1253 {
1254 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
1255 $ForestObj += $Obj
1256 }
1257
1258 Remove-Variable ADForest
1259 }
1260 }
1261
1262 If ($ForestObj)
1263 {
1264 Return $ForestObj
1265 }
1266 Else
1267 {
1268 Return $null
1269 }
1270}
1271
1272Function Get-ADRTrust
1273{
1274<#
1275.SYNOPSIS
1276 Returns the Trusts of the current (or specified) domain.
1277
1278.DESCRIPTION
1279 Returns the Trusts of the current (or specified) domain.
1280
1281.PARAMETER Protocol
1282 [string]
1283 Which protocol to use; ADWS (default) or LDAP.
1284
1285.PARAMETER objDomain
1286 [DirectoryServices.DirectoryEntry]
1287 Domain Directory Entry object.
1288
1289.OUTPUTS
1290 PSObject.
1291#>
1292 param(
1293 [Parameter(Mandatory = $true)]
1294 [string] $Protocol,
1295
1296 [Parameter(Mandatory = $false)]
1297 [DirectoryServices.DirectoryEntry] $objDomain
1298 )
1299
1300 # Values taken from https://msdn.microsoft.com/en-us/library/cc223768.aspx
1301 $TDAD = @{
1302 0 = "Disabled";
1303 1 = "Inbound";
1304 2 = "Outbound";
1305 3 = "BiDirectional";
1306 }
1307
1308 # Values taken from https://msdn.microsoft.com/en-us/library/cc223771.aspx
1309 $TTAD = @{
1310 1 = "Downlevel";
1311 2 = "Uplevel";
1312 3 = "MIT";
1313 4 = "DCE";
1314 }
1315
1316 If ($Protocol -eq 'ADWS')
1317 {
1318 Try
1319 {
1320 $ADTrusts = Get-ADObject -LDAPFilter "(objectClass=trustedDomain)" -Properties DistinguishedName,trustPartner,trustdirection,trusttype,TrustAttributes,whenCreated,whenChanged
1321 }
1322 Catch
1323 {
1324 Write-Warning "[Get-ADRTrust] Error while enumerating trustedDomain Objects"
1325 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1326 Return $null
1327 }
1328
1329 If ($ADTrusts)
1330 {
1331 Write-Verbose "[*] Total Trusts: $([ADRecon.ADWSClass]::ObjectCount($ADTrusts))"
1332 # Trust Info
1333 $ADTrustObj = @()
1334 $ADTrusts | ForEach-Object {
1335 # Create the object for each instance.
1336 $Obj = New-Object PSObject
1337 $Obj | Add-Member -MemberType NoteProperty -Name "Source Domain" -Value (Get-DNtoFQDN $_.DistinguishedName)
1338 $Obj | Add-Member -MemberType NoteProperty -Name "Target Domain" -Value $_.trustPartner
1339 $TrustDirection = [string] $TDAD[$_.trustdirection]
1340 $Obj | Add-Member -MemberType NoteProperty -Name "Trust Direction" -Value $TrustDirection
1341 $TrustType = [string] $TTAD[$_.trusttype]
1342 $Obj | Add-Member -MemberType NoteProperty -Name "Trust Type" -Value $TrustType
1343
1344 $TrustAttributes = $null
1345 If ([int32] $_.TrustAttributes -band 0x00000001) { $TrustAttributes += "Non Transitive," }
1346 If ([int32] $_.TrustAttributes -band 0x00000002) { $TrustAttributes += "UpLevel," }
1347 If ([int32] $_.TrustAttributes -band 0x00000004) { $TrustAttributes += "Quarantined," } #SID Filtering
1348 If ([int32] $_.TrustAttributes -band 0x00000008) { $TrustAttributes += "Forest Transitive," }
1349 If ([int32] $_.TrustAttributes -band 0x00000010) { $TrustAttributes += "Cross Organization," } #Selective Auth
1350 If ([int32] $_.TrustAttributes -band 0x00000020) { $TrustAttributes += "Within Forest," }
1351 If ([int32] $_.TrustAttributes -band 0x00000040) { $TrustAttributes += "Treat as External," }
1352 If ([int32] $_.TrustAttributes -band 0x00000080) { $TrustAttributes += "Uses RC4 Encryption," }
1353 If ([int32] $_.TrustAttributes -band 0x00000200) { $TrustAttributes += "No TGT Delegation," }
1354 If ([int32] $_.TrustAttributes -band 0x00000400) { $TrustAttributes += "PIM Trust," }
1355 If ($TrustAttributes)
1356 {
1357 $TrustAttributes = $TrustAttributes.TrimEnd(",")
1358 }
1359 $Obj | Add-Member -MemberType NoteProperty -Name "Attributes" -Value $TrustAttributes
1360 $Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value ([DateTime] $($_.whenCreated))
1361 $Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value ([DateTime] $($_.whenChanged))
1362 $ADTrustObj += $Obj
1363 }
1364 Remove-Variable ADTrusts
1365 }
1366 }
1367
1368 If ($Protocol -eq 'LDAP')
1369 {
1370 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
1371 $ObjSearcher.PageSize = $PageSize
1372 $ObjSearcher.Filter = "(objectClass=trustedDomain)"
1373 $ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","trustpartner","trustdirection","trusttype","trustattributes","whencreated","whenchanged"))
1374 $ObjSearcher.SearchScope = "Subtree"
1375
1376 Try
1377 {
1378 $ADTrusts = $ObjSearcher.FindAll()
1379 }
1380 Catch
1381 {
1382 Write-Warning "[Get-ADRTrust] Error while enumerating trustedDomain Objects"
1383 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1384 Return $null
1385 }
1386 $ObjSearcher.dispose()
1387
1388 If ($ADTrusts)
1389 {
1390 Write-Verbose "[*] Total Trusts: $([ADRecon.LDAPClass]::ObjectCount($ADTrusts))"
1391 # Trust Info
1392 $ADTrustObj = @()
1393 $ADTrusts | ForEach-Object {
1394 # Create the object for each instance.
1395 $Obj = New-Object PSObject
1396 $Obj | Add-Member -MemberType NoteProperty -Name "Source Domain" -Value $(Get-DNtoFQDN ([string] $_.Properties.distinguishedname))
1397 $Obj | Add-Member -MemberType NoteProperty -Name "Target Domain" -Value $([string] $_.Properties.trustpartner)
1398 $TrustDirection = [string] $TDAD[$_.Properties.trustdirection]
1399 $Obj | Add-Member -MemberType NoteProperty -Name "Trust Direction" -Value $TrustDirection
1400 $TrustType = [string] $TTAD[$_.Properties.trusttype]
1401 $Obj | Add-Member -MemberType NoteProperty -Name "Trust Type" -Value $TrustType
1402
1403 $TrustAttributes = $null
1404 If ([int32] $_.Properties.trustattributes[0] -band 0x00000001) { $TrustAttributes += "Non Transitive," }
1405 If ([int32] $_.Properties.trustattributes[0] -band 0x00000002) { $TrustAttributes += "UpLevel," }
1406 If ([int32] $_.Properties.trustattributes[0] -band 0x00000004) { $TrustAttributes += "Quarantined," } #SID Filtering
1407 If ([int32] $_.Properties.trustattributes[0] -band 0x00000008) { $TrustAttributes += "Forest Transitive," }
1408 If ([int32] $_.Properties.trustattributes[0] -band 0x00000010) { $TrustAttributes += "Cross Organization," } #Selective Auth
1409 If ([int32] $_.Properties.trustattributes[0] -band 0x00000020) { $TrustAttributes += "Within Forest," }
1410 If ([int32] $_.Properties.trustattributes[0] -band 0x00000040) { $TrustAttributes += "Treat as External," }
1411 If ([int32] $_.Properties.trustattributes[0] -band 0x00000080) { $TrustAttributes += "Uses RC4 Encryption," }
1412 If ([int32] $_.Properties.trustattributes[0] -band 0x00000200) { $TrustAttributes += "No TGT Delegation," }
1413 If ([int32] $_.Properties.trustattributes[0] -band 0x00000400) { $TrustAttributes += "PIM Trust," }
1414 If ($TrustAttributes)
1415 {
1416 $TrustAttributes = $TrustAttributes.TrimEnd(",")
1417 }
1418 $Obj | Add-Member -MemberType NoteProperty -Name "Attributes" -Value $TrustAttributes
1419 $Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value ([DateTime] $($_.Properties.whencreated))
1420 $Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value ([DateTime] $($_.Properties.whenchanged))
1421 $ADTrustObj += $Obj
1422 }
1423 Remove-Variable ADTrusts
1424 }
1425 }
1426
1427 If ($ADTrustObj)
1428 {
1429 Return $ADTrustObj
1430 }
1431 Else
1432 {
1433 Return $null
1434 }
1435}
1436
1437Function Get-ADRSite
1438{
1439<#
1440.SYNOPSIS
1441 Returns the Sites of the current (or specified) domain.
1442
1443.DESCRIPTION
1444 Returns the Sites of the current (or specified) domain.
1445
1446.PARAMETER Protocol
1447 [string]
1448 Which protocol to use; ADWS (default) or LDAP.
1449
1450.PARAMETER objDomain
1451 [DirectoryServices.DirectoryEntry]
1452 Domain Directory Entry object.
1453
1454.PARAMETER objDomainRootDSE
1455 [DirectoryServices.DirectoryEntry]
1456 RootDSE Directory Entry object.
1457
1458.PARAMETER DomainController
1459 [string]
1460 IP Address of the Domain Controller.
1461
1462.PARAMETER Credential
1463 [Management.Automation.PSCredential]
1464 Credentials.
1465
1466.OUTPUTS
1467 PSObject.
1468#>
1469 param(
1470 [Parameter(Mandatory = $true)]
1471 [string] $Protocol,
1472
1473 [Parameter(Mandatory = $false)]
1474 [DirectoryServices.DirectoryEntry] $objDomain,
1475
1476 [Parameter(Mandatory = $false)]
1477 [DirectoryServices.DirectoryEntry] $objDomainRootDSE,
1478
1479 [Parameter(Mandatory = $false)]
1480 [string] $DomainController,
1481
1482 [Parameter(Mandatory = $false)]
1483 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty
1484 )
1485
1486 If ($Protocol -eq 'ADWS')
1487 {
1488 Try
1489 {
1490 $SearchPath = "CN=Sites"
1491 $ADSites = Get-ADObject -SearchBase "$SearchPath,$((Get-ADRootDSE).configurationNamingContext)" -LDAPFilter "(objectClass=site)" -Properties Name,Description,whenCreated,whenChanged
1492 }
1493 Catch
1494 {
1495 Write-Warning "[Get-ADRSite] Error while enumerating Site Objects"
1496 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1497 Return $null
1498 }
1499
1500 If ($ADSites)
1501 {
1502 Write-Verbose "[*] Total Sites: $([ADRecon.ADWSClass]::ObjectCount($ADSites))"
1503 # Sites Info
1504 $ADSiteObj = @()
1505 $ADSites | ForEach-Object {
1506 # Create the object for each instance.
1507 $Obj = New-Object PSObject
1508 $Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $_.Name
1509 $Obj | Add-Member -MemberType NoteProperty -Name "Description" -Value $_.Description
1510 $Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value $_.whenCreated
1511 $Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value $_.whenChanged
1512 $ADSiteObj += $Obj
1513 }
1514 Remove-Variable ADSites
1515 }
1516 }
1517
1518 If ($Protocol -eq 'LDAP')
1519 {
1520 $SearchPath = "CN=Sites"
1521 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
1522 {
1523 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$SearchPath,$($objDomainRootDSE.ConfigurationNamingContext)", $Credential.UserName,$Credential.GetNetworkCredential().Password
1524 }
1525 Else
1526 {
1527 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$SearchPath,$($objDomainRootDSE.ConfigurationNamingContext)"
1528 }
1529 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
1530 $ObjSearcher.Filter = "(objectClass=site)"
1531 $ObjSearcher.SearchScope = "Subtree"
1532
1533 Try
1534 {
1535 $ADSites = $ObjSearcher.FindAll()
1536 }
1537 Catch
1538 {
1539 Write-Warning "[Get-ADRSite] Error while enumerating Site Objects"
1540 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1541 Return $null
1542 }
1543 $ObjSearcher.dispose()
1544
1545 If ($ADSites)
1546 {
1547 Write-Verbose "[*] Total Sites: $([ADRecon.LDAPClass]::ObjectCount($ADSites))"
1548 # Site Info
1549 $ADSiteObj = @()
1550 $ADSites | ForEach-Object {
1551 # Create the object for each instance.
1552 $Obj = New-Object PSObject
1553 $Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $([string] $_.Properties.name)
1554 $Obj | Add-Member -MemberType NoteProperty -Name "Description" -Value $([string] $_.Properties.description)
1555 $Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value ([DateTime] $($_.Properties.whencreated))
1556 $Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value ([DateTime] $($_.Properties.whenchanged))
1557 $ADSiteObj += $Obj
1558 }
1559 Remove-Variable ADSites
1560 }
1561 }
1562
1563 If ($ADSiteObj)
1564 {
1565 Return $ADSiteObj
1566 }
1567 Else
1568 {
1569 Return $null
1570 }
1571}
1572
1573Function Get-ADRSubnet
1574{
1575<#
1576.SYNOPSIS
1577 Returns the Subnets of the current (or specified) domain.
1578
1579.DESCRIPTION
1580 Returns the Subnets of the current (or specified) domain.
1581
1582.PARAMETER Protocol
1583 [string]
1584 Which protocol to use; ADWS (default) or LDAP.
1585
1586.PARAMETER objDomain
1587 [DirectoryServices.DirectoryEntry]
1588 Domain Directory Entry object.
1589
1590.PARAMETER objDomainRootDSE
1591 [DirectoryServices.DirectoryEntry]
1592 RootDSE Directory Entry object.
1593
1594.PARAMETER DomainController
1595 [string]
1596 IP Address of the Domain Controller.
1597
1598.PARAMETER Credential
1599 [Management.Automation.PSCredential]
1600 Credentials.
1601
1602.OUTPUTS
1603 PSObject.
1604#>
1605 param(
1606 [Parameter(Mandatory = $true)]
1607 [string] $Protocol,
1608
1609 [Parameter(Mandatory = $false)]
1610 [DirectoryServices.DirectoryEntry] $objDomain,
1611
1612 [Parameter(Mandatory = $false)]
1613 [DirectoryServices.DirectoryEntry] $objDomainRootDSE,
1614
1615 [Parameter(Mandatory = $false)]
1616 [string] $DomainController,
1617
1618 [Parameter(Mandatory = $false)]
1619 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty
1620 )
1621
1622 If ($Protocol -eq 'ADWS')
1623 {
1624 Try
1625 {
1626 $SearchPath = "CN=Subnets,CN=Sites"
1627 $ADSubnets = Get-ADObject -SearchBase "$SearchPath,$((Get-ADRootDSE).configurationNamingContext)" -LDAPFilter "(objectClass=subnet)" -Properties Name,Description,siteObject,whenCreated,whenChanged
1628 }
1629 Catch
1630 {
1631 Write-Warning "[Get-ADRSubnet] Error while enumerating Subnet Objects"
1632 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1633 Return $null
1634 }
1635
1636 If ($ADSubnets)
1637 {
1638 Write-Verbose "[*] Total Subnets: $([ADRecon.ADWSClass]::ObjectCount($ADSubnets))"
1639 # Subnets Info
1640 $ADSubnetObj = @()
1641 $ADSubnets | ForEach-Object {
1642 # Create the object for each instance.
1643 $Obj = New-Object PSObject
1644 $Obj | Add-Member -MemberType NoteProperty -Name "Site" -Value $(($_.siteObject -Split ",")[0] -replace 'CN=','')
1645 $Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $_.Name
1646 $Obj | Add-Member -MemberType NoteProperty -Name "Description" -Value $_.Description
1647 $Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value $_.whenCreated
1648 $Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value $_.whenChanged
1649 $ADSubnetObj += $Obj
1650 }
1651 Remove-Variable ADSubnets
1652 }
1653 }
1654
1655 If ($Protocol -eq 'LDAP')
1656 {
1657 $SearchPath = "CN=Subnets,CN=Sites"
1658 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
1659 {
1660 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$SearchPath,$($objDomainRootDSE.ConfigurationNamingContext)", $Credential.UserName,$Credential.GetNetworkCredential().Password
1661 }
1662 Else
1663 {
1664 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$SearchPath,$($objDomainRootDSE.ConfigurationNamingContext)"
1665 }
1666 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
1667 $ObjSearcher.Filter = "(objectClass=subnet)"
1668 $ObjSearcher.SearchScope = "Subtree"
1669
1670 Try
1671 {
1672 $ADSubnets = $ObjSearcher.FindAll()
1673 }
1674 Catch
1675 {
1676 Write-Warning "[Get-ADRSubnet] Error while enumerating Subnet Objects"
1677 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1678 Return $null
1679 }
1680 $ObjSearcher.dispose()
1681
1682 If ($ADSubnets)
1683 {
1684 Write-Verbose "[*] Total Subnets: $([ADRecon.LDAPClass]::ObjectCount($ADSubnets))"
1685 # Subnets Info
1686 $ADSubnetObj = @()
1687 $ADSubnets | ForEach-Object {
1688 # Create the object for each instance.
1689 $Obj = New-Object PSObject
1690 $Obj | Add-Member -MemberType NoteProperty -Name "Site" -Value $((([string] $_.Properties.siteobject) -Split ",")[0] -replace 'CN=','')
1691 $Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $([string] $_.Properties.name)
1692 $Obj | Add-Member -MemberType NoteProperty -Name "Description" -Value $([string] $_.Properties.description)
1693 $Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value ([DateTime] $($_.Properties.whencreated))
1694 $Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value ([DateTime] $($_.Properties.whenchanged))
1695 $ADSubnetObj += $Obj
1696 }
1697 Remove-Variable ADSubnets
1698 }
1699 }
1700
1701 If ($ADSubnetObj)
1702 {
1703 Return $ADSubnetObj
1704 }
1705 Else
1706 {
1707 Return $null
1708 }
1709}
1710
1711Function Get-ADRDefaultPasswordPolicy
1712{
1713<#
1714.SYNOPSIS
1715 Returns the Default Password Policy of the current (or specified) domain.
1716
1717.DESCRIPTION
1718 Returns the Default Password Policy of the current (or specified) domain.
1719
1720.PARAMETER Protocol
1721 [string]
1722 Which protocol to use; ADWS (default) or LDAP.
1723
1724.PARAMETER objDomain
1725 [DirectoryServices.DirectoryEntry]
1726 Domain Directory Entry object.
1727
1728.OUTPUTS
1729 PSObject.
1730#>
1731 param(
1732 [Parameter(Mandatory = $true)]
1733 [string] $Protocol,
1734
1735 [Parameter(Mandatory = $false)]
1736 [DirectoryServices.DirectoryEntry] $objDomain
1737 )
1738
1739 If ($Protocol -eq 'ADWS')
1740 {
1741 Try
1742 {
1743 $ADpasspolicy = Get-ADDefaultDomainPasswordPolicy
1744 }
1745 Catch
1746 {
1747 Write-Warning "[Get-ADRDefaultPasswordPolicy] Error while enumerating the Default Password Policy"
1748 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1749 Return $null
1750 }
1751
1752 If ($ADpasspolicy)
1753 {
1754 $ObjValues = @( "Enforce password history (passwords)", $ADpasspolicy.PasswordHistoryCount, "4", "Req. 8.2.5", "8", "Control: 0423", "24 or more",
1755 "Maximum password age (days)", $ADpasspolicy.MaxPasswordAge.days, "90", "Req. 8.2.4", "90", "Control: 0423", "1 to 60",
1756 "Minimum password age (days)", $ADpasspolicy.MinPasswordAge.days, "N/A", "-", "1", "Control: 0423", "1 or more",
1757 "Minimum password length (characters)", $ADpasspolicy.MinPasswordLength, "7", "Req. 8.2.3", "13", "Control: 0421", "14 or more",
1758 "Password must meet complexity requirements", $ADpasspolicy.ComplexityEnabled, $true, "Req. 8.2.3", $true, "Control: 0421", $true,
1759 "Store password using reversible encryption for all users in the domain", $ADpasspolicy.ReversibleEncryptionEnabled, "N/A", "-", "N/A", "-", $false,
1760 "Account lockout duration (mins)", $ADpasspolicy.LockoutDuration.minutes, "0 (manual unlock) or 30", "Req. 8.1.7", "N/A", "-", "15 or more",
1761 "Account lockout threshold (attempts)", $ADpasspolicy.LockoutThreshold, "1 to 6", "Req. 8.1.6", "1 to 5", "Control: 1403", "1 to 10",
1762 "Reset account lockout counter after (mins)", $ADpasspolicy.LockoutObservationWindow.minutes, "N/A", "-", "N/A", "-", "15 or more" )
1763
1764 Remove-Variable ADpasspolicy
1765 }
1766 }
1767
1768 If ($Protocol -eq 'LDAP')
1769 {
1770 If ($ObjDomain)
1771 {
1772 #Value taken from https://msdn.microsoft.com/en-us/library/ms679431(v=vs.85).aspx
1773 $pwdProperties = @{
1774 "DOMAIN_PASSWORD_COMPLEX" = 1;
1775 "DOMAIN_PASSWORD_NO_ANON_CHANGE" = 2;
1776 "DOMAIN_PASSWORD_NO_CLEAR_CHANGE" = 4;
1777 "DOMAIN_LOCKOUT_ADMINS" = 8;
1778 "DOMAIN_PASSWORD_STORE_CLEARTEXT" = 16;
1779 "DOMAIN_REFUSE_PASSWORD_CHANGE" = 32
1780 }
1781
1782 If (($ObjDomain.pwdproperties.value -band $pwdProperties["DOMAIN_PASSWORD_COMPLEX"]) -eq $pwdProperties["DOMAIN_PASSWORD_COMPLEX"])
1783 {
1784 $ComplexPasswords = $true
1785 }
1786 Else
1787 {
1788 $ComplexPasswords = $false
1789 }
1790
1791 If (($ObjDomain.pwdproperties.value -band $pwdProperties["DOMAIN_PASSWORD_STORE_CLEARTEXT"]) -eq $pwdProperties["DOMAIN_PASSWORD_STORE_CLEARTEXT"])
1792 {
1793 $ReversibleEncryption = $true
1794 }
1795 Else
1796 {
1797 $ReversibleEncryption = $false
1798 }
1799
1800 $LockoutDuration = $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.lockoutduration.value)/-600000000)
1801
1802 If ($LockoutDuration -gt 99999)
1803 {
1804 $LockoutDuration = 0
1805 }
1806
1807 $ObjValues = @( "Enforce password history (passwords)", $ObjDomain.PwdHistoryLength.value, "4", "Req. 8.2.5", "8", "Control: 0423", "24 or more",
1808 "Maximum password age (days)", $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.maxpwdage.value) /-864000000000), "90", "Req. 8.2.4", "90", "Control: 0423", "1 to 60",
1809 "Minimum password age (days)", $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.minpwdage.value) /-864000000000), "N/A", "-", "1", "Control: 0423", "1 or more",
1810 "Minimum password length (characters)", $ObjDomain.MinPwdLength.value, "7", "Req. 8.2.3", "13", "Control: 0421", "14 or more",
1811 "Password must meet complexity requirements", $ComplexPasswords, $true, "Req. 8.2.3", $true, "Control: 0421", $true,
1812 "Store password using reversible encryption for all users in the domain", $ReversibleEncryption, "N/A", "-", "N/A", "-", $false,
1813 "Account lockout duration (mins)", $LockoutDuration, "0 (manual unlock) or 30", "Req. 8.1.7", "N/A", "-", "15 or more",
1814 "Account lockout threshold (attempts)", $ObjDomain.LockoutThreshold.value, "1 to 6", "Req. 8.1.6", "1 to 5", "Control: 1403", "1 to 10",
1815 "Reset account lockout counter after (mins)", $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.lockoutobservationWindow.value)/-600000000), "N/A", "-", "N/A", "-", "15 or more" )
1816
1817 Remove-Variable pwdProperties
1818 Remove-Variable ComplexPasswords
1819 Remove-Variable ReversibleEncryption
1820 }
1821 }
1822
1823 If ($ObjValues)
1824 {
1825 $ADPassPolObj = @()
1826 For ($i = 0; $i -lt $($ObjValues.Count); $i++)
1827 {
1828 $Obj = New-Object PSObject
1829 $Obj | Add-Member -MemberType NoteProperty -Name "Policy" -Value $ObjValues[$i]
1830 $Obj | Add-Member -MemberType NoteProperty -Name "Current Value" -Value $ObjValues[$i+1]
1831 $Obj | Add-Member -MemberType NoteProperty -Name "PCI DSS Requirement" -Value $ObjValues[$i+2]
1832 $Obj | Add-Member -MemberType NoteProperty -Name "PCI DSS v3.2.1" -Value $ObjValues[$i+3]
1833 $Obj | Add-Member -MemberType NoteProperty -Name "ASD ISM" -Value $ObjValues[$i+4]
1834 $Obj | Add-Member -MemberType NoteProperty -Name "2018 ISM Controls" -Value $ObjValues[$i+5]
1835 $Obj | Add-Member -MemberType NoteProperty -Name "CIS Benchmark 2016" -Value $ObjValues[$i+6]
1836 $i += 6
1837 $ADPassPolObj += $Obj
1838 }
1839 Remove-Variable ObjValues
1840 Return $ADPassPolObj
1841 }
1842 Else
1843 {
1844 Return $null
1845 }
1846}
1847
1848Function Get-ADRFineGrainedPasswordPolicy
1849{
1850<#
1851.SYNOPSIS
1852 Returns the Fine Grained Password Policy of the current (or specified) domain.
1853
1854.DESCRIPTION
1855 Returns the Fine Grained Password Policy of the current (or specified) domain.
1856
1857.PARAMETER Protocol
1858 [string]
1859 Which protocol to use; ADWS (default) or LDAP.
1860
1861.PARAMETER objDomain
1862 [DirectoryServices.DirectoryEntry]
1863 Domain Directory Entry object.
1864
1865.OUTPUTS
1866 PSObject.
1867#>
1868 param(
1869 [Parameter(Mandatory = $true)]
1870 [string] $Protocol,
1871
1872 [Parameter(Mandatory = $false)]
1873 [DirectoryServices.DirectoryEntry] $objDomain
1874 )
1875
1876 If ($Protocol -eq 'ADWS')
1877 {
1878 Try
1879 {
1880 $ADFinepasspolicy = Get-ADFineGrainedPasswordPolicy -Filter *
1881 }
1882 Catch
1883 {
1884 Write-Warning "[Get-ADRFineGrainedPasswordPolicy] Error while enumerating the Fine Grained Password Policy"
1885 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1886 Return $null
1887 }
1888
1889 If ($ADFinepasspolicy)
1890 {
1891 $ADPassPolObj = @()
1892
1893 $ADFinepasspolicy | ForEach-Object {
1894 For($i=0; $i -lt $($_.AppliesTo.Count); $i++)
1895 {
1896 $AppliesTo = $AppliesTo + "," + $_.AppliesTo[$i]
1897 }
1898 If ($null -ne $AppliesTo)
1899 {
1900 $AppliesTo = $AppliesTo.TrimStart(",")
1901 }
1902 $ObjValues = @("Name", $($_.Name), "Applies To", $AppliesTo, "Enforce password history", $_.PasswordHistoryCount, "Maximum password age (days)", $_.MaxPasswordAge.days, "Minimum password age (days)", $_.MinPasswordAge.days, "Minimum password length", $_.MinPasswordLength, "Password must meet complexity requirements", $_.ComplexityEnabled, "Store password using reversible encryption", $_.ReversibleEncryptionEnabled, "Account lockout duration (mins)", $_.LockoutDuration.minutes, "Account lockout threshold", $_.LockoutThreshold, "Reset account lockout counter after (mins)", $_.LockoutObservationWindow.minutes, "Precedence", $($_.Precedence))
1903 For ($i = 0; $i -lt $($ObjValues.Count); $i++)
1904 {
1905 $Obj = New-Object PSObject
1906 $Obj | Add-Member -MemberType NoteProperty -Name "Policy" -Value $ObjValues[$i]
1907 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ObjValues[$i+1]
1908 $i++
1909 $ADPassPolObj += $Obj
1910 }
1911 }
1912 Remove-Variable ADFinepasspolicy
1913 }
1914 }
1915
1916 If ($Protocol -eq 'LDAP')
1917 {
1918 If ($ObjDomain)
1919 {
1920 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
1921 $ObjSearcher.PageSize = $PageSize
1922 $ObjSearcher.Filter = "(objectClass=msDS-PasswordSettings)"
1923 $ObjSearcher.SearchScope = "Subtree"
1924 Try
1925 {
1926 $ADFinepasspolicy = $ObjSearcher.FindAll()
1927 }
1928 Catch
1929 {
1930 Write-Warning "[Get-ADRFineGrainedPasswordPolicy] Error while enumerating the Fine Grained Password Policy"
1931 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
1932 Return $null
1933 }
1934
1935 If ($ADFinepasspolicy)
1936 {
1937 If ([ADRecon.LDAPClass]::ObjectCount($ADFinepasspolicy) -ge 1)
1938 {
1939 $ADPassPolObj = @()
1940 $ADFinepasspolicy | ForEach-Object {
1941 For($i=0; $i -lt $($_.Properties.'msds-psoappliesto'.Count); $i++)
1942 {
1943 $AppliesTo = $AppliesTo + "," + $_.Properties.'msds-psoappliesto'[$i]
1944 }
1945 If ($null -ne $AppliesTo)
1946 {
1947 $AppliesTo = $AppliesTo.TrimStart(",")
1948 }
1949 $ObjValues = @("Name", $($_.Properties.name), "Applies To", $AppliesTo, "Enforce password history", $($_.Properties.'msds-passwordhistorylength'), "Maximum password age (days)", $($($_.Properties.'msds-maximumpasswordage') /-864000000000), "Minimum password age (days)", $($($_.Properties.'msds-minimumpasswordage') /-864000000000), "Minimum password length", $($_.Properties.'msds-minimumpasswordlength'), "Password must meet complexity requirements", $($_.Properties.'msds-passwordcomplexityenabled'), "Store password using reversible encryption", $($_.Properties.'msds-passwordreversibleencryptionenabled'), "Account lockout duration (mins)", $($($_.Properties.'msds-lockoutduration')/-600000000), "Account lockout threshold", $($_.Properties.'msds-lockoutthreshold'), "Reset account lockout counter after (mins)", $($($_.Properties.'msds-lockoutobservationwindow')/-600000000), "Precedence", $($_.Properties.'msds-passwordsettingsprecedence'))
1950 For ($i = 0; $i -lt $($ObjValues.Count); $i++)
1951 {
1952 $Obj = New-Object PSObject
1953 $Obj | Add-Member -MemberType NoteProperty -Name "Policy" -Value $ObjValues[$i]
1954 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ObjValues[$i+1]
1955 $i++
1956 $ADPassPolObj += $Obj
1957 }
1958 }
1959 }
1960 Remove-Variable ADFinepasspolicy
1961 }
1962 }
1963 }
1964
1965 If ($ADPassPolObj)
1966 {
1967 Return $ADPassPolObj
1968 }
1969 Else
1970 {
1971 Return $null
1972 }
1973}
1974
1975Function Get-ADRDomainController
1976{
1977<#
1978.SYNOPSIS
1979 Returns the domain controllers for the current (or specified) forest.
1980
1981.DESCRIPTION
1982 Returns the domain controllers for the current (or specified) forest.
1983
1984.PARAMETER Protocol
1985 [string]
1986 Which protocol to use; ADWS (default) or LDAP.
1987
1988.PARAMETER objDomain
1989 [DirectoryServices.DirectoryEntry]
1990 Domain Directory Entry object.
1991
1992.PARAMETER Credential
1993 [Management.Automation.PSCredential]
1994 Credentials.
1995
1996.OUTPUTS
1997 PSObject.
1998#>
1999 param(
2000 [Parameter(Mandatory = $true)]
2001 [string] $Protocol,
2002
2003 [Parameter(Mandatory = $false)]
2004 [DirectoryServices.DirectoryEntry] $objDomain,
2005
2006 [Parameter(Mandatory = $false)]
2007 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty
2008 )
2009
2010 If ($Protocol -eq 'ADWS')
2011 {
2012 Try
2013 {
2014 $ADDomainControllers = Get-ADDomainController -Filter *
2015 }
2016 Catch
2017 {
2018 Write-Warning "[Get-ADRDomainController] Error while enumerating DomainController Objects"
2019 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2020 Return $null
2021 }
2022
2023 # DC Info
2024 If ($ADDomainControllers)
2025 {
2026 Write-Verbose "[*] Total Domain Controllers: $([ADRecon.ADWSClass]::ObjectCount($ADDomainControllers))"
2027 # DC Info
2028 $DCObj = @()
2029 $ADDomainControllers | ForEach-Object {
2030 # Create the object for each instance.
2031 $Obj = New-Object PSObject
2032 $Obj | Add-Member -MemberType NoteProperty -Name "Domain" -Value $_.Domain
2033 $Obj | Add-Member -MemberType NoteProperty -Name "Site" -Value $_.Site
2034 $Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $_.Name
2035 $Obj | Add-Member -MemberType NoteProperty -Name "IPv4Address" -Value $_.IPv4Address
2036 $OSVersion = [ADRecon.ADWSClass]::CleanString($($_.OperatingSystem + " " + $_.OperatingSystemHotfix + " " + $_.OperatingSystemServicePack + " " + $_.OperatingSystemVersion))
2037 $Obj | Add-Member -MemberType NoteProperty -Name "Operating System" -Value $OSVersion
2038 Remove-Variable OSVersion
2039 $Obj | Add-Member -MemberType NoteProperty -Name "Hostname" -Value $_.HostName
2040 If ($_.OperationMasterRoles -like 'DomainNamingMaster')
2041 {
2042 $Obj | Add-Member -MemberType NoteProperty -Name "Naming" -Value $true
2043 }
2044 Else
2045 {
2046 $Obj | Add-Member -MemberType NoteProperty -Name "Naming" -Value $false
2047 }
2048 If ($_.OperationMasterRoles -like 'SchemaMaster')
2049 {
2050 $Obj | Add-Member -MemberType NoteProperty -Name "Schema" -Value $true
2051 }
2052 Else
2053 {
2054 $Obj | Add-Member -MemberType NoteProperty -Name "Schema" -Value $false
2055 }
2056 If ($_.OperationMasterRoles -like 'InfrastructureMaster')
2057 {
2058 $Obj | Add-Member -MemberType NoteProperty -Name "Infra" -Value $true
2059 }
2060 Else
2061 {
2062 $Obj | Add-Member -MemberType NoteProperty -Name "Infra" -Value $false
2063 }
2064 If ($_.OperationMasterRoles -like 'RIDMaster')
2065 {
2066 $Obj | Add-Member -MemberType NoteProperty -Name "RID" -Value $true
2067 }
2068 Else
2069 {
2070 $Obj | Add-Member -MemberType NoteProperty -Name "RID" -Value $false
2071 }
2072 If ($_.OperationMasterRoles -like 'PDCEmulator')
2073 {
2074 $Obj | Add-Member -MemberType NoteProperty -Name "PDC" -Value $true
2075 }
2076 Else
2077 {
2078 $Obj | Add-Member -MemberType NoteProperty -Name "PDC" -Value $false
2079 }
2080 $DCSMBObj = [ADRecon.PingCastleScannersSMBScanner]::GetPSObject($_.IPv4Address)
2081 ForEach ($Property in $DCSMBObj.psobject.Properties)
2082 {
2083 $Obj | Add-Member -MemberType NoteProperty -Name $Property.Name -Value $Property.value
2084 }
2085 $DCObj += $Obj
2086 }
2087 Remove-Variable ADDomainControllers
2088 }
2089 }
2090
2091 If ($Protocol -eq 'LDAP')
2092 {
2093 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
2094 {
2095 $DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
2096 $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$($DomainFQDN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
2097 Try
2098 {
2099 $ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
2100 }
2101 Catch
2102 {
2103 Write-Warning "[Get-ADRDomainController] Error getting Domain Context"
2104 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2105 Return $null
2106 }
2107 Remove-Variable DomainContext
2108 }
2109 Else
2110 {
2111 $ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
2112 }
2113
2114 If ($ADDomain.DomainControllers)
2115 {
2116 Write-Verbose "[*] Total Domain Controllers: $([ADRecon.LDAPClass]::ObjectCount($ADDomain.DomainControllers))"
2117 # DC Info
2118 $DCObj = @()
2119 $ADDomain.DomainControllers | ForEach-Object {
2120 # Create the object for each instance.
2121 $Obj = New-Object PSObject
2122 $Obj | Add-Member -MemberType NoteProperty -Name "Domain" -Value $_.Domain
2123 $Obj | Add-Member -MemberType NoteProperty -Name "Site" -Value $_.SiteName
2124 $Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value ($_.Name -Split ("\."))[0]
2125 $Obj | Add-Member -MemberType NoteProperty -Name "IPAddress" -Value $_.IPAddress
2126 $Obj | Add-Member -MemberType NoteProperty -Name "Operating System" -Value $_.OSVersion
2127 $Obj | Add-Member -MemberType NoteProperty -Name "Hostname" -Value $_.Name
2128 If ($null -ne $_.Roles)
2129 {
2130 $Obj | Add-Member -MemberType NoteProperty -Name "Naming" -Value $($_.Roles.Contains("NamingRole"))
2131 $Obj | Add-Member -MemberType NoteProperty -Name "Schema" -Value $($_.Roles.Contains("SchemaRole"))
2132 $Obj | Add-Member -MemberType NoteProperty -Name "Infra" -Value $($_.Roles.Contains("InfrastructureRole"))
2133 $Obj | Add-Member -MemberType NoteProperty -Name "RID" -Value $($_.Roles.Contains("RidRole"))
2134 $Obj | Add-Member -MemberType NoteProperty -Name "PDC" -Value $($_.Roles.Contains("PdcRole"))
2135 }
2136 Else
2137 {
2138
2139 "Naming", "Schema", "Infra", "RID", "PDC" | ForEach-Object {
2140 $Obj | Add-Member -MemberType NoteProperty -Name $_ -Value $false
2141 }
2142 }
2143 $DCSMBObj = [ADRecon.PingCastleScannersSMBScanner]::GetPSObject($_.IPAddress)
2144 ForEach ($Property in $DCSMBObj.psobject.Properties)
2145 {
2146 $Obj | Add-Member -MemberType NoteProperty -Name $Property.Name -Value $Property.value
2147 }
2148 $DCObj += $Obj
2149 }
2150 Remove-Variable ADDomain
2151 }
2152 }
2153
2154 If ($DCObj)
2155 {
2156 Return $DCObj
2157 }
2158 Else
2159 {
2160 Return $null
2161 }
2162}
2163
2164Function Get-ADRUser
2165{
2166<#
2167.SYNOPSIS
2168 Returns all users in the current (or specified) domain.
2169
2170.DESCRIPTION
2171 Returns all users in the current (or specified) domain.
2172
2173.PARAMETER Protocol
2174 [string]
2175 Which protocol to use; ADWS (default) or LDAP.
2176
2177.PARAMETER date
2178 [DateTime]
2179 Date when ADRecon was executed.
2180
2181.PARAMETER objDomain
2182 [DirectoryServices.DirectoryEntry]
2183 Domain Directory Entry object.
2184
2185.PARAMETER DormantTimeSpan
2186 [int]
2187 Timespan for Dormant accounts. Default 90 days.
2188
2189.PARAMETER PageSize
2190 [int]
2191 The PageSize to set for the LDAP searcher object. Default 200.
2192
2193.PARAMETER Threads
2194 [int]
2195 The number of threads to use during processing of objects. Default 10.
2196
2197.OUTPUTS
2198 PSObject.
2199#>
2200 param(
2201 [Parameter(Mandatory = $true)]
2202 [string] $Protocol,
2203
2204 [Parameter(Mandatory = $true)]
2205 [DateTime] $date,
2206
2207 [Parameter(Mandatory = $false)]
2208 [DirectoryServices.DirectoryEntry] $objDomain,
2209
2210 [Parameter(Mandatory = $true)]
2211 [int] $DormantTimeSpan = 90,
2212
2213 [Parameter(Mandatory = $true)]
2214 [int] $PageSize,
2215
2216 [Parameter(Mandatory = $false)]
2217 [int] $Threads = 10
2218 )
2219
2220 If ($Protocol -eq 'ADWS')
2221 {
2222 Try
2223 {
2224 $ADUsers = @( Get-ADUser -Filter * -ResultPageSize $PageSize -Properties AccountExpirationDate,accountExpires,AccountNotDelegated,AdminCount,AllowReversiblePasswordEncryption,c,CannotChangePassword,CanonicalName,Company,Department,Description,DistinguishedName,DoesNotRequirePreAuth,Enabled,givenName,homeDirectory,Info,LastLogonDate,lastLogonTimestamp,LockedOut,LogonWorkstations,mail,Manager,middleName,mobile,'msDS-AllowedToDelegateTo','msDS-SupportedEncryptionTypes',Name,PasswordExpired,PasswordLastSet,PasswordNeverExpires,PasswordNotRequired,primaryGroupID,profilePath,pwdlastset,SamAccountName,ScriptPath,SID,SIDHistory,SmartcardLogonRequired,sn,Title,TrustedForDelegation,TrustedToAuthForDelegation,UseDESKeyOnly,UserAccountControl,whenChanged,whenCreated )
2225 }
2226 Catch
2227 {
2228 Write-Warning "[Get-ADRUser] Error while enumerating User Objects"
2229 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2230 Return $null
2231 }
2232
2233 If ($ADUsers)
2234 {
2235 Try
2236 {
2237 $ADpasspolicy = Get-ADDefaultDomainPasswordPolicy
2238 $PassMaxAge = $ADpasspolicy.MaxPasswordAge.days
2239 Remove-Variable ADpasspolicy
2240 }
2241 Catch
2242 {
2243 Write-Warning "[Get-ADRUser] Error retrieving Max Password Age from the Default Password Policy. Using value as 90 days"
2244 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2245 $PassMaxAge = 90
2246 }
2247
2248 Write-Verbose "[*] Total Users: $([ADRecon.ADWSClass]::ObjectCount($ADUsers))"
2249 $UserObj = [ADRecon.ADWSClass]::UserParser($ADUsers, $date, $DormantTimeSpan, $PassMaxAge, $Threads)
2250 Remove-Variable ADUsers
2251 }
2252 }
2253
2254 If ($Protocol -eq 'LDAP')
2255 {
2256 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
2257 $ObjSearcher.PageSize = $PageSize
2258 $ObjSearcher.Filter = "(samAccountType=805306368)"
2259 # https://msdn.microsoft.com/en-us/library/system.directoryservices.securitymasks(v=vs.110).aspx
2260 $ObjSearcher.SecurityMasks = [System.DirectoryServices.SecurityMasks]'Dacl'
2261 $ObjSearcher.PropertiesToLoad.AddRange(("accountExpires","admincount","c","canonicalname","company","department","description","distinguishedname","givenName","homedirectory","info","lastLogontimestamp","mail","manager","middleName","mobile","msDS-AllowedToDelegateTo","msDS-SupportedEncryptionTypes","name","ntsecuritydescriptor","objectsid","primarygroupid","profilepath","pwdLastSet","samaccountName","scriptpath","sidhistory","sn","title","useraccountcontrol","userworkstations","whenchanged","whencreated"))
2262 $ObjSearcher.SearchScope = "Subtree"
2263 Try
2264 {
2265 $ADUsers = $ObjSearcher.FindAll()
2266 }
2267 Catch
2268 {
2269 Write-Warning "[Get-ADRUser] Error while enumerating User Objects"
2270 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2271 Return $null
2272 }
2273 $ObjSearcher.dispose()
2274
2275 If ($ADUsers)
2276 {
2277 $PassMaxAge = $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.maxpwdage.value) /-864000000000)
2278 If (-Not $PassMaxAge)
2279 {
2280 Write-Warning "[Get-ADRUser] Error retrieving Max Password Age from the Default Password Policy. Using value as 90 days"
2281 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2282 $PassMaxAge = 90
2283 }
2284
2285 Write-Verbose "[*] Total Users: $([ADRecon.LDAPClass]::ObjectCount($ADUsers))"
2286 $UserObj = [ADRecon.LDAPClass]::UserParser($ADUsers, $date, $DormantTimeSpan, $PassMaxAge, $Threads)
2287 Remove-Variable ADUsers
2288 }
2289 }
2290
2291 If ($UserObj)
2292 {
2293 Return $UserObj
2294 }
2295 Else
2296 {
2297 Return $null
2298 }
2299}
2300
2301Function Get-ADRUserSPN
2302{
2303<#
2304.SYNOPSIS
2305 Returns all user service principal name (SPN) in the current (or specified) domain.
2306
2307.DESCRIPTION
2308 Returns all user service principal name (SPN) in the current (or specified) domain.
2309
2310.PARAMETER Protocol
2311 [string]
2312 Which protocol to use; ADWS (default) or LDAP.
2313
2314.PARAMETER objDomain
2315 [DirectoryServices.DirectoryEntry]
2316 Domain Directory Entry object.
2317
2318.PARAMETER PageSize
2319 [int]
2320 The PageSize to set for the LDAP searcher object. Default 200.
2321
2322.PARAMETER Threads
2323 [int]
2324 The number of threads to use during processing of objects. Default 10.
2325
2326.OUTPUTS
2327 PSObject.
2328#>
2329 param(
2330 [Parameter(Mandatory = $true)]
2331 [string] $Protocol,
2332
2333 [Parameter(Mandatory = $false)]
2334 [DirectoryServices.DirectoryEntry] $objDomain,
2335
2336 [Parameter(Mandatory = $true)]
2337 [int] $PageSize,
2338
2339 [Parameter(Mandatory = $false)]
2340 [int] $Threads = 10
2341 )
2342
2343 If ($Protocol -eq 'ADWS')
2344 {
2345 Try
2346 {
2347 $ADUsers = @( Get-ADObject -LDAPFilter "(&(samAccountType=805306368)(servicePrincipalName=*))" -Properties Name,Description,memberOf,sAMAccountName,servicePrincipalName,primaryGroupID,pwdLastSet,userAccountControl -ResultPageSize $PageSize )
2348 }
2349 Catch
2350 {
2351 Write-Warning "[Get-ADRUserSPN] Error while enumerating UserSPN Objects"
2352 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2353 Return $null
2354 }
2355
2356 If ($ADUsers)
2357 {
2358 Write-Verbose "[*] Total UserSPNs: $([ADRecon.ADWSClass]::ObjectCount($ADUsers))"
2359 $UserSPNObj = [ADRecon.ADWSClass]::UserSPNParser($ADUsers, $Threads)
2360 Remove-Variable ADUsers
2361 }
2362 }
2363
2364 If ($Protocol -eq 'LDAP')
2365 {
2366 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
2367 $ObjSearcher.PageSize = $PageSize
2368 $ObjSearcher.Filter = "(&(samAccountType=805306368)(servicePrincipalName=*))"
2369 $ObjSearcher.PropertiesToLoad.AddRange(("name","description","memberof","samaccountname","serviceprincipalname","primarygroupid","pwdlastset","useraccountcontrol"))
2370 $ObjSearcher.SearchScope = "Subtree"
2371 Try
2372 {
2373 $ADUsers = $ObjSearcher.FindAll()
2374 }
2375 Catch
2376 {
2377 Write-Warning "[Get-ADRUserSPN] Error while enumerating UserSPN Objects"
2378 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2379 Return $null
2380 }
2381 $ObjSearcher.dispose()
2382
2383 If ($ADUsers)
2384 {
2385 Write-Verbose "[*] Total UserSPNs: $([ADRecon.LDAPClass]::ObjectCount($ADUsers))"
2386 $UserSPNObj = [ADRecon.LDAPClass]::UserSPNParser($ADUsers, $Threads)
2387 Remove-Variable ADUsers
2388 }
2389 }
2390
2391 If ($UserSPNObj)
2392 {
2393 Return $UserSPNObj
2394 }
2395 Else
2396 {
2397 Return $null
2398 }
2399
2400}
2401
2402#TODO
2403Function Get-ADRPasswordAttributes
2404{
2405<#
2406.SYNOPSIS
2407 Returns all objects with plaintext passwords in the current (or specified) domain.
2408
2409.DESCRIPTION
2410 Returns all objects with plaintext passwords in the current (or specified) domain.
2411
2412.PARAMETER Protocol
2413 [string]
2414 Which protocol to use; ADWS (default) or LDAP.
2415
2416.PARAMETER objDomain
2417 [DirectoryServices.DirectoryEntry]
2418 Domain Directory Entry object.
2419
2420.PARAMETER PageSize
2421 [int]
2422 The PageSize to set for the LDAP searcher object. Default 200.
2423
2424.OUTPUTS
2425 PSObject.
2426
2427.LINK
2428 https://www.ibm.com/support/knowledgecenter/en/ssw_aix_71/com.ibm.aix.security/ad_password_attribute_selection.htm
2429 https://msdn.microsoft.com/en-us/library/cc223248.aspx
2430 https://msdn.microsoft.com/en-us/library/cc223249.aspx
2431#>
2432 param(
2433 [Parameter(Mandatory = $true)]
2434 [string] $Protocol,
2435
2436 [Parameter(Mandatory = $false)]
2437 [DirectoryServices.DirectoryEntry] $objDomain,
2438
2439 [Parameter(Mandatory = $true)]
2440 [int] $PageSize
2441 )
2442
2443 If ($Protocol -eq 'ADWS')
2444 {
2445 Try
2446 {
2447 $ADUsers = Get-ADObject -LDAPFilter '(|(UserPassword=*)(UnixUserPassword=*)(unicodePwd=*)(msSFU30Password=*))' -ResultPageSize $PageSize -Properties *
2448 }
2449 Catch
2450 {
2451 Write-Warning "[Get-ADRPasswordAttributes] Error while enumerating Password Attributes"
2452 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2453 Return $null
2454 }
2455
2456 If ($ADUsers)
2457 {
2458 Write-Warning "[*] Total PasswordAttribute Objects: $([ADRecon.ADWSClass]::ObjectCount($ADUsers))"
2459 $UserObj = $ADUsers
2460 Remove-Variable ADUsers
2461 }
2462 }
2463
2464 If ($Protocol -eq 'LDAP')
2465 {
2466 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
2467 $ObjSearcher.PageSize = $PageSize
2468 $ObjSearcher.Filter = "(|(UserPassword=*)(UnixUserPassword=*)(unicodePwd=*)(msSFU30Password=*))"
2469 $ObjSearcher.SearchScope = "Subtree"
2470 Try
2471 {
2472 $ADUsers = $ObjSearcher.FindAll()
2473 }
2474 Catch
2475 {
2476 Write-Warning "[Get-ADRPasswordAttributes] Error while enumerating Password Attributes"
2477 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2478 Return $null
2479 }
2480 $ObjSearcher.dispose()
2481
2482 If ($ADUsers)
2483 {
2484 $cnt = [ADRecon.LDAPClass]::ObjectCount($ADUsers)
2485 If ($cnt -gt 0)
2486 {
2487 Write-Warning "[*] Total PasswordAttribute Objects: $cnt"
2488 }
2489 $UserObj = $ADUsers
2490 Remove-Variable ADUsers
2491 }
2492 }
2493
2494 If ($UserObj)
2495 {
2496 Return $UserObj
2497 }
2498 Else
2499 {
2500 Return $null
2501 }
2502}
2503
2504Function Get-ADRGroup
2505{
2506<#
2507.SYNOPSIS
2508 Returns all groups in the current (or specified) domain.
2509
2510.DESCRIPTION
2511 Returns all groups in the current (or specified) domain.
2512
2513.PARAMETER Protocol
2514 [string]
2515 Which protocol to use; ADWS (default) or LDAP.
2516
2517.PARAMETER objDomain
2518 [DirectoryServices.DirectoryEntry]
2519 Domain Directory Entry object.
2520
2521.PARAMETER PageSize
2522 [int]
2523 The PageSize to set for the LDAP searcher object. Default 200.
2524
2525.PARAMETER Threads
2526 [int]
2527 The number of threads to use during processing of objects. Default 10.
2528
2529.OUTPUTS
2530 PSObject.
2531#>
2532 param(
2533 [Parameter(Mandatory = $true)]
2534 [string] $Protocol,
2535
2536 [Parameter(Mandatory = $false)]
2537 [DirectoryServices.DirectoryEntry] $objDomain,
2538
2539 [Parameter(Mandatory = $true)]
2540 [int] $PageSize,
2541
2542 [Parameter(Mandatory = $false)]
2543 [int] $Threads = 10
2544 )
2545
2546 If ($Protocol -eq 'ADWS')
2547 {
2548 Try
2549 {
2550 $ADGroups = @( Get-ADGroup -Filter * -ResultPageSize $PageSize -Properties AdminCount,CanonicalName,DistinguishedName,Description,GroupCategory,GroupScope,SamAccountName,SID,SIDHistory,managedBy,whenChanged,whenCreated )
2551 }
2552 Catch
2553 {
2554 Write-Warning "[Get-ADRGroup] Error while enumerating Group Objects"
2555 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2556 Return $null
2557 }
2558
2559 If ($ADGroups)
2560 {
2561 Write-Verbose "[*] Total Groups: $([ADRecon.ADWSClass]::ObjectCount($ADGroups))"
2562 $GroupObj = [ADRecon.ADWSClass]::GroupParser($ADGroups, $Threads)
2563 Remove-Variable ADGroups
2564 }
2565 }
2566
2567 If ($Protocol -eq 'LDAP')
2568 {
2569 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
2570 $ObjSearcher.PageSize = $PageSize
2571 $ObjSearcher.Filter = "(objectClass=group)"
2572 $ObjSearcher.PropertiesToLoad.AddRange(("admincount","canonicalname", "distinguishedname", "description", "grouptype","samaccountname", "sidhistory", "managedby", "objectsid", "whencreated", "whenchanged"))
2573 $ObjSearcher.SearchScope = "Subtree"
2574
2575 Try
2576 {
2577 $ADGroups = $ObjSearcher.FindAll()
2578 }
2579 Catch
2580 {
2581 Write-Warning "[Get-ADRGroup] Error while enumerating Group Objects"
2582 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2583 Return $null
2584 }
2585 $ObjSearcher.dispose()
2586
2587 If ($ADGroups)
2588 {
2589 Write-Verbose "[*] Total Groups: $([ADRecon.LDAPClass]::ObjectCount($ADGroups))"
2590 $GroupObj = [ADRecon.LDAPClass]::GroupParser($ADGroups, $Threads)
2591 Remove-Variable ADGroups
2592 }
2593 }
2594
2595 If ($GroupObj)
2596 {
2597 Return $GroupObj
2598 }
2599 Else
2600 {
2601 Return $null
2602 }
2603}
2604
2605Function Get-ADRGroupMember
2606{
2607<#
2608.SYNOPSIS
2609 Returns all groups and their members in the current (or specified) domain.
2610
2611.DESCRIPTION
2612 Returns all groups and their members in the current (or specified) domain.
2613
2614.PARAMETER Protocol
2615 [string]
2616 Which protocol to use; ADWS (default) or LDAP.
2617
2618.PARAMETER objDomain
2619 [DirectoryServices.DirectoryEntry]
2620 Domain Directory Entry object.
2621
2622.PARAMETER PageSize
2623 [int]
2624 The PageSize to set for the LDAP searcher object. Default 200.
2625
2626.PARAMETER Threads
2627 [int]
2628 The number of threads to use during processing of objects. Default 10.
2629
2630.OUTPUTS
2631 PSObject.
2632#>
2633 param(
2634 [Parameter(Mandatory = $true)]
2635 [string] $Protocol,
2636
2637 [Parameter(Mandatory = $false)]
2638 [DirectoryServices.DirectoryEntry] $objDomain,
2639
2640 [Parameter(Mandatory = $true)]
2641 [int] $PageSize,
2642
2643 [Parameter(Mandatory = $false)]
2644 [int] $Threads = 10
2645 )
2646
2647 If ($Protocol -eq 'ADWS')
2648 {
2649 Try
2650 {
2651 $ADDomain = Get-ADDomain
2652 $ADDomainSID = $ADDomain.DomainSID.Value
2653 Remove-Variable ADDomain
2654 }
2655 Catch
2656 {
2657 Write-Warning "[Get-ADRGroupMember] Error getting Domain Context"
2658 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2659 Return $null
2660 }
2661
2662 Try
2663 {
2664 $ADGroups = $ADGroups = @( Get-ADGroup -Filter * -ResultPageSize $PageSize -Properties SamAccountName,SID )
2665 }
2666 Catch
2667 {
2668 Write-Warning "[Get-ADRGroupMember] Error while enumerating Group Objects"
2669 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2670 }
2671
2672 Try
2673 {
2674 $ADGroupMembers = @( Get-ADObject -LDAPFilter '(|(memberof=*)(primarygroupid=*))' -Properties DistinguishedName,memberof,primaryGroupID,sAMAccountName,samaccounttype )
2675 }
2676 Catch
2677 {
2678 Write-Warning "[Get-ADRGroupMember] Error while enumerating GroupMember Objects"
2679 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2680 Return $null
2681 }
2682
2683 If ( ($ADDomainSID) -and ($ADGroups) -and ($ADGroupMembers) )
2684 {
2685 Write-Verbose "[*] Total GroupMember Objects: $([ADRecon.ADWSClass]::ObjectCount($ADGroupMembers))"
2686 $GroupMemberObj = [ADRecon.ADWSClass]::GroupMemberParser($ADGroups, $ADGroupMembers, $ADDomainSID, $Threads)
2687 Remove-Variable ADGroups
2688 Remove-Variable ADGroupMembers
2689 Remove-Variable ADDomainSID
2690 }
2691 }
2692
2693 If ($Protocol -eq 'LDAP')
2694 {
2695
2696 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
2697 {
2698 $DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
2699 $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$($DomainFQDN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
2700 Try
2701 {
2702 $ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
2703 }
2704 Catch
2705 {
2706 Write-Warning "[Get-ADRGroupMember] Error getting Domain Context"
2707 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2708 Return $null
2709 }
2710 Remove-Variable DomainContext
2711 Try
2712 {
2713 $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Forest",$($ADDomain.Forest),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
2714 $ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
2715 }
2716 Catch
2717 {
2718 Write-Warning "[Get-ADRGroupMember] Error getting Forest Context"
2719 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2720 }
2721 If ($ForestContext)
2722 {
2723 Remove-Variable ForestContext
2724 }
2725 If ($ADForest)
2726 {
2727 $GlobalCatalog = $ADForest.FindGlobalCatalog()
2728 }
2729 If ($GlobalCatalog)
2730 {
2731 $DN = "GC://$($GlobalCatalog.IPAddress)/$($objDomain.distinguishedname)"
2732 Try
2733 {
2734 $ADObject = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList ($($DN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
2735 $ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($ADObject.objectSid[0], 0)
2736 $ADObject.Dispose()
2737 }
2738 Catch
2739 {
2740 Write-Warning "[Get-ADRGroupMember] Error retrieving Domain SID using the GlobalCatalog $($GlobalCatalog.IPAddress). Using SID from the ObjDomain."
2741 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2742 $ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objectSid[0], 0)
2743 }
2744 }
2745 Else
2746 {
2747 $ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objectSid[0], 0)
2748 }
2749 }
2750 Else
2751 {
2752 $ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
2753 $ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
2754 Try
2755 {
2756 $GlobalCatalog = $ADForest.FindGlobalCatalog()
2757 $DN = "GC://$($GlobalCatalog)/$($objDomain.distinguishedname)"
2758 $ADObject = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList ($DN)
2759 $ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($ADObject.objectSid[0], 0)
2760 $ADObject.dispose()
2761 }
2762 Catch
2763 {
2764 Write-Warning "[Get-ADRGroupMember] Error retrieving Domain SID using the GlobalCatalog $($GlobalCatalog.IPAddress). Using SID from the ObjDomain."
2765 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2766 $ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objectSid[0], 0)
2767 }
2768 }
2769
2770 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
2771 $ObjSearcher.PageSize = $PageSize
2772 $ObjSearcher.Filter = "(objectClass=group)"
2773 $ObjSearcher.PropertiesToLoad.AddRange(("samaccountname", "objectsid"))
2774 $ObjSearcher.SearchScope = "Subtree"
2775
2776 Try
2777 {
2778 $ADGroups = $ObjSearcher.FindAll()
2779 }
2780 Catch
2781 {
2782 Write-Warning "[Get-ADRGroupMember] Error while enumerating Group Objects"
2783 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2784 Return $null
2785 }
2786 $ObjSearcher.dispose()
2787
2788 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
2789 $ObjSearcher.PageSize = $PageSize
2790 $ObjSearcher.Filter = "(|(memberof=*)(primarygroupid=*))"
2791 $ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname", "dnshostname", "primarygroupid", "memberof", "samaccountname", "samaccounttype"))
2792 $ObjSearcher.SearchScope = "Subtree"
2793
2794 Try
2795 {
2796 $ADGroupMembers = $ObjSearcher.FindAll()
2797 }
2798 Catch
2799 {
2800 Write-Warning "[Get-ADRGroupMember] Error while enumerating GroupMember Objects"
2801 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2802 Return $null
2803 }
2804 $ObjSearcher.dispose()
2805
2806 If ( ($ADDomainSID) -and ($ADGroups) -and ($ADGroupMembers) )
2807 {
2808 Write-Verbose "[*] Total GroupMember Objects: $([ADRecon.LDAPClass]::ObjectCount($ADGroupMembers))"
2809 $GroupMemberObj = [ADRecon.LDAPClass]::GroupMemberParser($ADGroups, $ADGroupMembers, $ADDomainSID, $Threads)
2810 Remove-Variable ADGroups
2811 Remove-Variable ADGroupMembers
2812 Remove-Variable ADDomainSID
2813 }
2814 }
2815
2816 If ($GroupMemberObj)
2817 {
2818 Return $GroupMemberObj
2819 }
2820 Else
2821 {
2822 Return $null
2823 }
2824}
2825
2826Function Get-ADROU
2827{
2828<#
2829.SYNOPSIS
2830 Returns all Organizational Units (OU) in the current (or specified) domain.
2831
2832.DESCRIPTION
2833 Returns all Organizational Units (OU) in the current (or specified) domain.
2834
2835.PARAMETER Protocol
2836 [string]
2837 Which protocol to use; ADWS (default) or LDAP.
2838
2839.PARAMETER objDomain
2840 [DirectoryServices.DirectoryEntry]
2841 Domain Directory Entry object.
2842
2843.PARAMETER PageSize
2844 [int]
2845 The PageSize to set for the LDAP searcher object. Default 200.
2846
2847.PARAMETER Threads
2848 [int]
2849 The number of threads to use during processing of objects. Default 10.
2850
2851.OUTPUTS
2852 PSObject.
2853#>
2854 param(
2855 [Parameter(Mandatory = $true)]
2856 [string] $Protocol,
2857
2858 [Parameter(Mandatory = $false)]
2859 [DirectoryServices.DirectoryEntry] $objDomain,
2860
2861 [Parameter(Mandatory = $true)]
2862 [int] $PageSize,
2863
2864 [Parameter(Mandatory = $false)]
2865 [int] $Threads = 10
2866 )
2867
2868 If ($Protocol -eq 'ADWS')
2869 {
2870 Try
2871 {
2872 $ADOUs = @( Get-ADOrganizationalUnit -Filter * -Properties DistinguishedName,Description,Name,whenCreated,whenChanged )
2873 }
2874 Catch
2875 {
2876 Write-Warning "[Get-ADROU] Error while enumerating OU Objects"
2877 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2878 Return $null
2879 }
2880
2881 If ($ADOUs)
2882 {
2883 Write-Verbose "[*] Total OUs: $([ADRecon.ADWSClass]::ObjectCount($ADOUs))"
2884 $OUObj = [ADRecon.ADWSClass]::OUParser($ADOUs, $Threads)
2885 Remove-Variable ADOUs
2886 }
2887 }
2888
2889 If ($Protocol -eq 'LDAP')
2890 {
2891 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
2892 $ObjSearcher.PageSize = $PageSize
2893 $ObjSearcher.Filter = "(objectclass=organizationalunit)"
2894 $ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","description","name","whencreated","whenchanged"))
2895 $ObjSearcher.SearchScope = "Subtree"
2896
2897 Try
2898 {
2899 $ADOUs = $ObjSearcher.FindAll()
2900 }
2901 Catch
2902 {
2903 Write-Warning "[Get-ADROU] Error while enumerating OU Objects"
2904 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2905 Return $null
2906 }
2907 $ObjSearcher.dispose()
2908
2909 If ($ADOUs)
2910 {
2911 Write-Verbose "[*] Total OUs: $([ADRecon.LDAPClass]::ObjectCount($ADOUs))"
2912 $OUObj = [ADRecon.LDAPClass]::OUParser($ADOUs, $Threads)
2913 Remove-Variable ADOUs
2914 }
2915 }
2916
2917 If ($OUObj)
2918 {
2919 Return $OUObj
2920 }
2921 Else
2922 {
2923 Return $null
2924 }
2925}
2926
2927Function Get-ADRGPO
2928{
2929<#
2930.SYNOPSIS
2931 Returns all Group Policy Objects (GPO) in the current (or specified) domain.
2932
2933.DESCRIPTION
2934 Returns all Group Policy Objects (GPO) in the current (or specified) domain.
2935
2936.PARAMETER Protocol
2937 [string]
2938 Which protocol to use; ADWS (default) or LDAP.
2939
2940.PARAMETER objDomain
2941 [DirectoryServices.DirectoryEntry]
2942 Domain Directory Entry object.
2943
2944.PARAMETER Credential
2945 [Management.Automation.PSCredential]
2946 Credentials.
2947
2948.PARAMETER PageSize
2949 [int]
2950 The PageSize to set for the LDAP searcher object. Default 200.
2951
2952.PARAMETER Threads
2953 [int]
2954 The number of threads to use during processing of objects. Default 10.
2955
2956.OUTPUTS
2957 PSObject.
2958#>
2959 param(
2960 [Parameter(Mandatory = $true)]
2961 [string] $Protocol,
2962
2963 [Parameter(Mandatory = $false)]
2964 [DirectoryServices.DirectoryEntry] $objDomain,
2965
2966 [Parameter(Mandatory = $true)]
2967 [int] $PageSize,
2968
2969 [Parameter(Mandatory = $false)]
2970 [int] $Threads = 10
2971 )
2972
2973 If ($Protocol -eq 'ADWS')
2974 {
2975 Try
2976 {
2977 $ADGPOs = @( Get-ADObject -LDAPFilter '(objectCategory=groupPolicyContainer)' -Properties DisplayName,DistinguishedName,Name,gPCFileSysPath,whenCreated,whenChanged )
2978 }
2979 Catch
2980 {
2981 Write-Warning "[Get-ADRGPO] Error while enumerating groupPolicyContainer Objects"
2982 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
2983 Return $null
2984 }
2985
2986 If ($ADGPOs)
2987 {
2988 Write-Verbose "[*] Total GPOs: $([ADRecon.ADWSClass]::ObjectCount($ADGPOs))"
2989 $GPOsObj = [ADRecon.ADWSClass]::GPOParser($ADGPOs, $Threads)
2990 Remove-Variable ADGPOs
2991 }
2992 }
2993
2994 If ($Protocol -eq 'LDAP')
2995 {
2996 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
2997 $ObjSearcher.PageSize = $PageSize
2998 $ObjSearcher.Filter = "(objectCategory=groupPolicyContainer)"
2999 $ObjSearcher.SearchScope = "Subtree"
3000
3001 Try
3002 {
3003 $ADGPOs = $ObjSearcher.FindAll()
3004 }
3005 Catch
3006 {
3007 Write-Warning "[Get-ADRGPO] Error while enumerating groupPolicyContainer Objects"
3008 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3009 Return $null
3010 }
3011 $ObjSearcher.dispose()
3012
3013 If ($ADGPOs)
3014 {
3015 Write-Verbose "[*] Total GPOs: $([ADRecon.LDAPClass]::ObjectCount($ADGPOs))"
3016 $GPOsObj = [ADRecon.LDAPClass]::GPOParser($ADGPOs, $Threads)
3017 Remove-Variable ADGPOs
3018 }
3019 }
3020
3021 If ($GPOsObj)
3022 {
3023 Return $GPOsObj
3024 }
3025 Else
3026 {
3027 Return $null
3028 }
3029}
3030
3031# based on https://github.com/GoateePFE/GPLinkReport/blob/master/gPLinkReport.ps1
3032Function Get-ADRGPLink
3033{
3034<#
3035.SYNOPSIS
3036 Returns all group policy links (gPLink) applied to Scope of Management (SOM) in the current (or specified) domain.
3037
3038.DESCRIPTION
3039 Returns all group policy links (gPLink) applied to Scope of Management (SOM) in the current (or specified) domain.
3040
3041.PARAMETER Protocol
3042 [string]
3043 Which protocol to use; ADWS (default) or LDAP.
3044
3045.PARAMETER objDomain
3046 [DirectoryServices.DirectoryEntry]
3047 Domain Directory Entry object.
3048
3049.PARAMETER PageSize
3050 [int]
3051 The PageSize to set for the LDAP searcher object. Default 200.
3052
3053.PARAMETER Threads
3054 [int]
3055 The number of threads to use during processing of objects. Default 10.
3056
3057.OUTPUTS
3058 PSObject.
3059#>
3060 param(
3061 [Parameter(Mandatory = $true)]
3062 [string] $Protocol,
3063
3064 [Parameter(Mandatory = $false)]
3065 [DirectoryServices.DirectoryEntry] $objDomain,
3066
3067 [Parameter(Mandatory = $true)]
3068 [int] $PageSize,
3069
3070 [Parameter(Mandatory = $false)]
3071 [int] $Threads = 10
3072 )
3073
3074 If ($Protocol -eq 'ADWS')
3075 {
3076 Try
3077 {
3078 $ADSOMs = @( Get-ADObject -LDAPFilter '(|(objectclass=domain)(objectclass=organizationalUnit))' -Properties DistinguishedName,Name,gPLink,gPOptions )
3079 $ADSOMs += @( Get-ADObject -SearchBase "CN=Sites,$((Get-ADRootDSE).configurationNamingContext)" -LDAPFilter "(objectclass=site)" -Properties DistinguishedName,Name,gPLink,gPOptions )
3080 }
3081 Catch
3082 {
3083 Write-Warning "[Get-ADRGPLink] Error while enumerating SOM Objects"
3084 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3085 Return $null
3086 }
3087
3088 Try
3089 {
3090 $ADGPOs = @( Get-ADObject -LDAPFilter '(objectCategory=groupPolicyContainer)' -Properties DisplayName,DistinguishedName )
3091 }
3092 Catch
3093 {
3094 Write-Warning "[Get-ADRGPLink] Error while enumerating groupPolicyContainer Objects"
3095 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3096 Return $null
3097 }
3098
3099 If ( ($ADSOMs) -and ($ADGPOs) )
3100 {
3101 Write-Verbose "[*] Total SOMs: $([ADRecon.ADWSClass]::ObjectCount($ADSOMs))"
3102 $SOMObj = [ADRecon.ADWSClass]::SOMParser($ADGPOs, $ADSOMs, $Threads)
3103 Remove-Variable ADSOMs
3104 Remove-Variable ADGPOs
3105 }
3106 }
3107
3108 If ($Protocol -eq 'LDAP')
3109 {
3110 $ADSOMs = @()
3111 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
3112 $ObjSearcher.PageSize = $PageSize
3113 $ObjSearcher.Filter = "(|(objectclass=domain)(objectclass=organizationalUnit))"
3114 $ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","name","gplink","gpoptions"))
3115 $ObjSearcher.SearchScope = "Subtree"
3116
3117 Try
3118 {
3119 $ADSOMs += $ObjSearcher.FindAll()
3120 }
3121 Catch
3122 {
3123 Write-Warning "[Get-ADRGPLink] Error while enumerating SOM Objects"
3124 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3125 Return $null
3126 }
3127 $ObjSearcher.dispose()
3128
3129 $SearchPath = "CN=Sites"
3130 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
3131 {
3132 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$SearchPath,$($objDomainRootDSE.ConfigurationNamingContext)", $Credential.UserName,$Credential.GetNetworkCredential().Password
3133 }
3134 Else
3135 {
3136 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$SearchPath,$($objDomainRootDSE.ConfigurationNamingContext)"
3137 }
3138 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
3139 $ObjSearcher.Filter = "(objectclass=site)"
3140 $ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","name","gplink","gpoptions"))
3141 $ObjSearcher.SearchScope = "Subtree"
3142
3143 Try
3144 {
3145 $ADSOMs += $ObjSearcher.FindAll()
3146 }
3147 Catch
3148 {
3149 Write-Warning "[Get-ADRGPLink] Error while enumerating SOM Objects"
3150 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3151 Return $null
3152 }
3153 $ObjSearcher.dispose()
3154
3155 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
3156 $ObjSearcher.PageSize = $PageSize
3157 $ObjSearcher.Filter = "(objectCategory=groupPolicyContainer)"
3158 $ObjSearcher.SearchScope = "Subtree"
3159
3160 Try
3161 {
3162 $ADGPOs = $ObjSearcher.FindAll()
3163 }
3164 Catch
3165 {
3166 Write-Warning "[Get-ADRGPLink] Error while enumerating groupPolicyContainer Objects"
3167 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3168 Return $null
3169 }
3170 $ObjSearcher.dispose()
3171
3172 If ( ($ADSOMs) -and ($ADGPOs) )
3173 {
3174 Write-Verbose "[*] Total SOMs: $([ADRecon.LDAPClass]::ObjectCount($ADSOMs))"
3175 $SOMObj = [ADRecon.LDAPClass]::SOMParser($ADGPOs, $ADSOMs, $Threads)
3176 Remove-Variable ADSOMs
3177 Remove-Variable ADGPOs
3178 }
3179 }
3180
3181 If ($SOMObj)
3182 {
3183 Return $SOMObj
3184 }
3185 Else
3186 {
3187 Return $null
3188 }
3189}
3190
3191# Modified Convert-DNSRecord function from https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1
3192Function Convert-DNSRecord
3193{
3194<#
3195.SYNOPSIS
3196
3197Helpers that decodes a binary DNS record blob.
3198
3199Author: Michael B. Smith, Will Schroeder (@harmj0y)
3200License: BSD 3-Clause
3201Required Dependencies: None
3202
3203.DESCRIPTION
3204
3205Decodes a binary blob representing an Active Directory DNS entry.
3206Used by Get-DomainDNSRecord.
3207
3208Adapted/ported from Michael B. Smith's code at https://raw.githubusercontent.com/mmessano/PowerShell/master/dns-dump.ps1
3209
3210.PARAMETER DNSRecord
3211
3212A byte array representing the DNS record.
3213
3214.OUTPUTS
3215
3216System.Management.Automation.PSCustomObject
3217
3218Outputs custom PSObjects with detailed information about the DNS record entry.
3219
3220.LINK
3221
3222https://raw.githubusercontent.com/mmessano/PowerShell/master/dns-dump.ps1
3223#>
3224
3225 [OutputType('System.Management.Automation.PSCustomObject')]
3226 [CmdletBinding()]
3227 Param(
3228 [Parameter(Position = 0, Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
3229 [Byte[]]
3230 $DNSRecord
3231 )
3232
3233 BEGIN {
3234 Function Get-Name
3235 {
3236 [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '')]
3237 [CmdletBinding()]
3238 Param(
3239 [Byte[]]
3240 $Raw
3241 )
3242
3243 [Int]$Length = $Raw[0]
3244 [Int]$Segments = $Raw[1]
3245 [Int]$Index = 2
3246 [String]$Name = ''
3247
3248 while ($Segments-- -gt 0)
3249 {
3250 [Int]$SegmentLength = $Raw[$Index++]
3251 while ($SegmentLength-- -gt 0)
3252 {
3253 $Name += [Char]$Raw[$Index++]
3254 }
3255 $Name += "."
3256 }
3257 $Name
3258 }
3259 }
3260
3261 PROCESS
3262 {
3263 # $RDataLen = [BitConverter]::ToUInt16($DNSRecord, 0)
3264 $RDataType = [BitConverter]::ToUInt16($DNSRecord, 2)
3265 $UpdatedAtSerial = [BitConverter]::ToUInt32($DNSRecord, 8)
3266
3267 $TTLRaw = $DNSRecord[12..15]
3268
3269 # reverse for big endian
3270 $Null = [array]::Reverse($TTLRaw)
3271 $TTL = [BitConverter]::ToUInt32($TTLRaw, 0)
3272
3273 $Age = [BitConverter]::ToUInt32($DNSRecord, 20)
3274 If ($Age -ne 0)
3275 {
3276 $TimeStamp = ((Get-Date -Year 1601 -Month 1 -Day 1 -Hour 0 -Minute 0 -Second 0).AddHours($age)).ToString()
3277 }
3278 Else
3279 {
3280 $TimeStamp = '[static]'
3281 }
3282
3283 $DNSRecordObject = New-Object PSObject
3284
3285 switch ($RDataType)
3286 {
3287 1
3288 {
3289 $IP = "{0}.{1}.{2}.{3}" -f $DNSRecord[24], $DNSRecord[25], $DNSRecord[26], $DNSRecord[27]
3290 $Data = $IP
3291 $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'A'
3292 }
3293
3294 2
3295 {
3296 $NSName = Get-Name $DNSRecord[24..$DNSRecord.length]
3297 $Data = $NSName
3298 $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'NS'
3299 }
3300
3301 5
3302 {
3303 $Alias = Get-Name $DNSRecord[24..$DNSRecord.length]
3304 $Data = $Alias
3305 $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'CNAME'
3306 }
3307
3308 6
3309 {
3310 $PrimaryNS = Get-Name $DNSRecord[44..$DNSRecord.length]
3311 $ResponsibleParty = Get-Name $DNSRecord[$(46+$DNSRecord[44])..$DNSRecord.length]
3312 $SerialRaw = $DNSRecord[24..27]
3313 # reverse for big endian
3314 $Null = [array]::Reverse($SerialRaw)
3315 $Serial = [BitConverter]::ToUInt32($SerialRaw, 0)
3316
3317 $RefreshRaw = $DNSRecord[28..31]
3318 $Null = [array]::Reverse($RefreshRaw)
3319 $Refresh = [BitConverter]::ToUInt32($RefreshRaw, 0)
3320
3321 $RetryRaw = $DNSRecord[32..35]
3322 $Null = [array]::Reverse($RetryRaw)
3323 $Retry = [BitConverter]::ToUInt32($RetryRaw, 0)
3324
3325 $ExpiresRaw = $DNSRecord[36..39]
3326 $Null = [array]::Reverse($ExpiresRaw)
3327 $Expires = [BitConverter]::ToUInt32($ExpiresRaw, 0)
3328
3329 $MinTTLRaw = $DNSRecord[40..43]
3330 $Null = [array]::Reverse($MinTTLRaw)
3331 $MinTTL = [BitConverter]::ToUInt32($MinTTLRaw, 0)
3332
3333 $Data = "[" + $Serial + "][" + $PrimaryNS + "][" + $ResponsibleParty + "][" + $Refresh + "][" + $Retry + "][" + $Expires + "][" + $MinTTL + "]"
3334 $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'SOA'
3335 }
3336
3337 12
3338 {
3339 $Ptr = Get-Name $DNSRecord[24..$DNSRecord.length]
3340 $Data = $Ptr
3341 $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'PTR'
3342 }
3343
3344 13
3345 {
3346 [string]$CPUType = ""
3347 [string]$OSType = ""
3348 [int]$SegmentLength = $DNSRecord[24]
3349 $Index = 25
3350 while ($SegmentLength-- -gt 0)
3351 {
3352 $CPUType += [char]$DNSRecord[$Index++]
3353 }
3354 $Index = 24 + $DNSRecord[24] + 1
3355 [int]$SegmentLength = $Index++
3356 while ($SegmentLength-- -gt 0)
3357 {
3358 $OSType += [char]$DNSRecord[$Index++]
3359 }
3360 $Data = "[" + $CPUType + "][" + $OSType + "]"
3361 $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'HINFO'
3362 }
3363
3364 15
3365 {
3366 $PriorityRaw = $DNSRecord[24..25]
3367 # reverse for big endian
3368 $Null = [array]::Reverse($PriorityRaw)
3369 $Priority = [BitConverter]::ToUInt16($PriorityRaw, 0)
3370 $MXHost = Get-Name $DNSRecord[26..$DNSRecord.length]
3371 $Data = "[" + $Priority + "][" + $MXHost + "]"
3372 $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'MX'
3373 }
3374
3375 16
3376 {
3377 [string]$TXT = ''
3378 [int]$SegmentLength = $DNSRecord[24]
3379 $Index = 25
3380 while ($SegmentLength-- -gt 0)
3381 {
3382 $TXT += [char]$DNSRecord[$Index++]
3383 }
3384 $Data = $TXT
3385 $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'TXT'
3386 }
3387
3388 28
3389 {
3390 ### yeah, this doesn't do all the fancy formatting that can be done for IPv6
3391 $AAAA = ""
3392 for ($i = 24; $i -lt 40; $i+=2)
3393 {
3394 $BlockRaw = $DNSRecord[$i..$($i+1)]
3395 # reverse for big endian
3396 $Null = [array]::Reverse($BlockRaw)
3397 $Block = [BitConverter]::ToUInt16($BlockRaw, 0)
3398 $AAAA += ($Block).ToString('x4')
3399 If ($i -ne 38)
3400 {
3401 $AAAA += ':'
3402 }
3403 }
3404 $Data = $AAAA
3405 $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'AAAA'
3406 }
3407
3408 33
3409 {
3410 $PriorityRaw = $DNSRecord[24..25]
3411 # reverse for big endian
3412 $Null = [array]::Reverse($PriorityRaw)
3413 $Priority = [BitConverter]::ToUInt16($PriorityRaw, 0)
3414
3415 $WeightRaw = $DNSRecord[26..27]
3416 $Null = [array]::Reverse($WeightRaw)
3417 $Weight = [BitConverter]::ToUInt16($WeightRaw, 0)
3418
3419 $PortRaw = $DNSRecord[28..29]
3420 $Null = [array]::Reverse($PortRaw)
3421 $Port = [BitConverter]::ToUInt16($PortRaw, 0)
3422
3423 $SRVHost = Get-Name $DNSRecord[30..$DNSRecord.length]
3424 $Data = "[" + $Priority + "][" + $Weight + "][" + $Port + "][" + $SRVHost + "]"
3425 $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'SRV'
3426 }
3427
3428 default
3429 {
3430 $Data = $([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
3431 $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'UNKNOWN'
3432 }
3433 }
3434 $DNSRecordObject | Add-Member Noteproperty 'UpdatedAtSerial' $UpdatedAtSerial
3435 $DNSRecordObject | Add-Member Noteproperty 'TTL' $TTL
3436 $DNSRecordObject | Add-Member Noteproperty 'Age' $Age
3437 $DNSRecordObject | Add-Member Noteproperty 'TimeStamp' $TimeStamp
3438 $DNSRecordObject | Add-Member Noteproperty 'Data' $Data
3439 Return $DNSRecordObject
3440 }
3441}
3442
3443Function Get-ADRDNSZone
3444{
3445<#
3446.SYNOPSIS
3447 Returns all DNS Zones and Records in the current (or specified) domain.
3448
3449.DESCRIPTION
3450 Returns all DNS Zones and Records in the current (or specified) domain.
3451
3452.PARAMETER Protocol
3453 [string]
3454 Which protocol to use; ADWS (default) or LDAP.
3455
3456.PARAMETER ADROutputDir
3457 [string]
3458 Path for ADRecon output folder.
3459
3460.PARAMETER objDomain
3461 [DirectoryServices.DirectoryEntry]
3462 Domain Directory Entry object.
3463
3464.PARAMETER DomainController
3465 [string]
3466 IP Address of the Domain Controller.
3467
3468.PARAMETER Credential
3469 [Management.Automation.PSCredential]
3470 Credentials.
3471
3472.PARAMETER PageSize
3473 [int]
3474 The PageSize to set for the LDAP searcher object. Default 200.
3475
3476.PARAMETER OutputType
3477 [array]
3478 Output Type.
3479
3480.OUTPUTS
3481 CSV files are created in the folder specified with the information.
3482#>
3483 param(
3484 [Parameter(Mandatory = $true)]
3485 [string] $Protocol,
3486
3487 [Parameter(Mandatory = $true)]
3488 [string] $ADROutputDir,
3489
3490 [Parameter(Mandatory = $false)]
3491 [DirectoryServices.DirectoryEntry] $objDomain,
3492
3493 [Parameter(Mandatory = $false)]
3494 [string] $DomainController,
3495
3496 [Parameter(Mandatory = $false)]
3497 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
3498
3499 [Parameter(Mandatory = $true)]
3500 [int] $PageSize,
3501
3502 [Parameter(Mandatory = $true)]
3503 [array] $OutputType
3504 )
3505
3506 If ($Protocol -eq 'ADWS')
3507 {
3508 Try
3509 {
3510 $ADDNSZones = Get-ADObject -LDAPFilter '(objectClass=dnsZone)' -Properties Name,whenCreated,whenChanged,usncreated,usnchanged,distinguishedname
3511 }
3512 Catch
3513 {
3514 Write-Warning "[Get-ADRDNSZone] Error while enumerating dnsZone Objects"
3515 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3516 }
3517
3518 $DNSZoneArray = @()
3519 If ($ADDNSZones)
3520 {
3521 $DNSZoneArray += $ADDNSZones
3522 Remove-Variable ADDNSZones
3523 }
3524
3525 Try
3526 {
3527 $ADDNSZones1 = Get-ADObject -LDAPFilter '(objectClass=dnsZone)' -SearchBase "DC=DomainDnsZones,$((Get-ADDomain).DistinguishedName)" -Properties Name,whenCreated,whenChanged,usncreated,usnchanged,distinguishedname
3528 }
3529 Catch
3530 {
3531 Write-Warning "[Get-ADRDNSZone] Error while enumerating DomainDnsZones dnsZone Objects"
3532 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3533 }
3534 If ($ADDNSZones1)
3535 {
3536 $DNSZoneArray += $ADDNSZones1
3537 Remove-Variable ADDNSZones1
3538 }
3539
3540 Try
3541 {
3542 $ADDNSZones2 = Get-ADObject -LDAPFilter '(objectClass=dnsZone)' -SearchBase "DC=ForestDnsZones,$((Get-ADDomain).DistinguishedName)" -Properties Name,whenCreated,whenChanged,usncreated,usnchanged,distinguishedname
3543 }
3544 Catch
3545 {
3546 Write-Warning "[Get-ADRDNSZone] Error while enumerating DC=ForestDnsZones,$((Get-ADDomain).DistinguishedName) dnsZone Objects"
3547 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3548 }
3549 If ($ADDNSZones2)
3550 {
3551 $DNSZoneArray += $ADDNSZones2
3552 Remove-Variable ADDNSZones2
3553 }
3554
3555 Write-Verbose "[*] Total DNS Zones: $([ADRecon.ADWSClass]::ObjectCount($DNSZoneArray))"
3556
3557 If ($DNSZoneArray)
3558 {
3559 $ADDNSZonesObj = @()
3560 $ADDNSNodesObj = @()
3561 $DNSZoneArray | ForEach-Object {
3562 # Create the object for each instance.
3563 $Obj = New-Object PSObject
3564 $Obj | Add-Member -MemberType NoteProperty -Name Name -Value $([ADRecon.ADWSClass]::CleanString($_.Name))
3565 Try
3566 {
3567 $DNSNodes = Get-ADObject -SearchBase $($_.DistinguishedName) -LDAPFilter '(objectClass=dnsNode)' -Properties DistinguishedName,dnsrecord,dNSTombstoned,Name,ProtectedFromAccidentalDeletion,showInAdvancedViewOnly,whenChanged,whenCreated
3568 }
3569 Catch
3570 {
3571 Write-Warning "[Get-ADRDNSZone] Error while enumerating $($_.DistinguishedName) dnsNode Objects"
3572 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3573 }
3574 If ($DNSNodes)
3575 {
3576 $Obj | Add-Member -MemberType NoteProperty -Name RecordCount -Value $($DNSNodes | Measure-Object | Select-Object -ExpandProperty Count)
3577 $DNSNodes | ForEach-Object {
3578 $ObjNode = New-Object PSObject
3579 $ObjNode | Add-Member -MemberType NoteProperty -Name ZoneName -Value $Obj.Name
3580 $ObjNode | Add-Member -MemberType NoteProperty -Name Name -Value $_.Name
3581 Try
3582 {
3583 $DNSRecord = Convert-DNSRecord $_.dnsrecord[0]
3584 }
3585 Catch
3586 {
3587 Write-Warning "[Get-ADRDNSZone] Error while converting the DNSRecord"
3588 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3589 }
3590 $ObjNode | Add-Member -MemberType NoteProperty -Name RecordType -Value $DNSRecord.RecordType
3591 $ObjNode | Add-Member -MemberType NoteProperty -Name Data -Value $DNSRecord.Data
3592 $ObjNode | Add-Member -MemberType NoteProperty -Name TTL -Value $DNSRecord.TTL
3593 $ObjNode | Add-Member -MemberType NoteProperty -Name Age -Value $DNSRecord.Age
3594 $ObjNode | Add-Member -MemberType NoteProperty -Name TimeStamp -Value $DNSRecord.TimeStamp
3595 $ObjNode | Add-Member -MemberType NoteProperty -Name UpdatedAtSerial -Value $DNSRecord.UpdatedAtSerial
3596 $ObjNode | Add-Member -MemberType NoteProperty -Name whenCreated -Value $_.whenCreated
3597 $ObjNode | Add-Member -MemberType NoteProperty -Name whenChanged -Value $_.whenChanged
3598 # TO DO LDAP part
3599 #$ObjNode | Add-Member -MemberType NoteProperty -Name dNSTombstoned -Value $_.dNSTombstoned
3600 #$ObjNode | Add-Member -MemberType NoteProperty -Name ProtectedFromAccidentalDeletion -Value $_.ProtectedFromAccidentalDeletion
3601 $ObjNode | Add-Member -MemberType NoteProperty -Name showInAdvancedViewOnly -Value $_.showInAdvancedViewOnly
3602 $ObjNode | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value $_.DistinguishedName
3603 $ADDNSNodesObj += $ObjNode
3604 If ($DNSRecord)
3605 {
3606 Remove-Variable DNSRecord
3607 }
3608 }
3609 }
3610 Else
3611 {
3612 $Obj | Add-Member -MemberType NoteProperty -Name RecordCount -Value $null
3613 }
3614 $Obj | Add-Member -MemberType NoteProperty -Name USNCreated -Value $_.usncreated
3615 $Obj | Add-Member -MemberType NoteProperty -Name USNChanged -Value $_.usnchanged
3616 $Obj | Add-Member -MemberType NoteProperty -Name whenCreated -Value $_.whenCreated
3617 $Obj | Add-Member -MemberType NoteProperty -Name whenChanged -Value $_.whenChanged
3618 $Obj | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value $_.DistinguishedName
3619 $ADDNSZonesObj += $Obj
3620 }
3621 Write-Verbose "[*] Total DNS Records: $([ADRecon.ADWSClass]::ObjectCount($ADDNSNodesObj))"
3622 Remove-Variable DNSZoneArray
3623 }
3624 }
3625
3626 If ($Protocol -eq 'LDAP')
3627 {
3628 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
3629 $ObjSearcher.PageSize = $PageSize
3630 $ObjSearcher.PropertiesToLoad.AddRange(("name","whencreated","whenchanged","usncreated","usnchanged","distinguishedname"))
3631 $ObjSearcher.Filter = "(objectClass=dnsZone)"
3632 $ObjSearcher.SearchScope = "Subtree"
3633
3634 Try
3635 {
3636 $ADDNSZones = $ObjSearcher.FindAll()
3637 }
3638 Catch
3639 {
3640 Write-Warning "[Get-ADRDNSZone] Error while enumerating dnsZone Objects"
3641 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3642 }
3643 $ObjSearcher.dispose()
3644
3645 $DNSZoneArray = @()
3646 If ($ADDNSZones)
3647 {
3648 $DNSZoneArray += $ADDNSZones
3649 Remove-Variable ADDNSZones
3650 }
3651
3652 $SearchPath = "DC=DomainDnsZones"
3653 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
3654 {
3655 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$($SearchPath),$($objDomain.distinguishedName)", $Credential.UserName,$Credential.GetNetworkCredential().Password
3656 }
3657 Else
3658 {
3659 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($SearchPath),$($objDomain.distinguishedName)"
3660 }
3661 $objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
3662 $objSearcherPath.Filter = "(objectClass=dnsZone)"
3663 $objSearcherPath.PageSize = $PageSize
3664 $objSearcherPath.PropertiesToLoad.AddRange(("name","whencreated","whenchanged","usncreated","usnchanged","distinguishedname"))
3665 $objSearcherPath.SearchScope = "Subtree"
3666
3667 Try
3668 {
3669 $ADDNSZones1 = $objSearcherPath.FindAll()
3670 }
3671 Catch
3672 {
3673 Write-Warning "[Get-ADRDNSZone] Error while enumerating DomainDnsZones dnsZone Objects."
3674 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3675 }
3676 $objSearcherPath.dispose()
3677
3678 If ($ADDNSZones1)
3679 {
3680 $DNSZoneArray += $ADDNSZones1
3681 Remove-Variable ADDNSZones1
3682 }
3683
3684 $SearchPath = "DC=ForestDnsZones"
3685 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
3686 {
3687 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$($SearchPath),$($objDomain.distinguishedName)", $Credential.UserName,$Credential.GetNetworkCredential().Password
3688 }
3689 Else
3690 {
3691 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($SearchPath),$($objDomain.distinguishedName)"
3692 }
3693 $objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
3694 $objSearcherPath.Filter = "(objectClass=dnsZone)"
3695 $objSearcherPath.PageSize = $PageSize
3696 $objSearcherPath.PropertiesToLoad.AddRange(("name","whencreated","whenchanged","usncreated","usnchanged","distinguishedname"))
3697 $objSearcherPath.SearchScope = "Subtree"
3698
3699 Try
3700 {
3701 $ADDNSZones2 = $objSearcherPath.FindAll()
3702 }
3703 Catch
3704 {
3705 Write-Warning "[Get-ADRDNSZone] Error while enumerating ForestDnsZones dnsZone Objects."
3706 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3707 }
3708 $objSearcherPath.dispose()
3709
3710 If ($ADDNSZones2)
3711 {
3712 $DNSZoneArray += $ADDNSZones2
3713 Remove-Variable ADDNSZones2
3714 }
3715
3716 Write-Verbose "[*] Total DNS Zones: $([ADRecon.LDAPClass]::ObjectCount($DNSZoneArray))"
3717
3718 If ($DNSZoneArray)
3719 {
3720 $ADDNSZonesObj = @()
3721 $ADDNSNodesObj = @()
3722 $DNSZoneArray | ForEach-Object {
3723 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
3724 {
3725 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$($_.Properties.distinguishedname)", $Credential.UserName,$Credential.GetNetworkCredential().Password
3726 }
3727 Else
3728 {
3729 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($_.Properties.distinguishedname)"
3730 }
3731 $objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
3732 $objSearcherPath.Filter = "(objectClass=dnsNode)"
3733 $objSearcherPath.PageSize = $PageSize
3734 $objSearcherPath.PropertiesToLoad.AddRange(("distinguishedname","dnsrecord","name","dc","showinadvancedviewonly","whenchanged","whencreated"))
3735 Try
3736 {
3737 $DNSNodes = $objSearcherPath.FindAll()
3738 }
3739 Catch
3740 {
3741 Write-Warning "[Get-ADRDNSZone] Error while enumerating $($_.Properties.distinguishedname) dnsNode Objects"
3742 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3743 }
3744 $objSearcherPath.dispose()
3745 Remove-Variable objSearchPath
3746
3747 # Create the object for each instance.
3748 $Obj = New-Object PSObject
3749 $Obj | Add-Member -MemberType NoteProperty -Name Name -Value $([ADRecon.LDAPClass]::CleanString($_.Properties.name[0]))
3750 If ($DNSNodes)
3751 {
3752 $Obj | Add-Member -MemberType NoteProperty -Name RecordCount -Value $($DNSNodes | Measure-Object | Select-Object -ExpandProperty Count)
3753 $DNSNodes | ForEach-Object {
3754 $ObjNode = New-Object PSObject
3755 $ObjNode | Add-Member -MemberType NoteProperty -Name ZoneName -Value $Obj.Name
3756 $name = ([string] $($_.Properties.name))
3757 If (-Not $name)
3758 {
3759 $name = ([string] $($_.Properties.dc))
3760 }
3761 $ObjNode | Add-Member -MemberType NoteProperty -Name Name -Value $name
3762 Try
3763 {
3764 $DNSRecord = Convert-DNSRecord $_.Properties.dnsrecord[0]
3765 }
3766 Catch
3767 {
3768 Write-Warning "[Get-ADRDNSZone] Error while converting the DNSRecord"
3769 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3770 }
3771 $ObjNode | Add-Member -MemberType NoteProperty -Name RecordType -Value $DNSRecord.RecordType
3772 $ObjNode | Add-Member -MemberType NoteProperty -Name Data -Value $DNSRecord.Data
3773 $ObjNode | Add-Member -MemberType NoteProperty -Name TTL -Value $DNSRecord.TTL
3774 $ObjNode | Add-Member -MemberType NoteProperty -Name Age -Value $DNSRecord.Age
3775 $ObjNode | Add-Member -MemberType NoteProperty -Name TimeStamp -Value $DNSRecord.TimeStamp
3776 $ObjNode | Add-Member -MemberType NoteProperty -Name UpdatedAtSerial -Value $DNSRecord.UpdatedAtSerial
3777 $ObjNode | Add-Member -MemberType NoteProperty -Name whenCreated -Value ([DateTime] $($_.Properties.whencreated))
3778 $ObjNode | Add-Member -MemberType NoteProperty -Name whenChanged -Value ([DateTime] $($_.Properties.whenchanged))
3779 # TO DO
3780 #$ObjNode | Add-Member -MemberType NoteProperty -Name dNSTombstoned -Value $null
3781 #$ObjNode | Add-Member -MemberType NoteProperty -Name ProtectedFromAccidentalDeletion -Value $null
3782 $ObjNode | Add-Member -MemberType NoteProperty -Name showInAdvancedViewOnly -Value ([string] $($_.Properties.showinadvancedviewonly))
3783 $ObjNode | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value ([string] $($_.Properties.distinguishedname))
3784 $ADDNSNodesObj += $ObjNode
3785 If ($DNSRecord)
3786 {
3787 Remove-Variable DNSRecord
3788 }
3789 }
3790 }
3791 Else
3792 {
3793 $Obj | Add-Member -MemberType NoteProperty -Name RecordCount -Value $null
3794 }
3795 $Obj | Add-Member -MemberType NoteProperty -Name USNCreated -Value ([string] $($_.Properties.usncreated))
3796 $Obj | Add-Member -MemberType NoteProperty -Name USNChanged -Value ([string] $($_.Properties.usnchanged))
3797 $Obj | Add-Member -MemberType NoteProperty -Name whenCreated -Value ([DateTime] $($_.Properties.whencreated))
3798 $Obj | Add-Member -MemberType NoteProperty -Name whenChanged -Value ([DateTime] $($_.Properties.whenchanged))
3799 $Obj | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value ([string] $($_.Properties.distinguishedname))
3800 $ADDNSZonesObj += $Obj
3801 }
3802 Write-Verbose "[*] Total DNS Records: $([ADRecon.LDAPClass]::ObjectCount($ADDNSNodesObj))"
3803 Remove-Variable DNSZoneArray
3804 }
3805 }
3806
3807 If ($ADDNSZonesObj)
3808 {
3809 Export-ADR $ADDNSZonesObj $ADROutputDir $OutputType "DNSZones"
3810 Remove-Variable ADDNSZonesObj
3811 }
3812
3813 If ($ADDNSNodesObj)
3814 {
3815 Export-ADR $ADDNSNodesObj $ADROutputDir $OutputType "DNSNodes"
3816 Remove-Variable ADDNSNodesObj
3817 }
3818}
3819
3820Function Get-ADRPrinter
3821{
3822<#
3823.SYNOPSIS
3824 Returns all printers in the current (or specified) domain.
3825
3826.DESCRIPTION
3827 Returns all printers in the current (or specified) domain.
3828
3829.PARAMETER Protocol
3830 [string]
3831 Which protocol to use; ADWS (default) or LDAP.
3832
3833.PARAMETER objDomain
3834 [DirectoryServices.DirectoryEntry]
3835 Domain Directory Entry object.
3836
3837.PARAMETER PageSize
3838 [int]
3839 The PageSize to set for the LDAP searcher object. Default 200.
3840
3841.PARAMETER Threads
3842 [int]
3843 The number of threads to use during processing of objects. Default 10.
3844
3845.OUTPUTS
3846 PSObject.
3847#>
3848
3849 param(
3850 [Parameter(Mandatory = $true)]
3851 [string] $Protocol,
3852
3853 [Parameter(Mandatory = $false)]
3854 [DirectoryServices.DirectoryEntry] $objDomain,
3855
3856 [Parameter(Mandatory = $true)]
3857 [int] $PageSize,
3858
3859 [Parameter(Mandatory = $false)]
3860 [int] $Threads = 10
3861 )
3862
3863 If ($Protocol -eq 'ADWS')
3864 {
3865 Try
3866 {
3867 $ADPrinters = @( Get-ADObject -LDAPFilter '(objectCategory=printQueue)' -Properties driverName,driverVersion,Name,portName,printShareName,serverName,url,whenChanged,whenCreated )
3868 }
3869 Catch
3870 {
3871 Write-Warning "[Get-ADRPrinter] Error while enumerating printQueue Objects"
3872 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3873 Return $null
3874 }
3875
3876 If ($ADPrinters)
3877 {
3878 Write-Verbose "[*] Total Printers: $([ADRecon.ADWSClass]::ObjectCount($ADPrinters))"
3879 $PrintersObj = [ADRecon.ADWSClass]::PrinterParser($ADPrinters, $Threads)
3880 Remove-Variable ADPrinters
3881 }
3882 }
3883
3884 If ($Protocol -eq 'LDAP')
3885 {
3886 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
3887 $ObjSearcher.PageSize = $PageSize
3888 $ObjSearcher.Filter = "(objectCategory=printQueue)"
3889 $ObjSearcher.SearchScope = "Subtree"
3890
3891 Try
3892 {
3893 $ADPrinters = $ObjSearcher.FindAll()
3894 }
3895 Catch
3896 {
3897 Write-Warning "[Get-ADRPrinter] Error while enumerating printQueue Objects"
3898 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3899 Return $null
3900 }
3901 $ObjSearcher.dispose()
3902
3903 If ($ADPrinters)
3904 {
3905 $cnt = $([ADRecon.LDAPClass]::ObjectCount($ADPrinters))
3906 If ($cnt -ge 1)
3907 {
3908 Write-Verbose "[*] Total Printers: $cnt"
3909 $PrintersObj = [ADRecon.LDAPClass]::PrinterParser($ADPrinters, $Threads)
3910 }
3911 Remove-Variable ADPrinters
3912 }
3913 }
3914
3915 If ($PrintersObj)
3916 {
3917 Return $PrintersObj
3918 }
3919 Else
3920 {
3921 Return $null
3922 }
3923}
3924
3925Function Get-ADRComputer
3926{
3927<#
3928.SYNOPSIS
3929 Returns all computers in the current (or specified) domain.
3930
3931.DESCRIPTION
3932 Returns all computers in the current (or specified) domain.
3933
3934.PARAMETER Protocol
3935 [string]
3936 Which protocol to use; ADWS (default) or LDAP.
3937
3938.PARAMETER date
3939 [DateTime]
3940 Date when ADRecon was executed.
3941
3942.PARAMETER objDomain
3943 [DirectoryServices.DirectoryEntry]
3944 Domain Directory Entry object.
3945
3946.PARAMETER DormantTimeSpan
3947 [int]
3948 Timespan for Dormant accounts. Default 90 days.
3949
3950.PARAMTER PassMaxAge
3951 [int]
3952 Maximum machine account password age. Default 30 days
3953 https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/domain-member-maximum-machine-account-password-age
3954
3955.PARAMETER PageSize
3956 [int]
3957 The PageSize to set for the LDAP searcher object. Default 200.
3958
3959.PARAMETER Threads
3960 [int]
3961 The number of threads to use during processing of objects. Default 10.
3962
3963.OUTPUTS
3964 PSObject.
3965#>
3966 param(
3967 [Parameter(Mandatory = $true)]
3968 [string] $Protocol,
3969
3970 [Parameter(Mandatory = $true)]
3971 [DateTime] $date,
3972
3973 [Parameter(Mandatory = $false)]
3974 [DirectoryServices.DirectoryEntry] $objDomain,
3975
3976 [Parameter(Mandatory = $true)]
3977 [int] $DormantTimeSpan = 90,
3978
3979 [Parameter(Mandatory = $true)]
3980 [int] $PassMaxAge = 30,
3981
3982 [Parameter(Mandatory = $true)]
3983 [int] $PageSize,
3984
3985 [Parameter(Mandatory = $false)]
3986 [int] $Threads = 10
3987 )
3988
3989 If ($Protocol -eq 'ADWS')
3990 {
3991 Try
3992 {
3993 $ADComputers = @( Get-ADComputer -Filter * -ResultPageSize $PageSize -Properties Description,DistinguishedName,DNSHostName,Enabled,IPv4Address,LastLogonDate,'msDS-AllowedToDelegateTo','ms-ds-CreatorSid','msDS-SupportedEncryptionTypes',Name,OperatingSystem,OperatingSystemHotfix,OperatingSystemServicePack,OperatingSystemVersion,PasswordLastSet,primaryGroupID,SamAccountName,SID,SIDHistory,TrustedForDelegation,TrustedToAuthForDelegation,UserAccountControl,whenChanged,whenCreated )
3994 }
3995 Catch
3996 {
3997 Write-Warning "[Get-ADRComputer] Error while enumerating Computer Objects"
3998 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3999 Return $null
4000 }
4001
4002 If ($ADComputers)
4003 {
4004 Write-Verbose "[*] Total Computers: $([ADRecon.ADWSClass]::ObjectCount($ADComputers))"
4005 $ComputerObj = [ADRecon.ADWSClass]::ComputerParser($ADComputers, $date, $DormantTimeSpan, $PassMaxAge, $Threads)
4006 Remove-Variable ADComputers
4007 }
4008 }
4009
4010 If ($Protocol -eq 'LDAP')
4011 {
4012 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
4013 $ObjSearcher.PageSize = $PageSize
4014 $ObjSearcher.Filter = "(samAccountType=805306369)"
4015 $ObjSearcher.PropertiesToLoad.AddRange(("description","distinguishedname","dnshostname","lastlogontimestamp","msDS-AllowedToDelegateTo","ms-ds-CreatorSid","msDS-SupportedEncryptionTypes","name","objectsid","operatingsystem","operatingsystemhotfix","operatingsystemservicepack","operatingsystemversion","primarygroupid","pwdlastset","samaccountname","sidhistory","useraccountcontrol","whenchanged","whencreated"))
4016 $ObjSearcher.SearchScope = "Subtree"
4017
4018 Try
4019 {
4020 $ADComputers = $ObjSearcher.FindAll()
4021 }
4022 Catch
4023 {
4024 Write-Warning "[Get-ADRComputer] Error while enumerating Computer Objects"
4025 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4026 Return $null
4027 }
4028 $ObjSearcher.dispose()
4029
4030 If ($ADComputers)
4031 {
4032 Write-Verbose "[*] Total Computers: $([ADRecon.LDAPClass]::ObjectCount($ADComputers))"
4033 $ComputerObj = [ADRecon.LDAPClass]::ComputerParser($ADComputers, $date, $DormantTimeSpan, $PassMaxAge, $Threads)
4034 Remove-Variable ADComputers
4035 }
4036 }
4037
4038 If ($ComputerObj)
4039 {
4040 Return $ComputerObj
4041 }
4042 Else
4043 {
4044 Return $null
4045 }
4046}
4047
4048Function Get-ADRComputerSPN
4049{
4050<#
4051.SYNOPSIS
4052 Returns all computer service principal name (SPN) in the current (or specified) domain.
4053
4054.DESCRIPTION
4055 Returns all computer service principal name (SPN) in the current (or specified) domain.
4056
4057.PARAMETER Protocol
4058 [string]
4059 Which protocol to use; ADWS (default) or LDAP.
4060
4061.PARAMETER objDomain
4062 [DirectoryServices.DirectoryEntry]
4063 Domain Directory Entry object.
4064
4065.PARAMETER PageSize
4066 [int]
4067 The PageSize to set for the LDAP searcher object. Default 200.
4068
4069.PARAMETER Threads
4070 [int]
4071 The number of threads to use during processing of objects. Default 10.
4072
4073.OUTPUTS
4074 PSObject.
4075#>
4076 param(
4077 [Parameter(Mandatory = $true)]
4078 [string] $Protocol,
4079
4080 [Parameter(Mandatory = $false)]
4081 [DirectoryServices.DirectoryEntry] $objDomain,
4082
4083 [Parameter(Mandatory = $true)]
4084 [int] $PageSize,
4085
4086 [Parameter(Mandatory = $false)]
4087 [int] $Threads = 10
4088 )
4089
4090 If ($Protocol -eq 'ADWS')
4091 {
4092 Try
4093 {
4094 $ADComputers = @( Get-ADObject -LDAPFilter "(&(samAccountType=805306369)(servicePrincipalName=*))" -Properties Name,servicePrincipalName -ResultPageSize $PageSize )
4095 }
4096 Catch
4097 {
4098 Write-Warning "[Get-ADRComputerSPN] Error while enumerating ComputerSPN Objects"
4099 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4100 Return $null
4101 }
4102
4103 If ($ADComputers)
4104 {
4105 Write-Verbose "[*] Total ComputerSPNs: $([ADRecon.ADWSClass]::ObjectCount($ADComputers))"
4106 $ComputerSPNObj = [ADRecon.ADWSClass]::ComputerSPNParser($ADComputers, $Threads)
4107 Remove-Variable ADComputers
4108 }
4109 }
4110
4111 If ($Protocol -eq 'LDAP')
4112 {
4113 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
4114 $ObjSearcher.PageSize = $PageSize
4115 $ObjSearcher.Filter = "(&(samAccountType=805306369)(servicePrincipalName=*))"
4116 $ObjSearcher.PropertiesToLoad.AddRange(("name","serviceprincipalname"))
4117 $ObjSearcher.SearchScope = "Subtree"
4118 Try
4119 {
4120 $ADComputers = $ObjSearcher.FindAll()
4121 }
4122 Catch
4123 {
4124 Write-Warning "[Get-ADRComputerSPN] Error while enumerating ComputerSPN Objects"
4125 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4126 Return $null
4127 }
4128 $ObjSearcher.dispose()
4129
4130 If ($ADComputers)
4131 {
4132 Write-Verbose "[*] Total ComputerSPNs: $([ADRecon.LDAPClass]::ObjectCount($ADComputers))"
4133 $ComputerSPNObj = [ADRecon.LDAPClass]::ComputerSPNParser($ADComputers, $Threads)
4134 Remove-Variable ADComputers
4135 }
4136 }
4137
4138 If ($ComputerSPNObj)
4139 {
4140 Return $ComputerSPNObj
4141 }
4142 Else
4143 {
4144 Return $null
4145 }
4146}
4147
4148# based on https://github.com/kfosaaen/Get-LAPSPasswords/blob/master/Get-LAPSPasswords.ps1
4149Function Get-ADRLAPSCheck
4150{
4151<#
4152.SYNOPSIS
4153 Returns all LAPS (local administrator) stored passwords in the current (or specified) domain.
4154
4155.DESCRIPTION
4156 Returns all LAPS (local administrator) stored passwords in the current (or specified) domain. Other details such as the Password Expiration, whether the password is readable by the current user are also returned.
4157
4158.PARAMETER Protocol
4159 [string]
4160 Which protocol to use; ADWS (default) or LDAP.
4161
4162.PARAMETER objDomain
4163 [DirectoryServices.DirectoryEntry]
4164 Domain Directory Entry object.
4165
4166.PARAMETER PageSize
4167 [int]
4168 The PageSize to set for the LDAP searcher object. Default 200.
4169
4170.PARAMETER Threads
4171 [int]
4172 The number of threads to use during processing of objects. Default 10.
4173
4174.OUTPUTS
4175 PSObject.
4176#>
4177 param(
4178 [Parameter(Mandatory = $true)]
4179 [string] $Protocol,
4180
4181 [Parameter(Mandatory = $false)]
4182 [DirectoryServices.DirectoryEntry] $objDomain,
4183
4184 [Parameter(Mandatory = $true)]
4185 [int] $PageSize,
4186
4187 [Parameter(Mandatory = $false)]
4188 [int] $Threads = 10
4189 )
4190
4191 If ($Protocol -eq 'ADWS')
4192 {
4193 Try
4194 {
4195 $ADComputers = @( Get-ADObject -LDAPFilter "(samAccountType=805306369)" -Properties CN,DNSHostName,'ms-Mcs-AdmPwd','ms-Mcs-AdmPwdExpirationTime' -ResultPageSize $PageSize )
4196 }
4197 Catch [System.ArgumentException]
4198 {
4199 Write-Warning "[*] LAPS is not implemented."
4200 Return $null
4201 }
4202 Catch
4203 {
4204 Write-Warning "[Get-ADRLAPSCheck] Error while enumerating LAPS Objects"
4205 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4206 Return $null
4207 }
4208
4209 If ($ADComputers)
4210 {
4211 Write-Verbose "[*] Total LAPS Objects: $([ADRecon.ADWSClass]::ObjectCount($ADComputers))"
4212 $LAPSObj = [ADRecon.ADWSClass]::LAPSParser($ADComputers, $Threads)
4213 Remove-Variable ADComputers
4214 }
4215 }
4216
4217 If ($Protocol -eq 'LDAP')
4218 {
4219 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
4220 $ObjSearcher.PageSize = $PageSize
4221 $ObjSearcher.Filter = "(samAccountType=805306369)"
4222 $ObjSearcher.PropertiesToLoad.AddRange(("cn","dnshostname","ms-mcs-admpwd","ms-mcs-admpwdexpirationtime"))
4223 $ObjSearcher.SearchScope = "Subtree"
4224 Try
4225 {
4226 $ADComputers = $ObjSearcher.FindAll()
4227 }
4228 Catch
4229 {
4230 Write-Warning "[Get-ADRLAPSCheck] Error while enumerating LAPS Objects"
4231 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4232 Return $null
4233 }
4234 $ObjSearcher.dispose()
4235
4236 If ($ADComputers)
4237 {
4238 $LAPSCheck = [ADRecon.LDAPClass]::LAPSCheck($ADComputers)
4239 If (-Not $LAPSCheck)
4240 {
4241 Write-Warning "[*] LAPS is not implemented."
4242 Return $null
4243 }
4244 Else
4245 {
4246 Write-Verbose "[*] Total LAPS Objects: $([ADRecon.LDAPClass]::ObjectCount($ADComputers))"
4247 $LAPSObj = [ADRecon.LDAPClass]::LAPSParser($ADComputers, $Threads)
4248 Remove-Variable ADComputers
4249 }
4250 }
4251 }
4252
4253 If ($LAPSObj)
4254 {
4255 Return $LAPSObj
4256 }
4257 Else
4258 {
4259 Return $null
4260 }
4261}
4262
4263Function Get-ADRBitLocker
4264{
4265<#
4266.SYNOPSIS
4267 Returns all BitLocker Recovery Keys stored in the current (or specified) domain.
4268
4269.DESCRIPTION
4270 Returns all BitLocker Recovery Keys stored in the current (or specified) domain.
4271
4272.PARAMETER Protocol
4273 [string]
4274 Which protocol to use; ADWS (default) or LDAP.
4275
4276.PARAMETER objDomain
4277 [DirectoryServices.DirectoryEntry]
4278 Domain Directory Entry object.
4279
4280.PARAMETER DomainController
4281 [string]
4282 IP Address of the Domain Controller.
4283
4284.PARAMETER Credential
4285 [Management.Automation.PSCredential]
4286 Credentials.
4287
4288.OUTPUTS
4289 PSObject.
4290#>
4291 param(
4292 [Parameter(Mandatory = $true)]
4293 [string] $Protocol,
4294
4295 [Parameter(Mandatory = $false)]
4296 [DirectoryServices.DirectoryEntry] $objDomain,
4297
4298 [Parameter(Mandatory = $false)]
4299 [string] $DomainController,
4300
4301 [Parameter(Mandatory = $false)]
4302 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty
4303 )
4304
4305 If ($Protocol -eq 'ADWS')
4306 {
4307 Try
4308 {
4309 $ADBitLockerRecoveryKeys = Get-ADObject -LDAPFilter '(objectClass=msFVE-RecoveryInformation)' -Properties distinguishedName,msFVE-RecoveryPassword,msFVE-RecoveryGuid,msFVE-VolumeGuid,Name,whenCreated
4310 }
4311 Catch
4312 {
4313 Write-Warning "[Get-ADRBitLocker] Error while enumerating msFVE-RecoveryInformation Objects"
4314 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4315 Return $null
4316 }
4317
4318 If ($ADBitLockerRecoveryKeys)
4319 {
4320 $cnt = $([ADRecon.ADWSClass]::ObjectCount($ADBitLockerRecoveryKeys))
4321 If ($cnt -ge 1)
4322 {
4323 Write-Verbose "[*] Total BitLocker Recovery Keys: $cnt"
4324 $BitLockerObj = @()
4325 $ADBitLockerRecoveryKeys | ForEach-Object {
4326 # Create the object for each instance.
4327 $Obj = New-Object PSObject
4328 $Obj | Add-Member -MemberType NoteProperty -Name "Distinguished Name" -Value $((($_.distinguishedName -split '}')[1]).substring(1))
4329 $Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $_.Name
4330 $Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value $_.whenCreated
4331 $Obj | Add-Member -MemberType NoteProperty -Name "Recovery Key ID" -Value $([GUID] $_.'msFVE-RecoveryGuid')
4332 $Obj | Add-Member -MemberType NoteProperty -Name "Recovery Key" -Value $_.'msFVE-RecoveryPassword'
4333 $Obj | Add-Member -MemberType NoteProperty -Name "Volume GUID" -Value $([GUID] $_.'msFVE-VolumeGuid')
4334 Try
4335 {
4336 $TempComp = Get-ADComputer -Identity $Obj.'Distinguished Name' -Properties msTPM-OwnerInformation,msTPM-TpmInformationForComputer
4337 }
4338 Catch
4339 {
4340 Write-Warning "[Get-ADRBitLocker] Error while enumerating $($Obj.'Distinguished Name') Computer Object"
4341 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4342 }
4343 If ($TempComp)
4344 {
4345 # msTPM-OwnerInformation (Vista/7 or Server 2008/R2)
4346 $Obj | Add-Member -MemberType NoteProperty -Name "msTPM-OwnerInformation" -Value $TempComp.'msTPM-OwnerInformation'
4347
4348 # msTPM-TpmInformationForComputer (Windows 8/10 or Server 2012/R2)
4349 $Obj | Add-Member -MemberType NoteProperty -Name "msTPM-TpmInformationForComputer" -Value $TempComp.'msTPM-TpmInformationForComputer'
4350 If ($null -ne $TempComp.'msTPM-TpmInformationForComputer')
4351 {
4352 # Grab the TPM Owner Info from the msTPM-InformationObject
4353 $TPMObject = Get-ADObject -Identity $TempComp.'msTPM-TpmInformationForComputer' -Properties msTPM-OwnerInformation
4354 $TPMRecoveryInfo = $TPMObject.'msTPM-OwnerInformation'
4355 }
4356 Else
4357 {
4358 $TPMRecoveryInfo = $null
4359 }
4360 }
4361 Else
4362 {
4363 $Obj | Add-Member -MemberType NoteProperty -Name "msTPM-OwnerInformation" -Value $null
4364 $Obj | Add-Member -MemberType NoteProperty -Name "msTPM-TpmInformationForComputer" -Value $null
4365 $TPMRecoveryInfo = $null
4366
4367 }
4368 $Obj | Add-Member -MemberType NoteProperty -Name "TPM Owner Password" -Value $TPMRecoveryInfo
4369 $BitLockerObj += $Obj
4370 }
4371 }
4372 Remove-Variable ADBitLockerRecoveryKeys
4373 }
4374 }
4375
4376 If ($Protocol -eq 'LDAP')
4377 {
4378 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
4379 $ObjSearcher.PageSize = $PageSize
4380 $ObjSearcher.Filter = "(objectClass=msFVE-RecoveryInformation)"
4381 $ObjSearcher.PropertiesToLoad.AddRange(("distinguishedName","msfve-recoverypassword","msfve-recoveryguid","msfve-volumeguid","mstpm-ownerinformation","mstpm-tpminformationforcomputer","name","whencreated"))
4382 $ObjSearcher.SearchScope = "Subtree"
4383
4384 Try
4385 {
4386 $ADBitLockerRecoveryKeys = $ObjSearcher.FindAll()
4387 }
4388 Catch
4389 {
4390 Write-Warning "[Get-ADRBitLocker] Error while enumerating msFVE-RecoveryInformation Objects"
4391 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4392 Return $null
4393 }
4394 $ObjSearcher.dispose()
4395
4396 If ($ADBitLockerRecoveryKeys)
4397 {
4398 $cnt = $([ADRecon.LDAPClass]::ObjectCount($ADBitLockerRecoveryKeys))
4399 If ($cnt -ge 1)
4400 {
4401 Write-Verbose "[*] Total BitLocker Recovery Keys: $cnt"
4402 $BitLockerObj = @()
4403 $ADBitLockerRecoveryKeys | ForEach-Object {
4404 # Create the object for each instance.
4405 $Obj = New-Object PSObject
4406 $Obj | Add-Member -MemberType NoteProperty -Name "Distinguished Name" -Value $((($_.Properties.distinguishedname -split '}')[1]).substring(1))
4407 $Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value ([string] ($_.Properties.name))
4408 $Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value ([DateTime] $($_.Properties.whencreated))
4409 $Obj | Add-Member -MemberType NoteProperty -Name "Recovery Key ID" -Value $([GUID] $_.Properties.'msfve-recoveryguid'[0])
4410 $Obj | Add-Member -MemberType NoteProperty -Name "Recovery Key" -Value ([string] ($_.Properties.'msfve-recoverypassword'))
4411 $Obj | Add-Member -MemberType NoteProperty -Name "Volume GUID" -Value $([GUID] $_.Properties.'msfve-volumeguid'[0])
4412
4413 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
4414 $ObjSearcher.PageSize = $PageSize
4415 $ObjSearcher.Filter = "(&(samAccountType=805306369)(distinguishedName=$($Obj.'Distinguished Name')))"
4416 $ObjSearcher.PropertiesToLoad.AddRange(("mstpm-ownerinformation","mstpm-tpminformationforcomputer"))
4417 $ObjSearcher.SearchScope = "Subtree"
4418
4419 Try
4420 {
4421 $TempComp = $ObjSearcher.FindAll()
4422 }
4423 Catch
4424 {
4425 Write-Warning "[Get-ADRBitLocker] Error while enumerating $($Obj.'Distinguished Name') Computer Object"
4426 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4427 }
4428 $ObjSearcher.dispose()
4429
4430 If ($TempComp)
4431 {
4432 # msTPM-OwnerInformation (Vista/7 or Server 2008/R2)
4433 $Obj | Add-Member -MemberType NoteProperty -Name "msTPM-OwnerInformation" -Value $([string] $TempComp.Properties.'mstpm-ownerinformation')
4434
4435 # msTPM-TpmInformationForComputer (Windows 8/10 or Server 2012/R2)
4436 $Obj | Add-Member -MemberType NoteProperty -Name "msTPM-TpmInformationForComputer" -Value $([string] $TempComp.Properties.'mstpm-tpminformationforcomputer')
4437 If ($null -ne $TempComp.Properties.'mstpm-tpminformationforcomputer')
4438 {
4439 # Grab the TPM Owner Info from the msTPM-InformationObject
4440 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
4441 {
4442 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$($TempComp.Properties.'mstpm-tpminformationforcomputer')", $Credential.UserName,$Credential.GetNetworkCredential().Password
4443 $objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
4444 $objSearcherPath.PropertiesToLoad.AddRange(("mstpm-ownerinformation"))
4445 Try
4446 {
4447 $TPMObject = $objSearcherPath.FindAll()
4448 }
4449 Catch
4450 {
4451 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4452 }
4453 $objSearcherPath.dispose()
4454
4455 If ($TPMObject)
4456 {
4457 $TPMRecoveryInfo = $([string] $TPMObject.Properties.'mstpm-ownerinformation')
4458 }
4459 Else
4460 {
4461 $TPMRecoveryInfo = $null
4462 }
4463 }
4464 Else
4465 {
4466 Try
4467 {
4468 $TPMObject = ([ADSI]"LDAP://$($TempComp.Properties.'mstpm-tpminformationforcomputer')")
4469 }
4470 Catch
4471 {
4472 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4473 }
4474 If ($TPMObject)
4475 {
4476 $TPMRecoveryInfo = $([string] $TPMObject.Properties.'mstpm-ownerinformation')
4477 }
4478 Else
4479 {
4480 $TPMRecoveryInfo = $null
4481 }
4482 }
4483 }
4484 }
4485 Else
4486 {
4487 $Obj | Add-Member -MemberType NoteProperty -Name "msTPM-OwnerInformation" -Value $null
4488 $Obj | Add-Member -MemberType NoteProperty -Name "msTPM-TpmInformationForComputer" -Value $null
4489 $TPMRecoveryInfo = $null
4490 }
4491 $Obj | Add-Member -MemberType NoteProperty -Name "TPM Owner Password" -Value $TPMRecoveryInfo
4492 $BitLockerObj += $Obj
4493 }
4494 }
4495 Remove-Variable cnt
4496 Remove-Variable ADBitLockerRecoveryKeys
4497 }
4498 }
4499
4500 If ($BitLockerObj)
4501 {
4502 Return $BitLockerObj
4503 }
4504 Else
4505 {
4506 Return $null
4507 }
4508}
4509
4510# Modified ConvertFrom-SID function from https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1
4511Function ConvertFrom-SID
4512{
4513<#
4514.SYNOPSIS
4515 Converts a security identifier (SID) to a group/user name.
4516
4517 Author: Will Schroeder (@harmj0y)
4518 License: BSD 3-Clause
4519
4520.DESCRIPTION
4521 Converts a security identifier string (SID) to a group/user name using IADsNameTranslate interface.
4522
4523.PARAMETER Protocol
4524 [string]
4525 Which protocol to use; ADWS (default) or LDAP.
4526
4527.PARAMETER ObjectSid
4528 Specifies one or more SIDs to convert.
4529
4530.PARAMETER DomainFQDN
4531 Specifies the FQDN of the Domain.
4532
4533.PARAMETER Credential
4534 Specifies an alternate credential to use for the translation.
4535
4536.PARAMETER ResolveSIDs
4537 [bool]
4538 Whether to resolve SIDs in the ACLs module. (Default False)
4539
4540.EXAMPLE
4541
4542 ConvertFrom-SID S-1-5-21-890171859-3433809279-3366196753-1108
4543
4544 TESTLAB\harmj0y
4545
4546.EXAMPLE
4547
4548 "S-1-5-21-890171859-3433809279-3366196753-1107", "S-1-5-21-890171859-3433809279-3366196753-1108", "S-1-5-32-562" | ConvertFrom-SID
4549
4550 TESTLAB\WINDOWS2$
4551 TESTLAB\harmj0y
4552 BUILTIN\Distributed COM Users
4553
4554.EXAMPLE
4555
4556 $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
4557 $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm', $SecPassword)
4558 ConvertFrom-SID S-1-5-21-890171859-3433809279-3366196753-1108 -Credential $Cred
4559
4560 TESTLAB\harmj0y
4561
4562.INPUTS
4563 [String]
4564 Accepts one or more SID strings on the pipeline.
4565
4566.OUTPUTS
4567 [String]
4568 The converted DOMAIN\username.
4569#>
4570 Param(
4571 [Parameter(Mandatory = $true)]
4572 [string] $Protocol,
4573
4574 [Parameter(Mandatory = $true)]
4575 [Alias('SID')]
4576 #[ValidatePattern('^S-1-.*')]
4577 [String]
4578 $ObjectSid,
4579
4580 [Parameter(Mandatory = $false)]
4581 [string] $DomainFQDN,
4582
4583 [Parameter(Mandatory = $false)]
4584 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
4585
4586 [Parameter(Mandatory = $false)]
4587 [bool] $ResolveSID = $false
4588 )
4589
4590 BEGIN {
4591 # Name Translator Initialization Types
4592 # https://msdn.microsoft.com/en-us/library/aa772266%28v=vs.85%29.aspx
4593 $ADS_NAME_INITTYPE_DOMAIN = 1 # Initializes a NameTranslate object by setting the domain that the object binds to.
4594 #$ADS_NAME_INITTYPE_SERVER = 2 # Initializes a NameTranslate object by setting the server that the object binds to.
4595 $ADS_NAME_INITTYPE_GC = 3 # Initializes a NameTranslate object by locating the global catalog that the object binds to.
4596
4597 # Name Transator Name Types
4598 # https://msdn.microsoft.com/en-us/library/aa772267%28v=vs.85%29.aspx
4599 #$ADS_NAME_TYPE_1779 = 1 # Name format as specified in RFC 1779. For example, "CN=Jeff Smith,CN=users,DC=Fabrikam,DC=com".
4600 #$ADS_NAME_TYPE_CANONICAL = 2 # Canonical name format. For example, "Fabrikam.com/Users/Jeff Smith".
4601 $ADS_NAME_TYPE_NT4 = 3 # Account name format used in Windows. For example, "Fabrikam\JeffSmith".
4602 #$ADS_NAME_TYPE_DISPLAY = 4 # Display name format. For example, "Jeff Smith".
4603 #$ADS_NAME_TYPE_DOMAIN_SIMPLE = 5 # Simple domain name format. For example, "JeffSmith@Fabrikam.com".
4604 #$ADS_NAME_TYPE_ENTERPRISE_SIMPLE = 6 # Simple enterprise name format. For example, "JeffSmith@Fabrikam.com".
4605 #$ADS_NAME_TYPE_GUID = 7 # Global Unique Identifier format. For example, "{95ee9fff-3436-11d1-b2b0-d15ae3ac8436}".
4606 $ADS_NAME_TYPE_UNKNOWN = 8 # Unknown name type. The system will estimate the format. This element is a meaningful option only with the IADsNameTranslate.Set or the IADsNameTranslate.SetEx method, but not with the IADsNameTranslate.Get or IADsNameTranslate.GetEx method.
4607 #$ADS_NAME_TYPE_USER_PRINCIPAL_NAME = 9 # User principal name format. For example, "JeffSmith@Fabrikam.com".
4608 #$ADS_NAME_TYPE_CANONICAL_EX = 10 # Extended canonical name format. For example, "Fabrikam.com/Users Jeff Smith".
4609 #$ADS_NAME_TYPE_SERVICE_PRINCIPAL_NAME = 11 # Service principal name format. For example, "www/www.fabrikam.com@fabrikam.com".
4610 #$ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME = 12 # A SID string, as defined in the Security Descriptor Definition Language (SDDL), for either the SID of the current object or one from the object SID history. For example, "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)"
4611
4612 # https://msdn.microsoft.com/en-us/library/aa772250.aspx
4613 #$ADS_CHASE_REFERRALS_NEVER = (0x00) # The client should never chase the referred-to server. Setting this option prevents a client from contacting other servers in a referral process.
4614 #$ADS_CHASE_REFERRALS_SUBORDINATE = (0x20) # The client chases only subordinate referrals which are a subordinate naming context in a directory tree. For example, if the base search is requested for "DC=Fabrikam,DC=Com", and the server returns a result set and a referral of "DC=Sales,DC=Fabrikam,DC=Com" on the AdbSales server, the client can contact the AdbSales server to continue the search. The ADSI LDAP provider always turns off this flag for paged searches.
4615 #$ADS_CHASE_REFERRALS_EXTERNAL = (0x40) # The client chases external referrals. For example, a client requests server A to perform a search for "DC=Fabrikam,DC=Com". However, server A does not contain the object, but knows that an independent server, B, owns it. It then refers the client to server B.
4616 $ADS_CHASE_REFERRALS_ALWAYS = (0x60) # Referrals are chased for either the subordinate or external type.
4617 }
4618
4619 PROCESS {
4620 $TargetSid = $($ObjectSid.TrimStart("O:"))
4621 $TargetSid = $($TargetSid.Trim('*'))
4622 If ($TargetSid -match '^S-1-.*')
4623 {
4624 Try
4625 {
4626 # try to resolve any built-in SIDs first - https://support.microsoft.com/en-us/kb/243330
4627 Switch ($TargetSid) {
4628 'S-1-0' { 'Null Authority' }
4629 'S-1-0-0' { 'Nobody' }
4630 'S-1-1' { 'World Authority' }
4631 'S-1-1-0' { 'Everyone' }
4632 'S-1-2' { 'Local Authority' }
4633 'S-1-2-0' { 'Local' }
4634 'S-1-2-1' { 'Console Logon ' }
4635 'S-1-3' { 'Creator Authority' }
4636 'S-1-3-0' { 'Creator Owner' }
4637 'S-1-3-1' { 'Creator Group' }
4638 'S-1-3-2' { 'Creator Owner Server' }
4639 'S-1-3-3' { 'Creator Group Server' }
4640 'S-1-3-4' { 'Owner Rights' }
4641 'S-1-4' { 'Non-unique Authority' }
4642 'S-1-5' { 'NT Authority' }
4643 'S-1-5-1' { 'Dialup' }
4644 'S-1-5-2' { 'Network' }
4645 'S-1-5-3' { 'Batch' }
4646 'S-1-5-4' { 'Interactive' }
4647 'S-1-5-6' { 'Service' }
4648 'S-1-5-7' { 'Anonymous' }
4649 'S-1-5-8' { 'Proxy' }
4650 'S-1-5-9' { 'Enterprise Domain Controllers' }
4651 'S-1-5-10' { 'Principal Self' }
4652 'S-1-5-11' { 'Authenticated Users' }
4653 'S-1-5-12' { 'Restricted Code' }
4654 'S-1-5-13' { 'Terminal Server Users' }
4655 'S-1-5-14' { 'Remote Interactive Logon' }
4656 'S-1-5-15' { 'This Organization ' }
4657 'S-1-5-17' { 'This Organization ' }
4658 'S-1-5-18' { 'Local System' }
4659 'S-1-5-19' { 'NT Authority' }
4660 'S-1-5-20' { 'NT Authority' }
4661 'S-1-5-80-0' { 'All Services ' }
4662 'S-1-5-32-544' { 'BUILTIN\Administrators' }
4663 'S-1-5-32-545' { 'BUILTIN\Users' }
4664 'S-1-5-32-546' { 'BUILTIN\Guests' }
4665 'S-1-5-32-547' { 'BUILTIN\Power Users' }
4666 'S-1-5-32-548' { 'BUILTIN\Account Operators' }
4667 'S-1-5-32-549' { 'BUILTIN\Server Operators' }
4668 'S-1-5-32-550' { 'BUILTIN\Print Operators' }
4669 'S-1-5-32-551' { 'BUILTIN\Backup Operators' }
4670 'S-1-5-32-552' { 'BUILTIN\Replicators' }
4671 'S-1-5-32-554' { 'BUILTIN\Pre-Windows 2000 Compatible Access' }
4672 'S-1-5-32-555' { 'BUILTIN\Remote Desktop Users' }
4673 'S-1-5-32-556' { 'BUILTIN\Network Configuration Operators' }
4674 'S-1-5-32-557' { 'BUILTIN\Incoming Forest Trust Builders' }
4675 'S-1-5-32-558' { 'BUILTIN\Performance Monitor Users' }
4676 'S-1-5-32-559' { 'BUILTIN\Performance Log Users' }
4677 'S-1-5-32-560' { 'BUILTIN\Windows Authorization Access Group' }
4678 'S-1-5-32-561' { 'BUILTIN\Terminal Server License Servers' }
4679 'S-1-5-32-562' { 'BUILTIN\Distributed COM Users' }
4680 'S-1-5-32-569' { 'BUILTIN\Cryptographic Operators' }
4681 'S-1-5-32-573' { 'BUILTIN\Event Log Readers' }
4682 'S-1-5-32-574' { 'BUILTIN\Certificate Service DCOM Access' }
4683 'S-1-5-32-575' { 'BUILTIN\RDS Remote Access Servers' }
4684 'S-1-5-32-576' { 'BUILTIN\RDS Endpoint Servers' }
4685 'S-1-5-32-577' { 'BUILTIN\RDS Management Servers' }
4686 'S-1-5-32-578' { 'BUILTIN\Hyper-V Administrators' }
4687 'S-1-5-32-579' { 'BUILTIN\Access Control Assistance Operators' }
4688 'S-1-5-32-580' { 'BUILTIN\Remote Management Users' }
4689 Default {
4690 # based on Convert-ADName function from https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1
4691 If ( ($TargetSid -match '^S-1-.*') -and ($ResolveSID) )
4692 {
4693 If ($Protocol -eq 'ADWS')
4694 {
4695 Try
4696 {
4697 $ADObject = Get-ADObject -Filter "objectSid -eq '$TargetSid'" -Properties DistinguishedName,sAMAccountName
4698 }
4699 Catch
4700 {
4701 Write-Warning "[ConvertFrom-SID] Error while enumerating Object using SID"
4702 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4703 }
4704 If ($ADObject)
4705 {
4706 $UserDomain = Get-DNtoFQDN -ADObjectDN $ADObject.DistinguishedName
4707 $ADSOutput = $UserDomain + "\" + $ADObject.sAMAccountName
4708 Remove-Variable UserDomain
4709 }
4710 }
4711
4712 If ($Protocol -eq 'LDAP')
4713 {
4714 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
4715 {
4716 $ADObject = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainFQDN/<SID=$TargetSid>",($Credential.GetNetworkCredential()).UserName,($Credential.GetNetworkCredential()).Password)
4717 }
4718 Else
4719 {
4720 $ADObject = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainFQDN/<SID=$TargetSid>")
4721 }
4722 If ($ADObject)
4723 {
4724 If (-Not ([string]::IsNullOrEmpty($ADObject.Properties.samaccountname)) )
4725 {
4726 $UserDomain = Get-DNtoFQDN -ADObjectDN $([string] ($ADObject.Properties.distinguishedname))
4727 $ADSOutput = $UserDomain + "\" + $([string] ($ADObject.Properties.samaccountname))
4728 Remove-Variable UserDomain
4729 }
4730 }
4731 }
4732
4733 If ( (-Not $ADSOutput) -or ([string]::IsNullOrEmpty($ADSOutput)) )
4734 {
4735 $ADSOutputType = $ADS_NAME_TYPE_NT4
4736 $Init = $true
4737 $Translate = New-Object -ComObject NameTranslate
4738 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
4739 {
4740 $ADSInitType = $ADS_NAME_INITTYPE_DOMAIN
4741 Try
4742 {
4743 [System.__ComObject].InvokeMember(“InitExâ€,â€InvokeMethodâ€,$null,$Translate,$(@($ADSInitType,$DomainFQDN,($Credential.GetNetworkCredential()).UserName,$DomainFQDN,($Credential.GetNetworkCredential()).Password)))
4744 }
4745 Catch
4746 {
4747 $Init = $false
4748 #Write-Verbose "[ConvertFrom-SID] Error initializing translation for $($TargetSid) using alternate credentials"
4749 #Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4750 }
4751 }
4752 Else
4753 {
4754 $ADSInitType = $ADS_NAME_INITTYPE_GC
4755 Try
4756 {
4757 [System.__ComObject].InvokeMember(“Initâ€,â€InvokeMethodâ€,$null,$Translate,($ADSInitType,$null))
4758 }
4759 Catch
4760 {
4761 $Init = $false
4762 #Write-Verbose "[ConvertFrom-SID] Error initializing translation for $($TargetSid)"
4763 #Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4764 }
4765 }
4766 If ($Init)
4767 {
4768 [System.__ComObject].InvokeMember(“ChaseReferralâ€,â€SetPropertyâ€,$null,$Translate,$ADS_CHASE_REFERRALS_ALWAYS)
4769 Try
4770 {
4771 [System.__ComObject].InvokeMember(“Setâ€,â€InvokeMethodâ€,$null,$Translate,($ADS_NAME_TYPE_UNKNOWN, $TargetSID))
4772 $ADSOutput = [System.__ComObject].InvokeMember(“Getâ€,â€InvokeMethodâ€,$null,$Translate,$ADSOutputType)
4773 }
4774 Catch
4775 {
4776 #Write-Verbose "[ConvertFrom-SID] Error translating $($TargetSid)"
4777 #Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4778 }
4779 }
4780 }
4781 }
4782 If (-Not ([string]::IsNullOrEmpty($ADSOutput)) )
4783 {
4784 Return $ADSOutput
4785 }
4786 Else
4787 {
4788 Return $TargetSid
4789 }
4790 }
4791 }
4792 }
4793 Catch
4794 {
4795 #Write-Output "[ConvertFrom-SID] Error converting SID $($TargetSid)"
4796 #Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4797 }
4798 }
4799 Else
4800 {
4801 Return $TargetSid
4802 }
4803 }
4804}
4805
4806# based on https://gallery.technet.microsoft.com/Active-Directory-OU-1d09f989
4807Function Get-ADRACL
4808{
4809<#
4810.SYNOPSIS
4811 Returns all ACLs for the Domain, OUs, Root Containers, GPO, User, Computer and Group objects in the current (or specified) domain.
4812
4813.DESCRIPTION
4814 Returns all ACLs for the Domain, OUs, Root Containers, GPO, User, Computer and Group objects in the current (or specified) domain.
4815
4816.PARAMETER Protocol
4817 [string]
4818 Which protocol to use; ADWS (default) or LDAP.
4819
4820.PARAMETER objDomain
4821 [DirectoryServices.DirectoryEntry]
4822 Domain Directory Entry object.
4823
4824.PARAMETER DomainController
4825 [string]
4826 IP Address of the Domain Controller.
4827
4828.PARAMETER Credential
4829 [Management.Automation.PSCredential]
4830 Credentials.
4831
4832.PARAMETER ResolveSIDs
4833 [bool]
4834 Whether to resolve SIDs in the ACLs module. (Default False)
4835
4836.PARAMETER PageSize
4837 [int]
4838 The PageSize to set for the LDAP searcher object. Default 200.
4839
4840.PARAMETER Threads
4841 [int]
4842 The number of threads to use during processing of objects. Default 10.
4843
4844.OUTPUTS
4845 PSObject.
4846
4847.LINK
4848 https://gallery.technet.microsoft.com/Active-Directory-OU-1d09f989
4849#>
4850 param(
4851 [Parameter(Mandatory = $true)]
4852 [string] $Protocol,
4853
4854 [Parameter(Mandatory = $false)]
4855 [DirectoryServices.DirectoryEntry] $objDomain,
4856
4857 [Parameter(Mandatory = $false)]
4858 [string] $DomainController,
4859
4860 [Parameter(Mandatory = $false)]
4861 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
4862
4863 [Parameter(Mandatory = $false)]
4864 [bool] $ResolveSID = $false,
4865
4866 [Parameter(Mandatory = $true)]
4867 [int] $PageSize,
4868
4869 [Parameter(Mandatory = $false)]
4870 [int] $Threads = 10
4871 )
4872
4873 If ($Protocol -eq 'ADWS')
4874 {
4875 If ($Credential -eq [Management.Automation.PSCredential]::Empty)
4876 {
4877 If (Test-Path AD:)
4878 {
4879 Set-Location AD:
4880 }
4881 Else
4882 {
4883 Write-Warning "Default AD drive not found ... Skipping ACL enumeration"
4884 Return $null
4885 }
4886 }
4887 $GUIDs = @{'00000000-0000-0000-0000-000000000000' = 'All'}
4888 Try
4889 {
4890 Write-Verbose "[*] Enumerating schemaIDs"
4891 $schemaIDs = Get-ADObject -SearchBase (Get-ADRootDSE).schemaNamingContext -LDAPFilter '(schemaIDGUID=*)' -Properties name, schemaIDGUID
4892 }
4893 Catch
4894 {
4895 Write-Warning "[Get-ADRACL] Error while enumerating schemaIDs"
4896 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4897 }
4898
4899 If ($schemaIDs)
4900 {
4901 $schemaIDs | Where-Object {$_} | ForEach-Object {
4902 # convert the GUID
4903 $GUIDs[(New-Object Guid (,$_.schemaIDGUID)).Guid] = $_.name
4904 }
4905 Remove-Variable schemaIDs
4906 }
4907
4908 Try
4909 {
4910 Write-Verbose "[*] Enumerating Active Directory Rights"
4911 $schemaIDs = Get-ADObject -SearchBase "CN=Extended-Rights,$((Get-ADRootDSE).configurationNamingContext)" -LDAPFilter '(objectClass=controlAccessRight)' -Properties name, rightsGUID
4912 }
4913 Catch
4914 {
4915 Write-Warning "[Get-ADRACL] Error while enumerating Active Directory Rights"
4916 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4917 }
4918
4919 If ($schemaIDs)
4920 {
4921 $schemaIDs | Where-Object {$_} | ForEach-Object {
4922 # convert the GUID
4923 $GUIDs[(New-Object Guid (,$_.rightsGUID)).Guid] = $_.name
4924 }
4925 Remove-Variable schemaIDs
4926 }
4927
4928 # Get the DistinguishedNames of Domain, OUs, Root Containers and GroupPolicy objects.
4929 $Objs = @()
4930 Try
4931 {
4932 $ADDomain = Get-ADDomain
4933 }
4934 Catch
4935 {
4936 Write-Warning "[Get-ADRACL] Error getting Domain Context"
4937 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4938 }
4939
4940 Try
4941 {
4942 Write-Verbose "[*] Enumerating Domain, OU, GPO, User, Computer and Group Objects"
4943 $Objs += Get-ADObject -LDAPFilter '(|(objectClass=domain)(objectCategory=organizationalunit)(objectCategory=groupPolicyContainer)(samAccountType=805306368)(samAccountType=805306369)(samaccounttype=268435456)(samaccounttype=268435457)(samaccounttype=536870912)(samaccounttype=536870913))' -Properties DisplayName, DistinguishedName, Name, ntsecuritydescriptor, ObjectClass, objectsid
4944 }
4945 Catch
4946 {
4947 Write-Warning "[Get-ADRACL] Error while enumerating Domain, OU, GPO, User, Computer and Group Objects"
4948 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4949 }
4950
4951 If ($ADDomain)
4952 {
4953 Try
4954 {
4955 Write-Verbose "[*] Enumerating Root Container Objects"
4956 $Objs += Get-ADObject -SearchBase $($ADDomain.DistinguishedName) -SearchScope OneLevel -LDAPFilter '(objectClass=container)' -Properties DistinguishedName, Name, ntsecuritydescriptor, ObjectClass
4957 }
4958 Catch
4959 {
4960 Write-Warning "[Get-ADRACL] Error while enumerating Root Container Objects"
4961 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4962 }
4963 }
4964
4965 If ($Objs)
4966 {
4967 $ACLObj = @()
4968 Write-Verbose "[*] Total Objects: $([ADRecon.ADWSClass]::ObjectCount($Objs))"
4969 Write-Verbose "[-] DACLs"
4970 $DACLObj = [ADRecon.ADWSClass]::DACLParser($Objs, $GUIDs, $Threads)
4971 #Write-Verbose "[-] SACLs - May need a Privileged Account"
4972 Write-Warning "[*] SACLs - Currently, the module is only supported with LDAP."
4973 #$SACLObj = [ADRecon.ADWSClass]::SACLParser($Objs, $GUIDs, $Threads)
4974 Remove-Variable Objs
4975 Remove-Variable GUIDs
4976 }
4977 }
4978
4979 If ($Protocol -eq 'LDAP')
4980 {
4981 $GUIDs = @{'00000000-0000-0000-0000-000000000000' = 'All'}
4982
4983 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
4984 {
4985 $DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
4986 $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$($DomainFQDN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
4987 Try
4988 {
4989 $ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
4990 }
4991 Catch
4992 {
4993 Write-Warning "[Get-ADRACL] Error getting Domain Context"
4994 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4995 }
4996
4997 Try
4998 {
4999 $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Forest",$($ADDomain.Forest),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
5000 $ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
5001 $SchemaPath = $ADForest.Schema.Name
5002 Remove-Variable ADForest
5003 }
5004 Catch
5005 {
5006 Write-Warning "[Get-ADRACL] Error enumerating SchemaPath"
5007 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
5008 }
5009 }
5010 Else
5011 {
5012 $ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
5013 $ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
5014 $SchemaPath = $ADForest.Schema.Name
5015 Remove-Variable ADForest
5016 }
5017
5018 If ($SchemaPath)
5019 {
5020 Write-Verbose "[*] Enumerating schemaIDs"
5021 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
5022 {
5023 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$($SchemaPath)", $Credential.UserName,$Credential.GetNetworkCredential().Password
5024 $objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
5025 }
5026 Else
5027 {
5028 $objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher ([ADSI] "LDAP://$($SchemaPath)")
5029 }
5030 $objSearcherPath.PageSize = $PageSize
5031 $objSearcherPath.filter = "(schemaIDGUID=*)"
5032
5033 Try
5034 {
5035 $SchemaSearcher = $objSearcherPath.FindAll()
5036 }
5037 Catch
5038 {
5039 Write-Warning "[Get-ADRACL] Error enumerating SchemaIDs"
5040 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
5041 }
5042
5043 If ($SchemaSearcher)
5044 {
5045 $SchemaSearcher | Where-Object {$_} | ForEach-Object {
5046 # convert the GUID
5047 $GUIDs[(New-Object Guid (,$_.properties.schemaidguid[0])).Guid] = $_.properties.name[0]
5048 }
5049 $SchemaSearcher.dispose()
5050 }
5051 $objSearcherPath.dispose()
5052
5053 Write-Verbose "[*] Enumerating Active Directory Rights"
5054 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
5055 {
5056 $objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$($SchemaPath.replace("Schema","Extended-Rights"))", $Credential.UserName,$Credential.GetNetworkCredential().Password
5057 $objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
5058 }
5059 Else
5060 {
5061 $objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher ([ADSI] "LDAP://$($SchemaPath.replace("Schema","Extended-Rights"))")
5062 }
5063 $objSearcherPath.PageSize = $PageSize
5064 $objSearcherPath.filter = "(objectClass=controlAccessRight)"
5065
5066 Try
5067 {
5068 $RightsSearcher = $objSearcherPath.FindAll()
5069 }
5070 Catch
5071 {
5072 Write-Warning "[Get-ADRACL] Error enumerating Active Directory Rights"
5073 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
5074 }
5075
5076 If ($RightsSearcher)
5077 {
5078 $RightsSearcher | Where-Object {$_} | ForEach-Object {
5079 # convert the GUID
5080 $GUIDs[$_.properties.rightsguid[0].toString()] = $_.properties.name[0]
5081 }
5082 $RightsSearcher.dispose()
5083 }
5084 $objSearcherPath.dispose()
5085 }
5086
5087 # Get the Domain, OUs, Root Containers, GPO, User, Computer and Group objects.
5088 $Objs = @()
5089 Write-Verbose "[*] Enumerating Domain, OU, GPO, User, Computer and Group Objects"
5090 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
5091 $ObjSearcher.PageSize = $PageSize
5092 $ObjSearcher.Filter = "(|(objectClass=domain)(objectCategory=organizationalunit)(objectCategory=groupPolicyContainer)(samAccountType=805306368)(samAccountType=805306369)(samaccounttype=268435456)(samaccounttype=268435457)(samaccounttype=536870912)(samaccounttype=536870913))"
5093 # https://msdn.microsoft.com/en-us/library/system.directoryservices.securitymasks(v=vs.110).aspx
5094 $ObjSearcher.SecurityMasks = [System.DirectoryServices.SecurityMasks]::Dacl -bor [System.DirectoryServices.SecurityMasks]::Group -bor [System.DirectoryServices.SecurityMasks]::Owner -bor [System.DirectoryServices.SecurityMasks]::Sacl
5095 $ObjSearcher.PropertiesToLoad.AddRange(("displayname","distinguishedname","name","ntsecuritydescriptor","objectclass","objectsid"))
5096 $ObjSearcher.SearchScope = "Subtree"
5097
5098 Try
5099 {
5100 $Objs += $ObjSearcher.FindAll()
5101 }
5102 Catch
5103 {
5104 Write-Warning "[Get-ADRACL] Error while enumerating Domain, OU, GPO, User, Computer and Group Objects"
5105 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
5106 }
5107 $ObjSearcher.dispose()
5108
5109 Write-Verbose "[*] Enumerating Root Container Objects"
5110 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
5111 $ObjSearcher.PageSize = $PageSize
5112 $ObjSearcher.Filter = "(objectClass=container)"
5113 # https://msdn.microsoft.com/en-us/library/system.directoryservices.securitymasks(v=vs.110).aspx
5114 $ObjSearcher.SecurityMasks = $ObjSearcher.SecurityMasks = [System.DirectoryServices.SecurityMasks]::Dacl -bor [System.DirectoryServices.SecurityMasks]::Group -bor [System.DirectoryServices.SecurityMasks]::Owner -bor [System.DirectoryServices.SecurityMasks]::Sacl
5115 $ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","name","ntsecuritydescriptor","objectclass"))
5116 $ObjSearcher.SearchScope = "OneLevel"
5117
5118 Try
5119 {
5120 $Objs += $ObjSearcher.FindAll()
5121 }
5122 Catch
5123 {
5124 Write-Warning "[Get-ADRACL] Error while enumerating Root Container Objects"
5125 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
5126 }
5127 $ObjSearcher.dispose()
5128
5129 If ($Objs)
5130 {
5131 Write-Verbose "[*] Total Objects: $([ADRecon.LDAPClass]::ObjectCount($Objs))"
5132 Write-Verbose "[-] DACLs"
5133 $DACLObj = [ADRecon.LDAPClass]::DACLParser($Objs, $GUIDs, $Threads)
5134 Write-Verbose "[-] SACLs - May need a Privileged Account"
5135 $SACLObj = [ADRecon.LDAPClass]::SACLParser($Objs, $GUIDs, $Threads)
5136 Remove-Variable Objs
5137 Remove-Variable GUIDs
5138 }
5139 }
5140
5141 If ($DACLObj)
5142 {
5143 Export-ADR $DACLObj $ADROutputDir $OutputType "DACLs"
5144 Remove-Variable DACLObj
5145 }
5146
5147 If ($SACLObj)
5148 {
5149 Export-ADR $SACLObj $ADROutputDir $OutputType "SACLs"
5150 Remove-Variable SACLObj
5151 }
5152}
5153
5154Function Get-ADRGPOReport
5155{
5156<#
5157.SYNOPSIS
5158 Runs the Get-GPOReport cmdlet if available.
5159
5160.DESCRIPTION
5161 Runs the Get-GPOReport cmdlet if available and saves in HTML and XML formats.
5162
5163.PARAMETER Protocol
5164 [string]
5165 Which protocol to use; ADWS (default) or LDAP.
5166
5167.PARAMETER UseAltCreds
5168 [bool]
5169 Whether to use provided credentials or not.
5170
5171.PARAMETER ADROutputDir
5172 [string]
5173 Path for ADRecon output folder.
5174
5175.OUTPUTS
5176 HTML and XML GPOReports are created in the folder specified.
5177#>
5178 param(
5179 [Parameter(Mandatory = $true)]
5180 [string] $Protocol,
5181
5182 [Parameter(Mandatory = $true)]
5183 [bool] $UseAltCreds,
5184
5185 [Parameter(Mandatory = $true)]
5186 [string] $ADROutputDir
5187 )
5188
5189 If ($Protocol -eq 'ADWS')
5190 {
5191 Try
5192 {
5193 # Suppress verbose output on module import
5194 $SaveVerbosePreference = $script:VerbosePreference
5195 $script:VerbosePreference = 'SilentlyContinue'
5196 Import-Module GroupPolicy -WarningAction Stop -ErrorAction Stop | Out-Null
5197 If ($SaveVerbosePreference)
5198 {
5199 $script:VerbosePreference = $SaveVerbosePreference
5200 Remove-Variable SaveVerbosePreference
5201 }
5202 }
5203 Catch
5204 {
5205 Write-Warning "[Get-ADRGPOReport] Error importing the GroupPolicy Module. Skipping GPOReport"
5206 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
5207 If ($SaveVerbosePreference)
5208 {
5209 $script:VerbosePreference = $SaveVerbosePreference
5210 Remove-Variable SaveVerbosePreference
5211 }
5212 Return $null
5213 }
5214 Try
5215 {
5216 Write-Verbose "[*] GPOReport XML"
5217 $ADFileName = -join($ADROutputDir,'\','GPO-Report','.xml')
5218 Get-GPOReport -All -ReportType XML -Path $ADFileName
5219 }
5220 Catch
5221 {
5222 If ($UseAltCreds)
5223 {
5224 Write-Warning "[*] Run the tool using RUNAS."
5225 Write-Warning "[*] runas /user:<Domain FQDN>\<Username> /netonly powershell.exe"
5226 Return $null
5227 }
5228 Write-Warning "[Get-ADRGPOReport] Error getting the GPOReport in XML"
5229 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
5230 }
5231 Try
5232 {
5233 Write-Verbose "[*] GPOReport HTML"
5234 $ADFileName = -join($ADROutputDir,'\','GPO-Report','.html')
5235 Get-GPOReport -All -ReportType HTML -Path $ADFileName
5236 }
5237 Catch
5238 {
5239 If ($UseAltCreds)
5240 {
5241 Write-Warning "[*] Run the tool using RUNAS."
5242 Write-Warning "[*] runas /user:<Domain FQDN>\<Username> /netonly powershell.exe"
5243 Return $null
5244 }
5245 Write-Warning "[Get-ADRGPOReport] Error getting the GPOReport in XML"
5246 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
5247 }
5248 }
5249 If ($Protocol -eq 'LDAP')
5250 {
5251 Write-Warning "[*] Currently, the module is only supported with ADWS."
5252 }
5253}
5254
5255# Modified Invoke-UserImpersonation function from https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1
5256Function Get-ADRUserImpersonation
5257{
5258<#
5259.SYNOPSIS
5260
5261Creates a new "runas /netonly" type logon and impersonates the token.
5262
5263Author: Will Schroeder (@harmj0y)
5264License: BSD 3-Clause
5265Required Dependencies: PSReflect
5266
5267.DESCRIPTION
5268
5269This function uses LogonUser() with the LOGON32_LOGON_NEW_CREDENTIALS LogonType
5270to simulate "runas /netonly". The resulting token is then impersonated with
5271ImpersonateLoggedOnUser() and the token handle is returned for later usage
5272with Invoke-RevertToSelf.
5273
5274.PARAMETER Credential
5275
5276A [Management.Automation.PSCredential] object with alternate credentials
5277to impersonate in the current thread space.
5278
5279.PARAMETER TokenHandle
5280
5281An IntPtr TokenHandle returned by a previous Invoke-UserImpersonation.
5282If this is supplied, LogonUser() is skipped and only ImpersonateLoggedOnUser()
5283is executed.
5284
5285.PARAMETER Quiet
5286
5287Suppress any warnings about STA vs MTA.
5288
5289.EXAMPLE
5290
5291$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
5292$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
5293Invoke-UserImpersonation -Credential $Cred
5294
5295.OUTPUTS
5296
5297IntPtr
5298
5299The TokenHandle result from LogonUser.
5300#>
5301
5302 [OutputType([IntPtr])]
5303 [CmdletBinding(DefaultParameterSetName = 'Credential')]
5304 Param(
5305 [Parameter(Mandatory = $True, ParameterSetName = 'Credential')]
5306 [Management.Automation.PSCredential]
5307 [Management.Automation.CredentialAttribute()]
5308 $Credential,
5309
5310 [Parameter(Mandatory = $True, ParameterSetName = 'TokenHandle')]
5311 [ValidateNotNull()]
5312 [IntPtr]
5313 $TokenHandle,
5314
5315 [Switch]
5316 $Quiet
5317 )
5318
5319 If (([System.Threading.Thread]::CurrentThread.GetApartmentState() -ne 'STA') -and (-not $PSBoundParameters['Quiet']))
5320 {
5321 Write-Warning "[Get-ADRUserImpersonation] powershell.exe is not currently in a single-threaded apartment state, token impersonation may not work."
5322 }
5323
5324 If ($PSBoundParameters['TokenHandle'])
5325 {
5326 $LogonTokenHandle = $TokenHandle
5327 }
5328 Else
5329 {
5330 $LogonTokenHandle = [IntPtr]::Zero
5331 $NetworkCredential = $Credential.GetNetworkCredential()
5332 $UserDomain = $NetworkCredential.Domain
5333 If (-Not $UserDomain)
5334 {
5335 Write-Warning "[Get-ADRUserImpersonation] Use credential with Domain FQDN. (<Domain FQDN>\<Username>)"
5336 }
5337 $UserName = $NetworkCredential.UserName
5338 Write-Warning "[Get-ADRUserImpersonation] Executing LogonUser() with user: $($UserDomain)\$($UserName)"
5339
5340 # LOGON32_LOGON_NEW_CREDENTIALS = 9, LOGON32_PROVIDER_WINNT50 = 3
5341 # this is to simulate "runas.exe /netonly" functionality
5342 $Result = $Advapi32::LogonUser($UserName, $UserDomain, $NetworkCredential.Password, 9, 3, [ref]$LogonTokenHandle)
5343 $LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error();
5344
5345 If (-not $Result)
5346 {
5347 throw "[Get-ADRUserImpersonation] LogonUser() Error: $(([ComponentModel.Win32Exception] $LastError).Message)"
5348 }
5349 }
5350
5351 # actually impersonate the token from LogonUser()
5352 $Result = $Advapi32::ImpersonateLoggedOnUser($LogonTokenHandle)
5353
5354 If (-not $Result)
5355 {
5356 throw "[Get-ADRUserImpersonation] ImpersonateLoggedOnUser() Error: $(([ComponentModel.Win32Exception] $LastError).Message)"
5357 }
5358
5359 Write-Verbose "[Get-ADR-UserImpersonation] Alternate credentials successfully impersonated"
5360 $LogonTokenHandle
5361}
5362
5363# Modified Invoke-RevertToSelf function from https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1
5364Function Get-ADRRevertToSelf
5365{
5366<#
5367.SYNOPSIS
5368
5369Reverts any token impersonation.
5370
5371Author: Will Schroeder (@harmj0y)
5372License: BSD 3-Clause
5373Required Dependencies: PSReflect
5374
5375.DESCRIPTION
5376
5377This function uses RevertToSelf() to revert any impersonated tokens.
5378If -TokenHandle is passed (the token handle returned by Invoke-UserImpersonation),
5379CloseHandle() is used to close the opened handle.
5380
5381.PARAMETER TokenHandle
5382
5383An optional IntPtr TokenHandle returned by Invoke-UserImpersonation.
5384
5385.EXAMPLE
5386
5387$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
5388$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
5389$Token = Invoke-UserImpersonation -Credential $Cred
5390Invoke-RevertToSelf -TokenHandle $Token
5391#>
5392
5393 [CmdletBinding()]
5394 Param(
5395 [ValidateNotNull()]
5396 [IntPtr]
5397 $TokenHandle
5398 )
5399
5400 If ($PSBoundParameters['TokenHandle'])
5401 {
5402 Write-Warning "[Get-ADRRevertToSelf] Reverting token impersonation and closing LogonUser() token handle"
5403 $Result = $Kernel32::CloseHandle($TokenHandle)
5404 }
5405
5406 $Result = $Advapi32::RevertToSelf()
5407 $LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error();
5408
5409 If (-not $Result)
5410 {
5411 Write-Error "[Get-ADRRevertToSelf] RevertToSelf() Error: $(([ComponentModel.Win32Exception] $LastError).Message)"
5412 }
5413
5414 Write-Verbose "[Get-ADRRevertToSelf] Token impersonation successfully reverted"
5415}
5416
5417# Modified Get-DomainSPNTicket function from https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1
5418Function Get-ADRSPNTicket
5419{
5420<#
5421<#
5422.SYNOPSIS
5423 Request the kerberos ticket for a specified service principal name (SPN).
5424
5425 Author: machosec, Will Schroeder (@harmj0y)
5426 License: BSD 3-Clause
5427 Required Dependencies: Invoke-UserImpersonation, Invoke-RevertToSelf
5428
5429.DESCRIPTION
5430 This function will either take one SPN strings, and will request a kerberos ticket for the given SPN using System.IdentityModel.Tokens.KerberosRequestorSecurityToken. The encrypted portion of the ticket is then extracted and output in either crackable Hashcat format.
5431
5432.PARAMETER UserSPN
5433 [string]
5434 Service Principal Name.
5435
5436.OUTPUTS
5437 PSObject.
5438#>
5439 param(
5440 [Parameter(Mandatory = $true)]
5441 [string] $UserSPN
5442 )
5443
5444 Try
5445 {
5446 $Null = [Reflection.Assembly]::LoadWithPartialName('System.IdentityModel')
5447 $Ticket = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $UserSPN
5448 }
5449 Catch
5450 {
5451 Write-Warning "[Get-ADRSPNTicket] Error requesting ticket for SPN $UserSPN"
5452 Write-Warning "[EXCEPTION] $($_.Exception.Message)"
5453 Return $null
5454 }
5455
5456 If ($Ticket)
5457 {
5458 $TicketByteStream = $Ticket.GetRequest()
5459 }
5460
5461 If ($TicketByteStream)
5462 {
5463 $TicketHexStream = [System.BitConverter]::ToString($TicketByteStream) -replace '-'
5464
5465 # TicketHexStream == GSS-API Frame (see https://tools.ietf.org/html/rfc4121#section-4.1)
5466 # No easy way to parse ASN1, so we'll try some janky regex to parse the embedded KRB_AP_REQ.Ticket object
5467 If ($TicketHexStream -match 'a382....3082....A0030201(?<EtypeLen>..)A1.{1,4}.......A282(?<CipherTextLen>....)........(?<DataToEnd>.+)')
5468 {
5469 $Etype = [Convert]::ToByte( $Matches.EtypeLen, 16 )
5470 $CipherTextLen = [Convert]::ToUInt32($Matches.CipherTextLen, 16)-4
5471 $CipherText = $Matches.DataToEnd.Substring(0,$CipherTextLen*2)
5472
5473 # Make sure the next field matches the beginning of the KRB_AP_REQ.Authenticator object
5474 If ($Matches.DataToEnd.Substring($CipherTextLen*2, 4) -ne 'A482')
5475 {
5476 Write-Warning '[Get-ADRSPNTicket] Error parsing ciphertext for the SPN $($Ticket.ServicePrincipalName).' # Use the TicketByteHexStream field and extract the hash offline with Get-KerberoastHashFromAPReq
5477 $Hash = $null
5478 }
5479 Else
5480 {
5481 $Hash = "$($CipherText.Substring(0,32))`$$($CipherText.Substring(32))"
5482 }
5483 }
5484 Else
5485 {
5486 Write-Warning "[Get-ADRSPNTicket] Unable to parse ticket structure for the SPN $($Ticket.ServicePrincipalName)." # Use the TicketByteHexStream field and extract the hash offline with Get-KerberoastHashFromAPReq
5487 $Hash = $null
5488 }
5489 }
5490 $Obj = New-Object PSObject
5491 $Obj | Add-Member -MemberType NoteProperty -Name "ServicePrincipalName" -Value $Ticket.ServicePrincipalName
5492 $Obj | Add-Member -MemberType NoteProperty -Name "Etype" -Value $Etype
5493 $Obj | Add-Member -MemberType NoteProperty -Name "Hash" -Value $Hash
5494 Return $Obj
5495}
5496
5497Function Get-ADRKerberoast
5498{
5499<#
5500.SYNOPSIS
5501 Returns all user service principal name (SPN) hashes in the current (or specified) domain.
5502
5503.DESCRIPTION
5504 Returns all user service principal name (SPN) hashes in the current (or specified) domain.
5505
5506.PARAMETER Protocol
5507 [string]
5508 Which protocol to use; ADWS (default) or LDAP.
5509
5510.PARAMETER objDomain
5511 [DirectoryServices.DirectoryEntry]
5512 Domain Directory Entry object.
5513
5514.PARAMETER Credential
5515 [Management.Automation.PSCredential]
5516 Credentials.
5517
5518.PARAMETER PageSize
5519 [int]
5520 The PageSize to set for the LDAP searcher object. Default 200.
5521
5522.OUTPUTS
5523 PSObject.
5524#>
5525 param(
5526 [Parameter(Mandatory = $true)]
5527 [string] $Protocol,
5528
5529 [Parameter(Mandatory = $false)]
5530 [DirectoryServices.DirectoryEntry] $objDomain,
5531
5532 [Parameter(Mandatory = $false)]
5533 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
5534
5535 [Parameter(Mandatory = $true)]
5536 [int] $PageSize
5537 )
5538
5539 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
5540 {
5541 $LogonToken = Get-ADRUserImpersonation -Credential $Credential
5542 }
5543
5544 If ($Protocol -eq 'ADWS')
5545 {
5546 Try
5547 {
5548 $ADUsers = Get-ADObject -LDAPFilter "(&(!objectClass=computer)(servicePrincipalName=*)(!userAccountControl:1.2.840.113556.1.4.803:=2))" -Properties sAMAccountName,servicePrincipalName,DistinguishedName -ResultPageSize $PageSize
5549 }
5550 Catch
5551 {
5552 Write-Warning "[Get-ADRKerberoast] Error while enumerating UserSPN Objects"
5553 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
5554 Return $null
5555 }
5556
5557 If ($ADUsers)
5558 {
5559 $UserSPNObj = @()
5560 $ADUsers | ForEach-Object {
5561 ForEach ($UserSPN in $_.servicePrincipalName)
5562 {
5563 $Obj = New-Object PSObject
5564 $Obj | Add-Member -MemberType NoteProperty -Name "Username" -Value $_.sAMAccountName
5565 $Obj | Add-Member -MemberType NoteProperty -Name "ServicePrincipalName" -Value $UserSPN
5566
5567 $HashObj = Get-ADRSPNTicket $UserSPN
5568 If ($HashObj)
5569 {
5570 $UserDomain = $_.DistinguishedName.SubString($_.DistinguishedName.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
5571 # JohnTheRipper output format
5572 $JTRHash = "`$krb5tgs`$$($HashObj.ServicePrincipalName):$($HashObj.Hash)"
5573 # hashcat output format
5574 $HashcatHash = "`$krb5tgs`$$($HashObj.Etype)`$*$($_.SamAccountName)`$$UserDomain`$$($HashObj.ServicePrincipalName)*`$$($HashObj.Hash)"
5575 }
5576 Else
5577 {
5578 $JTRHash = $null
5579 $HashcatHash = $null
5580 }
5581 $Obj | Add-Member -MemberType NoteProperty -Name "John" -Value $JTRHash
5582 $Obj | Add-Member -MemberType NoteProperty -Name "Hashcat" -Value $HashcatHash
5583 $UserSPNObj += $Obj
5584 }
5585 }
5586 Remove-Variable ADUsers
5587 }
5588 }
5589
5590 If ($Protocol -eq 'LDAP')
5591 {
5592 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
5593 $ObjSearcher.PageSize = $PageSize
5594 $ObjSearcher.Filter = "(&(!objectClass=computer)(servicePrincipalName=*)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
5595 $ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","samaccountname","serviceprincipalname","useraccountcontrol"))
5596 $ObjSearcher.SearchScope = "Subtree"
5597 Try
5598 {
5599 $ADUsers = $ObjSearcher.FindAll()
5600 }
5601 Catch
5602 {
5603 Write-Warning "[Get-ADRKerberoast] Error while enumerating UserSPN Objects"
5604 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
5605 Return $null
5606 }
5607 $ObjSearcher.dispose()
5608
5609 If ($ADUsers)
5610 {
5611 $UserSPNObj = @()
5612 $ADUsers | ForEach-Object {
5613 ForEach ($UserSPN in $_.Properties.serviceprincipalname)
5614 {
5615 $Obj = New-Object PSObject
5616 $Obj | Add-Member -MemberType NoteProperty -Name "Username" -Value $_.Properties.samaccountname[0]
5617 $Obj | Add-Member -MemberType NoteProperty -Name "ServicePrincipalName" -Value $UserSPN
5618
5619 $HashObj = Get-ADRSPNTicket $UserSPN
5620 If ($HashObj)
5621 {
5622 $UserDomain = $_.Properties.distinguishedname[0].SubString($_.Properties.distinguishedname[0].IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
5623 # JohnTheRipper output format
5624 $JTRHash = "`$krb5tgs`$$($HashObj.ServicePrincipalName):$($HashObj.Hash)"
5625 # hashcat output format
5626 $HashcatHash = "`$krb5tgs`$$($HashObj.Etype)`$*$($_.Properties.samaccountname)`$$UserDomain`$$($HashObj.ServicePrincipalName)*`$$($HashObj.Hash)"
5627 }
5628 Else
5629 {
5630 $JTRHash = $null
5631 $HashcatHash = $null
5632 }
5633 $Obj | Add-Member -MemberType NoteProperty -Name "John" -Value $JTRHash
5634 $Obj | Add-Member -MemberType NoteProperty -Name "Hashcat" -Value $HashcatHash
5635 $UserSPNObj += $Obj
5636 }
5637 }
5638 Remove-Variable ADUsers
5639 }
5640 }
5641
5642 If ($LogonToken)
5643 {
5644 Get-ADRRevertToSelf -TokenHandle $LogonToken
5645 }
5646
5647 If ($UserSPNObj)
5648 {
5649 Return $UserSPNObj
5650 }
5651 Else
5652 {
5653 Return $null
5654 }
5655}
5656
5657# based on https://gallery.technet.microsoft.com/scriptcenter/PowerShell-script-to-find-6fc15ecb
5658Function Get-ADRDomainAccountsusedforServiceLogon
5659{
5660<#
5661.SYNOPSIS
5662 Returns all accounts used by services on computers in an Active Directory domain.
5663
5664.DESCRIPTION
5665 Retrieves a list of all computers in the current domain and reads service configuration using Get-WmiObject.
5666
5667.PARAMETER Protocol
5668 [string]
5669 Which protocol to use; ADWS (default) or LDAP.
5670
5671.PARAMETER objDomain
5672 [DirectoryServices.DirectoryEntry]
5673 Domain Directory Entry object.
5674
5675.PARAMETER PageSize
5676 [int]
5677 The PageSize to set for the LDAP searcher object. Default 200.
5678
5679.PARAMETER Threads
5680 [int]
5681 The number of threads to use during processing of objects. Default 10.
5682
5683.OUTPUTS
5684 PSObject.
5685#>
5686 param(
5687 [Parameter(Mandatory = $true)]
5688 [string] $Protocol,
5689
5690 [Parameter(Mandatory = $false)]
5691 [DirectoryServices.DirectoryEntry] $objDomain,
5692
5693 [Parameter(Mandatory = $false)]
5694 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
5695
5696 [Parameter(Mandatory = $true)]
5697 [int] $PageSize,
5698
5699 [Parameter(Mandatory = $false)]
5700 [int] $Threads = 10
5701 )
5702
5703 BEGIN {
5704 $readServiceAccounts = [scriptblock] {
5705 # scriptblock to retrieve service list form a remove machine
5706 $hostname = [string] $args[0]
5707 $OperatingSystem = [string] $args[1]
5708 #$Credential = [Management.Automation.PSCredential] $args[2]
5709 $Credential = $args[2]
5710 $timeout = 250
5711 $port = 135
5712 Try
5713 {
5714 $tcpclient = New-Object System.Net.Sockets.TcpClient
5715 $result = $tcpclient.BeginConnect($hostname,$port,$null,$null)
5716 $success = $result.AsyncWaitHandle.WaitOne($timeout,$null)
5717 }
5718 Catch
5719 {
5720 $warning = "$hostname ($OperatingSystem) is unreachable $($_.Exception.Message)"
5721 $success = $false
5722 $tcpclient.Close()
5723 }
5724 If ($success)
5725 {
5726 # PowerShellv2 does not support New-CimSession
5727 If ($PSVersionTable.PSVersion.Major -ne 2)
5728 {
5729 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
5730 {
5731 $session = New-CimSession -ComputerName $hostname -SessionOption $(New-CimSessionOption –Protocol DCOM) -Credential $Credential
5732 If ($session)
5733 {
5734 $serviceList = @( Get-CimInstance -ClassName Win32_Service -Property Name,StartName,SystemName -CimSession $session -ErrorAction Stop)
5735 }
5736 }
5737 Else
5738 {
5739 $session = New-CimSession -ComputerName $hostname -SessionOption $(New-CimSessionOption –Protocol DCOM)
5740 If ($session)
5741 {
5742 $serviceList = @( Get-CimInstance -ClassName Win32_Service -Property Name,StartName,SystemName -CimSession $session -ErrorAction Stop )
5743 }
5744 }
5745 }
5746 Else
5747 {
5748 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
5749 {
5750 $serviceList = @( Get-WmiObject -Class Win32_Service -ComputerName $hostname -Credential $Credential -Impersonation 3 -Property Name,StartName,SystemName -ErrorAction Stop )
5751 }
5752 Else
5753 {
5754 $serviceList = @( Get-WmiObject -Class Win32_Service -ComputerName $hostname -Property Name,StartName,SystemName -ErrorAction Stop )
5755 }
5756 }
5757 $serviceList
5758 }
5759 Try
5760 {
5761 If ($tcpclient) { $tcpclient.EndConnect($result) | Out-Null }
5762 }
5763 Catch
5764 {
5765 $warning = "$hostname ($OperatingSystem) : $($_.Exception.Message)"
5766 }
5767 $warning
5768 }
5769
5770 Function processCompletedJobs()
5771 {
5772 # reads service list from completed jobs,
5773 # updates $serviceAccount table and removes completed job
5774
5775 $jobs = Get-Job -State Completed
5776 ForEach( $job in $jobs )
5777 {
5778 If ($null -ne $job)
5779 {
5780 $data = Receive-Job $job
5781 Remove-Job $job
5782 }
5783
5784 If ($data)
5785 {
5786 If ( $data.GetType() -eq [Object[]] )
5787 {
5788 $serviceList = $data | Where-Object { if ($_.StartName) { $_ }}
5789 $serviceList | ForEach-Object {
5790 $Obj = New-Object PSObject
5791 $Obj | Add-Member -MemberType NoteProperty -Name "Account" -Value $_.StartName
5792 $Obj | Add-Member -MemberType NoteProperty -Name "Service Name" -Value $_.Name
5793 $Obj | Add-Member -MemberType NoteProperty -Name "SystemName" -Value $_.SystemName
5794 If ($_.StartName.toUpper().Contains($currentDomain))
5795 {
5796 $Obj | Add-Member -MemberType NoteProperty -Name "Running as Domain User" -Value $true
5797 }
5798 Else
5799 {
5800 $Obj | Add-Member -MemberType NoteProperty -Name "Running as Domain User" -Value $false
5801 }
5802 $script:serviceAccounts += $Obj
5803 }
5804 }
5805 ElseIf ( $data.GetType() -eq [String] )
5806 {
5807 $script:warnings += $data
5808 Write-Verbose $data
5809 }
5810 }
5811 }
5812 }
5813 }
5814
5815 PROCESS
5816 {
5817 $script:serviceAccounts = @()
5818 [string[]] $warnings = @()
5819 If ($Protocol -eq 'ADWS')
5820 {
5821 Try
5822 {
5823 $ADDomain = Get-ADDomain
5824 }
5825 Catch
5826 {
5827 Write-Warning "[Get-ADRDomainAccountsusedforServiceLogon] Error getting Domain Context"
5828 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
5829 Return $null
5830 }
5831 If ($ADDomain)
5832 {
5833 $currentDomain = $ADDomain.NetBIOSName.toUpper()
5834 Remove-Variable ADDomain
5835 }
5836 Else
5837 {
5838 $currentDomain = ""
5839 Write-Warning "Current Domain could not be retrieved."
5840 }
5841
5842 Try
5843 {
5844 $ADComputers = Get-ADComputer -Filter { Enabled -eq $true -and OperatingSystem -Like "*Windows*" } -Properties Name,DNSHostName,OperatingSystem
5845 }
5846 Catch
5847 {
5848 Write-Warning "[Get-ADRDomainAccountsusedforServiceLogon] Error while enumerating Windows Computer Objects"
5849 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
5850 Return $null
5851 }
5852
5853 If ($ADComputers)
5854 {
5855 # start data retrieval job for each server in the list
5856 # use up to $Threads threads
5857 $cnt = $([ADRecon.ADWSClass]::ObjectCount($ADComputers))
5858 Write-Verbose "[*] Total Windows Hosts: $cnt"
5859 $icnt = 0
5860 $ADComputers | ForEach-Object {
5861 $StopWatch = [System.Diagnostics.StopWatch]::StartNew()
5862 If( $_.dnshostname )
5863 {
5864 $args = @($_.DNSHostName, $_.OperatingSystem, $Credential)
5865 Start-Job -ScriptBlock $readServiceAccounts -Name "read_$($_.name)" -ArgumentList $args | Out-Null
5866 ++$icnt
5867 If ($StopWatch.Elapsed.TotalMilliseconds -ge 1000)
5868 {
5869 Write-Progress -Activity "Retrieving data from servers" -Status "$("{0:N2}" -f (($icnt/$cnt*100),2)) % Complete:" -PercentComplete 100
5870 $StopWatch.Reset()
5871 $StopWatch.Start()
5872 }
5873 while ( ( Get-Job -State Running).count -ge $Threads ) { Start-Sleep -Seconds 3 }
5874 processCompletedJobs
5875 }
5876 }
5877
5878 # process remaining jobs
5879
5880 Write-Progress -Activity "Retrieving data from servers" -Status "Waiting for background jobs to complete..." -PercentComplete 100
5881 Wait-Job -State Running -Timeout 30 | Out-Null
5882 Get-Job -State Running | Stop-Job
5883 processCompletedJobs
5884 Write-Progress -Activity "Retrieving data from servers" -Completed -Status "All Done"
5885 }
5886 }
5887
5888 If ($Protocol -eq 'LDAP')
5889 {
5890 $currentDomain = ([string]($objDomain.name)).toUpper()
5891
5892 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
5893 $ObjSearcher.PageSize = $PageSize
5894 $ObjSearcher.Filter = "(&(samAccountType=805306369)(!userAccountControl:1.2.840.113556.1.4.803:=2)(operatingSystem=*Windows*))"
5895 $ObjSearcher.PropertiesToLoad.AddRange(("name","dnshostname","operatingsystem"))
5896 $ObjSearcher.SearchScope = "Subtree"
5897
5898 Try
5899 {
5900 $ADComputers = $ObjSearcher.FindAll()
5901 }
5902 Catch
5903 {
5904 Write-Warning "[Get-ADRDomainAccountsusedforServiceLogon] Error while enumerating Windows Computer Objects"
5905 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
5906 Return $null
5907 }
5908 $ObjSearcher.dispose()
5909
5910 If ($ADComputers)
5911 {
5912 # start data retrieval job for each server in the list
5913 # use up to $Threads threads
5914 $cnt = $([ADRecon.LDAPClass]::ObjectCount($ADComputers))
5915 Write-Verbose "[*] Total Windows Hosts: $cnt"
5916 $icnt = 0
5917 $ADComputers | ForEach-Object {
5918 If( $_.Properties.dnshostname )
5919 {
5920 $args = @($_.Properties.dnshostname, $_.Properties.operatingsystem, $Credential)
5921 Start-Job -ScriptBlock $readServiceAccounts -Name "read_$($_.Properties.name)" -ArgumentList $args | Out-Null
5922 ++$icnt
5923 If ($StopWatch.Elapsed.TotalMilliseconds -ge 1000)
5924 {
5925 Write-Progress -Activity "Retrieving data from servers" -Status "$("{0:N2}" -f (($icnt/$cnt*100),2)) % Complete:" -PercentComplete 100
5926 $StopWatch.Reset()
5927 $StopWatch.Start()
5928 }
5929 while ( ( Get-Job -State Running).count -ge $Threads ) { Start-Sleep -Seconds 3 }
5930 processCompletedJobs
5931 }
5932 }
5933
5934 # process remaining jobs
5935 Write-Progress -Activity "Retrieving data from servers" -Status "Waiting for background jobs to complete..." -PercentComplete 100
5936 Wait-Job -State Running -Timeout 30 | Out-Null
5937 Get-Job -State Running | Stop-Job
5938 processCompletedJobs
5939 Write-Progress -Activity "Retrieving data from servers" -Completed -Status "All Done"
5940 }
5941 }
5942
5943 If ($script:serviceAccounts)
5944 {
5945 Return $script:serviceAccounts
5946 }
5947 Else
5948 {
5949 Return $null
5950 }
5951 }
5952}
5953
5954Function Remove-EmptyADROutputDir
5955{
5956<#
5957.SYNOPSIS
5958 Removes ADRecon output folder if empty.
5959
5960.DESCRIPTION
5961 Removes ADRecon output folder if empty.
5962
5963.PARAMETER ADROutputDir
5964 [string]
5965 Path for ADRecon output folder.
5966
5967.PARAMETER OutputType
5968 [array]
5969 Output Type.
5970#>
5971 param(
5972 [Parameter(Mandatory = $true)]
5973 [string] $ADROutputDir,
5974
5975 [Parameter(Mandatory = $true)]
5976 [array] $OutputType
5977 )
5978
5979 Switch ($OutputType)
5980 {
5981 'CSV'
5982 {
5983 $CSVPath = -join($ADROutputDir,'\','CSV-Files')
5984 If (!(Test-Path -Path $CSVPath\*))
5985 {
5986 Write-Verbose "Removed Empty Directory $CSVPath"
5987 Remove-Item $CSVPath
5988 }
5989 }
5990 'XML'
5991 {
5992 $XMLPath = -join($ADROutputDir,'\','XML-Files')
5993 If (!(Test-Path -Path $XMLPath\*))
5994 {
5995 Write-Verbose "Removed Empty Directory $XMLPath"
5996 Remove-Item $XMLPath
5997 }
5998 }
5999 'JSON'
6000 {
6001 $JSONPath = -join($ADROutputDir,'\','JSON-Files')
6002 If (!(Test-Path -Path $JSONPath\*))
6003 {
6004 Write-Verbose "Removed Empty Directory $JSONPath"
6005 Remove-Item $JSONPath
6006 }
6007 }
6008 'HTML'
6009 {
6010 $HTMLPath = -join($ADROutputDir,'\','HTML-Files')
6011 If (!(Test-Path -Path $HTMLPath\*))
6012 {
6013 Write-Verbose "Removed Empty Directory $HTMLPath"
6014 Remove-Item $HTMLPath
6015 }
6016 }
6017 }
6018 If (!(Test-Path -Path $ADROutputDir\*))
6019 {
6020 Remove-Item $ADROutputDir
6021 Write-Verbose "Removed Empty Directory $ADROutputDir"
6022 }
6023}
6024
6025Function Get-ADRAbout
6026{
6027<#
6028.SYNOPSIS
6029 Returns information about ADRecon.
6030
6031.DESCRIPTION
6032 Returns information about ADRecon.
6033
6034.PARAMETER Protocol
6035 [string]
6036 Which protocol to use; ADWS (default) or LDAP.
6037
6038.PARAMETER date
6039 [DateTime]
6040 Date
6041
6042.PARAMETER ADReconVersion
6043 [string]
6044 ADRecon Version.
6045
6046.PARAMETER Credential
6047 [Management.Automation.PSCredential]
6048 Credentials.
6049
6050.PARAMETER RanonComputer
6051 [string]
6052 Details of the Computer running ADRecon.
6053
6054.PARAMETER TotalTime
6055 [string]
6056 TotalTime.
6057
6058.OUTPUTS
6059 PSObject.
6060#>
6061 param(
6062 [Parameter(Mandatory = $true)]
6063 [string] $Protocol,
6064
6065 [Parameter(Mandatory = $true)]
6066 [DateTime] $date,
6067
6068 [Parameter(Mandatory = $true)]
6069 [string] $ADReconVersion,
6070
6071 [Parameter(Mandatory = $false)]
6072 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
6073
6074 [Parameter(Mandatory = $true)]
6075 [string] $RanonComputer,
6076
6077 [Parameter(Mandatory = $true)]
6078 [string] $TotalTime
6079 )
6080
6081 $AboutADRecon = @()
6082
6083 If ($Protocol -eq 'ADWS')
6084 {
6085 $Version = "RSAT Version"
6086 }
6087 Else
6088 {
6089 $Version = "LDAP Version"
6090 }
6091
6092 If ($Credential -ne [Management.Automation.PSCredential]::Empty)
6093 {
6094 $Username = $($Credential.UserName)
6095 }
6096 Else
6097 {
6098 $Username = $([Environment]::UserName)
6099 }
6100
6101 $ObjValues = @("Date", $($date), "ADRecon", "https://github.com/sense-of-security/ADRecon", $Version, $($ADReconVersion), "Ran as user", $Username, "Ran on computer", $RanonComputer, "Execution Time (mins)", $($TotalTime))
6102
6103 For ($i = 0; $i -lt $($ObjValues.Count); $i++)
6104 {
6105 $Obj = New-Object PSObject
6106 $Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value $ObjValues[$i]
6107 $Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ObjValues[$i+1]
6108 $i++
6109 $AboutADRecon += $Obj
6110 }
6111 Return $AboutADRecon
6112}
6113
6114Function Invoke-ADRecon
6115{
6116<#
6117.SYNOPSIS
6118 Wrapper function to run ADRecon modules.
6119
6120.DESCRIPTION
6121 Wrapper function to set variables, check dependencies and run ADRecon modules.
6122
6123.PARAMETER Protocol
6124 [string]
6125 Which protocol to use; ADWS (default) or LDAP.
6126
6127.PARAMETER Collect
6128 [array]
6129 Which modules to run; Forest, Domain, Trusts, Sites, Subnets, PasswordPolicy, FineGrainedPasswordPolicy, DomainControllers, Users, UserSPNs, PasswordAttributes, Groups, GroupMembers, OUs, GPOs, gPLinks, DNSZones, Printers, Computers, ComputerSPNs, LAPS, BitLocker, ACLs, GPOReport, Kerberoast, DomainAccountsusedforServiceLogon.
6130
6131.PARAMETER DomainController
6132 [string]
6133 IP Address of the Domain Controller.
6134
6135.PARAMETER Credential
6136 [Management.Automation.PSCredential]
6137 Credentials.
6138
6139.PARAMETER OutputDir
6140 [string]
6141 Path for ADRecon output folder to save the CSV files and the ADRecon-Report.xlsx.
6142
6143.PARAMETER DormantTimeSpan
6144 [int]
6145 Timespan for Dormant accounts. Default 90 days.
6146
6147.PARAMTER PassMaxAge
6148 [int]
6149 Maximum machine account password age. Default 30 days
6150
6151.PARAMETER PageSize
6152 [int]
6153 The PageSize to set for the LDAP searcher object. Default 200.
6154
6155.PARAMETER Threads
6156 [int]
6157 The number of threads to use during processing of objects. Default 10.
6158
6159.PARAMETER UseAltCreds
6160 [bool]
6161 Whether to use provided credentials or not.
6162
6163.OUTPUTS
6164 STDOUT, CSV, XML, JSON, HTML and/or Excel file is created in the folder specified with the information.
6165#>
6166 param(
6167 [Parameter(Mandatory = $false)]
6168 [string] $GenExcel,
6169
6170 [Parameter(Mandatory = $false)]
6171 [ValidateSet('ADWS', 'LDAP')]
6172 [string] $Protocol = 'ADWS',
6173
6174 [Parameter(Mandatory = $true)]
6175 [array] $Collect,
6176
6177 [Parameter(Mandatory = $false)]
6178 [string] $DomainController = '',
6179
6180 [Parameter(Mandatory = $false)]
6181 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
6182
6183 [Parameter(Mandatory = $true)]
6184 [array] $OutputType,
6185
6186 [Parameter(Mandatory = $false)]
6187 [string] $ADROutputDir,
6188
6189 [Parameter(Mandatory = $false)]
6190 [int] $DormantTimeSpan = 90,
6191
6192 [Parameter(Mandatory = $false)]
6193 [int] $PassMaxAge = 30,
6194
6195 [Parameter(Mandatory = $false)]
6196 [int] $PageSize = 200,
6197
6198 [Parameter(Mandatory = $false)]
6199 [int] $Threads = 10,
6200
6201 [Parameter(Mandatory = $false)]
6202 [bool] $UseAltCreds = $false
6203 )
6204
6205 [string] $ADReconVersion = "v1.1"
6206 Write-Output "[*] ADRecon $ADReconVersion by Prashant Mahajan (@prashant3535)"
6207
6208 If ($GenExcel)
6209 {
6210 If (!(Test-Path $GenExcel))
6211 {
6212 Write-Output "[Invoke-ADRecon] Invalid Path ... Exiting"
6213 Return $null
6214 }
6215 Export-ADRExcel -ExcelPath $GenExcel
6216 Return $null
6217 }
6218
6219 # Suppress verbose output
6220 $SaveVerbosePreference = $script:VerbosePreference
6221 $script:VerbosePreference = 'SilentlyContinue'
6222 Try
6223 {
6224 If ($PSVersionTable.PSVersion.Major -ne 2)
6225 {
6226 $computer = Get-CimInstance -ClassName Win32_ComputerSystem
6227 $computerdomainrole = ($computer).DomainRole
6228 }
6229 Else
6230 {
6231 $computer = Get-WMIObject win32_computersystem
6232 $computerdomainrole = ($computer).DomainRole
6233 }
6234 }
6235 Catch
6236 {
6237 Write-Output "[Invoke-ADRecon] $($_.Exception.Message)"
6238 }
6239 If ($SaveVerbosePreference)
6240 {
6241 $script:VerbosePreference = $SaveVerbosePreference
6242 Remove-Variable SaveVerbosePreference
6243 }
6244
6245 switch ($computerdomainrole)
6246 {
6247 0
6248 {
6249 [string] $computerrole = "Standalone Workstation"
6250 $Env:ADPS_LoadDefaultDrive = 0
6251 $UseAltCreds = $true
6252 }
6253 1 { [string] $computerrole = "Member Workstation" }
6254 2
6255 {
6256 [string] $computerrole = "Standalone Server"
6257 $UseAltCreds = $true
6258 $Env:ADPS_LoadDefaultDrive = 0
6259 }
6260 3 { [string] $computerrole = "Member Server" }
6261 4 { [string] $computerrole = "Backup Domain Controller" }
6262 5 { [string] $computerrole = "Primary Domain Controller" }
6263 default { Write-Output "Computer Role could not be identified." }
6264 }
6265
6266 $RanonComputer = "$($computer.domain)\$([Environment]::MachineName) - $($computerrole)"
6267 Remove-Variable computer
6268 Remove-Variable computerdomainrole
6269 Remove-Variable computerrole
6270
6271 # If either DomainController or Credentials are provided, treat as non-member
6272 If (($DomainController -ne "") -or ($Credential -ne [Management.Automation.PSCredential]::Empty))
6273 {
6274 # Disable loading of default drive on member
6275 If (($Protocol -eq 'ADWS') -and (-Not $UseAltCreds))
6276 {
6277 $Env:ADPS_LoadDefaultDrive = 0
6278 }
6279 $UseAltCreds = $true
6280 }
6281
6282 # Import ActiveDirectory module
6283 If ($Protocol -eq 'ADWS')
6284 {
6285 Try
6286 {
6287 # Suppress verbose output on module import
6288 $SaveVerbosePreference = $script:VerbosePreference;
6289 $script:VerbosePreference = 'SilentlyContinue';
6290 Import-Module ActiveDirectory -WarningAction Stop -ErrorAction Stop | Out-Null
6291 If ($SaveVerbosePreference)
6292 {
6293 $script:VerbosePreference = $SaveVerbosePreference
6294 Remove-Variable SaveVerbosePreference
6295 }
6296 }
6297 Catch
6298 {
6299 Write-Warning "[Invoke-ADRecon] Error importing ActiveDirectory Module from RSAT (Remote Server Administration Tools) ... Continuing with LDAP"
6300 $Protocol = 'LDAP'
6301 If ($SaveVerbosePreference)
6302 {
6303 $script:VerbosePreference = $SaveVerbosePreference
6304 Remove-Variable SaveVerbosePreference
6305 }
6306 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
6307 }
6308 }
6309
6310 # Compile C# code
6311 # Suppress Debug output
6312 $SaveDebugPreference = $script:DebugPreference
6313 $script:DebugPreference = 'SilentlyContinue'
6314 Try
6315 {
6316 $Advapi32 = Add-Type -MemberDefinition $Advapi32Def -Name "Advapi32" -Namespace ADRecon -PassThru
6317 $Kernel32 = Add-Type -MemberDefinition $Kernel32Def -Name "Kernel32" -Namespace ADRecon -PassThru
6318 Add-Type -TypeDefinition $PingCastleSMBScannerSource
6319 $CLR = ([System.Reflection.Assembly]::GetExecutingAssembly().ImageRuntimeVersion)[1]
6320 If ($Protocol -eq 'ADWS')
6321 {
6322 If ($CLR -eq "4")
6323 {
6324 Add-Type -TypeDefinition $ADWSSource -ReferencedAssemblies ([System.String[]]@(([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.ActiveDirectory.Management")).Location,([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).Location))
6325 }
6326 Else
6327 {
6328 Add-Type -TypeDefinition $ADWSSource -ReferencedAssemblies ([System.String[]]@(([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.ActiveDirectory.Management")).Location,([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).Location)) -Language CSharpVersion3
6329 }
6330 }
6331
6332 If ($Protocol -eq 'LDAP')
6333 {
6334 If ($CLR -eq "4")
6335 {
6336 Add-Type -TypeDefinition $LDAPSource -ReferencedAssemblies ([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).Location
6337 }
6338 Else
6339 {
6340 Add-Type -TypeDefinition $LDAPSource -ReferencedAssemblies ([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).Location -Language CSharpVersion3
6341 }
6342 }
6343 }
6344 Catch
6345 {
6346 Write-Output "[Invoke-ADRecon] $($_.Exception.Message)"
6347 Return $null
6348 }
6349 If ($SaveDebugPreference)
6350 {
6351 $script:DebugPreference = $SaveDebugPreference
6352 Remove-Variable SaveDebugPreference
6353 }
6354
6355 # Allow running using RUNAS from a non-domain joined machine
6356 # runas /user:<Domain FQDN>\<Username> /netonly powershell.exe
6357 If (($Protocol -eq 'LDAP') -and ($UseAltCreds) -and ($DomainController -eq "") -and ($Credential -eq [Management.Automation.PSCredential]::Empty))
6358 {
6359 Try
6360 {
6361 $objDomain = [ADSI]""
6362 If(!($objDomain.name))
6363 {
6364 Write-Verbose "[Invoke-ADRecon] RUNAS Check, LDAP bind Unsuccessful"
6365 }
6366 $UseAltCreds = $false
6367 $objDomain.Dispose()
6368 }
6369 Catch
6370 {
6371 $UseAltCreds = $true
6372 }
6373 }
6374
6375 If ($UseAltCreds -and (($DomainController -eq "") -or ($Credential -eq [Management.Automation.PSCredential]::Empty)))
6376 {
6377
6378 If (($DomainController -ne "") -and ($Credential -eq [Management.Automation.PSCredential]::Empty))
6379 {
6380 Try
6381 {
6382 $Credential = Get-Credential
6383 }
6384 Catch
6385 {
6386 Write-Output "[Invoke-ADRecon] $($_.Exception.Message)"
6387 Return $null
6388 }
6389 }
6390 Else
6391 {
6392 Write-Output "Run Get-Help .\ADRecon.ps1 -Examples for additional information."
6393 Write-Output "[Invoke-ADRecon] Use the -DomainController and -Credential parameter."`n
6394 Return $null
6395 }
6396 }
6397
6398 Write-Output "[*] Running on $RanonComputer"
6399
6400 Switch ($Collect)
6401 {
6402 'Forest' { $ADRForest = $true }
6403 'Domain' {$ADRDomain = $true }
6404 'Trusts' { $ADRTrust = $true }
6405 'Sites' { $ADRSite = $true }
6406 'Subnets' { $ADRSubnet = $true }
6407 'PasswordPolicy' { $ADRPasswordPolicy = $true }
6408 'FineGrainedPasswordPolicy' { $ADRFineGrainedPasswordPolicy = $true }
6409 'DomainControllers' { $ADRDomainControllers = $true }
6410 'Users' { $ADRUsers = $true }
6411 'UserSPNs' { $ADRUserSPNs = $true }
6412 'PasswordAttributes' { $ADRPasswordAttributes = $true }
6413 'Groups' { $ADRGroups = $true }
6414 'GroupMembers' { $ADRGroupMembers = $true }
6415 'OUs' { $ADROUs = $true }
6416 'GPOs' { $ADRGPOs = $true }
6417 'gPLinks' { $ADRgPLinks = $true }
6418 'DNSZones' { $ADRDNSZones = $true }
6419 'Printers' { $ADRPrinters = $true }
6420 'Computers' { $ADRComputers = $true }
6421 'ComputerSPNs' { $ADRComputerSPNs = $true }
6422 'LAPS' { $ADRLAPS = $true }
6423 'BitLocker' { $ADRBitLocker = $true }
6424 'ACLs' { $ADRACLs = $true }
6425 'GPOReport'
6426 {
6427 $ADRGPOReport = $true
6428 $ADRCreate = $true
6429 }
6430 'Kerberoast' { $ADRKerberoast = $true }
6431 'DomainAccountsusedforServiceLogon' { $ADRDomainAccountsusedforServiceLogon = $true }
6432 'Default'
6433 {
6434 $ADRForest = $true
6435 $ADRDomain = $true
6436 $ADRTrust = $true
6437 $ADRSite = $true
6438 $ADRSubnet = $true
6439 $ADRPasswordPolicy = $true
6440 $ADRFineGrainedPasswordPolicy = $true
6441 $ADRDomainControllers = $true
6442 $ADRUsers = $true
6443 $ADRUserSPNs = $true
6444 $ADRPasswordAttributes = $true
6445 $ADRGroups = $true
6446 $ADRGroupMembers = $true
6447 $ADROUs = $true
6448 $ADRGPOs = $true
6449 $ADRgPLinks = $true
6450 $ADRDNSZones = $true
6451 $ADRPrinters = $true
6452 $ADRComputers = $true
6453 $ADRComputerSPNs = $true
6454 $ADRLAPS = $true
6455 $ADRBitLocker = $true
6456 $ADRACLs = $true
6457 $ADRGPOReport = $true
6458 #$ADRKerberoast = $true
6459 #$ADRDomainAccountsusedforServiceLogon = $true
6460 If ($OutputType -eq "Default")
6461 {
6462 [array] $OutputType = "CSV","Excel"
6463 }
6464 }
6465 }
6466
6467 Switch ($OutputType)
6468 {
6469 'STDOUT' { $ADRSTDOUT = $true }
6470 'CSV'
6471 {
6472 $ADRCSV = $true
6473 $ADRCreate = $true
6474 }
6475 'XML'
6476 {
6477 $ADRXML = $true
6478 $ADRCreate = $true
6479 }
6480 'JSON'
6481 {
6482 $ADRJSON = $true
6483 $ADRCreate = $true
6484 }
6485 'HTML'
6486 {
6487 $ADRHTML = $true
6488 $ADRCreate = $true
6489 }
6490 'Excel'
6491 {
6492 $ADRExcel = $true
6493 $ADRCreate = $true
6494 }
6495 'All'
6496 {
6497 #$ADRSTDOUT = $true
6498 $ADRCSV = $true
6499 $ADRXML = $true
6500 $ADRJSON = $true
6501 $ADRHTML = $true
6502 $ADRExcel = $true
6503 $ADRCreate = $true
6504 [array] $OutputType = "CSV","XML","JSON","HTML","Excel"
6505 }
6506 'Default'
6507 {
6508 [array] $OutputType = "STDOUT"
6509 $ADRSTDOUT = $true
6510 }
6511 }
6512
6513 If ( ($ADRExcel) -and (-Not $ADRCSV) )
6514 {
6515 $ADRCSV = $true
6516 [array] $OutputType += "CSV"
6517 }
6518
6519 $returndir = Get-Location
6520 $date = Get-Date
6521
6522 # Create Output dir
6523 If ( ($ADROutputDir) -and ($ADRCreate) )
6524 {
6525 If (!(Test-Path $ADROutputDir))
6526 {
6527 New-Item $ADROutputDir -type directory | Out-Null
6528 If (!(Test-Path $ADROutputDir))
6529 {
6530 Write-Output "[Invoke-ADRecon] Error, invalid OutputDir Path ... Exiting"
6531 Return $null
6532 }
6533 }
6534 $ADROutputDir = $((Convert-Path $ADROutputDir).TrimEnd("\"))
6535 Write-Verbose "[*] Output Directory: $ADROutputDir"
6536 }
6537 ElseIf ($ADRCreate)
6538 {
6539 $ADROutputDir = -join($returndir,'\','ADRecon-Report-',$(Get-Date -UFormat %Y%m%d%H%M%S))
6540 New-Item $ADROutputDir -type directory | Out-Null
6541 If (!(Test-Path $ADROutputDir))
6542 {
6543 Write-Output "[Invoke-ADRecon] Error, could not create output directory"
6544 Return $null
6545 }
6546 $ADROutputDir = $((Convert-Path $ADROutputDir).TrimEnd("\"))
6547 Remove-Variable ADRCreate
6548 }
6549 Else
6550 {
6551 $ADROutputDir = $returndir
6552 }
6553
6554 If ($ADRCSV)
6555 {
6556 $CSVPath = [System.IO.DirectoryInfo] -join($ADROutputDir,'\','CSV-Files')
6557 New-Item $CSVPath -type directory | Out-Null
6558 If (!(Test-Path $CSVPath))
6559 {
6560 Write-Output "[Invoke-ADRecon] Error, could not create output directory"
6561 Return $null
6562 }
6563 Remove-Variable ADRCSV
6564 }
6565
6566 If ($ADRXML)
6567 {
6568 $XMLPath = [System.IO.DirectoryInfo] -join($ADROutputDir,'\','XML-Files')
6569 New-Item $XMLPath -type directory | Out-Null
6570 If (!(Test-Path $XMLPath))
6571 {
6572 Write-Output "[Invoke-ADRecon] Error, could not create output directory"
6573 Return $null
6574 }
6575 Remove-Variable ADRXML
6576 }
6577
6578 If ($ADRJSON)
6579 {
6580 $JSONPath = [System.IO.DirectoryInfo] -join($ADROutputDir,'\','JSON-Files')
6581 New-Item $JSONPath -type directory | Out-Null
6582 If (!(Test-Path $JSONPath))
6583 {
6584 Write-Output "[Invoke-ADRecon] Error, could not create output directory"
6585 Return $null
6586 }
6587 Remove-Variable ADRJSON
6588 }
6589
6590 If ($ADRHTML)
6591 {
6592 $HTMLPath = [System.IO.DirectoryInfo] -join($ADROutputDir,'\','HTML-Files')
6593 New-Item $HTMLPath -type directory | Out-Null
6594 If (!(Test-Path $HTMLPath))
6595 {
6596 Write-Output "[Invoke-ADRecon] Error, could not create output directory"
6597 Return $null
6598 }
6599 Remove-Variable ADRHTML
6600 }
6601
6602 # AD Login
6603 If ($UseAltCreds -and ($Protocol -eq 'ADWS'))
6604 {
6605 If (!(Test-Path ADR:))
6606 {
6607 Try
6608 {
6609 New-PSDrive -PSProvider ActiveDirectory -Name ADR -Root "" -Server $DomainController -Credential $Credential -ErrorAction Stop | Out-Null
6610 }
6611 Catch
6612 {
6613 Write-Output "[Invoke-ADRecon] $($_.Exception.Message)"
6614 If ($ADROutputDir)
6615 {
6616 Remove-EmptyADROutputDir $ADROutputDir $OutputType
6617 }
6618 Return $null
6619 }
6620 }
6621 Else
6622 {
6623 Remove-PSDrive ADR
6624 Try
6625 {
6626 New-PSDrive -PSProvider ActiveDirectory -Name ADR -Root "" -Server $DomainController -Credential $Credential -ErrorAction Stop | Out-Null
6627 }
6628 Catch
6629 {
6630 Write-Output "[Invoke-ADRecon] $($_.Exception.Message)"
6631 If ($ADROutputDir)
6632 {
6633 Remove-EmptyADROutputDir $ADROutputDir $OutputType
6634 }
6635 Return $null
6636 }
6637 }
6638 Set-Location ADR:
6639 Write-Debug "ADR PSDrive Created"
6640 }
6641
6642 If ($Protocol -eq 'LDAP')
6643 {
6644 If ($UseAltCreds)
6645 {
6646 Try
6647 {
6648 $objDomain = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)", $Credential.UserName,$Credential.GetNetworkCredential().Password
6649 $objDomainRootDSE = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/RootDSE", $Credential.UserName,$Credential.GetNetworkCredential().Password
6650 }
6651 Catch
6652 {
6653 Write-Output "[Invoke-ADRecon] $($_.Exception.Message)"
6654 If ($ADROutputDir)
6655 {
6656 Remove-EmptyADROutputDir $ADROutputDir $OutputType
6657 }
6658 Return $null
6659 }
6660 If(!($objDomain.name))
6661 {
6662 Write-Output "[Invoke-ADRecon] LDAP bind Unsuccessful"
6663 If ($ADROutputDir)
6664 {
6665 Remove-EmptyADROutputDir $ADROutputDir $OutputType
6666 }
6667 Return $null
6668 }
6669 Else
6670 {
6671 Write-Output "[*] LDAP bind Successful"
6672 }
6673 }
6674 Else
6675 {
6676 $objDomain = [ADSI]""
6677 $objDomainRootDSE = ([ADSI] "LDAP://RootDSE")
6678 If(!($objDomain.name))
6679 {
6680 Write-Output "[Invoke-ADRecon] LDAP bind Unsuccessful"
6681 If ($ADROutputDir)
6682 {
6683 Remove-EmptyADROutputDir $ADROutputDir $OutputType
6684 }
6685 Return $null
6686 }
6687 }
6688 Write-Debug "LDAP Bing Successful"
6689 }
6690
6691 Write-Output "[*] Commencing - $date"
6692 If ($ADRDomain)
6693 {
6694 Write-Output "[-] Domain"
6695 $ADRObject = Get-ADRDomain -Protocol $Protocol -objDomain $objDomain -objDomainRootDSE $objDomainRootDSE -DomainController $DomainController -Credential $Credential
6696 If ($ADRObject)
6697 {
6698 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Domain"
6699 Remove-Variable ADRObject
6700 }
6701 Remove-Variable ADRDomain
6702 }
6703 If ($ADRForest)
6704 {
6705 Write-Output "[-] Forest"
6706 $ADRObject = Get-ADRForest -Protocol $Protocol -objDomain $objDomain -objDomainRootDSE $objDomainRootDSE -DomainController $DomainController -Credential $Credential
6707 If ($ADRObject)
6708 {
6709 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Forest"
6710 Remove-Variable ADRObject
6711 }
6712 Remove-Variable ADRForest
6713 }
6714 If ($ADRTrust)
6715 {
6716 Write-Output "[-] Trusts"
6717 $ADRObject = Get-ADRTrust -Protocol $Protocol -objDomain $objDomain
6718 If ($ADRObject)
6719 {
6720 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Trusts"
6721 Remove-Variable ADRObject
6722 }
6723 Remove-Variable ADRTrust
6724 }
6725 If ($ADRSite)
6726 {
6727 Write-Output "[-] Sites"
6728 $ADRObject = Get-ADRSite -Protocol $Protocol -objDomain $objDomain -objDomainRootDSE $objDomainRootDSE -DomainController $DomainController -Credential $Credential
6729 If ($ADRObject)
6730 {
6731 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Sites"
6732 Remove-Variable ADRObject
6733 }
6734 Remove-Variable ADRSite
6735 }
6736 If ($ADRSubnet)
6737 {
6738 Write-Output "[-] Subnets"
6739 $ADRObject = Get-ADRSubnet -Protocol $Protocol -objDomain $objDomain -objDomainRootDSE $objDomainRootDSE -DomainController $DomainController -Credential $Credential
6740 If ($ADRObject)
6741 {
6742 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Subnets"
6743 Remove-Variable ADRObject
6744 }
6745 Remove-Variable ADRSubnet
6746 }
6747 If ($ADRPasswordPolicy)
6748 {
6749 Write-Output "[-] Default Password Policy"
6750 $ADRObject = Get-ADRDefaultPasswordPolicy -Protocol $Protocol -objDomain $objDomain
6751 If ($ADRObject)
6752 {
6753 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "DefaultPasswordPolicy"
6754 Remove-Variable ADRObject
6755 }
6756 Remove-Variable ADRPasswordPolicy
6757 }
6758 If ($ADRFineGrainedPasswordPolicy)
6759 {
6760 Write-Output "[-] Fine Grained Password Policy - May need a Privileged Account"
6761 $ADRObject = Get-ADRFineGrainedPasswordPolicy -Protocol $Protocol -objDomain $objDomain
6762 If ($ADRObject)
6763 {
6764 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "FineGrainedPasswordPolicy"
6765 Remove-Variable ADRObject
6766 }
6767 Remove-Variable ADRFineGrainedPasswordPolicy
6768 }
6769 If ($ADRDomainControllers)
6770 {
6771 Write-Output "[-] Domain Controllers"
6772 $ADRObject = Get-ADRDomainController -Protocol $Protocol -objDomain $objDomain -Credential $Credential
6773 If ($ADRObject)
6774 {
6775 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "DomainControllers"
6776 Remove-Variable ADRObject
6777 }
6778 Remove-Variable ADRDomainControllers
6779 }
6780 If ($ADRUsers)
6781 {
6782 Write-Output "[-] Users - May take some time"
6783 $ADRObject = Get-ADRUser -Protocol $Protocol -date $date -objDomain $objDomain -DormantTimeSpan $DormantTimeSpan -PageSize $PageSize -Threads $Threads
6784 If ($ADRObject)
6785 {
6786 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Users"
6787 Remove-Variable ADRObject
6788 }
6789 Remove-Variable ADRUsers
6790 }
6791 If ($ADRUserSPNs)
6792 {
6793 Write-Output "[-] User SPNs"
6794 $ADRObject = Get-ADRUserSPN -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
6795 If ($ADRObject)
6796 {
6797 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "UserSPNs"
6798 Remove-Variable ADRObject
6799 }
6800 Remove-Variable ADRUserSPNs
6801 }
6802 If ($ADRPasswordAttributes)
6803 {
6804 Write-Output "[-] PasswordAttributes - Experimental"
6805 $ADRObject = Get-ADRPasswordAttributes -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize
6806 If ($ADRObject)
6807 {
6808 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "PasswordAttributes"
6809 Remove-Variable ADRObject
6810 }
6811 Remove-Variable ADRPasswordAttributes
6812 }
6813 If ($ADRGroups)
6814 {
6815 Write-Output "[-] Groups - May take some time"
6816 $ADRObject = Get-ADRGroup -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
6817 If ($ADRObject)
6818 {
6819 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Groups"
6820 Remove-Variable ADRObject
6821 }
6822 Remove-Variable ADRGroups
6823 }
6824 If ($ADRGroupMembers)
6825 {
6826 Write-Output "[-] Group Memberships - May take some time"
6827
6828 $ADRObject = Get-ADRGroupMember -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
6829 If ($ADRObject)
6830 {
6831 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "GroupMembers"
6832 Remove-Variable ADRObject
6833 }
6834 Remove-Variable ADRGroupMembers
6835 }
6836 If ($ADROUs)
6837 {
6838 Write-Output "[-] OrganizationalUnits (OUs)"
6839 $ADRObject = Get-ADROU -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
6840 If ($ADRObject)
6841 {
6842 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "OUs"
6843 Remove-Variable ADRObject
6844 }
6845 Remove-Variable ADROUs
6846 }
6847 If ($ADRGPOs)
6848 {
6849 Write-Output "[-] GPOs"
6850 $ADRObject = Get-ADRGPO -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
6851 If ($ADRObject)
6852 {
6853 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "GPOs"
6854 Remove-Variable ADRObject
6855 }
6856 Remove-Variable ADRGPOs
6857 }
6858 If ($ADRgPLinks)
6859 {
6860 Write-Output "[-] gPLinks - Scope of Management (SOM)"
6861 $ADRObject = Get-ADRgPLink -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
6862 If ($ADRObject)
6863 {
6864 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "gPLinks"
6865 Remove-Variable ADRObject
6866 }
6867 Remove-Variable ADRgPLinks
6868 }
6869 If ($ADRDNSZones)
6870 {
6871 Write-Output "[-] DNS Zones and Records"
6872 Get-ADRDNSZone -Protocol $Protocol -ADROutputDir $ADROutputDir -objDomain $objDomain -DomainController $DomainController -Credential $Credential -PageSize $PageSize -OutputType $OutputType
6873 Remove-Variable ADRDNSZones
6874 }
6875 If ($ADRPrinters)
6876 {
6877 Write-Output "[-] Printers"
6878 $ADRObject = Get-ADRPrinter -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
6879 If ($ADRObject)
6880 {
6881 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Printers"
6882 Remove-Variable ADRObject
6883 }
6884 Remove-Variable ADRPrinters
6885 }
6886 If ($ADRComputers)
6887 {
6888 Write-Output "[-] Computers - May take some time"
6889 $ADRObject = Get-ADRComputer -Protocol $Protocol -date $date -objDomain $objDomain -DormantTimeSpan $DormantTimeSpan -PassMaxAge $PassMaxAge -PageSize $PageSize -Threads $Threads
6890 If ($ADRObject)
6891 {
6892 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Computers"
6893 Remove-Variable ADRObject
6894 }
6895 Remove-Variable ADRComputers
6896 }
6897 If ($ADRComputerSPNs)
6898 {
6899 Write-Output "[-] Computer SPNs"
6900 $ADRObject = Get-ADRComputerSPN -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
6901 If ($ADRObject)
6902 {
6903 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "ComputerSPNs"
6904 Remove-Variable ADRObject
6905 }
6906 Remove-Variable ADRComputerSPNs
6907 }
6908 If ($ADRLAPS)
6909 {
6910 Write-Output "[-] LAPS - Needs Privileged Account"
6911 $ADRObject = Get-ADRLAPSCheck -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
6912 If ($ADRObject)
6913 {
6914 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "LAPS"
6915 Remove-Variable ADRObject
6916 }
6917 Remove-Variable ADRLAPS
6918 }
6919 If ($ADRBitLocker)
6920 {
6921 Write-Output "[-] BitLocker Recovery Keys - Needs Privileged Account"
6922 $ADRObject = Get-ADRBitLocker -Protocol $Protocol -objDomain $objDomain -DomainController $DomainController -Credential $Credential
6923 If ($ADRObject)
6924 {
6925 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "BitLockerRecoveryKeys"
6926 Remove-Variable ADRObject
6927 }
6928 Remove-Variable ADRBitLocker
6929 }
6930 If ($ADRACLs)
6931 {
6932 Write-Output "[-] ACLs - May take some time"
6933 $ADRObject = Get-ADRACL -Protocol $Protocol -objDomain $objDomain -DomainController $DomainController -Credential $Credential -PageSize $PageSize -Threads $Threads
6934 Remove-Variable ADRACLs
6935 }
6936 If ($ADRGPOReport)
6937 {
6938 Write-Output "[-] GPOReport - May take some time"
6939 Get-ADRGPOReport -Protocol $Protocol -UseAltCreds $UseAltCreds -ADROutputDir $ADROutputDir
6940 Remove-Variable ADRGPOReport
6941 }
6942 If ($ADRKerberoast)
6943 {
6944 Write-Output "[-] Kerberoast"
6945 $ADRObject = Get-ADRKerberoast -Protocol $Protocol -objDomain $objDomain -Credential $Credential -PageSize $PageSize
6946 If ($ADRObject)
6947 {
6948 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Kerberoast"
6949 Remove-Variable ADRObject
6950 }
6951 Remove-Variable ADRKerberoast
6952 }
6953 If ($ADRDomainAccountsusedforServiceLogon)
6954 {
6955 Write-Output "[-] Domain Accounts used for Service Logon - Needs Privileged Account"
6956 $ADRObject = Get-ADRDomainAccountsusedforServiceLogon -Protocol $Protocol -objDomain $objDomain -Credential $Credential -PageSize $PageSize -Threads $Threads
6957 If ($ADRObject)
6958 {
6959 Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "DomainAccountsusedforServiceLogon"
6960 Remove-Variable ADRObject
6961 }
6962 Remove-Variable ADRDomainAccountsusedforServiceLogon
6963 }
6964
6965 $TotalTime = "{0:N2}" -f ((Get-DateDiff -Date1 (Get-Date) -Date2 $date).TotalMinutes)
6966
6967 $AboutADRecon = Get-ADRAbout -Protocol $Protocol -date $date -ADReconVersion $ADReconVersion -Credential $Credential -RanonComputer $RanonComputer -TotalTime $TotalTime
6968
6969 If ( ($OutputType -Contains "CSV") -or ($OutputType -Contains "XML") -or ($OutputType -Contains "JSON") -or ($OutputType -Contains "HTML") )
6970 {
6971 If ($AboutADRecon)
6972 {
6973 Export-ADR -ADRObj $AboutADRecon -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "AboutADRecon"
6974 }
6975 Write-Output "[*] Total Execution Time (mins): $($TotalTime)"
6976 Write-Output "[*] Output Directory: $ADROutputDir"
6977 $ADRSTDOUT = $false
6978 }
6979
6980 Switch ($OutputType)
6981 {
6982 'STDOUT'
6983 {
6984 If ($ADRSTDOUT)
6985 {
6986 Write-Output "[*] Total Execution Time (mins): $($TotalTime)"
6987 }
6988 }
6989 'HTML'
6990 {
6991 Export-ADR -ADRObj $(New-Object PSObject) -ADROutputDir $ADROutputDir -OutputType $([array] "HTML") -ADRModuleName "Index"
6992 }
6993 'EXCEL'
6994 {
6995 Export-ADRExcel $ADROutputDir
6996 }
6997 }
6998 Remove-Variable TotalTime
6999 Remove-Variable AboutADRecon
7000 Set-Location $returndir
7001 Remove-Variable returndir
7002
7003 If (($Protocol -eq 'ADWS') -and $UseAltCreds)
7004 {
7005 Remove-PSDrive ADR
7006 }
7007
7008 If ($Protocol -eq 'LDAP')
7009 {
7010 $objDomain.Dispose()
7011 $objDomainRootDSE.Dispose()
7012 }
7013
7014 If ($ADROutputDir)
7015 {
7016 Remove-EmptyADROutputDir $ADROutputDir $OutputType
7017 }
7018
7019 Remove-Variable ADReconVersion
7020 Remove-Variable RanonComputer
7021}
7022
7023If ($Log)
7024{
7025 Start-Transcript -Path "$(Get-Location)\ADRecon-Console-Log.txt"
7026}
7027
7028Invoke-ADRecon -GenExcel $GenExcel -Protocol $Protocol -Collect $Collect -DomainController $DomainController -Credential $Credential -OutputType $OutputType -ADROutputDir $OutputDir -DormantTimeSpan $DormantTimeSpan -PassMaxAge $PassMaxAge -PageSize $PageSize -Threads $Threads
7029
7030If ($Log)
7031{
7032 Stop-Transcript
7033}