· 6 years ago · Jul 01, 2019, 08:56 PM
1<#
2
3.SYNOPSIS
4
5 ADRecon is a tool which gathers information about the Active Directory and generates a report which can provide a holistic picture of the current state of the target AD environment.
6
7.DESCRIPTION
8
9 ADRecon is a tool which extracts and combines various artefacts (as highlighted below) out of an AD environment. The information can be presented in a specially formatted Microsoft Excel report that includes summary views with metrics to facilitate analysis and provide a holistic picture of the current state of the target AD environment.
10 The tool is useful to various classes of security professionals like auditors, DFIR, students, administrators, etc. It can also be an invaluable post-exploitation tool for a penetration tester.
11 It can be run from any workstation that is connected to the environment, even hosts that are not domain members. Furthermore, the tool can be executed in the context of a non-privileged (i.e. standard domain user) account.
12 Fine Grained Password Policy, LAPS and BitLocker may require Privileged user accounts.
13 The tool will use Microsoft Remote Server Administration Tools (RSAT) if available, otherwise it will communicate with the Domain Controller using LDAP.
14 The following information is gathered by the tool:
15 - Forest;
16 - Domain;
17 - Trusts;
18 - Sites;
19 - Subnets;
20 - Default and Fine Grained Password Policy (if implemented);
21 - Domain Controllers, SMB versions, whether SMB Signing is supported and FSMO roles;
22 - Users and their attributes;
23 - Service Principal Names (SPNs);
24 - Groups and memberships;
25 - Organizational Units (OUs);
26 - Group Policy Object and gPLink details;
27 - DNS Zones and Records;
28 - Printers;
29 - Computers and their attributes;
30 - PasswordAttributes (Experimental);
31 - LAPS passwords (if implemented);
32 - BitLocker Recovery Keys (if implemented);
33 - ACLs (DACLs and SACLs) for the Domain, OUs, Root Containers, GPO, Users, Computers and Groups objects;
34 - GPOReport (requires RSAT);
35 - Kerberoast (not included in the default collection method); and
36 - Domain accounts used for service accounts (requires privileged account and not included in the default collection method).
37
38 Author : Prashant Mahajan
39 Company : https://www.senseofsecurity.com.au
40
41.NOTES
42
43 The following commands can be used to turn off ExecutionPolicy: (Requires Admin Privs)
44
45 PS > $ExecPolicy = Get-ExecutionPolicy
46 PS > Set-ExecutionPolicy bypass
47 PS > .\ADRecon.ps1
48 PS > Set-ExecutionPolicy $ExecPolicy
49
50 OR
51
52 Start the PowerShell as follows:
53 powershell.exe -ep bypass
54
55 OR
56
57 Already have a PowerShell open ?
58 PS > $Env:PSExecutionPolicyPreference = 'Bypass'
59
60 OR
61
62 powershell.exe -nologo -executionpolicy bypass -noprofile -file ADRecon.ps1
63
64.PARAMETER Protocol
65 Which protocol to use; ADWS (default) or LDAP
66
67.PARAMETER DomainController
68 Domain Controller IP Address or Domain FQDN.
69
70.PARAMETER Credential
71 Domain Credentials.
72
73.PARAMETER GenExcel
74 Path for ADRecon output folder containing the CSV files to generate the ADRecon-Report.xlsx. Use it to generate the ADRecon-Report.xlsx when Microsoft Excel is not installed on the host used to run ADRecon.
75
76.PARAMETER OutputDir
77 Path for ADRecon output folder to save the files and the ADRecon-Report.xlsx. (The folder specified will be created if it doesn't exist)
78
79.PARAMETER Collect
80 Which modules to run; Comma separated; e.g Forest,Domain (Default all except Kerberoast, DomainAccountsusedforServiceLogon)
81 Valid values include: 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.
82
83.PARAMETER OutputType
84 Output Type; Comma seperated; e.g STDOUT,CSV,XML,JSON,HTML,Excel (Default STDOUT with -Collect parameter, else CSV and Excel).
85 Valid values include: STDOUT, CSV, XML, JSON, HTML, Excel, All (excludes STDOUT).
86
87.PARAMETER DormantTimeSpan
88 Timespan for Dormant accounts. (Default 90 days)
89
90.PARAMETER PassMaxAge
91 Maximum machine account password age. (Default 30 days)
92
93.PARAMETER PageSize
94 The PageSize to set for the LDAP searcher object.
95
96.PARAMETER Threads
97 The number of threads to use during processing objects. (Default 10)
98
99.PARAMETER Log
100 Create ADRecon Log using Start-Transcript
101
102.EXAMPLE
103
104 .\ADRecon.ps1 -GenExcel C:\ADRecon-Report-<timestamp>
105 [*] ADRecon <version> by Prashant Mahajan (@prashant3535) from Sense of Security.
106 [*] Generating ADRecon-Report.xlsx
107 [+] Excelsheet Saved to: C:\ADRecon-Report-<timestamp>\<domain>-ADRecon-Report.xlsx
108
109.EXAMPLE
110
111 .\ADRecon.ps1 -DomainController <IP or FQDN> -Credential <domain\username>
112 [*] ADRecon <version> by Prashant Mahajan (@prashant3535) from Sense of Security.
113 [*] Running on <domain>\<hostname> - Member Workstation
114 <snip>
115
116 Example output from Domain Member with Alternate Credentials.
117
118.EXAMPLE
119
120 .\ADRecon.ps1 -DomainController <IP or FQDN> -Credential <domain\username> -Collect DomainControllers -OutputType Excel
121 [*] ADRecon <version> by Prashant Mahajan (@prashant3535) from Sense of Security.
122 [*] Running on WORKGROUP\<hostname> - Standalone Workstation
123 [*] Commencing - <timestamp>
124 [-] Domain Controllers
125 [*] Total Execution Time (mins): <minutes>
126 [*] Generating ADRecon-Report.xlsx
127 [+] Excelsheet Saved to: C:\ADRecon-Report-<timestamp>\<domain>-ADRecon-Report.xlsx
128 [*] Completed.
129 [*] Output Directory: C:\ADRecon-Report-<timestamp>
130
131 Example output from from a Non-Member using RSAT to only enumerate Domain Controllers.
132
133.EXAMPLE
134
135 .\ADRecon.ps1 -Protocol ADWS -DomainController <IP or FQDN> -Credential <domain\username>
136 [*] ADRecon <version> by Prashant Mahajan (@prashant3535) from Sense of Security.
137 [*] Running on WORKGROUP\<hostname> - Standalone Workstation
138 [*] Commencing - <timestamp>
139 [-] Domain
140 [-] Forest
141 [-] Trusts
142 [-] Sites
143 [-] Subnets
144 [-] Default Password Policy
145 [-] Fine Grained Password Policy - May need a Privileged Account
146 [-] Domain Controllers
147 [-] Users - May take some time
148 [-] User SPNs
149 [-] PasswordAttributes - Experimental
150 [-] Groups - May take some time
151 [-] Group Memberships - May take some time
152 [-] OrganizationalUnits (OUs)
153 [-] GPOs
154 [-] gPLinks - Scope of Management (SOM)
155 [-] DNS Zones and Records
156 [-] Printers
157 [-] Computers - May take some time
158 [-] Computer SPNs
159 [-] LAPS - Needs Privileged Account
160 WARNING: [*] LAPS is not implemented.
161 [-] BitLocker Recovery Keys - Needs Privileged Account
162 [-] ACLs - May take some time
163 WARNING: [*] SACLs - Currently, the module is only supported with LDAP.
164 [-] GPOReport - May take some time
165 WARNING: [EXCEPTION] Current security context is not associated with an Active Directory domain or forest.
166 WARNING: [*] Run the tool using RUNAS.
167 WARNING: [*] runas /user:<Domain FQDN>\<Username> /netonly powershell.exe
168 [*] Total Execution Time (mins): <minutes>
169 [*] Output Directory: C:\ADRecon-Report-<timestamp>
170 [*] Generating ADRecon-Report.xlsx
171 [+] Excelsheet Saved to: C:\ADRecon-Report-<timestamp>\<domain>-ADRecon-Report.xlsx
172
173 Example output from a Non-Member using RSAT.
174
175.EXAMPLE
176
177 .\ADRecon.ps1 -Protocol LDAP -DomainController <IP or FQDN> -Credential <domain\username>
178 [*] ADRecon <version> by Prashant Mahajan (@prashant3535) from Sense of Security.
179 [*] Running on WORKGROUP\<hostname> - Standalone Workstation
180 [*] LDAP bind Successful
181 [*] Commencing - <timestamp>
182 [-] Domain
183 [-] Forest
184 [-] Trusts
185 [-] Sites
186 [-] Subnets
187 [-] Default Password Policy
188 [-] Fine Grained Password Policy - May need a Privileged Account
189 [-] Domain Controllers
190 [-] Users - May take some time
191 [-] User SPNs
192 [-] PasswordAttributes - Experimental
193 [-] Groups - May take some time
194 [-] Group Memberships - May take some time
195 [-] OrganizationalUnits (OUs)
196 [-] GPOs
197 [-] gPLinks - Scope of Management (SOM)
198 [-] DNS Zones and Records
199 [-] Printers
200 [-] Computers - May take some time
201 [-] Computer SPNs
202 [-] LAPS - Needs Privileged Account
203 WARNING: [*] LAPS is not implemented.
204 [-] BitLocker Recovery Keys - Needs Privileged Account
205 [-] ACLs - May take some time
206 [-] GPOReport - May take some time
207 WARNING: [*] Currently, the module is only supported with ADWS.
208 [*] Total Execution Time (mins): <minutes>
209 [*] Output Directory: C:\ADRecon-Report-<timestamp>
210 [*] Generating ADRecon-Report.xlsx
211 [+] Excelsheet Saved to: C:\ADRecon-Report-<timestamp>\<domain>-ADRecon-Report.xlsx
212
213 Example output from a Non-Member using LDAP.
214
215.LINK
216
217 https://github.com/sense-of-security/ADRecon
218#>
219
220[CmdletBinding()]
221param
222(
223 [Parameter(Mandatory = $false, HelpMessage = "Which protocol to use; ADWS (default) or LDAP.")]
224 [ValidateSet('ADWS', 'LDAP')]
225 [string] $Protocol = 'ADWS',
226
227 [Parameter(Mandatory = $false, HelpMessage = "Domain Controller IP Address or Domain FQDN.")]
228 [string] $DomainController = '',
229
230 [Parameter(Mandatory = $false, HelpMessage = "Domain Credentials.")]
231 [Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
232
233 [Parameter(Mandatory = $false, HelpMessage = "Path for ADRecon output folder containing the CSV files to generate the ADRecon-Report.xlsx. Use it to generate the ADRecon-Report.xlsx when Microsoft Excel is not installed on the host used to run ADRecon.")]
234 [string] $GenExcel,
235
236 [Parameter(Mandatory = $false, HelpMessage = "Path for ADRecon output folder to save the CSV/XML/JSON/HTML files and the ADRecon-Report.xlsx. (The folder specified will be created if it doesn't exist)")]
237 [string] $OutputDir,
238
239 [Parameter(Mandatory = $false, HelpMessage = "Which modules to run; Comma separated; e.g Forest,Domain (Default all except Kerberoast, DomainAccountsusedforServiceLogon) Valid values include: 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")]
240 [ValidateSet('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', 'Default')]
241 [array] $Collect = 'Default',
242
243 [Parameter(Mandatory = $false, HelpMessage = "Output type; Comma seperated; e.g STDOUT,CSV,XML,JSON,HTML,Excel (Default STDOUT with -Collect parameter, else CSV and Excel)")]
244 [ValidateSet('STDOUT', 'CSV', 'XML', 'JSON', 'EXCEL', 'HTML', 'All', 'Default')]
245 [array] $OutputType = 'Default',
246
247 [Parameter(Mandatory = $false, HelpMessage = "Timespan for Dormant accounts. Default 90 days")]
248 [ValidateRange(1,1000)]
249 [int] $DormantTimeSpan = 90,
250
251 [Parameter(Mandatory = $false, HelpMessage = "Maximum machine account password age. Default 30 days")]
252 [ValidateRange(1,1000)]
253 [int] $PassMaxAge = 30,
254
255 [Parameter(Mandatory = $false, HelpMessage = "The PageSize to set for the LDAP searcher object. Default 200")]
256 [ValidateRange(1,10000)]
257 [int] $PageSize = 200,
258
259 [Parameter(Mandatory = $false, HelpMessage = "The number of threads to use during processing of objects. Default 10")]
260 [ValidateRange(1,100)]
261 [int] $Threads = 10,
262
263 [Parameter(Mandatory = $false, HelpMessage = "Create ADRecon Log using Start-Transcript")]
264 [switch] $Log
265)
266
267$ADWSSource = @"
268// Thanks Dennis Albuquerque for the C# multithreading code
269using System;
270using System.Collections;
271using System.Collections.Generic;
272using System.Linq;
273using System.Threading;
274using System.DirectoryServices;
275using System.Security.Principal;
276using System.Security.AccessControl;
277using System.Management.Automation;
278
279namespace ADRecon
280{
281 public static class ADWSClass
282 {
283 private static DateTime Date1;
284 private static int PassMaxAge;
285 private static int DormantTimeSpan;
286 private static Dictionary<String, String> AdGroupDictionary = new Dictionary<String, String>();
287 private static String DomainSID;
288 private static Dictionary<String, String> AdGPODictionary = new Dictionary<String, String>();
289 private static Hashtable GUIDs = new Hashtable();
290 private static Dictionary<String, String> AdSIDDictionary = new Dictionary<String, String>();
291 private static readonly HashSet<string> Groups = new HashSet<string> ( new String[] {"268435456", "268435457", "536870912", "536870913"} );
292 private static readonly HashSet<string> Users = new HashSet<string> ( new String[] { "805306368" } );
293 private static readonly HashSet<string> Computers = new HashSet<string> ( new String[] { "805306369" }) ;
294 private static readonly HashSet<string> TrustAccounts = new HashSet<string> ( new String[] { "805306370" } );
295
296 [Flags]
297 //Values taken from https://support.microsoft.com/en-au/kb/305144
298 public enum UACFlags
299 {
300 SCRIPT = 1, // 0x1
301 ACCOUNTDISABLE = 2, // 0x2
302 HOMEDIR_REQUIRED = 8, // 0x8
303 LOCKOUT = 16, // 0x10
304 PASSWD_NOTREQD = 32, // 0x20
305 PASSWD_CANT_CHANGE = 64, // 0x40
306 ENCRYPTED_TEXT_PASSWORD_ALLOWED = 128, // 0x80
307 TEMP_DUPLICATE_ACCOUNT = 256, // 0x100
308 NORMAL_ACCOUNT = 512, // 0x200
309 INTERDOMAIN_TRUST_ACCOUNT = 2048, // 0x800
310 WORKSTATION_TRUST_ACCOUNT = 4096, // 0x1000
311 SERVER_TRUST_ACCOUNT = 8192, // 0x2000
312 DONT_EXPIRE_PASSWD = 65536, // 0x10000
313 MNS_LOGON_ACCOUNT = 131072, // 0x20000
314 SMARTCARD_REQUIRED = 262144, // 0x40000
315 TRUSTED_FOR_DELEGATION = 524288, // 0x80000
316 NOT_DELEGATED = 1048576, // 0x100000
317 USE_DES_KEY_ONLY = 2097152, // 0x200000
318 DONT_REQUIRE_PREAUTH = 4194304, // 0x400000
319 PASSWORD_EXPIRED = 8388608, // 0x800000
320 TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 16777216, // 0x1000000
321 PARTIAL_SECRETS_ACCOUNT = 67108864 // 0x04000000
322 }
323
324 [Flags]
325 //Values taken from https://blogs.msdn.microsoft.com/openspecification/2011/05/30/windows-configurations-for-kerberos-supported-encryption-type/
326 public enum KerbEncFlags
327 {
328 ZERO = 0,
329 DES_CBC_CRC = 1, // 0x1
330 DES_CBC_MD5 = 2, // 0x2
331 RC4_HMAC = 4, // 0x4
332 AES128_CTS_HMAC_SHA1_96 = 8, // 0x18
333 AES256_CTS_HMAC_SHA1_96 = 16 // 0x10
334 }
335
336 private static readonly Dictionary<String, String> Replacements = new Dictionary<String, String>()
337 {
338 //{System.Environment.NewLine, ""},
339 //{",", ";"},
340 {"\"", "'"}
341 };
342
343 public static String CleanString(Object StringtoClean)
344 {
345 // Remove extra spaces and new lines
346 String CleanedString = String.Join(" ", ((Convert.ToString(StringtoClean)).Split((string[]) null, StringSplitOptions.RemoveEmptyEntries)));
347 foreach (String Replacement in Replacements.Keys)
348 {
349 CleanedString = CleanedString.Replace(Replacement, Replacements[Replacement]);
350 }
351 return CleanedString;
352 }
353
354 public static int ObjectCount(Object[] ADRObject)
355 {
356 return ADRObject.Length;
357 }
358
359 public static Object[] UserParser(Object[] AdUsers, DateTime Date1, int DormantTimeSpan, int PassMaxAge, int numOfThreads)
360 {
361 ADWSClass.Date1 = Date1;
362 ADWSClass.DormantTimeSpan = DormantTimeSpan;
363 ADWSClass.PassMaxAge = PassMaxAge;
364
365 Object[] ADRObj = runProcessor(AdUsers, numOfThreads, "Users");
366 return ADRObj;
367 }
368
369 public static Object[] UserSPNParser(Object[] AdUsers, int numOfThreads)
370 {
371 Object[] ADRObj = runProcessor(AdUsers, numOfThreads, "UserSPNs");
372 return ADRObj;
373 }
374
375 public static Object[] GroupParser(Object[] AdGroups, int numOfThreads)
376 {
377 Object[] ADRObj = runProcessor(AdGroups, numOfThreads, "Groups");
378 return ADRObj;
379 }
380
381 public static Object[] GroupMemberParser(Object[] AdGroups, Object[] AdGroupMembers, String DomainSID, int numOfThreads)
382 {
383 ADWSClass.AdGroupDictionary = new Dictionary<String, String>();
384 runProcessor(AdGroups, numOfThreads, "GroupsDictionary");
385 ADWSClass.DomainSID = DomainSID;
386 Object[] ADRObj = runProcessor(AdGroupMembers, numOfThreads, "GroupMembers");
387 return ADRObj;
388 }
389
390 public static Object[] OUParser(Object[] AdOUs, int numOfThreads)
391 {
392 Object[] ADRObj = runProcessor(AdOUs, numOfThreads, "OUs");
393 return ADRObj;
394 }
395
396 public static Object[] GPOParser(Object[] AdGPOs, int numOfThreads)
397 {
398 Object[] ADRObj = runProcessor(AdGPOs, numOfThreads, "GPOs");
399 return ADRObj;
400 }
401
402 public static Object[] SOMParser(Object[] AdGPOs, Object[] AdSOMs, int numOfThreads)
403 {
404 ADWSClass.AdGPODictionary = new Dictionary<String, String>();
405 runProcessor(AdGPOs, numOfThreads, "GPOsDictionary");
406 Object[] ADRObj = runProcessor(AdSOMs, numOfThreads, "SOMs");
407 return ADRObj;
408 }
409
410 public static Object[] PrinterParser(Object[] ADPrinters, int numOfThreads)
411 {
412 Object[] ADRObj = runProcessor(ADPrinters, numOfThreads, "Printers");
413 return ADRObj;
414 }
415
416 public static Object[] ComputerParser(Object[] AdComputers, DateTime Date1, int DormantTimeSpan, int PassMaxAge, int numOfThreads)
417 {
418 ADWSClass.Date1 = Date1;
419 ADWSClass.DormantTimeSpan = DormantTimeSpan;
420 ADWSClass.PassMaxAge = PassMaxAge;
421
422 Object[] ADRObj = runProcessor(AdComputers, numOfThreads, "Computers");
423 return ADRObj;
424 }
425
426 public static Object[] ComputerSPNParser(Object[] AdComputers, int numOfThreads)
427 {
428 Object[] ADRObj = runProcessor(AdComputers, numOfThreads, "ComputerSPNs");
429 return ADRObj;
430 }
431
432 public static Object[] LAPSParser(Object[] AdComputers, int numOfThreads)
433 {
434 Object[] ADRObj = runProcessor(AdComputers, numOfThreads, "LAPS");
435 return ADRObj;
436 }
437
438 public static Object[] DACLParser(Object[] ADObjects, Object PSGUIDs, int numOfThreads)
439 {
440 ADWSClass.AdSIDDictionary = new Dictionary<String, String>();
441 runProcessor(ADObjects, numOfThreads, "SIDDictionary");
442 ADWSClass.GUIDs = (Hashtable) PSGUIDs;
443 Object[] ADRObj = runProcessor(ADObjects, numOfThreads, "DACLs");
444 return ADRObj;
445 }
446
447 public static Object[] SACLParser(Object[] ADObjects, Object PSGUIDs, int numOfThreads)
448 {
449 ADWSClass.GUIDs = (Hashtable) PSGUIDs;
450 Object[] ADRObj = runProcessor(ADObjects, numOfThreads, "SACLs");
451 return ADRObj;
452 }
453
454 static Object[] runProcessor(Object[] arrayToProcess, int numOfThreads, string processorType)
455 {
456 int totalRecords = arrayToProcess.Length;
457 IRecordProcessor recordProcessor = recordProcessorFactory(processorType);
458 IResultsHandler resultsHandler = new SimpleResultsHandler ();
459 int numberOfRecordsPerThread = totalRecords / numOfThreads;
460 int remainders = totalRecords % numOfThreads;
461
462 Thread[] threads = new Thread[numOfThreads];
463 for (int i = 0; i < numOfThreads; i++)
464 {
465 int numberOfRecordsToProcess = numberOfRecordsPerThread;
466 if (i == (numOfThreads - 1))
467 {
468 //last thread, do the remaining records
469 numberOfRecordsToProcess += remainders;
470 }
471
472 //split the full array into chunks to be given to different threads
473 Object[] sliceToProcess = new Object[numberOfRecordsToProcess];
474 Array.Copy(arrayToProcess, i * numberOfRecordsPerThread, sliceToProcess, 0, numberOfRecordsToProcess);
475 ProcessorThread processorThread = new ProcessorThread(i, recordProcessor, resultsHandler, sliceToProcess);
476 threads[i] = new Thread(processorThread.processThreadRecords);
477 threads[i].Start();
478 }
479 foreach (Thread t in threads)
480 {
481 t.Join();
482 }
483
484 return resultsHandler.finalise();
485 }
486
487 static IRecordProcessor recordProcessorFactory(String name)
488 {
489 switch (name)
490 {
491 case "Users":
492 return new UserRecordProcessor();
493 case "UserSPNs":
494 return new UserSPNRecordProcessor();
495 case "Groups":
496 return new GroupRecordProcessor();
497 case "GroupsDictionary":
498 return new GroupRecordDictionaryProcessor();
499 case "GroupMembers":
500 return new GroupMemberRecordProcessor();
501 case "OUs":
502 return new OURecordProcessor();
503 case "GPOs":
504 return new GPORecordProcessor();
505 case "GPOsDictionary":
506 return new GPORecordDictionaryProcessor();
507 case "SOMs":
508 return new SOMRecordProcessor();
509 case "Printers":
510 return new PrinterRecordProcessor();
511 case "Computers":
512 return new ComputerRecordProcessor();
513 case "ComputerSPNs":
514 return new ComputerSPNRecordProcessor();
515 case "LAPS":
516 return new LAPSRecordProcessor();
517 case "SIDDictionary":
518 return new SIDRecordDictionaryProcessor();
519 case "DACLs":
520 return new DACLRecordProcessor();
521 case "SACLs":
522 return new SACLRecordProcessor();
523 }
524 throw new ArgumentException("Invalid processor type " + name);
525 }
526
527 class ProcessorThread
528 {
529 readonly int id;
530 readonly IRecordProcessor recordProcessor;
531 readonly IResultsHandler resultsHandler;
532 readonly Object[] objectsToBeProcessed;
533
534 public ProcessorThread(int id, IRecordProcessor recordProcessor, IResultsHandler resultsHandler, Object[] objectsToBeProcessed)
535 {
536 this.recordProcessor = recordProcessor;
537 this.id = id;
538 this.resultsHandler = resultsHandler;
539 this.objectsToBeProcessed = objectsToBeProcessed;
540 }
541
542 public void processThreadRecords()
543 {
544 for (int i = 0; i < objectsToBeProcessed.Length; i++)
545 {
546 Object[] result = recordProcessor.processRecord(objectsToBeProcessed[i]);
547 resultsHandler.processResults(result); //this is a thread safe operation
548 }
549 }
550 }
551
552 //The interface and implmentation class used to process a record (this implemmentation just returns a log type string)
553
554 interface IRecordProcessor
555 {
556 PSObject[] processRecord(Object record);
557 }
558
559 class UserRecordProcessor : IRecordProcessor
560 {
561 public PSObject[] processRecord(Object record)
562 {
563 try
564 {
565 PSObject AdUser = (PSObject) record;
566 bool? Enabled = null;
567 bool MustChangePasswordatLogon = false;
568 bool PasswordNotChangedafterMaxAge = false;
569 bool NeverLoggedIn = false;
570 int? DaysSinceLastLogon = null;
571 int? DaysSinceLastPasswordChange = null;
572 int? AccountExpirationNumofDays = null;
573 bool Dormant = false;
574 String SIDHistory = "";
575 bool? KerberosRC4 = null;
576 bool? KerberosAES128 = null;
577 bool? KerberosAES256 = null;
578 String DelegationType = null;
579 String DelegationProtocol = null;
580 String DelegationServices = null;
581 DateTime? LastLogonDate = null;
582 DateTime? PasswordLastSet = null;
583 DateTime? AccountExpires = null;
584
585 try
586 {
587 // The Enabled field can be blank which raises an exception. This may occur when the user is not allowed to query the UserAccountControl attribute.
588 Enabled = (bool) AdUser.Members["Enabled"].Value;
589 }
590 catch //(Exception e)
591 {
592 //Console.WriteLine("{0} Exception caught.", e);
593 }
594 if (AdUser.Members["lastLogonTimeStamp"].Value != null)
595 {
596 //LastLogonDate = DateTime.FromFileTime((long)(AdUser.Members["lastLogonTimeStamp"].Value));
597 // LastLogonDate is lastLogonTimeStamp converted to local time
598 LastLogonDate = Convert.ToDateTime(AdUser.Members["LastLogonDate"].Value);
599 DaysSinceLastLogon = Math.Abs((Date1 - (DateTime)LastLogonDate).Days);
600 if (DaysSinceLastLogon > DormantTimeSpan)
601 {
602 Dormant = true;
603 }
604 }
605 else
606 {
607 NeverLoggedIn = true;
608 }
609 if (Convert.ToString(AdUser.Members["pwdLastSet"].Value) == "0")
610 {
611 if ((bool) AdUser.Members["PasswordNeverExpires"].Value == false)
612 {
613 MustChangePasswordatLogon = true;
614 }
615 }
616 if (AdUser.Members["PasswordLastSet"].Value != null)
617 {
618 //PasswordLastSet = DateTime.FromFileTime((long)(AdUser.Members["pwdLastSet"].Value));
619 // PasswordLastSet is pwdLastSet converted to local time
620 PasswordLastSet = Convert.ToDateTime(AdUser.Members["PasswordLastSet"].Value);
621 DaysSinceLastPasswordChange = Math.Abs((Date1 - (DateTime)PasswordLastSet).Days);
622 if (DaysSinceLastPasswordChange > PassMaxAge)
623 {
624 PasswordNotChangedafterMaxAge = true;
625 }
626 }
627 //https://msdn.microsoft.com/en-us/library/ms675098(v=vs.85).aspx
628 //if ((Int64) AdUser.Members["accountExpires"].Value != (Int64) 9223372036854775807)
629 //{
630 //if ((Int64) AdUser.Members["accountExpires"].Value != (Int64) 0)
631 if (AdUser.Members["AccountExpirationDate"].Value != null)
632 {
633 try
634 {
635 //AccountExpires = DateTime.FromFileTime((long)(AdUser.Members["accountExpires"].Value));
636 // AccountExpirationDate is accountExpires converted to local time
637 AccountExpires = Convert.ToDateTime(AdUser.Members["AccountExpirationDate"].Value);
638 AccountExpirationNumofDays = ((int)((DateTime)AccountExpires - Date1).Days);
639
640 }
641 catch //(Exception e)
642 {
643 //Console.WriteLine("{0} Exception caught.", e);
644 }
645 }
646 //}
647 Microsoft.ActiveDirectory.Management.ADPropertyValueCollection history = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) AdUser.Members["SIDHistory"].Value;
648 if (history.Value is System.Security.Principal.SecurityIdentifier[])
649 {
650 string sids = "";
651 foreach (var value in (SecurityIdentifier[]) history.Value)
652 {
653 sids = sids + "," + Convert.ToString(value);
654 }
655 SIDHistory = sids.TrimStart(',');
656 }
657 else
658 {
659 SIDHistory = history != null ? Convert.ToString(history.Value) : "";
660 }
661 if (AdUser.Members["msDS-SupportedEncryptionTypes"].Value != null)
662 {
663 var userKerbEncFlags = (KerbEncFlags) AdUser.Members["msDS-SupportedEncryptionTypes"].Value;
664 if (userKerbEncFlags != KerbEncFlags.ZERO)
665 {
666 KerberosRC4 = (userKerbEncFlags & KerbEncFlags.RC4_HMAC) == KerbEncFlags.RC4_HMAC;
667 KerberosAES128 = (userKerbEncFlags & KerbEncFlags.AES128_CTS_HMAC_SHA1_96) == KerbEncFlags.AES128_CTS_HMAC_SHA1_96;
668 KerberosAES256 = (userKerbEncFlags & KerbEncFlags.AES256_CTS_HMAC_SHA1_96) == KerbEncFlags.AES256_CTS_HMAC_SHA1_96;
669 }
670 }
671 if ((bool) AdUser.Members["TrustedForDelegation"].Value)
672 {
673 DelegationType = "Unconstrained";
674 DelegationServices = "Any";
675 }
676 if (AdUser.Members["msDS-AllowedToDelegateTo"] != null)
677 {
678 Microsoft.ActiveDirectory.Management.ADPropertyValueCollection delegateto = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) AdUser.Members["msDS-AllowedToDelegateTo"].Value;
679 if (delegateto.Value != null)
680 {
681 DelegationType = "Constrained";
682 if (delegateto.Value is System.String[])
683 {
684 foreach (var value in (String[]) delegateto.Value)
685 {
686 DelegationServices = DelegationServices + "," + Convert.ToString(value);
687 }
688 DelegationServices = DelegationServices.TrimStart(',');
689 }
690 else
691 {
692 DelegationServices = Convert.ToString(delegateto.Value);
693 }
694 }
695 }
696 if ((bool) AdUser.Members["TrustedToAuthForDelegation"].Value == true)
697 {
698 DelegationProtocol = "Any";
699 }
700 else if (DelegationType != null)
701 {
702 DelegationProtocol = "Kerberos";
703 }
704
705 PSObject UserObj = new PSObject();
706 UserObj.Members.Add(new PSNoteProperty("UserName", AdUser.Members["SamAccountName"].Value));
707 UserObj.Members.Add(new PSNoteProperty("Name", CleanString(AdUser.Members["Name"].Value)));
708 UserObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
709 UserObj.Members.Add(new PSNoteProperty("Must Change Password at Logon", MustChangePasswordatLogon));
710 UserObj.Members.Add(new PSNoteProperty("Cannot Change Password", AdUser.Members["CannotChangePassword"].Value));
711 UserObj.Members.Add(new PSNoteProperty("Password Never Expires", AdUser.Members["PasswordNeverExpires"].Value));
712 UserObj.Members.Add(new PSNoteProperty("Reversible Password Encryption", AdUser.Members["AllowReversiblePasswordEncryption"].Value));
713 UserObj.Members.Add(new PSNoteProperty("Smartcard Logon Required", AdUser.Members["SmartcardLogonRequired"].Value));
714 UserObj.Members.Add(new PSNoteProperty("Delegation Permitted", !((bool) AdUser.Members["AccountNotDelegated"].Value)));
715 UserObj.Members.Add(new PSNoteProperty("Kerberos DES Only", AdUser.Members["UseDESKeyOnly"].Value));
716 UserObj.Members.Add(new PSNoteProperty("Kerberos RC4", KerberosRC4));
717 UserObj.Members.Add(new PSNoteProperty("Kerberos AES-128bit", KerberosAES128));
718 UserObj.Members.Add(new PSNoteProperty("Kerberos AES-256bit", KerberosAES256));
719 UserObj.Members.Add(new PSNoteProperty("Does Not Require Pre Auth", AdUser.Members["DoesNotRequirePreAuth"].Value));
720 UserObj.Members.Add(new PSNoteProperty("Never Logged in", NeverLoggedIn));
721 UserObj.Members.Add(new PSNoteProperty("Logon Age (days)", DaysSinceLastLogon));
722 UserObj.Members.Add(new PSNoteProperty("Password Age (days)", DaysSinceLastPasswordChange));
723 UserObj.Members.Add(new PSNoteProperty("Dormant (> " + DormantTimeSpan + " days)", Dormant));
724 UserObj.Members.Add(new PSNoteProperty("Password Age (> " + PassMaxAge + " days)", PasswordNotChangedafterMaxAge));
725 UserObj.Members.Add(new PSNoteProperty("Account Locked Out", AdUser.Members["LockedOut"].Value));
726 UserObj.Members.Add(new PSNoteProperty("Password Expired", AdUser.Members["PasswordExpired"].Value));
727 UserObj.Members.Add(new PSNoteProperty("Password Not Required", AdUser.Members["PasswordNotRequired"].Value));
728 UserObj.Members.Add(new PSNoteProperty("Delegation Type", DelegationType));
729 UserObj.Members.Add(new PSNoteProperty("Delegation Protocol", DelegationProtocol));
730 UserObj.Members.Add(new PSNoteProperty("Delegation Services", DelegationServices));
731 UserObj.Members.Add(new PSNoteProperty("Logon Workstations", AdUser.Members["LogonWorkstations"].Value));
732 UserObj.Members.Add(new PSNoteProperty("AdminCount", AdUser.Members["AdminCount"].Value));
733 UserObj.Members.Add(new PSNoteProperty("Primary GroupID", AdUser.Members["primaryGroupID"].Value));
734 UserObj.Members.Add(new PSNoteProperty("SID", AdUser.Members["SID"].Value));
735 UserObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
736 UserObj.Members.Add(new PSNoteProperty("Description", CleanString(AdUser.Members["Description"].Value)));
737 UserObj.Members.Add(new PSNoteProperty("Title", CleanString(AdUser.Members["Title"].Value)));
738 UserObj.Members.Add(new PSNoteProperty("Department", CleanString(AdUser.Members["Department"].Value)));
739 UserObj.Members.Add(new PSNoteProperty("Company", CleanString(AdUser.Members["Company"].Value)));
740 UserObj.Members.Add(new PSNoteProperty("Manager", CleanString(AdUser.Members["Manager"].Value)));
741 UserObj.Members.Add(new PSNoteProperty("Info", CleanString(AdUser.Members["Info"].Value)));
742 UserObj.Members.Add(new PSNoteProperty("Last Logon Date", LastLogonDate));
743 UserObj.Members.Add(new PSNoteProperty("Password LastSet", PasswordLastSet));
744 UserObj.Members.Add(new PSNoteProperty("Account Expiration Date", AccountExpires));
745 UserObj.Members.Add(new PSNoteProperty("Account Expiration (days)", AccountExpirationNumofDays));
746 UserObj.Members.Add(new PSNoteProperty("Mobile", CleanString(AdUser.Members["Mobile"].Value)));
747 UserObj.Members.Add(new PSNoteProperty("Email", CleanString(AdUser.Members["mail"].Value)));
748 UserObj.Members.Add(new PSNoteProperty("HomeDirectory", AdUser.Members["homeDirectory"].Value));
749 UserObj.Members.Add(new PSNoteProperty("ProfilePath", AdUser.Members["profilePath"].Value));
750 UserObj.Members.Add(new PSNoteProperty("ScriptPath", AdUser.Members["ScriptPath"].Value));
751 UserObj.Members.Add(new PSNoteProperty("UserAccountControl", AdUser.Members["UserAccountControl"].Value));
752 UserObj.Members.Add(new PSNoteProperty("First Name", CleanString(AdUser.Members["givenName"].Value)));
753 UserObj.Members.Add(new PSNoteProperty("Middle Name", CleanString(AdUser.Members["middleName"].Value)));
754 UserObj.Members.Add(new PSNoteProperty("Last Name", CleanString(AdUser.Members["sn"].Value)));
755 UserObj.Members.Add(new PSNoteProperty("Country", CleanString(AdUser.Members["c"].Value)));
756 UserObj.Members.Add(new PSNoteProperty("whenCreated", AdUser.Members["whenCreated"].Value));
757 UserObj.Members.Add(new PSNoteProperty("whenChanged", AdUser.Members["whenChanged"].Value));
758 UserObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdUser.Members["DistinguishedName"].Value)));
759 UserObj.Members.Add(new PSNoteProperty("CanonicalName", AdUser.Members["CanonicalName"].Value));
760 return new PSObject[] { UserObj };
761 }
762 catch (Exception e)
763 {
764 Console.WriteLine("{0} Exception caught.", e);
765 return new PSObject[] { };
766 }
767 }
768 }
769
770 class UserSPNRecordProcessor : IRecordProcessor
771 {
772 public PSObject[] processRecord(Object record)
773 {
774 try
775 {
776 PSObject AdUser = (PSObject) record;
777 List<PSObject> SPNList = new List<PSObject>();
778 bool? Enabled = null;
779 String Memberof = null;
780 DateTime? PasswordLastSet = null;
781
782 // When the user is not allowed to query the UserAccountControl attribute.
783 if (AdUser.Members["userAccountControl"].Value != null)
784 {
785 var userFlags = (UACFlags) AdUser.Members["userAccountControl"].Value;
786 Enabled = !((userFlags & UACFlags.ACCOUNTDISABLE) == UACFlags.ACCOUNTDISABLE);
787 }
788 if (Convert.ToString(AdUser.Members["pwdLastSet"].Value) != "0")
789 {
790 PasswordLastSet = DateTime.FromFileTime((long)AdUser.Members["pwdLastSet"].Value);
791 }
792 Microsoft.ActiveDirectory.Management.ADPropertyValueCollection SPNs = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)AdUser.Members["servicePrincipalName"].Value;
793 Microsoft.ActiveDirectory.Management.ADPropertyValueCollection MemberOfAttribute = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)AdUser.Members["memberof"].Value;
794 if (MemberOfAttribute.Value is System.String[])
795 {
796 foreach (String Member in (System.String[])MemberOfAttribute.Value)
797 {
798 Memberof = Memberof + "," + ((Convert.ToString(Member)).Split(',')[0]).Split('=')[1];
799 }
800 Memberof = Memberof.TrimStart(',');
801 }
802 else if (Memberof != null)
803 {
804 Memberof = ((Convert.ToString(MemberOfAttribute.Value)).Split(',')[0]).Split('=')[1];
805 }
806 String Description = CleanString(AdUser.Members["Description"].Value);
807 String PrimaryGroupID = Convert.ToString(AdUser.Members["primaryGroupID"].Value);
808 if (SPNs.Value is System.String[])
809 {
810 foreach (String SPN in (System.String[])SPNs.Value)
811 {
812 String[] SPNArray = SPN.Split('/');
813 PSObject UserSPNObj = new PSObject();
814 UserSPNObj.Members.Add(new PSNoteProperty("Name", AdUser.Members["Name"].Value));
815 UserSPNObj.Members.Add(new PSNoteProperty("Username", AdUser.Members["SamAccountName"].Value));
816 UserSPNObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
817 UserSPNObj.Members.Add(new PSNoteProperty("Service", SPNArray[0]));
818 UserSPNObj.Members.Add(new PSNoteProperty("Host", SPNArray[1]));
819 UserSPNObj.Members.Add(new PSNoteProperty("Password Last Set", PasswordLastSet));
820 UserSPNObj.Members.Add(new PSNoteProperty("Description", Description));
821 UserSPNObj.Members.Add(new PSNoteProperty("Primary GroupID", PrimaryGroupID));
822 UserSPNObj.Members.Add(new PSNoteProperty("Memberof", Memberof));
823 SPNList.Add( UserSPNObj );
824 }
825 }
826 else
827 {
828 String[] SPNArray = Convert.ToString(SPNs.Value).Split('/');
829 PSObject UserSPNObj = new PSObject();
830 UserSPNObj.Members.Add(new PSNoteProperty("Name", AdUser.Members["Name"].Value));
831 UserSPNObj.Members.Add(new PSNoteProperty("Username", AdUser.Members["SamAccountName"].Value));
832 UserSPNObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
833 UserSPNObj.Members.Add(new PSNoteProperty("Service", SPNArray[0]));
834 UserSPNObj.Members.Add(new PSNoteProperty("Host", SPNArray[1]));
835 UserSPNObj.Members.Add(new PSNoteProperty("Password Last Set", PasswordLastSet));
836 UserSPNObj.Members.Add(new PSNoteProperty("Description", Description));
837 UserSPNObj.Members.Add(new PSNoteProperty("Primary GroupID", PrimaryGroupID));
838 UserSPNObj.Members.Add(new PSNoteProperty("Memberof", Memberof));
839 SPNList.Add( UserSPNObj );
840 }
841 return SPNList.ToArray();
842 }
843 catch (Exception e)
844 {
845 Console.WriteLine("{0} Exception caught.", e);
846 return new PSObject[] { };
847 }
848 }
849 }
850
851 class GroupRecordProcessor : IRecordProcessor
852 {
853 public PSObject[] processRecord(Object record)
854 {
855 try
856 {
857 PSObject AdGroup = (PSObject) record;
858 string ManagedByValue = Convert.ToString(AdGroup.Members["managedBy"].Value);
859 string ManagedBy = "";
860 String SIDHistory = "";
861
862 if (AdGroup.Members["managedBy"].Value != null)
863 {
864 ManagedBy = (ManagedByValue.Split(',')[0]).Split('=')[1];
865 }
866 Microsoft.ActiveDirectory.Management.ADPropertyValueCollection history = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) AdGroup.Members["SIDHistory"].Value;
867 if (history.Value is System.Security.Principal.SecurityIdentifier[])
868 {
869 string sids = "";
870 foreach (var value in (SecurityIdentifier[]) history.Value)
871 {
872 sids = sids + "," + Convert.ToString(value);
873 }
874 SIDHistory = sids.TrimStart(',');
875 }
876 else
877 {
878 SIDHistory = history != null ? Convert.ToString(history.Value) : "";
879 }
880
881 PSObject GroupObj = new PSObject();
882 GroupObj.Members.Add(new PSNoteProperty("Name", AdGroup.Members["SamAccountName"].Value));
883 GroupObj.Members.Add(new PSNoteProperty("AdminCount", AdGroup.Members["AdminCount"].Value));
884 GroupObj.Members.Add(new PSNoteProperty("GroupCategory", AdGroup.Members["GroupCategory"].Value));
885 GroupObj.Members.Add(new PSNoteProperty("GroupScope", AdGroup.Members["GroupScope"].Value));
886 GroupObj.Members.Add(new PSNoteProperty("ManagedBy", ManagedBy));
887 GroupObj.Members.Add(new PSNoteProperty("SID", AdGroup.Members["sid"].Value));
888 GroupObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
889 GroupObj.Members.Add(new PSNoteProperty("Description", CleanString(AdGroup.Members["Description"].Value)));
890 GroupObj.Members.Add(new PSNoteProperty("whenCreated", AdGroup.Members["whenCreated"].Value));
891 GroupObj.Members.Add(new PSNoteProperty("whenChanged", AdGroup.Members["whenChanged"].Value));
892 GroupObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdGroup.Members["DistinguishedName"].Value)));
893 GroupObj.Members.Add(new PSNoteProperty("CanonicalName", AdGroup.Members["CanonicalName"].Value));
894 return new PSObject[] { GroupObj };
895 }
896 catch (Exception e)
897 {
898 Console.WriteLine("{0} Exception caught.", e);
899 return new PSObject[] { };
900 }
901 }
902 }
903
904
905 class GroupRecordDictionaryProcessor : IRecordProcessor
906 {
907 public PSObject[] processRecord(Object record)
908 {
909 try
910 {
911 PSObject AdGroup = (PSObject) record;
912 ADWSClass.AdGroupDictionary.Add((Convert.ToString(AdGroup.Properties["SID"].Value)), (Convert.ToString(AdGroup.Members["SamAccountName"].Value)));
913 return new PSObject[] { };
914 }
915 catch (Exception e)
916 {
917 Console.WriteLine("{0} Exception caught.", e);
918 return new PSObject[] { };
919 }
920 }
921 }
922
923 class GroupMemberRecordProcessor : IRecordProcessor
924 {
925 public PSObject[] processRecord(Object record)
926 {
927 try
928 {
929 // based on https://github.com/BloodHoundAD/BloodHound/blob/master/PowerShell/BloodHound.ps1
930 PSObject AdGroup = (PSObject) record;
931 List<PSObject> GroupsList = new List<PSObject>();
932 string SamAccountType = Convert.ToString(AdGroup.Members["samaccounttype"].Value);
933 string AccountType = "";
934 string GroupName = "";
935 string MemberUserName = "-";
936 string MemberName = "";
937
938 if (Groups.Contains(SamAccountType))
939 {
940 AccountType = "group";
941 MemberName = ((Convert.ToString(AdGroup.Members["DistinguishedName"].Value)).Split(',')[0]).Split('=')[1];
942 Microsoft.ActiveDirectory.Management.ADPropertyValueCollection MemberGroups = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)AdGroup.Members["memberof"].Value;
943 if (MemberGroups.Value != null)
944 {
945 if (MemberGroups.Value is System.String[])
946 {
947 foreach (String GroupMember in (System.String[])MemberGroups.Value)
948 {
949 GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
950 PSObject GroupMemberObj = new PSObject();
951 GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
952 GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
953 GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
954 GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
955 GroupsList.Add( GroupMemberObj );
956 }
957 }
958 else
959 {
960 GroupName = (Convert.ToString(MemberGroups.Value).Split(',')[0]).Split('=')[1];
961 PSObject GroupMemberObj = new PSObject();
962 GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
963 GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
964 GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
965 GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
966 GroupsList.Add( GroupMemberObj );
967 }
968 }
969 }
970 if (Users.Contains(SamAccountType))
971 {
972 AccountType = "user";
973 MemberName = ((Convert.ToString(AdGroup.Members["DistinguishedName"].Value)).Split(',')[0]).Split('=')[1];
974 MemberUserName = Convert.ToString(AdGroup.Members["sAMAccountName"].Value);
975 String PrimaryGroupID = Convert.ToString(AdGroup.Members["primaryGroupID"].Value);
976 try
977 {
978 GroupName = ADWSClass.AdGroupDictionary[ADWSClass.DomainSID + "-" + PrimaryGroupID];
979 }
980 catch //(Exception e)
981 {
982 //Console.WriteLine("{0} Exception caught.", e);
983 GroupName = PrimaryGroupID;
984 }
985
986 {
987 PSObject GroupMemberObj = new PSObject();
988 GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
989 GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
990 GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
991 GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
992 GroupsList.Add( GroupMemberObj );
993 }
994
995 Microsoft.ActiveDirectory.Management.ADPropertyValueCollection MemberGroups = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)AdGroup.Members["memberof"].Value;
996 if (MemberGroups.Value != null)
997 {
998 if (MemberGroups.Value is System.String[])
999 {
1000 foreach (String GroupMember in (System.String[])MemberGroups.Value)
1001 {
1002 GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
1003 PSObject GroupMemberObj = new PSObject();
1004 GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
1005 GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
1006 GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
1007 GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
1008 GroupsList.Add( GroupMemberObj );
1009 }
1010 }
1011 else
1012 {
1013 GroupName = (Convert.ToString(MemberGroups.Value).Split(',')[0]).Split('=')[1];
1014 PSObject GroupMemberObj = new PSObject();
1015 GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
1016 GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
1017 GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
1018 GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
1019 GroupsList.Add( GroupMemberObj );
1020 }
1021 }
1022 }
1023 if (Computers.Contains(SamAccountType))
1024 {
1025 AccountType = "computer";
1026 MemberName = ((Convert.ToString(AdGroup.Members["DistinguishedName"].Value)).Split(',')[0]).Split('=')[1];
1027 MemberUserName = Convert.ToString(AdGroup.Members["sAMAccountName"].Value);
1028 String PrimaryGroupID = Convert.ToString(AdGroup.Members["primaryGroupID"].Value);
1029 try
1030 {
1031 GroupName = ADWSClass.AdGroupDictionary[ADWSClass.DomainSID + "-" + PrimaryGroupID];
1032 }
1033 catch //(Exception e)
1034 {
1035 //Console.WriteLine("{0} Exception caught.", e);
1036 GroupName = PrimaryGroupID;
1037 }
1038
1039 {
1040 PSObject GroupMemberObj = new PSObject();
1041 GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
1042 GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
1043 GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
1044 GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
1045 GroupsList.Add( GroupMemberObj );
1046 }
1047
1048 Microsoft.ActiveDirectory.Management.ADPropertyValueCollection MemberGroups = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)AdGroup.Members["memberof"].Value;
1049 if (MemberGroups.Value != null)
1050 {
1051 if (MemberGroups.Value is System.String[])
1052 {
1053 foreach (String GroupMember in (System.String[])MemberGroups.Value)
1054 {
1055 GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
1056 PSObject GroupMemberObj = new PSObject();
1057 GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
1058 GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
1059 GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
1060 GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
1061 GroupsList.Add( GroupMemberObj );
1062 }
1063 }
1064 else
1065 {
1066 GroupName = (Convert.ToString(MemberGroups.Value).Split(',')[0]).Split('=')[1];
1067 PSObject GroupMemberObj = new PSObject();
1068 GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
1069 GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
1070 GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
1071 GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
1072 GroupsList.Add( GroupMemberObj );
1073 }
1074 }
1075 }
1076 if (TrustAccounts.Contains(SamAccountType))
1077 {
1078 // TO DO
1079 }
1080 return GroupsList.ToArray();
1081 }
1082 catch (Exception e)
1083 {
1084 Console.WriteLine("{0} Exception caught.", e);
1085 return new PSObject[] { };
1086 }
1087 }
1088 }
1089
1090 class OURecordProcessor : IRecordProcessor
1091 {
1092 public PSObject[] processRecord(Object record)
1093 {
1094 try
1095 {
1096 PSObject AdOU = (PSObject) record;
1097 PSObject OUObj = new PSObject();
1098 OUObj.Members.Add(new PSNoteProperty("Name", AdOU.Members["Name"].Value));
1099 OUObj.Members.Add(new PSNoteProperty("Depth", ((Convert.ToString(AdOU.Members["DistinguishedName"].Value).Split(new string[] { "OU=" }, StringSplitOptions.None)).Length -1)));
1100 OUObj.Members.Add(new PSNoteProperty("Description", AdOU.Members["Description"].Value));
1101 OUObj.Members.Add(new PSNoteProperty("whenCreated", AdOU.Members["whenCreated"].Value));
1102 OUObj.Members.Add(new PSNoteProperty("whenChanged", AdOU.Members["whenChanged"].Value));
1103 OUObj.Members.Add(new PSNoteProperty("DistinguishedName", AdOU.Members["DistinguishedName"].Value));
1104 return new PSObject[] { OUObj };
1105 }
1106 catch (Exception e)
1107 {
1108 Console.WriteLine("{0} Exception caught.", e);
1109 return new PSObject[] { };
1110 }
1111 }
1112 }
1113
1114 class GPORecordProcessor : IRecordProcessor
1115 {
1116 public PSObject[] processRecord(Object record)
1117 {
1118 try
1119 {
1120 PSObject AdGPO = (PSObject) record;
1121
1122 PSObject GPOObj = new PSObject();
1123 GPOObj.Members.Add(new PSNoteProperty("DisplayName", CleanString(AdGPO.Members["DisplayName"].Value)));
1124 GPOObj.Members.Add(new PSNoteProperty("GUID", CleanString(AdGPO.Members["Name"].Value)));
1125 GPOObj.Members.Add(new PSNoteProperty("whenCreated", AdGPO.Members["whenCreated"].Value));
1126 GPOObj.Members.Add(new PSNoteProperty("whenChanged", AdGPO.Members["whenChanged"].Value));
1127 GPOObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdGPO.Members["DistinguishedName"].Value)));
1128 GPOObj.Members.Add(new PSNoteProperty("FilePath", AdGPO.Members["gPCFileSysPath"].Value));
1129 return new PSObject[] { GPOObj };
1130 }
1131 catch (Exception e)
1132 {
1133 Console.WriteLine("{0} Exception caught.", e);
1134 return new PSObject[] { };
1135 }
1136 }
1137 }
1138
1139 class GPORecordDictionaryProcessor : IRecordProcessor
1140 {
1141 public PSObject[] processRecord(Object record)
1142 {
1143 try
1144 {
1145 PSObject AdGPO = (PSObject) record;
1146 ADWSClass.AdGPODictionary.Add((Convert.ToString(AdGPO.Members["DistinguishedName"].Value).ToUpper()), (Convert.ToString(AdGPO.Members["DisplayName"].Value)));
1147 return new PSObject[] { };
1148 }
1149 catch (Exception e)
1150 {
1151 Console.WriteLine("{0} Exception caught.", e);
1152 return new PSObject[] { };
1153 }
1154 }
1155 }
1156
1157 class SOMRecordProcessor : IRecordProcessor
1158 {
1159 public PSObject[] processRecord(Object record)
1160 {
1161 try
1162 {
1163 PSObject AdSOM = (PSObject) record;
1164 List<PSObject> SOMsList = new List<PSObject>();
1165 int Depth = 0;
1166 bool BlockInheritance = false;
1167 bool? LinkEnabled = null;
1168 bool? Enforced = null;
1169 String gPLink = Convert.ToString(AdSOM.Members["gPLink"].Value);
1170 String GPOName = null;
1171
1172 Depth = (Convert.ToString(AdSOM.Members["DistinguishedName"].Value).Split(new string[] { "OU=" }, StringSplitOptions.None)).Length -1;
1173 if (AdSOM.Members["gPOptions"].Value != null && (int) AdSOM.Members["gPOptions"].Value == 1)
1174 {
1175 BlockInheritance = true;
1176 }
1177 var GPLinks = gPLink.Split(']', '[').Where(x => x.StartsWith("LDAP"));
1178 int Order = (GPLinks.ToArray()).Length;
1179 if (Order == 0)
1180 {
1181 PSObject SOMObj = new PSObject();
1182 SOMObj.Members.Add(new PSNoteProperty("Name", AdSOM.Members["Name"].Value));
1183 SOMObj.Members.Add(new PSNoteProperty("Depth", Depth));
1184 SOMObj.Members.Add(new PSNoteProperty("DistinguishedName", AdSOM.Members["DistinguishedName"].Value));
1185 SOMObj.Members.Add(new PSNoteProperty("Link Order", null));
1186 SOMObj.Members.Add(new PSNoteProperty("GPO", GPOName));
1187 SOMObj.Members.Add(new PSNoteProperty("Enforced", Enforced));
1188 SOMObj.Members.Add(new PSNoteProperty("Link Enabled", LinkEnabled));
1189 SOMObj.Members.Add(new PSNoteProperty("BlockInheritance", BlockInheritance));
1190 SOMObj.Members.Add(new PSNoteProperty("gPLink", gPLink));
1191 SOMObj.Members.Add(new PSNoteProperty("gPOptions", AdSOM.Members["gPOptions"].Value));
1192 SOMsList.Add( SOMObj );
1193 }
1194 foreach (String link in GPLinks)
1195 {
1196 String[] linksplit = link.Split('/', ';');
1197 if (!Convert.ToBoolean((Convert.ToInt32(linksplit[3]) & 1)))
1198 {
1199 LinkEnabled = true;
1200 }
1201 else
1202 {
1203 LinkEnabled = false;
1204 }
1205 if (Convert.ToBoolean((Convert.ToInt32(linksplit[3]) & 2)))
1206 {
1207 Enforced = true;
1208 }
1209 else
1210 {
1211 Enforced = false;
1212 }
1213 GPOName = ADWSClass.AdGPODictionary.ContainsKey(linksplit[2].ToUpper()) ? ADWSClass.AdGPODictionary[linksplit[2].ToUpper()] : linksplit[2].Split('=',',')[1];
1214 PSObject SOMObj = new PSObject();
1215 SOMObj.Members.Add(new PSNoteProperty("Name", AdSOM.Members["Name"].Value));
1216 SOMObj.Members.Add(new PSNoteProperty("Depth", Depth));
1217 SOMObj.Members.Add(new PSNoteProperty("DistinguishedName", AdSOM.Members["DistinguishedName"].Value));
1218 SOMObj.Members.Add(new PSNoteProperty("Link Order", Order));
1219 SOMObj.Members.Add(new PSNoteProperty("GPO", GPOName));
1220 SOMObj.Members.Add(new PSNoteProperty("Enforced", Enforced));
1221 SOMObj.Members.Add(new PSNoteProperty("Link Enabled", LinkEnabled));
1222 SOMObj.Members.Add(new PSNoteProperty("BlockInheritance", BlockInheritance));
1223 SOMObj.Members.Add(new PSNoteProperty("gPLink", gPLink));
1224 SOMObj.Members.Add(new PSNoteProperty("gPOptions", AdSOM.Members["gPOptions"].Value));
1225 SOMsList.Add( SOMObj );
1226 Order--;
1227 }
1228 return SOMsList.ToArray();
1229 }
1230 catch (Exception e)
1231 {
1232 Console.WriteLine("{0} Exception caught.", e);
1233 return new PSObject[] { };
1234 }
1235 }
1236 }
1237
1238 class PrinterRecordProcessor : IRecordProcessor
1239 {
1240 public PSObject[] processRecord(Object record)
1241 {
1242 try
1243 {
1244 PSObject AdPrinter = (PSObject) record;
1245
1246 PSObject PrinterObj = new PSObject();
1247 PrinterObj.Members.Add(new PSNoteProperty("Name", AdPrinter.Members["Name"].Value));
1248 PrinterObj.Members.Add(new PSNoteProperty("ServerName", AdPrinter.Members["serverName"].Value));
1249 PrinterObj.Members.Add(new PSNoteProperty("ShareName", ((Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) (AdPrinter.Members["printShareName"].Value)).Value));
1250 PrinterObj.Members.Add(new PSNoteProperty("DriverName", AdPrinter.Members["driverName"].Value));
1251 PrinterObj.Members.Add(new PSNoteProperty("DriverVersion", AdPrinter.Members["driverVersion"].Value));
1252 PrinterObj.Members.Add(new PSNoteProperty("PortName", ((Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) (AdPrinter.Members["portName"].Value)).Value));
1253 PrinterObj.Members.Add(new PSNoteProperty("URL", ((Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) (AdPrinter.Members["url"].Value)).Value));
1254 PrinterObj.Members.Add(new PSNoteProperty("whenCreated", AdPrinter.Members["whenCreated"].Value));
1255 PrinterObj.Members.Add(new PSNoteProperty("whenChanged", AdPrinter.Members["whenChanged"].Value));
1256 return new PSObject[] { PrinterObj };
1257 }
1258 catch (Exception e)
1259 {
1260 Console.WriteLine("{0} Exception caught.", e);
1261 return new PSObject[] { };
1262 }
1263 }
1264 }
1265
1266 class ComputerRecordProcessor : IRecordProcessor
1267 {
1268 public PSObject[] processRecord(Object record)
1269 {
1270 try
1271 {
1272 PSObject AdComputer = (PSObject) record;
1273 int? DaysSinceLastLogon = null;
1274 int? DaysSinceLastPasswordChange = null;
1275 bool Dormant = false;
1276 bool PasswordNotChangedafterMaxAge = false;
1277 String SIDHistory = "";
1278 String DelegationType = null;
1279 String DelegationProtocol = null;
1280 String DelegationServices = null;
1281 DateTime? LastLogonDate = null;
1282 DateTime? PasswordLastSet = null;
1283
1284 if (AdComputer.Members["LastLogonDate"].Value != null)
1285 {
1286 //LastLogonDate = DateTime.FromFileTime((long)(AdComputer.Members["lastLogonTimeStamp"].Value));
1287 // LastLogonDate is lastLogonTimeStamp converted to local time
1288 LastLogonDate = Convert.ToDateTime(AdComputer.Members["LastLogonDate"].Value);
1289 DaysSinceLastLogon = Math.Abs((Date1 - (DateTime)LastLogonDate).Days);
1290 if (DaysSinceLastLogon > DormantTimeSpan)
1291 {
1292 Dormant = true;
1293 }
1294 }
1295 if (AdComputer.Members["PasswordLastSet"].Value != null)
1296 {
1297 //PasswordLastSet = DateTime.FromFileTime((long)(AdComputer.Members["pwdLastSet"].Value));
1298 // PasswordLastSet is pwdLastSet converted to local time
1299 PasswordLastSet = Convert.ToDateTime(AdComputer.Members["PasswordLastSet"].Value);
1300 DaysSinceLastPasswordChange = Math.Abs((Date1 - (DateTime)PasswordLastSet).Days);
1301 if (DaysSinceLastPasswordChange > PassMaxAge)
1302 {
1303 PasswordNotChangedafterMaxAge = true;
1304 }
1305 }
1306 if ( ((bool) AdComputer.Members["TrustedForDelegation"].Value) && ((int) AdComputer.Members["primaryGroupID"].Value == 515) )
1307 {
1308 DelegationType = "Unconstrained";
1309 DelegationServices = "Any";
1310 }
1311 if (AdComputer.Members["msDS-AllowedToDelegateTo"] != null)
1312 {
1313 Microsoft.ActiveDirectory.Management.ADPropertyValueCollection delegateto = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) AdComputer.Members["msDS-AllowedToDelegateTo"].Value;
1314 if (delegateto.Value != null)
1315 {
1316 DelegationType = "Constrained";
1317 if (delegateto.Value is System.String[])
1318 {
1319 foreach (var value in (String[]) delegateto.Value)
1320 {
1321 DelegationServices = DelegationServices + "," + Convert.ToString(value);
1322 }
1323 DelegationServices = DelegationServices.TrimStart(',');
1324 }
1325 else
1326 {
1327 DelegationServices = Convert.ToString(delegateto.Value);
1328 }
1329 }
1330 }
1331 if ((bool) AdComputer.Members["TrustedToAuthForDelegation"].Value)
1332 {
1333 DelegationProtocol = "Any";
1334 }
1335 else if (DelegationType != null)
1336 {
1337 DelegationProtocol = "Kerberos";
1338 }
1339 Microsoft.ActiveDirectory.Management.ADPropertyValueCollection history = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) AdComputer.Members["SIDHistory"].Value;
1340 if (history.Value is System.Security.Principal.SecurityIdentifier[])
1341 {
1342 string sids = "";
1343 foreach (var value in (SecurityIdentifier[]) history.Value)
1344 {
1345 sids = sids + "," + Convert.ToString(value);
1346 }
1347 SIDHistory = sids.TrimStart(',');
1348 }
1349 else
1350 {
1351 SIDHistory = history != null ? Convert.ToString(history.Value) : "";
1352 }
1353 String OperatingSystem = CleanString((AdComputer.Members["OperatingSystem"].Value != null ? AdComputer.Members["OperatingSystem"].Value : "-") + " " + AdComputer.Members["OperatingSystemHotfix"].Value + " " + AdComputer.Members["OperatingSystemServicePack"].Value + " " + AdComputer.Members["OperatingSystemVersion"].Value);
1354
1355 PSObject ComputerObj = new PSObject();
1356 ComputerObj.Members.Add(new PSNoteProperty("Name", AdComputer.Members["Name"].Value));
1357 ComputerObj.Members.Add(new PSNoteProperty("DNSHostName", AdComputer.Members["DNSHostName"].Value));
1358 ComputerObj.Members.Add(new PSNoteProperty("Enabled", AdComputer.Members["Enabled"].Value));
1359 ComputerObj.Members.Add(new PSNoteProperty("IPv4Address", AdComputer.Members["IPv4Address"].Value));
1360 ComputerObj.Members.Add(new PSNoteProperty("Operating System", OperatingSystem));
1361 ComputerObj.Members.Add(new PSNoteProperty("Logon Age (days)", DaysSinceLastLogon));
1362 ComputerObj.Members.Add(new PSNoteProperty("Password Age (days)", DaysSinceLastPasswordChange));
1363 ComputerObj.Members.Add(new PSNoteProperty("Dormant (> " + DormantTimeSpan + " days)", Dormant));
1364 ComputerObj.Members.Add(new PSNoteProperty("Password Age (> " + PassMaxAge + " days)", PasswordNotChangedafterMaxAge));
1365 ComputerObj.Members.Add(new PSNoteProperty("Delegation Type", DelegationType));
1366 ComputerObj.Members.Add(new PSNoteProperty("Delegation Protocol", DelegationProtocol));
1367 ComputerObj.Members.Add(new PSNoteProperty("Delegation Services", DelegationServices));
1368 ComputerObj.Members.Add(new PSNoteProperty("UserName", AdComputer.Members["SamAccountName"].Value));
1369 ComputerObj.Members.Add(new PSNoteProperty("Primary Group ID", AdComputer.Members["primaryGroupID"].Value));
1370 ComputerObj.Members.Add(new PSNoteProperty("SID", AdComputer.Members["SID"].Value));
1371 ComputerObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
1372 ComputerObj.Members.Add(new PSNoteProperty("Description", AdComputer.Members["Description"].Value));
1373 ComputerObj.Members.Add(new PSNoteProperty("ms-ds-CreatorSid", AdComputer.Members["ms-ds-CreatorSid"].Value));
1374 ComputerObj.Members.Add(new PSNoteProperty("Last Logon Date", LastLogonDate));
1375 ComputerObj.Members.Add(new PSNoteProperty("Password LastSet", PasswordLastSet));
1376 ComputerObj.Members.Add(new PSNoteProperty("UserAccountControl", AdComputer.Members["UserAccountControl"].Value));
1377 ComputerObj.Members.Add(new PSNoteProperty("whenCreated", AdComputer.Members["whenCreated"].Value));
1378 ComputerObj.Members.Add(new PSNoteProperty("whenChanged", AdComputer.Members["whenChanged"].Value));
1379 ComputerObj.Members.Add(new PSNoteProperty("Distinguished Name", AdComputer.Members["DistinguishedName"].Value));
1380 return new PSObject[] { ComputerObj };
1381 }
1382 catch (Exception e)
1383 {
1384 Console.WriteLine("{0} Exception caught.", e);
1385 return new PSObject[] { };
1386 }
1387 }
1388 }
1389
1390 class ComputerSPNRecordProcessor : IRecordProcessor
1391 {
1392 public PSObject[] processRecord(Object record)
1393 {
1394 try
1395 {
1396 PSObject AdComputer = (PSObject) record;
1397 List<PSObject> SPNList = new List<PSObject>();
1398
1399 Microsoft.ActiveDirectory.Management.ADPropertyValueCollection SPNs = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)AdComputer.Members["servicePrincipalName"].Value;
1400 if (SPNs.Value is System.String[])
1401 {
1402 foreach (String SPN in (System.String[])SPNs.Value)
1403 {
1404 bool flag = true;
1405 String[] SPNArray = SPN.Split('/');
1406 foreach (PSObject Obj in SPNList)
1407 {
1408 if ( (String) Obj.Members["Service"].Value == SPNArray[0] )
1409 {
1410 Obj.Members["Host"].Value = String.Join(",", (Obj.Members["Host"].Value + "," + SPNArray[1]).Split(',').Distinct().ToArray());
1411 flag = false;
1412 }
1413 }
1414 if (flag)
1415 {
1416 PSObject ComputerSPNObj = new PSObject();
1417 ComputerSPNObj.Members.Add(new PSNoteProperty("Name", AdComputer.Members["Name"].Value));
1418 ComputerSPNObj.Members.Add(new PSNoteProperty("Service", SPNArray[0]));
1419 ComputerSPNObj.Members.Add(new PSNoteProperty("Host", SPNArray[1]));
1420 SPNList.Add( ComputerSPNObj );
1421 }
1422 }
1423 }
1424 else
1425 {
1426 String[] SPNArray = Convert.ToString(SPNs.Value).Split('/');
1427 PSObject ComputerSPNObj = new PSObject();
1428 ComputerSPNObj.Members.Add(new PSNoteProperty("Name", AdComputer.Members["Name"].Value));
1429 ComputerSPNObj.Members.Add(new PSNoteProperty("Service", SPNArray[0]));
1430 ComputerSPNObj.Members.Add(new PSNoteProperty("Host", SPNArray[1]));
1431 SPNList.Add( ComputerSPNObj );
1432 }
1433 return SPNList.ToArray();
1434 }
1435 catch (Exception e)
1436 {
1437 Console.WriteLine("{0} Exception caught.", e);
1438 return new PSObject[] { };
1439 }
1440 }
1441 }
1442
1443 class LAPSRecordProcessor : IRecordProcessor
1444 {
1445 public PSObject[] processRecord(Object record)
1446 {
1447 try
1448 {
1449 PSObject AdComputer = (PSObject) record;
1450 bool PasswordStored = false;
1451 DateTime? CurrentExpiration = null;
1452 try
1453 {
1454 CurrentExpiration = DateTime.FromFileTime((long)(AdComputer.Members["ms-Mcs-AdmPwdExpirationTime"].Value));
1455 PasswordStored = true;
1456 }
1457 catch //(Exception e)
1458 {
1459 //Console.WriteLine("{0} Exception caught.", e);
1460 }
1461 PSObject LAPSObj = new PSObject();
1462 LAPSObj.Members.Add(new PSNoteProperty("Hostname", (AdComputer.Members["DNSHostName"].Value != null ? AdComputer.Members["DNSHostName"].Value : AdComputer.Members["CN"].Value )));
1463 LAPSObj.Members.Add(new PSNoteProperty("Stored", PasswordStored));
1464 LAPSObj.Members.Add(new PSNoteProperty("Readable", (AdComputer.Members["ms-Mcs-AdmPwd"].Value != null ? true : false)));
1465 LAPSObj.Members.Add(new PSNoteProperty("Password", AdComputer.Members["ms-Mcs-AdmPwd"].Value));
1466 LAPSObj.Members.Add(new PSNoteProperty("Expiration", CurrentExpiration));
1467 return new PSObject[] { LAPSObj };
1468 }
1469 catch (Exception e)
1470 {
1471 Console.WriteLine("{0} Exception caught.", e);
1472 return new PSObject[] { };
1473 }
1474 }
1475 }
1476
1477 class SIDRecordDictionaryProcessor : IRecordProcessor
1478 {
1479 public PSObject[] processRecord(Object record)
1480 {
1481 try
1482 {
1483 PSObject AdObject = (PSObject) record;
1484 switch (Convert.ToString(AdObject.Members["ObjectClass"].Value))
1485 {
1486 case "user":
1487 case "computer":
1488 case "group":
1489 ADWSClass.AdSIDDictionary.Add(Convert.ToString(AdObject.Members["objectsid"].Value), Convert.ToString(AdObject.Members["Name"].Value));
1490 break;
1491 }
1492 return new PSObject[] { };
1493 }
1494 catch (Exception e)
1495 {
1496 Console.WriteLine("{0} {1} Exception caught.", ((PSObject) record).Members["ObjectClass"].Value, e);
1497 return new PSObject[] { };
1498 }
1499 }
1500 }
1501
1502 class DACLRecordProcessor : IRecordProcessor
1503 {
1504 public PSObject[] processRecord(Object record)
1505 {
1506 try
1507 {
1508 PSObject AdObject = (PSObject) record;
1509 String Name = null;
1510 String Type = null;
1511 List<PSObject> DACLList = new List<PSObject>();
1512
1513 Name = Convert.ToString(AdObject.Members["Name"].Value);
1514
1515 switch (Convert.ToString(AdObject.Members["objectClass"].Value))
1516 {
1517 case "user":
1518 Type = "User";
1519 break;
1520 case "computer":
1521 Type = "Computer";
1522 break;
1523 case "group":
1524 Type = "Group";
1525 break;
1526 case "container":
1527 Type = "Container";
1528 break;
1529 case "groupPolicyContainer":
1530 Type = "GPO";
1531 Name = Convert.ToString(AdObject.Members["DisplayName"].Value);
1532 break;
1533 case "organizationalUnit":
1534 Type = "OU";
1535 break;
1536 case "domainDNS":
1537 Type = "Domain";
1538 break;
1539 default:
1540 Type = Convert.ToString(AdObject.Members["objectClass"].Value);
1541 break;
1542 }
1543
1544 // When the user is not allowed to query the ntsecuritydescriptor attribute.
1545 if (AdObject.Members["ntsecuritydescriptor"] != null)
1546 {
1547 DirectoryObjectSecurity DirObjSec = (DirectoryObjectSecurity) AdObject.Members["ntsecuritydescriptor"].Value;
1548 AuthorizationRuleCollection AccessRules = (AuthorizationRuleCollection) DirObjSec.GetAccessRules(true,true,typeof(System.Security.Principal.NTAccount));
1549 foreach (ActiveDirectoryAccessRule Rule in AccessRules)
1550 {
1551 String IdentityReference = Convert.ToString(Rule.IdentityReference);
1552 String Owner = Convert.ToString(DirObjSec.GetOwner(typeof(System.Security.Principal.SecurityIdentifier)));
1553 PSObject ObjectObj = new PSObject();
1554 ObjectObj.Members.Add(new PSNoteProperty("Name", CleanString(Name)));
1555 ObjectObj.Members.Add(new PSNoteProperty("Type", Type));
1556 ObjectObj.Members.Add(new PSNoteProperty("ObjectTypeName", ADWSClass.GUIDs[Convert.ToString(Rule.ObjectType)]));
1557 ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectTypeName", ADWSClass.GUIDs[Convert.ToString(Rule.InheritedObjectType)]));
1558 ObjectObj.Members.Add(new PSNoteProperty("ActiveDirectoryRights", Rule.ActiveDirectoryRights));
1559 ObjectObj.Members.Add(new PSNoteProperty("AccessControlType", Rule.AccessControlType));
1560 ObjectObj.Members.Add(new PSNoteProperty("IdentityReferenceName", ADWSClass.AdSIDDictionary.ContainsKey(IdentityReference) ? ADWSClass.AdSIDDictionary[IdentityReference] : IdentityReference));
1561 ObjectObj.Members.Add(new PSNoteProperty("OwnerName", ADWSClass.AdSIDDictionary.ContainsKey(Owner) ? ADWSClass.AdSIDDictionary[Owner] : Owner));
1562 ObjectObj.Members.Add(new PSNoteProperty("Inherited", Rule.IsInherited));
1563 ObjectObj.Members.Add(new PSNoteProperty("ObjectFlags", Rule.ObjectFlags));
1564 ObjectObj.Members.Add(new PSNoteProperty("InheritanceFlags", Rule.InheritanceFlags));
1565 ObjectObj.Members.Add(new PSNoteProperty("InheritanceType", Rule.InheritanceType));
1566 ObjectObj.Members.Add(new PSNoteProperty("PropagationFlags", Rule.PropagationFlags));
1567 ObjectObj.Members.Add(new PSNoteProperty("ObjectType", Rule.ObjectType));
1568 ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectType", Rule.InheritedObjectType));
1569 ObjectObj.Members.Add(new PSNoteProperty("IdentityReference", Rule.IdentityReference));
1570 ObjectObj.Members.Add(new PSNoteProperty("Owner", Owner));
1571 ObjectObj.Members.Add(new PSNoteProperty("DistinguishedName", AdObject.Members["DistinguishedName"].Value));
1572 DACLList.Add( ObjectObj );
1573 }
1574 }
1575
1576 return DACLList.ToArray();
1577 }
1578 catch (Exception e)
1579 {
1580 Console.WriteLine("{0} Exception caught.", e);
1581 return new PSObject[] { };
1582 }
1583 }
1584 }
1585
1586 class SACLRecordProcessor : IRecordProcessor
1587 {
1588 public PSObject[] processRecord(Object record)
1589 {
1590 try
1591 {
1592 PSObject AdObject = (PSObject) record;
1593 String Name = null;
1594 String Type = null;
1595 List<PSObject> SACLList = new List<PSObject>();
1596
1597 Name = Convert.ToString(AdObject.Members["Name"].Value);
1598
1599 switch (Convert.ToString(AdObject.Members["objectClass"].Value))
1600 {
1601 case "user":
1602 Type = "User";
1603 break;
1604 case "computer":
1605 Type = "Computer";
1606 break;
1607 case "group":
1608 Type = "Group";
1609 break;
1610 case "container":
1611 Type = "Container";
1612 break;
1613 case "groupPolicyContainer":
1614 Type = "GPO";
1615 Name = Convert.ToString(AdObject.Members["DisplayName"].Value);
1616 break;
1617 case "organizationalUnit":
1618 Type = "OU";
1619 break;
1620 case "domainDNS":
1621 Type = "Domain";
1622 break;
1623 default:
1624 Type = Convert.ToString(AdObject.Members["objectClass"].Value);
1625 break;
1626 }
1627
1628 // When the user is not allowed to query the ntsecuritydescriptor attribute.
1629 if (AdObject.Members["ntsecuritydescriptor"] != null)
1630 {
1631 DirectoryObjectSecurity DirObjSec = (DirectoryObjectSecurity) AdObject.Members["ntsecuritydescriptor"].Value;
1632 AuthorizationRuleCollection AuditRules = (AuthorizationRuleCollection) DirObjSec.GetAuditRules(true,true,typeof(System.Security.Principal.NTAccount));
1633 foreach (ActiveDirectoryAuditRule Rule in AuditRules)
1634 {
1635 PSObject ObjectObj = new PSObject();
1636 ObjectObj.Members.Add(new PSNoteProperty("Name", CleanString(Name)));
1637 ObjectObj.Members.Add(new PSNoteProperty("Type", Type));
1638 ObjectObj.Members.Add(new PSNoteProperty("ObjectTypeName", ADWSClass.GUIDs[Convert.ToString(Rule.ObjectType)]));
1639 ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectTypeName", ADWSClass.GUIDs[Convert.ToString(Rule.InheritedObjectType)]));
1640 ObjectObj.Members.Add(new PSNoteProperty("ActiveDirectoryRights", Rule.ActiveDirectoryRights));
1641 ObjectObj.Members.Add(new PSNoteProperty("IdentityReference", Rule.IdentityReference));
1642 ObjectObj.Members.Add(new PSNoteProperty("AuditFlags", Rule.AuditFlags));
1643 ObjectObj.Members.Add(new PSNoteProperty("ObjectFlags", Rule.ObjectFlags));
1644 ObjectObj.Members.Add(new PSNoteProperty("InheritanceFlags", Rule.InheritanceFlags));
1645 ObjectObj.Members.Add(new PSNoteProperty("InheritanceType", Rule.InheritanceType));
1646 ObjectObj.Members.Add(new PSNoteProperty("Inherited", Rule.IsInherited));
1647 ObjectObj.Members.Add(new PSNoteProperty("PropagationFlags", Rule.PropagationFlags));
1648 ObjectObj.Members.Add(new PSNoteProperty("ObjectType", Rule.ObjectType));
1649 ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectType", Rule.InheritedObjectType));
1650 SACLList.Add( ObjectObj );
1651 }
1652 }
1653
1654 return SACLList.ToArray();
1655 }
1656 catch (Exception e)
1657 {
1658 Console.WriteLine("{0} Exception caught.", e);
1659 return new PSObject[] { };
1660 }
1661 }
1662 }
1663
1664 //The interface and implmentation class used to handle the results (this implementation just writes the strings to a file)
1665
1666 interface IResultsHandler
1667 {
1668 void processResults(Object[] t);
1669
1670 Object[] finalise();
1671 }
1672
1673 class SimpleResultsHandler : IResultsHandler
1674 {
1675 private Object lockObj = new Object();
1676 private List<Object> processed = new List<Object>();
1677
1678 public SimpleResultsHandler()
1679 {
1680 }
1681
1682 public void processResults(Object[] results)
1683 {
1684 lock (lockObj)
1685 {
1686 if (results.Length != 0)
1687 {
1688 for (var i = 0; i < results.Length; i++)
1689 {
1690 processed.Add((PSObject)results[i]);
1691 }
1692 }
1693 }
1694 }
1695
1696 public Object[] finalise()
1697 {
1698 return processed.ToArray();
1699 }
1700 }
1701 }
1702}
1703"@
1704
1705$LDAPSource = @"
1706// Thanks Dennis Albuquerque for the C# multithreading code
1707using System;
1708using System.Collections;
1709using System.Collections.Generic;
1710using System.Linq;
1711using System.Net;
1712using System.Threading;
1713using System.DirectoryServices;
1714using System.Security.Principal;
1715using System.Security.AccessControl;
1716using System.Management.Automation;
1717
1718namespace ADRecon
1719{
1720 public static class LDAPClass
1721 {
1722 private static DateTime Date1;
1723 private static int PassMaxAge;
1724 private static int DormantTimeSpan;
1725 private static Dictionary<String, String> AdGroupDictionary = new Dictionary<String, String>();
1726 private static String DomainSID;
1727 private static Dictionary<String, String> AdGPODictionary = new Dictionary<String, String>();
1728 private static Hashtable GUIDs = new Hashtable();
1729 private static Dictionary<String, String> AdSIDDictionary = new Dictionary<String, String>();
1730 private static readonly HashSet<string> Groups = new HashSet<string> ( new String[] {"268435456", "268435457", "536870912", "536870913"} );
1731 private static readonly HashSet<string> Users = new HashSet<string> ( new String[] { "805306368" } );
1732 private static readonly HashSet<string> Computers = new HashSet<string> ( new String[] { "805306369" }) ;
1733 private static readonly HashSet<string> TrustAccounts = new HashSet<string> ( new String[] { "805306370" } );
1734
1735 [Flags]
1736 //Values taken from https://support.microsoft.com/en-au/kb/305144
1737 public enum UACFlags
1738 {
1739 SCRIPT = 1, // 0x1
1740 ACCOUNTDISABLE = 2, // 0x2
1741 HOMEDIR_REQUIRED = 8, // 0x8
1742 LOCKOUT = 16, // 0x10
1743 PASSWD_NOTREQD = 32, // 0x20
1744 PASSWD_CANT_CHANGE = 64, // 0x40
1745 ENCRYPTED_TEXT_PASSWORD_ALLOWED = 128, // 0x80
1746 TEMP_DUPLICATE_ACCOUNT = 256, // 0x100
1747 NORMAL_ACCOUNT = 512, // 0x200
1748 INTERDOMAIN_TRUST_ACCOUNT = 2048, // 0x800
1749 WORKSTATION_TRUST_ACCOUNT = 4096, // 0x1000
1750 SERVER_TRUST_ACCOUNT = 8192, // 0x2000
1751 DONT_EXPIRE_PASSWD = 65536, // 0x10000
1752 MNS_LOGON_ACCOUNT = 131072, // 0x20000
1753 SMARTCARD_REQUIRED = 262144, // 0x40000
1754 TRUSTED_FOR_DELEGATION = 524288, // 0x80000
1755 NOT_DELEGATED = 1048576, // 0x100000
1756 USE_DES_KEY_ONLY = 2097152, // 0x200000
1757 DONT_REQUIRE_PREAUTH = 4194304, // 0x400000
1758 PASSWORD_EXPIRED = 8388608, // 0x800000
1759 TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 16777216, // 0x1000000
1760 PARTIAL_SECRETS_ACCOUNT = 67108864 // 0x04000000
1761 }
1762
1763 [Flags]
1764 //Values taken from https://blogs.msdn.microsoft.com/openspecification/2011/05/30/windows-configurations-for-kerberos-supported-encryption-type/
1765 public enum KerbEncFlags
1766 {
1767 ZERO = 0,
1768 DES_CBC_CRC = 1, // 0x1
1769 DES_CBC_MD5 = 2, // 0x2
1770 RC4_HMAC = 4, // 0x4
1771 AES128_CTS_HMAC_SHA1_96 = 8, // 0x18
1772 AES256_CTS_HMAC_SHA1_96 = 16 // 0x10
1773 }
1774
1775 [Flags]
1776 //Values taken from https://support.microsoft.com/en-au/kb/305144
1777 public enum GroupTypeFlags
1778 {
1779 GLOBAL_GROUP = 2, // 0x00000002
1780 DOMAIN_LOCAL_GROUP = 4, // 0x00000004
1781 LOCAL_GROUP = 4, // 0x00000004
1782 UNIVERSAL_GROUP = 8, // 0x00000008
1783 SECURITY_ENABLED = -2147483648 // 0x80000000
1784 }
1785
1786 private static readonly Dictionary<String, String> Replacements = new Dictionary<String, String>()
1787 {
1788 //{System.Environment.NewLine, ""},
1789 //{",", ";"},
1790 {"\"", "'"}
1791 };
1792
1793 public static String CleanString(Object StringtoClean)
1794 {
1795 // Remove extra spaces and new lines
1796 String CleanedString = String.Join(" ", ((Convert.ToString(StringtoClean)).Split((string[]) null, StringSplitOptions.RemoveEmptyEntries)));
1797 foreach (String Replacement in Replacements.Keys)
1798 {
1799 CleanedString = CleanedString.Replace(Replacement, Replacements[Replacement]);
1800 }
1801 return CleanedString;
1802 }
1803
1804 public static int ObjectCount(Object[] ADRObject)
1805 {
1806 return ADRObject.Length;
1807 }
1808
1809 public static bool LAPSCheck(Object[] AdComputers)
1810 {
1811 bool LAPS = false;
1812 foreach (SearchResult AdComputer in AdComputers)
1813 {
1814 if (AdComputer.Properties["ms-mcs-admpwdexpirationtime"].Count == 1)
1815 {
1816 LAPS = true;
1817 return LAPS;
1818 }
1819 }
1820 return LAPS;
1821 }
1822
1823 public static Object[] UserParser(Object[] AdUsers, DateTime Date1, int DormantTimeSpan, int PassMaxAge, int numOfThreads)
1824 {
1825 LDAPClass.Date1 = Date1;
1826 LDAPClass.DormantTimeSpan = DormantTimeSpan;
1827 LDAPClass.PassMaxAge = PassMaxAge;
1828
1829 Object[] ADRObj = runProcessor(AdUsers, numOfThreads, "Users");
1830 return ADRObj;
1831 }
1832
1833 public static Object[] UserSPNParser(Object[] AdUsers, int numOfThreads)
1834 {
1835 Object[] ADRObj = runProcessor(AdUsers, numOfThreads, "UserSPNs");
1836 return ADRObj;
1837 }
1838
1839 public static Object[] GroupParser(Object[] AdGroups, int numOfThreads)
1840 {
1841 Object[] ADRObj = runProcessor(AdGroups, numOfThreads, "Groups");
1842 return ADRObj;
1843 }
1844
1845 public static Object[] GroupMemberParser(Object[] AdGroups, Object[] AdGroupMembers, String DomainSID, int numOfThreads)
1846 {
1847 LDAPClass.AdGroupDictionary = new Dictionary<String, String>();
1848 runProcessor(AdGroups, numOfThreads, "GroupsDictionary");
1849 LDAPClass.DomainSID = DomainSID;
1850 Object[] ADRObj = runProcessor(AdGroupMembers, numOfThreads, "GroupMembers");
1851 return ADRObj;
1852 }
1853
1854 public static Object[] OUParser(Object[] AdOUs, int numOfThreads)
1855 {
1856 Object[] ADRObj = runProcessor(AdOUs, numOfThreads, "OUs");
1857 return ADRObj;
1858 }
1859
1860 public static Object[] GPOParser(Object[] AdGPOs, int numOfThreads)
1861 {
1862 Object[] ADRObj = runProcessor(AdGPOs, numOfThreads, "GPOs");
1863 return ADRObj;
1864 }
1865
1866 public static Object[] SOMParser(Object[] AdGPOs, Object[] AdSOMs, int numOfThreads)
1867 {
1868 LDAPClass.AdGPODictionary = new Dictionary<String, String>();
1869 runProcessor(AdGPOs, numOfThreads, "GPOsDictionary");
1870 Object[] ADRObj = runProcessor(AdSOMs, numOfThreads, "SOMs");
1871 return ADRObj;
1872 }
1873
1874 public static Object[] PrinterParser(Object[] ADPrinters, int numOfThreads)
1875 {
1876 Object[] ADRObj = runProcessor(ADPrinters, numOfThreads, "Printers");
1877 return ADRObj;
1878 }
1879
1880 public static Object[] ComputerParser(Object[] AdComputers, DateTime Date1, int DormantTimeSpan, int PassMaxAge, int numOfThreads)
1881 {
1882 LDAPClass.Date1 = Date1;
1883 LDAPClass.DormantTimeSpan = DormantTimeSpan;
1884 LDAPClass.PassMaxAge = PassMaxAge;
1885
1886 Object[] ADRObj = runProcessor(AdComputers, numOfThreads, "Computers");
1887 return ADRObj;
1888 }
1889
1890 public static Object[] ComputerSPNParser(Object[] AdComputers, int numOfThreads)
1891 {
1892 Object[] ADRObj = runProcessor(AdComputers, numOfThreads, "ComputerSPNs");
1893 return ADRObj;
1894 }
1895
1896 public static Object[] LAPSParser(Object[] AdComputers, int numOfThreads)
1897 {
1898 Object[] ADRObj = runProcessor(AdComputers, numOfThreads, "LAPS");
1899 return ADRObj;
1900 }
1901
1902 public static Object[] DACLParser(Object[] ADObjects, Object PSGUIDs, int numOfThreads)
1903 {
1904 LDAPClass.AdSIDDictionary = new Dictionary<String, String>();
1905 runProcessor(ADObjects, numOfThreads, "SIDDictionary");
1906 LDAPClass.GUIDs = (Hashtable) PSGUIDs;
1907 Object[] ADRObj = runProcessor(ADObjects, numOfThreads, "DACLs");
1908 return ADRObj;
1909 }
1910
1911 public static Object[] SACLParser(Object[] ADObjects, Object PSGUIDs, int numOfThreads)
1912 {
1913 LDAPClass.GUIDs = (Hashtable) PSGUIDs;
1914 Object[] ADRObj = runProcessor(ADObjects, numOfThreads, "SACLs");
1915 return ADRObj;
1916 }
1917
1918 static Object[] runProcessor(Object[] arrayToProcess, int numOfThreads, string processorType)
1919 {
1920 int totalRecords = arrayToProcess.Length;
1921 IRecordProcessor recordProcessor = recordProcessorFactory(processorType);
1922 IResultsHandler resultsHandler = new SimpleResultsHandler ();
1923 int numberOfRecordsPerThread = totalRecords / numOfThreads;
1924 int remainders = totalRecords % numOfThreads;
1925
1926 Thread[] threads = new Thread[numOfThreads];
1927 for (int i = 0; i < numOfThreads; i++)
1928 {
1929 int numberOfRecordsToProcess = numberOfRecordsPerThread;
1930 if (i == (numOfThreads - 1))
1931 {
1932 //last thread, do the remaining records
1933 numberOfRecordsToProcess += remainders;
1934 }
1935
1936 //split the full array into chunks to be given to different threads
1937 Object[] sliceToProcess = new Object[numberOfRecordsToProcess];
1938 Array.Copy(arrayToProcess, i * numberOfRecordsPerThread, sliceToProcess, 0, numberOfRecordsToProcess);
1939 ProcessorThread processorThread = new ProcessorThread(i, recordProcessor, resultsHandler, sliceToProcess);
1940 threads[i] = new Thread(processorThread.processThreadRecords);
1941 threads[i].Start();
1942 }
1943 foreach (Thread t in threads)
1944 {
1945 t.Join();
1946 }
1947
1948 return resultsHandler.finalise();
1949 }
1950
1951 static IRecordProcessor recordProcessorFactory(String name)
1952 {
1953 switch (name)
1954 {
1955 case "Users":
1956 return new UserRecordProcessor();
1957 case "UserSPNs":
1958 return new UserSPNRecordProcessor();
1959 case "Groups":
1960 return new GroupRecordProcessor();
1961 case "GroupsDictionary":
1962 return new GroupRecordDictionaryProcessor();
1963 case "GroupMembers":
1964 return new GroupMemberRecordProcessor();
1965 case "OUs":
1966 return new OURecordProcessor();
1967 case "GPOs":
1968 return new GPORecordProcessor();
1969 case "GPOsDictionary":
1970 return new GPORecordDictionaryProcessor();
1971 case "SOMs":
1972 return new SOMRecordProcessor();
1973 case "Printers":
1974 return new PrinterRecordProcessor();
1975 case "Computers":
1976 return new ComputerRecordProcessor();
1977 case "ComputerSPNs":
1978 return new ComputerSPNRecordProcessor();
1979 case "LAPS":
1980 return new LAPSRecordProcessor();
1981 case "SIDDictionary":
1982 return new SIDRecordDictionaryProcessor();
1983 case "DACLs":
1984 return new DACLRecordProcessor();
1985 case "SACLs":
1986 return new SACLRecordProcessor();
1987 }
1988 throw new ArgumentException("Invalid processor type " + name);
1989 }
1990
1991 class ProcessorThread
1992 {
1993 readonly int id;
1994 readonly IRecordProcessor recordProcessor;
1995 readonly IResultsHandler resultsHandler;
1996 readonly Object[] objectsToBeProcessed;
1997
1998 public ProcessorThread(int id, IRecordProcessor recordProcessor, IResultsHandler resultsHandler, Object[] objectsToBeProcessed)
1999 {
2000 this.recordProcessor = recordProcessor;
2001 this.id = id;
2002 this.resultsHandler = resultsHandler;
2003 this.objectsToBeProcessed = objectsToBeProcessed;
2004 }
2005
2006 public void processThreadRecords()
2007 {
2008 for (int i = 0; i < objectsToBeProcessed.Length; i++)
2009 {
2010 Object[] result = recordProcessor.processRecord(objectsToBeProcessed[i]);
2011 resultsHandler.processResults(result); //this is a thread safe operation
2012 }
2013 }
2014 }
2015
2016 //The interface and implmentation class used to process a record (this implemmentation just returns a log type string)
2017
2018 interface IRecordProcessor
2019 {
2020 PSObject[] processRecord(Object record);
2021 }
2022
2023 class UserRecordProcessor : IRecordProcessor
2024 {
2025 public PSObject[] processRecord(Object record)
2026 {
2027 try
2028 {
2029 SearchResult AdUser = (SearchResult) record;
2030 bool? Enabled = null;
2031 bool? CannotChangePassword = null;
2032 bool? PasswordNeverExpires = null;
2033 bool? AccountLockedOut = null;
2034 bool? PasswordExpired = null;
2035 bool? ReversiblePasswordEncryption = null;
2036 bool? DelegationPermitted = null;
2037 bool? SmartcardRequired = null;
2038 bool? UseDESKeyOnly = null;
2039 bool? PasswordNotRequired = null;
2040 bool? TrustedforDelegation = null;
2041 bool? TrustedtoAuthforDelegation = null;
2042 bool? DoesNotRequirePreAuth = null;
2043 bool? KerberosRC4 = null;
2044 bool? KerberosAES128 = null;
2045 bool? KerberosAES256 = null;
2046 String DelegationType = null;
2047 String DelegationProtocol = null;
2048 String DelegationServices = null;
2049 bool MustChangePasswordatLogon = false;
2050 int? DaysSinceLastLogon = null;
2051 int? DaysSinceLastPasswordChange = null;
2052 int? AccountExpirationNumofDays = null;
2053 bool PasswordNotChangedafterMaxAge = false;
2054 bool NeverLoggedIn = false;
2055 bool Dormant = false;
2056 DateTime? LastLogonDate = null;
2057 DateTime? PasswordLastSet = null;
2058 DateTime? AccountExpires = null;
2059 byte[] ntSecurityDescriptor = null;
2060 bool DenyEveryone = false;
2061 bool DenySelf = false;
2062 String SIDHistory = "";
2063
2064 // When the user is not allowed to query the UserAccountControl attribute.
2065 if (AdUser.Properties["useraccountcontrol"].Count != 0)
2066 {
2067 var userFlags = (UACFlags) AdUser.Properties["useraccountcontrol"][0];
2068 Enabled = !((userFlags & UACFlags.ACCOUNTDISABLE) == UACFlags.ACCOUNTDISABLE);
2069 PasswordNeverExpires = (userFlags & UACFlags.DONT_EXPIRE_PASSWD) == UACFlags.DONT_EXPIRE_PASSWD;
2070 AccountLockedOut = (userFlags & UACFlags.LOCKOUT) == UACFlags.LOCKOUT;
2071 DelegationPermitted = !((userFlags & UACFlags.NOT_DELEGATED) == UACFlags.NOT_DELEGATED);
2072 SmartcardRequired = (userFlags & UACFlags.SMARTCARD_REQUIRED) == UACFlags.SMARTCARD_REQUIRED;
2073 ReversiblePasswordEncryption = (userFlags & UACFlags.ENCRYPTED_TEXT_PASSWORD_ALLOWED) == UACFlags.ENCRYPTED_TEXT_PASSWORD_ALLOWED;
2074 UseDESKeyOnly = (userFlags & UACFlags.USE_DES_KEY_ONLY) == UACFlags.USE_DES_KEY_ONLY;
2075 PasswordNotRequired = (userFlags & UACFlags.PASSWD_NOTREQD) == UACFlags.PASSWD_NOTREQD;
2076 PasswordExpired = (userFlags & UACFlags.PASSWORD_EXPIRED) == UACFlags.PASSWORD_EXPIRED;
2077 TrustedforDelegation = (userFlags & UACFlags.TRUSTED_FOR_DELEGATION) == UACFlags.TRUSTED_FOR_DELEGATION;
2078 TrustedtoAuthforDelegation = (userFlags & UACFlags.TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION) == UACFlags.TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION;
2079 DoesNotRequirePreAuth = (userFlags & UACFlags.DONT_REQUIRE_PREAUTH) == UACFlags.DONT_REQUIRE_PREAUTH;
2080 }
2081 if (AdUser.Properties["msds-supportedencryptiontypes"].Count != 0)
2082 {
2083 var userKerbEncFlags = (KerbEncFlags) AdUser.Properties["msds-supportedencryptiontypes"][0];
2084 if (userKerbEncFlags != KerbEncFlags.ZERO)
2085 {
2086 KerberosRC4 = (userKerbEncFlags & KerbEncFlags.RC4_HMAC) == KerbEncFlags.RC4_HMAC;
2087 KerberosAES128 = (userKerbEncFlags & KerbEncFlags.AES128_CTS_HMAC_SHA1_96) == KerbEncFlags.AES128_CTS_HMAC_SHA1_96;
2088 KerberosAES256 = (userKerbEncFlags & KerbEncFlags.AES256_CTS_HMAC_SHA1_96) == KerbEncFlags.AES256_CTS_HMAC_SHA1_96;
2089 }
2090 }
2091 // When the user is not allowed to query the ntsecuritydescriptor attribute.
2092 if (AdUser.Properties["ntsecuritydescriptor"].Count != 0)
2093 {
2094 ntSecurityDescriptor = (byte[]) AdUser.Properties["ntsecuritydescriptor"][0];
2095 }
2096 else
2097 {
2098 DirectoryEntry AdUserEntry = ((SearchResult)record).GetDirectoryEntry();
2099 ntSecurityDescriptor = (byte[]) AdUserEntry.ObjectSecurity.GetSecurityDescriptorBinaryForm();
2100 }
2101 if (ntSecurityDescriptor != null)
2102 {
2103 DirectoryObjectSecurity DirObjSec = new ActiveDirectorySecurity();
2104 DirObjSec.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor);
2105 AuthorizationRuleCollection AccessRules = (AuthorizationRuleCollection) DirObjSec.GetAccessRules(true,false,typeof(System.Security.Principal.NTAccount));
2106 foreach (ActiveDirectoryAccessRule Rule in AccessRules)
2107 {
2108 if ((Convert.ToString(Rule.ObjectType)).Equals("ab721a53-1e2f-11d0-9819-00aa0040529b"))
2109 {
2110 if (Rule.AccessControlType.ToString() == "Deny")
2111 {
2112 String ObjectName = Convert.ToString(Rule.IdentityReference);
2113 if (ObjectName == "Everyone")
2114 {
2115 DenyEveryone = true;
2116 }
2117 if (ObjectName == "NT AUTHORITY\\SELF")
2118 {
2119 DenySelf = true;
2120 }
2121 }
2122 }
2123 }
2124 if (DenyEveryone && DenySelf)
2125 {
2126 CannotChangePassword = true;
2127 }
2128 else
2129 {
2130 CannotChangePassword = false;
2131 }
2132 }
2133 if (AdUser.Properties["lastlogontimestamp"].Count != 0)
2134 {
2135 LastLogonDate = DateTime.FromFileTime((long)(AdUser.Properties["lastlogontimestamp"][0]));
2136 DaysSinceLastLogon = Math.Abs((Date1 - (DateTime)LastLogonDate).Days);
2137 if (DaysSinceLastLogon > DormantTimeSpan)
2138 {
2139 Dormant = true;
2140 }
2141 }
2142 else
2143 {
2144 NeverLoggedIn = true;
2145 }
2146 if (AdUser.Properties["pwdLastSet"].Count != 0)
2147 {
2148 if (Convert.ToString(AdUser.Properties["pwdlastset"][0]) == "0")
2149 {
2150 if ((bool) PasswordNeverExpires == false)
2151 {
2152 MustChangePasswordatLogon = true;
2153 }
2154 }
2155 else
2156 {
2157 PasswordLastSet = DateTime.FromFileTime((long)(AdUser.Properties["pwdlastset"][0]));
2158 DaysSinceLastPasswordChange = Math.Abs((Date1 - (DateTime)PasswordLastSet).Days);
2159 if (DaysSinceLastPasswordChange > PassMaxAge)
2160 {
2161 PasswordNotChangedafterMaxAge = true;
2162 }
2163 }
2164 }
2165 if ((Int64) AdUser.Properties["accountExpires"][0] != (Int64) 9223372036854775807)
2166 {
2167 if ((Int64) AdUser.Properties["accountExpires"][0] != (Int64) 0)
2168 {
2169 try
2170 {
2171 //https://msdn.microsoft.com/en-us/library/ms675098(v=vs.85).aspx
2172 AccountExpires = DateTime.FromFileTime((long)(AdUser.Properties["accountExpires"][0]));
2173 AccountExpirationNumofDays = ((int)((DateTime)AccountExpires - Date1).Days);
2174
2175 }
2176 catch //(Exception e)
2177 {
2178 // Console.WriteLine("{0} Exception caught.", e);
2179 }
2180 }
2181 }
2182 if ((bool) TrustedforDelegation)
2183 {
2184 DelegationType = "Unconstrained";
2185 DelegationServices = "Any";
2186 }
2187 if (AdUser.Properties["msDS-AllowedToDelegateTo"].Count >= 1)
2188 {
2189 DelegationType = "Constrained";
2190 for (int i = 0; i < AdUser.Properties["msDS-AllowedToDelegateTo"].Count; i++)
2191 {
2192 var delegateto = AdUser.Properties["msDS-AllowedToDelegateTo"][i];
2193 DelegationServices = DelegationServices + "," + Convert.ToString(delegateto);
2194 }
2195 DelegationServices = DelegationServices.TrimStart(',');
2196 }
2197 if ((bool) TrustedtoAuthforDelegation)
2198 {
2199 DelegationProtocol = "Any";
2200 }
2201 else if (DelegationType != null)
2202 {
2203 DelegationProtocol = "Kerberos";
2204 }
2205 if (AdUser.Properties["sidhistory"].Count >= 1)
2206 {
2207 string sids = "";
2208 for (int i = 0; i < AdUser.Properties["sidhistory"].Count; i++)
2209 {
2210 var history = AdUser.Properties["sidhistory"][i];
2211 sids = sids + "," + Convert.ToString(new SecurityIdentifier((byte[])history, 0));
2212 }
2213 SIDHistory = sids.TrimStart(',');
2214 }
2215
2216 PSObject UserObj = new PSObject();
2217 UserObj.Members.Add(new PSNoteProperty("UserName", (AdUser.Properties["samaccountname"].Count != 0 ? AdUser.Properties["samaccountname"][0] : "")));
2218 UserObj.Members.Add(new PSNoteProperty("Name", (AdUser.Properties["name"].Count != 0 ? CleanString(AdUser.Properties["name"][0]) : "")));
2219 UserObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
2220 UserObj.Members.Add(new PSNoteProperty("Must Change Password at Logon", MustChangePasswordatLogon));
2221 UserObj.Members.Add(new PSNoteProperty("Cannot Change Password", CannotChangePassword));
2222 UserObj.Members.Add(new PSNoteProperty("Password Never Expires", PasswordNeverExpires));
2223 UserObj.Members.Add(new PSNoteProperty("Reversible Password Encryption", ReversiblePasswordEncryption));
2224 UserObj.Members.Add(new PSNoteProperty("Smartcard Logon Required", SmartcardRequired));
2225 UserObj.Members.Add(new PSNoteProperty("Delegation Permitted", DelegationPermitted));
2226 UserObj.Members.Add(new PSNoteProperty("Kerberos DES Only", UseDESKeyOnly));
2227 UserObj.Members.Add(new PSNoteProperty("Kerberos RC4", KerberosRC4));
2228 UserObj.Members.Add(new PSNoteProperty("Kerberos AES-128bit", KerberosAES128));
2229 UserObj.Members.Add(new PSNoteProperty("Kerberos AES-256bit", KerberosAES256));
2230 UserObj.Members.Add(new PSNoteProperty("Does Not Require Pre Auth", DoesNotRequirePreAuth));
2231 UserObj.Members.Add(new PSNoteProperty("Never Logged in", NeverLoggedIn));
2232 UserObj.Members.Add(new PSNoteProperty("Logon Age (days)", DaysSinceLastLogon));
2233 UserObj.Members.Add(new PSNoteProperty("Password Age (days)", DaysSinceLastPasswordChange));
2234 UserObj.Members.Add(new PSNoteProperty("Dormant (> " + DormantTimeSpan + " days)", Dormant));
2235 UserObj.Members.Add(new PSNoteProperty("Password Age (> " + PassMaxAge + " days)", PasswordNotChangedafterMaxAge));
2236 UserObj.Members.Add(new PSNoteProperty("Account Locked Out", AccountLockedOut));
2237 UserObj.Members.Add(new PSNoteProperty("Password Expired", PasswordExpired));
2238 UserObj.Members.Add(new PSNoteProperty("Password Not Required", PasswordNotRequired));
2239 UserObj.Members.Add(new PSNoteProperty("Delegation Type", DelegationType));
2240 UserObj.Members.Add(new PSNoteProperty("Delegation Protocol", DelegationProtocol));
2241 UserObj.Members.Add(new PSNoteProperty("Delegation Services", DelegationServices));
2242 UserObj.Members.Add(new PSNoteProperty("Logon Workstations", (AdUser.Properties["userworkstations"].Count != 0 ? AdUser.Properties["userworkstations"][0] : "")));
2243 UserObj.Members.Add(new PSNoteProperty("AdminCount", (AdUser.Properties["admincount"].Count != 0 ? AdUser.Properties["admincount"][0] : "")));
2244 UserObj.Members.Add(new PSNoteProperty("Primary GroupID", (AdUser.Properties["primarygroupid"].Count != 0 ? AdUser.Properties["primarygroupid"][0] : "")));
2245 UserObj.Members.Add(new PSNoteProperty("SID", Convert.ToString(new SecurityIdentifier((byte[])AdUser.Properties["objectSID"][0], 0))));
2246 UserObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
2247 UserObj.Members.Add(new PSNoteProperty("Description", (AdUser.Properties["Description"].Count != 0 ? CleanString(AdUser.Properties["Description"][0]) : "")));
2248 UserObj.Members.Add(new PSNoteProperty("Title", (AdUser.Properties["Title"].Count != 0 ? CleanString(AdUser.Properties["Title"][0]) : "")));
2249 UserObj.Members.Add(new PSNoteProperty("Department", (AdUser.Properties["Department"].Count != 0 ? CleanString(AdUser.Properties["Department"][0]) : "")));
2250 UserObj.Members.Add(new PSNoteProperty("Company", (AdUser.Properties["Company"].Count != 0 ? CleanString(AdUser.Properties["Company"][0]) : "")));
2251 UserObj.Members.Add(new PSNoteProperty("Manager", (AdUser.Properties["Manager"].Count != 0 ? CleanString(AdUser.Properties["Manager"][0]) : "")));
2252 UserObj.Members.Add(new PSNoteProperty("Info", (AdUser.Properties["info"].Count != 0 ? CleanString(AdUser.Properties["info"][0]) : "")));
2253 UserObj.Members.Add(new PSNoteProperty("Last Logon Date", LastLogonDate));
2254 UserObj.Members.Add(new PSNoteProperty("Password LastSet", PasswordLastSet));
2255 UserObj.Members.Add(new PSNoteProperty("Account Expiration Date", AccountExpires));
2256 UserObj.Members.Add(new PSNoteProperty("Account Expiration (days)", AccountExpirationNumofDays));
2257 UserObj.Members.Add(new PSNoteProperty("Mobile", (AdUser.Properties["mobile"].Count != 0 ? CleanString(AdUser.Properties["mobile"][0]) : "")));
2258 UserObj.Members.Add(new PSNoteProperty("Email", (AdUser.Properties["mail"].Count != 0 ? CleanString(AdUser.Properties["mail"][0]) : "")));
2259 UserObj.Members.Add(new PSNoteProperty("HomeDirectory", (AdUser.Properties["homedirectory"].Count != 0 ? AdUser.Properties["homedirectory"][0] : "")));
2260 UserObj.Members.Add(new PSNoteProperty("ProfilePath", (AdUser.Properties["profilepath"].Count != 0 ? AdUser.Properties["profilepath"][0] : "")));
2261 UserObj.Members.Add(new PSNoteProperty("ScriptPath", (AdUser.Properties["scriptpath"].Count != 0 ? AdUser.Properties["scriptpath"][0] : "")));
2262 UserObj.Members.Add(new PSNoteProperty("UserAccountControl", (AdUser.Properties["useraccountcontrol"].Count != 0 ? AdUser.Properties["useraccountcontrol"][0] : "")));
2263 UserObj.Members.Add(new PSNoteProperty("First Name", (AdUser.Properties["givenName"].Count != 0 ? CleanString(AdUser.Properties["givenName"][0]) : "")));
2264 UserObj.Members.Add(new PSNoteProperty("Middle Name", (AdUser.Properties["middleName"].Count != 0 ? CleanString(AdUser.Properties["middleName"][0]) : "")));
2265 UserObj.Members.Add(new PSNoteProperty("Last Name", (AdUser.Properties["sn"].Count != 0 ? CleanString(AdUser.Properties["sn"][0]) : "")));
2266 UserObj.Members.Add(new PSNoteProperty("Country", (AdUser.Properties["c"].Count != 0 ? CleanString(AdUser.Properties["c"][0]) : "")));
2267 UserObj.Members.Add(new PSNoteProperty("whenCreated", (AdUser.Properties["whencreated"].Count != 0 ? AdUser.Properties["whencreated"][0] : "")));
2268 UserObj.Members.Add(new PSNoteProperty("whenChanged", (AdUser.Properties["whenchanged"].Count != 0 ? AdUser.Properties["whenchanged"][0] : "")));
2269 UserObj.Members.Add(new PSNoteProperty("DistinguishedName", (AdUser.Properties["distinguishedname"].Count != 0 ? CleanString(AdUser.Properties["distinguishedname"][0]) : "")));
2270 UserObj.Members.Add(new PSNoteProperty("CanonicalName", (AdUser.Properties["canonicalname"].Count != 0 ? AdUser.Properties["canonicalname"][0] : "")));
2271 return new PSObject[] { UserObj };
2272 }
2273 catch (Exception e)
2274 {
2275 Console.WriteLine("{0} Exception caught.", e);
2276 return new PSObject[] { };
2277 }
2278 }
2279 }
2280
2281 class UserSPNRecordProcessor : IRecordProcessor
2282 {
2283 public PSObject[] processRecord(Object record)
2284 {
2285 try
2286 {
2287 SearchResult AdUser = (SearchResult) record;
2288 List<PSObject> SPNList = new List<PSObject>();
2289 bool? Enabled = null;
2290 String Memberof = null;
2291 DateTime? PasswordLastSet = null;
2292
2293 if (AdUser.Properties["pwdlastset"].Count != 0)
2294 {
2295 if (Convert.ToString(AdUser.Properties["pwdlastset"][0]) != "0")
2296 {
2297 PasswordLastSet = DateTime.FromFileTime((long)(AdUser.Properties["pwdLastSet"][0]));
2298 }
2299 }
2300 // When the user is not allowed to query the UserAccountControl attribute.
2301 if (AdUser.Properties["useraccountcontrol"].Count != 0)
2302 {
2303 var userFlags = (UACFlags) AdUser.Properties["useraccountcontrol"][0];
2304 Enabled = !((userFlags & UACFlags.ACCOUNTDISABLE) == UACFlags.ACCOUNTDISABLE);
2305 }
2306 String Description = (AdUser.Properties["Description"].Count != 0 ? CleanString(AdUser.Properties["Description"][0]) : "");
2307 String PrimaryGroupID = (AdUser.Properties["primarygroupid"].Count != 0 ? Convert.ToString(AdUser.Properties["primarygroupid"][0]) : "");
2308 if (AdUser.Properties["memberof"].Count != 0)
2309 {
2310 foreach (String Member in AdUser.Properties["memberof"])
2311 {
2312 Memberof = Memberof + "," + ((Convert.ToString(Member)).Split(',')[0]).Split('=')[1];
2313 }
2314 Memberof = Memberof.TrimStart(',');
2315 }
2316 foreach (String SPN in AdUser.Properties["serviceprincipalname"])
2317 {
2318 String[] SPNArray = SPN.Split('/');
2319 PSObject UserSPNObj = new PSObject();
2320 UserSPNObj.Members.Add(new PSNoteProperty("Name", AdUser.Properties["name"][0]));
2321 UserSPNObj.Members.Add(new PSNoteProperty("Username", AdUser.Properties["samaccountname"][0]));
2322 UserSPNObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
2323 UserSPNObj.Members.Add(new PSNoteProperty("Service", SPNArray[0]));
2324 UserSPNObj.Members.Add(new PSNoteProperty("Host", SPNArray[1]));
2325 UserSPNObj.Members.Add(new PSNoteProperty("Password Last Set", PasswordLastSet));
2326 UserSPNObj.Members.Add(new PSNoteProperty("Description", Description));
2327 UserSPNObj.Members.Add(new PSNoteProperty("Primary GroupID", PrimaryGroupID));
2328 UserSPNObj.Members.Add(new PSNoteProperty("Memberof", Memberof));
2329 SPNList.Add( UserSPNObj );
2330 }
2331 return SPNList.ToArray();
2332 }
2333 catch (Exception e)
2334 {
2335 Console.WriteLine("{0} Exception caught.", e);
2336 return new PSObject[] { };
2337 }
2338 }
2339 }
2340
2341 class GroupRecordProcessor : IRecordProcessor
2342 {
2343 public PSObject[] processRecord(Object record)
2344 {
2345 try
2346 {
2347 SearchResult AdGroup = (SearchResult) record;
2348 String ManagedByValue = AdGroup.Properties["managedby"].Count != 0 ? Convert.ToString(AdGroup.Properties["managedby"][0]) : "";
2349 String ManagedBy = "";
2350 String GroupCategory = null;
2351 String GroupScope = null;
2352 String SIDHistory = "";
2353
2354 if (AdGroup.Properties["managedBy"].Count != 0)
2355 {
2356 ManagedBy = (ManagedByValue.Split(',')[0]).Split('=')[1];
2357 }
2358
2359 if (AdGroup.Properties["grouptype"].Count != 0)
2360 {
2361 var groupTypeFlags = (GroupTypeFlags) AdGroup.Properties["grouptype"][0];
2362 GroupCategory = (groupTypeFlags & GroupTypeFlags.SECURITY_ENABLED) == GroupTypeFlags.SECURITY_ENABLED ? "Security" : "Distribution";
2363
2364 if ((groupTypeFlags & GroupTypeFlags.UNIVERSAL_GROUP) == GroupTypeFlags.UNIVERSAL_GROUP)
2365 {
2366 GroupScope = "Universal";
2367 }
2368 else if ((groupTypeFlags & GroupTypeFlags.GLOBAL_GROUP) == GroupTypeFlags.GLOBAL_GROUP)
2369 {
2370 GroupScope = "Global";
2371 }
2372 else if ((groupTypeFlags & GroupTypeFlags.DOMAIN_LOCAL_GROUP) == GroupTypeFlags.DOMAIN_LOCAL_GROUP)
2373 {
2374 GroupScope = "DomainLocal";
2375 }
2376 }
2377 if (AdGroup.Properties["sidhistory"].Count >= 1)
2378 {
2379 string sids = "";
2380 for (int i = 0; i < AdGroup.Properties["sidhistory"].Count; i++)
2381 {
2382 var history = AdGroup.Properties["sidhistory"][i];
2383 sids = sids + "," + Convert.ToString(new SecurityIdentifier((byte[])history, 0));
2384 }
2385 SIDHistory = sids.TrimStart(',');
2386 }
2387
2388 PSObject GroupObj = new PSObject();
2389 GroupObj.Members.Add(new PSNoteProperty("Name", AdGroup.Properties["samaccountname"][0]));
2390 GroupObj.Members.Add(new PSNoteProperty("AdminCount", (AdGroup.Properties["admincount"].Count != 0 ? AdGroup.Properties["admincount"][0] : "")));
2391 GroupObj.Members.Add(new PSNoteProperty("GroupCategory", GroupCategory));
2392 GroupObj.Members.Add(new PSNoteProperty("GroupScope", GroupScope));
2393 GroupObj.Members.Add(new PSNoteProperty("ManagedBy", ManagedBy));
2394 GroupObj.Members.Add(new PSNoteProperty("SID", Convert.ToString(new SecurityIdentifier((byte[])AdGroup.Properties["objectSID"][0], 0))));
2395 GroupObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
2396 GroupObj.Members.Add(new PSNoteProperty("Description", (AdGroup.Properties["Description"].Count != 0 ? CleanString(AdGroup.Properties["Description"][0]) : "")));
2397 GroupObj.Members.Add(new PSNoteProperty("whenCreated", AdGroup.Properties["whencreated"][0]));
2398 GroupObj.Members.Add(new PSNoteProperty("whenChanged", AdGroup.Properties["whenchanged"][0]));
2399 GroupObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdGroup.Properties["distinguishedname"][0])));
2400 GroupObj.Members.Add(new PSNoteProperty("CanonicalName", AdGroup.Properties["canonicalname"][0]));
2401 return new PSObject[] { GroupObj };
2402 }
2403 catch (Exception e)
2404 {
2405 Console.WriteLine("{0} Exception caught.", e);
2406 return new PSObject[] { };
2407 }
2408 }
2409 }
2410
2411 class GroupRecordDictionaryProcessor : IRecordProcessor
2412 {
2413 public PSObject[] processRecord(Object record)
2414 {
2415 try
2416 {
2417 SearchResult AdGroup = (SearchResult) record;
2418 LDAPClass.AdGroupDictionary.Add((Convert.ToString(new SecurityIdentifier((byte[])AdGroup.Properties["objectSID"][0], 0))),(Convert.ToString(AdGroup.Properties["samaccountname"][0])));
2419 return new PSObject[] { };
2420 }
2421 catch (Exception e)
2422 {
2423 Console.WriteLine("{0} Exception caught.", e);
2424 return new PSObject[] { };
2425 }
2426 }
2427 }
2428
2429 class GroupMemberRecordProcessor : IRecordProcessor
2430 {
2431 public PSObject[] processRecord(Object record)
2432 {
2433 try
2434 {
2435 // https://github.com/BloodHoundAD/BloodHound/blob/master/PowerShell/BloodHound.ps1
2436 SearchResult AdGroup = (SearchResult) record;
2437 List<PSObject> GroupsList = new List<PSObject>();
2438 string SamAccountType = AdGroup.Properties["samaccounttype"].Count != 0 ? Convert.ToString(AdGroup.Properties["samaccounttype"][0]) : "";
2439 string AccountType = "";
2440 string GroupName = "";
2441 string MemberUserName = "-";
2442 string MemberName = "";
2443
2444 if (Groups.Contains(SamAccountType))
2445 {
2446 AccountType = "group";
2447 MemberName = ((Convert.ToString(AdGroup.Properties["DistinguishedName"][0])).Split(',')[0]).Split('=')[1];
2448 foreach (String GroupMember in AdGroup.Properties["memberof"])
2449 {
2450 GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
2451 PSObject GroupMemberObj = new PSObject();
2452 GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
2453 GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
2454 GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
2455 GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
2456 GroupsList.Add( GroupMemberObj );
2457 }
2458 }
2459 if (Users.Contains(SamAccountType))
2460 {
2461 AccountType = "user";
2462 MemberName = ((Convert.ToString(AdGroup.Properties["DistinguishedName"][0])).Split(',')[0]).Split('=')[1];
2463 MemberUserName = Convert.ToString(AdGroup.Properties["sAMAccountName"][0]);
2464 String PrimaryGroupID = Convert.ToString(AdGroup.Properties["primaryGroupID"][0]);
2465 try
2466 {
2467 GroupName = LDAPClass.AdGroupDictionary[LDAPClass.DomainSID + "-" + PrimaryGroupID];
2468 }
2469 catch //(Exception e)
2470 {
2471 //Console.WriteLine("{0} Exception caught.", e);
2472 GroupName = PrimaryGroupID;
2473 }
2474
2475 {
2476 PSObject GroupMemberObj = new PSObject();
2477 GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
2478 GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
2479 GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
2480 GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
2481 GroupsList.Add( GroupMemberObj );
2482 }
2483
2484 foreach (String GroupMember in AdGroup.Properties["memberof"])
2485 {
2486 GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
2487 PSObject GroupMemberObj = new PSObject();
2488 GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
2489 GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
2490 GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
2491 GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
2492 GroupsList.Add( GroupMemberObj );
2493 }
2494 }
2495 if (Computers.Contains(SamAccountType))
2496 {
2497 AccountType = "computer";
2498 MemberName = ((Convert.ToString(AdGroup.Properties["DistinguishedName"][0])).Split(',')[0]).Split('=')[1];
2499 MemberUserName = Convert.ToString(AdGroup.Properties["sAMAccountName"][0]);
2500 String PrimaryGroupID = Convert.ToString(AdGroup.Properties["primaryGroupID"][0]);
2501 try
2502 {
2503 GroupName = LDAPClass.AdGroupDictionary[LDAPClass.DomainSID + "-" + PrimaryGroupID];
2504 }
2505 catch //(Exception e)
2506 {
2507 //Console.WriteLine("{0} Exception caught.", e);
2508 GroupName = PrimaryGroupID;
2509 }
2510
2511 {
2512 PSObject GroupMemberObj = new PSObject();
2513 GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
2514 GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
2515 GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
2516 GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
2517 GroupsList.Add( GroupMemberObj );
2518 }
2519
2520 foreach (String GroupMember in AdGroup.Properties["memberof"])
2521 {
2522 GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
2523 PSObject GroupMemberObj = new PSObject();
2524 GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
2525 GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
2526 GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
2527 GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
2528 GroupsList.Add( GroupMemberObj );
2529 }
2530 }
2531 if (TrustAccounts.Contains(SamAccountType))
2532 {
2533 // TO DO
2534 }
2535 return GroupsList.ToArray();
2536 }
2537 catch (Exception e)
2538 {
2539 Console.WriteLine("{0} Exception caught.", e);
2540 return new PSObject[] { };
2541 }
2542 }
2543 }
2544
2545 class OURecordProcessor : IRecordProcessor
2546 {
2547 public PSObject[] processRecord(Object record)
2548 {
2549 try
2550 {
2551 SearchResult AdOU = (SearchResult) record;
2552
2553 PSObject OUObj = new PSObject();
2554 OUObj.Members.Add(new PSNoteProperty("Name", AdOU.Properties["name"][0]));
2555 OUObj.Members.Add(new PSNoteProperty("Depth", ((Convert.ToString(AdOU.Properties["distinguishedname"][0]).Split(new string[] { "OU=" }, StringSplitOptions.None)).Length -1)));
2556 OUObj.Members.Add(new PSNoteProperty("Description", (AdOU.Properties["description"].Count != 0 ? AdOU.Properties["description"][0] : "")));
2557 OUObj.Members.Add(new PSNoteProperty("whenCreated", AdOU.Properties["whencreated"][0]));
2558 OUObj.Members.Add(new PSNoteProperty("whenChanged", AdOU.Properties["whenchanged"][0]));
2559 OUObj.Members.Add(new PSNoteProperty("DistinguishedName", AdOU.Properties["distinguishedname"][0]));
2560 return new PSObject[] { OUObj };
2561 }
2562 catch (Exception e)
2563 {
2564 Console.WriteLine("{0} Exception caught.", e);
2565 return new PSObject[] { };
2566 }
2567 }
2568 }
2569
2570 class GPORecordProcessor : IRecordProcessor
2571 {
2572 public PSObject[] processRecord(Object record)
2573 {
2574 try
2575 {
2576 SearchResult AdGPO = (SearchResult) record;
2577
2578 PSObject GPOObj = new PSObject();
2579 GPOObj.Members.Add(new PSNoteProperty("DisplayName", CleanString(AdGPO.Properties["displayname"][0])));
2580 GPOObj.Members.Add(new PSNoteProperty("GUID", CleanString(AdGPO.Properties["name"][0])));
2581 GPOObj.Members.Add(new PSNoteProperty("whenCreated", AdGPO.Properties["whenCreated"][0]));
2582 GPOObj.Members.Add(new PSNoteProperty("whenChanged", AdGPO.Properties["whenChanged"][0]));
2583 GPOObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdGPO.Properties["distinguishedname"][0])));
2584 GPOObj.Members.Add(new PSNoteProperty("FilePath", AdGPO.Properties["gpcfilesyspath"][0]));
2585 return new PSObject[] { GPOObj };
2586 }
2587 catch (Exception e)
2588 {
2589 Console.WriteLine("{0} Exception caught.", e);
2590 return new PSObject[] { };
2591 }
2592 }
2593 }
2594
2595 class GPORecordDictionaryProcessor : IRecordProcessor
2596 {
2597 public PSObject[] processRecord(Object record)
2598 {
2599 try
2600 {
2601 SearchResult AdGPO = (SearchResult) record;
2602 LDAPClass.AdGPODictionary.Add((Convert.ToString(AdGPO.Properties["distinguishedname"][0]).ToUpper()), (Convert.ToString(AdGPO.Properties["displayname"][0])));
2603 return new PSObject[] { };
2604 }
2605 catch (Exception e)
2606 {
2607 Console.WriteLine("{0} Exception caught.", e);
2608 return new PSObject[] { };
2609 }
2610 }
2611 }
2612
2613 class SOMRecordProcessor : IRecordProcessor
2614 {
2615 public PSObject[] processRecord(Object record)
2616 {
2617 try
2618 {
2619 SearchResult AdSOM = (SearchResult) record;
2620
2621 List<PSObject> SOMsList = new List<PSObject>();
2622 int Depth = 0;
2623 bool BlockInheritance = false;
2624 bool? LinkEnabled = null;
2625 bool? Enforced = null;
2626 String gPLink = (AdSOM.Properties["gPLink"].Count != 0 ? Convert.ToString(AdSOM.Properties["gPLink"][0]) : "");
2627 String GPOName = null;
2628
2629 Depth = ((Convert.ToString(AdSOM.Properties["distinguishedname"][0]).Split(new string[] { "OU=" }, StringSplitOptions.None)).Length -1);
2630 if (AdSOM.Properties["gPOptions"].Count != 0)
2631 {
2632 if ((int) AdSOM.Properties["gPOptions"][0] == 1)
2633 {
2634 BlockInheritance = true;
2635 }
2636 }
2637 var GPLinks = gPLink.Split(']', '[').Where(x => x.StartsWith("LDAP"));
2638 int Order = (GPLinks.ToArray()).Length;
2639 if (Order == 0)
2640 {
2641 PSObject SOMObj = new PSObject();
2642 SOMObj.Members.Add(new PSNoteProperty("Name", AdSOM.Properties["name"][0]));
2643 SOMObj.Members.Add(new PSNoteProperty("Depth", Depth));
2644 SOMObj.Members.Add(new PSNoteProperty("DistinguishedName", AdSOM.Properties["distinguishedname"][0]));
2645 SOMObj.Members.Add(new PSNoteProperty("Link Order", null));
2646 SOMObj.Members.Add(new PSNoteProperty("GPO", GPOName));
2647 SOMObj.Members.Add(new PSNoteProperty("Enforced", Enforced));
2648 SOMObj.Members.Add(new PSNoteProperty("Link Enabled", LinkEnabled));
2649 SOMObj.Members.Add(new PSNoteProperty("BlockInheritance", BlockInheritance));
2650 SOMObj.Members.Add(new PSNoteProperty("gPLink", gPLink));
2651 SOMObj.Members.Add(new PSNoteProperty("gPOptions", (AdSOM.Properties["gpoptions"].Count != 0 ? AdSOM.Properties["gpoptions"][0] : "")));
2652 SOMsList.Add( SOMObj );
2653 }
2654 foreach (String link in GPLinks)
2655 {
2656 String[] linksplit = link.Split('/', ';');
2657 if (!Convert.ToBoolean((Convert.ToInt32(linksplit[3]) & 1)))
2658 {
2659 LinkEnabled = true;
2660 }
2661 else
2662 {
2663 LinkEnabled = false;
2664 }
2665 if (Convert.ToBoolean((Convert.ToInt32(linksplit[3]) & 2)))
2666 {
2667 Enforced = true;
2668 }
2669 else
2670 {
2671 Enforced = false;
2672 }
2673 GPOName = LDAPClass.AdGPODictionary.ContainsKey(linksplit[2].ToUpper()) ? LDAPClass.AdGPODictionary[linksplit[2].ToUpper()] : linksplit[2].Split('=',',')[1];
2674 PSObject SOMObj = new PSObject();
2675 SOMObj.Members.Add(new PSNoteProperty("Name", AdSOM.Properties["name"][0]));
2676 SOMObj.Members.Add(new PSNoteProperty("Depth", Depth));
2677 SOMObj.Members.Add(new PSNoteProperty("DistinguishedName", AdSOM.Properties["distinguishedname"][0]));
2678 SOMObj.Members.Add(new PSNoteProperty("Link Order", Order));
2679 SOMObj.Members.Add(new PSNoteProperty("GPO", GPOName));
2680 SOMObj.Members.Add(new PSNoteProperty("Enforced", Enforced));
2681 SOMObj.Members.Add(new PSNoteProperty("Link Enabled", LinkEnabled));
2682 SOMObj.Members.Add(new PSNoteProperty("BlockInheritance", BlockInheritance));
2683 SOMObj.Members.Add(new PSNoteProperty("gPLink", gPLink));
2684 SOMObj.Members.Add(new PSNoteProperty("gPOptions", (AdSOM.Properties["gpoptions"].Count != 0 ? AdSOM.Properties["gpoptions"][0] : "")));
2685 SOMsList.Add( SOMObj );
2686 Order--;
2687 }
2688 return SOMsList.ToArray();
2689 }
2690 catch (Exception e)
2691 {
2692 Console.WriteLine("{0} Exception caught.", e);
2693 return new PSObject[] { };
2694 }
2695 }
2696 }
2697
2698 class PrinterRecordProcessor : IRecordProcessor
2699 {
2700 public PSObject[] processRecord(Object record)
2701 {
2702 try
2703 {
2704 SearchResult AdPrinter = (SearchResult) record;
2705
2706 PSObject PrinterObj = new PSObject();
2707 PrinterObj.Members.Add(new PSNoteProperty("Name", AdPrinter.Properties["Name"][0]));
2708 PrinterObj.Members.Add(new PSNoteProperty("ServerName", AdPrinter.Properties["serverName"][0]));
2709 PrinterObj.Members.Add(new PSNoteProperty("ShareName", AdPrinter.Properties["printShareName"][0]));
2710 PrinterObj.Members.Add(new PSNoteProperty("DriverName", AdPrinter.Properties["driverName"][0]));
2711 PrinterObj.Members.Add(new PSNoteProperty("DriverVersion", AdPrinter.Properties["driverVersion"][0]));
2712 PrinterObj.Members.Add(new PSNoteProperty("PortName", AdPrinter.Properties["portName"][0]));
2713 PrinterObj.Members.Add(new PSNoteProperty("URL", AdPrinter.Properties["url"][0]));
2714 PrinterObj.Members.Add(new PSNoteProperty("whenCreated", AdPrinter.Properties["whenCreated"][0]));
2715 PrinterObj.Members.Add(new PSNoteProperty("whenChanged", AdPrinter.Properties["whenChanged"][0]));
2716 return new PSObject[] { PrinterObj };
2717 }
2718 catch (Exception e)
2719 {
2720 Console.WriteLine("{0} Exception caught.", e);
2721 return new PSObject[] { };
2722 }
2723 }
2724 }
2725
2726 class ComputerRecordProcessor : IRecordProcessor
2727 {
2728 public PSObject[] processRecord(Object record)
2729 {
2730 try
2731 {
2732 SearchResult AdComputer = (SearchResult) record;
2733 bool Dormant = false;
2734 bool? Enabled = null;
2735 bool PasswordNotChangedafterMaxAge = false;
2736 bool? TrustedforDelegation = null;
2737 bool? TrustedtoAuthforDelegation = null;
2738 String DelegationType = null;
2739 String DelegationProtocol = null;
2740 String DelegationServices = null;
2741 String StrIPAddress = null;
2742 int? DaysSinceLastLogon = null;
2743 int? DaysSinceLastPasswordChange = null;
2744 DateTime? LastLogonDate = null;
2745 DateTime? PasswordLastSet = null;
2746
2747 if (AdComputer.Properties["dnshostname"].Count != 0)
2748 {
2749 try
2750 {
2751 StrIPAddress = Convert.ToString(Dns.GetHostEntry(Convert.ToString(AdComputer.Properties["dnshostname"][0])).AddressList[0]);
2752 }
2753 catch
2754 {
2755 StrIPAddress = null;
2756 }
2757 }
2758 // When the user is not allowed to query the UserAccountControl attribute.
2759 if (AdComputer.Properties["useraccountcontrol"].Count != 0)
2760 {
2761 var userFlags = (UACFlags) AdComputer.Properties["useraccountcontrol"][0];
2762 Enabled = !((userFlags & UACFlags.ACCOUNTDISABLE) == UACFlags.ACCOUNTDISABLE);
2763 TrustedforDelegation = (userFlags & UACFlags.TRUSTED_FOR_DELEGATION) == UACFlags.TRUSTED_FOR_DELEGATION;
2764 TrustedtoAuthforDelegation = (userFlags & UACFlags.TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION) == UACFlags.TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION;
2765 }
2766 if (AdComputer.Properties["lastlogontimestamp"].Count != 0)
2767 {
2768 LastLogonDate = DateTime.FromFileTime((long)(AdComputer.Properties["lastlogontimestamp"][0]));
2769 DaysSinceLastLogon = Math.Abs((Date1 - (DateTime)LastLogonDate).Days);
2770 if (DaysSinceLastLogon > DormantTimeSpan)
2771 {
2772 Dormant = true;
2773 }
2774 }
2775 if (AdComputer.Properties["pwdlastset"].Count != 0)
2776 {
2777 PasswordLastSet = DateTime.FromFileTime((long)(AdComputer.Properties["pwdlastset"][0]));
2778 DaysSinceLastPasswordChange = Math.Abs((Date1 - (DateTime)PasswordLastSet).Days);
2779 if (DaysSinceLastPasswordChange > PassMaxAge)
2780 {
2781 PasswordNotChangedafterMaxAge = true;
2782 }
2783 }
2784 if ( ((bool) TrustedforDelegation) && ((int) AdComputer.Properties["primarygroupid"][0] == 515) )
2785 {
2786 DelegationType = "Unconstrained";
2787 DelegationServices = "Any";
2788 }
2789 if (AdComputer.Properties["msDS-AllowedToDelegateTo"].Count >= 1)
2790 {
2791 DelegationType = "Constrained";
2792 for (int i = 0; i < AdComputer.Properties["msDS-AllowedToDelegateTo"].Count; i++)
2793 {
2794 var delegateto = AdComputer.Properties["msDS-AllowedToDelegateTo"][i];
2795 DelegationServices = DelegationServices + "," + Convert.ToString(delegateto);
2796 }
2797 DelegationServices = DelegationServices.TrimStart(',');
2798 }
2799 if ((bool) TrustedtoAuthforDelegation)
2800 {
2801 DelegationProtocol = "Any";
2802 }
2803 else if (DelegationType != null)
2804 {
2805 DelegationProtocol = "Kerberos";
2806 }
2807 string SIDHistory = "";
2808 if (AdComputer.Properties["sidhistory"].Count >= 1)
2809 {
2810 string sids = "";
2811 for (int i = 0; i < AdComputer.Properties["sidhistory"].Count; i++)
2812 {
2813 var history = AdComputer.Properties["sidhistory"][i];
2814 sids = sids + "," + Convert.ToString(new SecurityIdentifier((byte[])history, 0));
2815 }
2816 SIDHistory = sids.TrimStart(',');
2817 }
2818 String OperatingSystem = CleanString((AdComputer.Properties["operatingsystem"].Count != 0 ? AdComputer.Properties["operatingsystem"][0] : "-") + " " + (AdComputer.Properties["operatingsystemhotfix"].Count != 0 ? AdComputer.Properties["operatingsystemhotfix"][0] : " ") + " " + (AdComputer.Properties["operatingsystemservicepack"].Count != 0 ? AdComputer.Properties["operatingsystemservicepack"][0] : " ") + " " + (AdComputer.Properties["operatingsystemversion"].Count != 0 ? AdComputer.Properties["operatingsystemversion"][0] : " "));
2819
2820 PSObject ComputerObj = new PSObject();
2821 ComputerObj.Members.Add(new PSNoteProperty("Name", (AdComputer.Properties["name"].Count != 0 ? AdComputer.Properties["name"][0] : "")));
2822 ComputerObj.Members.Add(new PSNoteProperty("DNSHostName", (AdComputer.Properties["dnshostname"].Count != 0 ? AdComputer.Properties["dnshostname"][0] : "")));
2823 ComputerObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
2824 ComputerObj.Members.Add(new PSNoteProperty("IPv4Address", StrIPAddress));
2825 ComputerObj.Members.Add(new PSNoteProperty("Operating System", OperatingSystem));
2826 ComputerObj.Members.Add(new PSNoteProperty("Logon Age (days)", DaysSinceLastLogon));
2827 ComputerObj.Members.Add(new PSNoteProperty("Password Age (days)", DaysSinceLastPasswordChange));
2828 ComputerObj.Members.Add(new PSNoteProperty("Dormant (> " + DormantTimeSpan + " days)", Dormant));
2829 ComputerObj.Members.Add(new PSNoteProperty("Password Age (> " + PassMaxAge + " days)", PasswordNotChangedafterMaxAge));
2830 ComputerObj.Members.Add(new PSNoteProperty("Delegation Type", DelegationType));
2831 ComputerObj.Members.Add(new PSNoteProperty("Delegation Protocol", DelegationProtocol));
2832 ComputerObj.Members.Add(new PSNoteProperty("Delegation Services", DelegationServices));
2833 ComputerObj.Members.Add(new PSNoteProperty("UserName", (AdComputer.Properties["samaccountname"].Count != 0 ? AdComputer.Properties["samaccountname"][0] : "")));
2834 ComputerObj.Members.Add(new PSNoteProperty("Primary Group ID", (AdComputer.Properties["primarygroupid"].Count != 0 ? AdComputer.Properties["primarygroupid"][0] : "")));
2835 ComputerObj.Members.Add(new PSNoteProperty("SID", Convert.ToString(new SecurityIdentifier((byte[])AdComputer.Properties["objectSID"][0], 0))));
2836 ComputerObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
2837 ComputerObj.Members.Add(new PSNoteProperty("Description", (AdComputer.Properties["Description"].Count != 0 ? AdComputer.Properties["Description"][0] : "")));
2838 ComputerObj.Members.Add(new PSNoteProperty("ms-ds-CreatorSid", (AdComputer.Properties["ms-ds-CreatorSid"].Count != 0 ? Convert.ToString(new SecurityIdentifier((byte[])AdComputer.Properties["ms-ds-CreatorSid"][0], 0)) : "")));
2839 ComputerObj.Members.Add(new PSNoteProperty("Last Logon Date", LastLogonDate));
2840 ComputerObj.Members.Add(new PSNoteProperty("Password LastSet", PasswordLastSet));
2841 ComputerObj.Members.Add(new PSNoteProperty("UserAccountControl", (AdComputer.Properties["useraccountcontrol"].Count != 0 ? AdComputer.Properties["useraccountcontrol"][0] : "")));
2842 ComputerObj.Members.Add(new PSNoteProperty("whenCreated", AdComputer.Properties["whencreated"][0]));
2843 ComputerObj.Members.Add(new PSNoteProperty("whenChanged", AdComputer.Properties["whenchanged"][0]));
2844 ComputerObj.Members.Add(new PSNoteProperty("Distinguished Name", AdComputer.Properties["distinguishedname"][0]));
2845 return new PSObject[] { ComputerObj };
2846 }
2847 catch (Exception e)
2848 {
2849 Console.WriteLine("{0} Exception caught.", e);
2850 return new PSObject[] { };
2851 }
2852 }
2853 }
2854
2855 class ComputerSPNRecordProcessor : IRecordProcessor
2856 {
2857 public PSObject[] processRecord(Object record)
2858 {
2859 try
2860 {
2861 SearchResult AdComputer = (SearchResult) record;
2862 List<PSObject> SPNList = new List<PSObject>();
2863
2864 foreach (String SPN in AdComputer.Properties["serviceprincipalname"])
2865 {
2866 String[] SPNArray = SPN.Split('/');
2867 bool flag = true;
2868 foreach (PSObject Obj in SPNList)
2869 {
2870 if ( (String) Obj.Members["Service"].Value == SPNArray[0] )
2871 {
2872 Obj.Members["Host"].Value = String.Join(",", (Obj.Members["Host"].Value + "," + SPNArray[1]).Split(',').Distinct().ToArray());
2873 flag = false;
2874 }
2875 }
2876 if (flag)
2877 {
2878 PSObject ComputerSPNObj = new PSObject();
2879 ComputerSPNObj.Members.Add(new PSNoteProperty("Name", AdComputer.Properties["name"][0]));
2880 ComputerSPNObj.Members.Add(new PSNoteProperty("Service", SPNArray[0]));
2881 ComputerSPNObj.Members.Add(new PSNoteProperty("Host", SPNArray[1]));
2882 SPNList.Add( ComputerSPNObj );
2883 }
2884 }
2885 return SPNList.ToArray();
2886 }
2887 catch (Exception e)
2888 {
2889 Console.WriteLine("{0} Exception caught.", e);
2890 return new PSObject[] { };
2891 }
2892 }
2893 }
2894
2895 class LAPSRecordProcessor : IRecordProcessor
2896 {
2897 public PSObject[] processRecord(Object record)
2898 {
2899 try
2900 {
2901 SearchResult AdComputer = (SearchResult) record;
2902 bool PasswordStored = false;
2903 DateTime? CurrentExpiration = null;
2904 if (AdComputer.Properties["ms-mcs-admpwdexpirationtime"].Count != 0)
2905 {
2906 CurrentExpiration = DateTime.FromFileTime((long)(AdComputer.Properties["ms-mcs-admpwdexpirationtime"][0]));
2907 PasswordStored = true;
2908 }
2909 PSObject LAPSObj = new PSObject();
2910 LAPSObj.Members.Add(new PSNoteProperty("Hostname", (AdComputer.Properties["dnshostname"].Count != 0 ? AdComputer.Properties["dnshostname"][0] : AdComputer.Properties["cn"][0] )));
2911 LAPSObj.Members.Add(new PSNoteProperty("Stored", PasswordStored));
2912 LAPSObj.Members.Add(new PSNoteProperty("Readable", (AdComputer.Properties["ms-mcs-admpwd"].Count != 0 ? true : false)));
2913 LAPSObj.Members.Add(new PSNoteProperty("Password", (AdComputer.Properties["ms-mcs-admpwd"].Count != 0 ? AdComputer.Properties["ms-mcs-admpwd"][0] : null)));
2914 LAPSObj.Members.Add(new PSNoteProperty("Expiration", CurrentExpiration));
2915 return new PSObject[] { LAPSObj };
2916 }
2917 catch (Exception e)
2918 {
2919 Console.WriteLine("{0} Exception caught.", e);
2920 return new PSObject[] { };
2921 }
2922 }
2923 }
2924
2925 class SIDRecordDictionaryProcessor : IRecordProcessor
2926 {
2927 public PSObject[] processRecord(Object record)
2928 {
2929 try
2930 {
2931 SearchResult AdObject = (SearchResult) record;
2932 switch (Convert.ToString(AdObject.Properties["objectclass"][AdObject.Properties["objectclass"].Count-1]))
2933 {
2934 case "user":
2935 case "computer":
2936 case "group":
2937 LDAPClass.AdSIDDictionary.Add(Convert.ToString(new SecurityIdentifier((byte[])AdObject.Properties["objectSID"][0], 0)), (Convert.ToString(AdObject.Properties["name"][0])));
2938 break;
2939 }
2940 return new PSObject[] { };
2941 }
2942 catch (Exception e)
2943 {
2944 Console.WriteLine("{0} Exception caught.", e);
2945 return new PSObject[] { };
2946 }
2947 }
2948 }
2949
2950 class DACLRecordProcessor : IRecordProcessor
2951 {
2952 public PSObject[] processRecord(Object record)
2953 {
2954 try
2955 {
2956 SearchResult AdObject = (SearchResult) record;
2957 byte[] ntSecurityDescriptor = null;
2958 String Name = null;
2959 String Type = null;
2960 List<PSObject> DACLList = new List<PSObject>();
2961
2962 Name = Convert.ToString(AdObject.Properties["name"][0]);
2963
2964 switch (Convert.ToString(AdObject.Properties["objectclass"][AdObject.Properties["objectclass"].Count-1]))
2965 {
2966 case "user":
2967 Type = "User";
2968 break;
2969 case "computer":
2970 Type = "Computer";
2971 break;
2972 case "group":
2973 Type = "Group";
2974 break;
2975 case "container":
2976 Type = "Container";
2977 break;
2978 case "groupPolicyContainer":
2979 Type = "GPO";
2980 Name = Convert.ToString(AdObject.Properties["displayname"][0]);
2981 break;
2982 case "organizationalUnit":
2983 Type = "OU";
2984 break;
2985 case "domainDNS":
2986 Type = "Domain";
2987 break;
2988 default:
2989 Type = Convert.ToString(AdObject.Properties["objectclass"][AdObject.Properties["objectclass"].Count-1]);
2990 break;
2991 }
2992
2993 // When the user is not allowed to query the ntsecuritydescriptor attribute.
2994 if (AdObject.Properties["ntsecuritydescriptor"].Count != 0)
2995 {
2996 ntSecurityDescriptor = (byte[]) AdObject.Properties["ntsecuritydescriptor"][0];
2997 }
2998 else
2999 {
3000 DirectoryEntry AdObjectEntry = ((SearchResult)record).GetDirectoryEntry();
3001 ntSecurityDescriptor = (byte[]) AdObjectEntry.ObjectSecurity.GetSecurityDescriptorBinaryForm();
3002 }
3003 if (ntSecurityDescriptor != null)
3004 {
3005 DirectoryObjectSecurity DirObjSec = new ActiveDirectorySecurity();
3006 DirObjSec.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor);
3007 AuthorizationRuleCollection AccessRules = (AuthorizationRuleCollection) DirObjSec.GetAccessRules(true,true,typeof(System.Security.Principal.NTAccount));
3008 foreach (ActiveDirectoryAccessRule Rule in AccessRules)
3009 {
3010 String IdentityReference = Convert.ToString(Rule.IdentityReference);
3011 String Owner = Convert.ToString(DirObjSec.GetOwner(typeof(System.Security.Principal.SecurityIdentifier)));
3012 PSObject ObjectObj = new PSObject();
3013 ObjectObj.Members.Add(new PSNoteProperty("Name", CleanString(Name)));
3014 ObjectObj.Members.Add(new PSNoteProperty("Type", Type));
3015 ObjectObj.Members.Add(new PSNoteProperty("ObjectTypeName", LDAPClass.GUIDs[Convert.ToString(Rule.ObjectType)]));
3016 ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectTypeName", LDAPClass.GUIDs[Convert.ToString(Rule.InheritedObjectType)]));
3017 ObjectObj.Members.Add(new PSNoteProperty("ActiveDirectoryRights", Rule.ActiveDirectoryRights));
3018 ObjectObj.Members.Add(new PSNoteProperty("AccessControlType", Rule.AccessControlType));
3019 ObjectObj.Members.Add(new PSNoteProperty("IdentityReferenceName", LDAPClass.AdSIDDictionary.ContainsKey(IdentityReference) ? LDAPClass.AdSIDDictionary[IdentityReference] : IdentityReference));
3020 ObjectObj.Members.Add(new PSNoteProperty("OwnerName", LDAPClass.AdSIDDictionary.ContainsKey(Owner) ? LDAPClass.AdSIDDictionary[Owner] : Owner));
3021 ObjectObj.Members.Add(new PSNoteProperty("Inherited", Rule.IsInherited));
3022 ObjectObj.Members.Add(new PSNoteProperty("ObjectFlags", Rule.ObjectFlags));
3023 ObjectObj.Members.Add(new PSNoteProperty("InheritanceFlags", Rule.InheritanceFlags));
3024 ObjectObj.Members.Add(new PSNoteProperty("InheritanceType", Rule.InheritanceType));
3025 ObjectObj.Members.Add(new PSNoteProperty("PropagationFlags", Rule.PropagationFlags));
3026 ObjectObj.Members.Add(new PSNoteProperty("ObjectType", Rule.ObjectType));
3027 ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectType", Rule.InheritedObjectType));
3028 ObjectObj.Members.Add(new PSNoteProperty("IdentityReference", Rule.IdentityReference));
3029 ObjectObj.Members.Add(new PSNoteProperty("Owner", Owner));
3030 ObjectObj.Members.Add(new PSNoteProperty("DistinguishedName", AdObject.Properties["distinguishedname"][0]));
3031 DACLList.Add( ObjectObj );
3032 }
3033 }
3034
3035 return DACLList.ToArray();
3036 }
3037 catch (Exception e)
3038 {
3039 Console.WriteLine("{0} Exception caught.", e);
3040 return new PSObject[] { };
3041 }
3042 }
3043 }
3044
3045 class SACLRecordProcessor : IRecordProcessor
3046 {
3047 public PSObject[] processRecord(Object record)
3048 {
3049 try
3050 {
3051 SearchResult AdObject = (SearchResult) record;
3052 byte[] ntSecurityDescriptor = null;
3053 String Name = null;
3054 String Type = null;
3055 List<PSObject> SACLList = new List<PSObject>();
3056
3057 Name = Convert.ToString(AdObject.Properties["name"][0]);
3058
3059 switch (Convert.ToString(AdObject.Properties["objectclass"][AdObject.Properties["objectclass"].Count-1]))
3060 {
3061 case "user":
3062 Type = "User";
3063 break;
3064 case "computer":
3065 Type = "Computer";
3066 break;
3067 case "group":
3068 Type = "Group";
3069 break;
3070 case "container":
3071 Type = "Container";
3072 break;
3073 case "groupPolicyContainer":
3074 Type = "GPO";
3075 Name = Convert.ToString(AdObject.Properties["displayname"][0]);
3076 break;
3077 case "organizationalUnit":
3078 Type = "OU";
3079 break;
3080 case "domainDNS":
3081 Type = "Domain";
3082 break;
3083 default:
3084 Type = Convert.ToString(AdObject.Properties["objectclass"][AdObject.Properties["objectclass"].Count-1]);
3085 break;
3086 }
3087
3088 // When the user is not allowed to query the ntsecuritydescriptor attribute.
3089 if (AdObject.Properties["ntsecuritydescriptor"].Count != 0)
3090 {
3091 ntSecurityDescriptor = (byte[]) AdObject.Properties["ntsecuritydescriptor"][0];
3092 }
3093 else
3094 {
3095 DirectoryEntry AdObjectEntry = ((SearchResult)record).GetDirectoryEntry();
3096 ntSecurityDescriptor = (byte[]) AdObjectEntry.ObjectSecurity.GetSecurityDescriptorBinaryForm();
3097 }
3098 if (ntSecurityDescriptor != null)
3099 {
3100 DirectoryObjectSecurity DirObjSec = new ActiveDirectorySecurity();
3101 DirObjSec.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor);
3102 AuthorizationRuleCollection AuditRules = (AuthorizationRuleCollection) DirObjSec.GetAuditRules(true,true,typeof(System.Security.Principal.NTAccount));
3103 foreach (ActiveDirectoryAuditRule Rule in AuditRules)
3104 {
3105 String IdentityReference = Convert.ToString(Rule.IdentityReference);
3106 PSObject ObjectObj = new PSObject();
3107 ObjectObj.Members.Add(new PSNoteProperty("Name", CleanString(Name)));
3108 ObjectObj.Members.Add(new PSNoteProperty("Type", Type));
3109 ObjectObj.Members.Add(new PSNoteProperty("ObjectTypeName", LDAPClass.GUIDs[Convert.ToString(Rule.ObjectType)]));
3110 ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectTypeName", LDAPClass.GUIDs[Convert.ToString(Rule.InheritedObjectType)]));
3111 ObjectObj.Members.Add(new PSNoteProperty("ActiveDirectoryRights", Rule.ActiveDirectoryRights));
3112 ObjectObj.Members.Add(new PSNoteProperty("IdentityReferenceName", LDAPClass.AdSIDDictionary.ContainsKey(IdentityReference) ? LDAPClass.AdSIDDictionary[IdentityReference] : IdentityReference));
3113 ObjectObj.Members.Add(new PSNoteProperty("AuditFlags", Rule.AuditFlags));
3114 ObjectObj.Members.Add(new PSNoteProperty("ObjectFlags", Rule.ObjectFlags));
3115 ObjectObj.Members.Add(new PSNoteProperty("InheritanceFlags", Rule.InheritanceFlags));
3116 ObjectObj.Members.Add(new PSNoteProperty("InheritanceType", Rule.InheritanceType));
3117 ObjectObj.Members.Add(new PSNoteProperty("Inherited", Rule.IsInherited));
3118 ObjectObj.Members.Add(new PSNoteProperty("PropagationFlags", Rule.PropagationFlags));
3119 ObjectObj.Members.Add(new PSNoteProperty("ObjectType", Rule.ObjectType));
3120 ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectType", Rule.InheritedObjectType));
3121 ObjectObj.Members.Add(new PSNoteProperty("IdentityReference", Rule.IdentityReference));
3122 SACLList.Add( ObjectObj );
3123 }
3124 }
3125
3126 return SACLList.ToArray();
3127 }
3128 catch (Exception e)
3129 {
3130 Console.WriteLine("{0} Exception caught.", e);
3131 return new PSObject[] { };
3132 }
3133 }
3134 }
3135
3136 //The interface and implmentation class used to handle the results (this implementation just writes the strings to a file)
3137
3138 interface IResultsHandler
3139 {
3140 void processResults(Object[] t);
3141
3142 Object[] finalise();
3143 }
3144
3145 class SimpleResultsHandler : IResultsHandler
3146 {
3147 private Object lockObj = new Object();
3148 private List<Object> processed = new List<Object>();
3149
3150 public SimpleResultsHandler()
3151 {
3152 }
3153
3154 public void processResults(Object[] results)
3155 {
3156 lock (lockObj)
3157 {
3158 if (results.Length != 0)
3159 {
3160 for (var i = 0; i < results.Length; i++)
3161 {
3162 processed.Add((PSObject)results[i]);
3163 }
3164 }
3165 }
3166 }
3167
3168 public Object[] finalise()
3169 {
3170 return processed.ToArray();
3171 }
3172 }
3173 }
3174}
3175"@
3176
3177#Add-Type -TypeDefinition $Source -ReferencedAssemblies ([System.String[]]@(([system.reflection.assembly]::LoadWithPartialName("Microsoft.ActiveDirectory.Management")).Location,([system.reflection.assembly]::LoadWithPartialName("System.DirectoryServices")).Location))
3178
3179# modified version from https://github.com/vletoux/SmbScanner/blob/master/smbscanner.ps1
3180$PingCastleSMBScannerSource = @"
3181using System;
3182using System.Collections.Generic;
3183using System.Diagnostics;
3184using System.IO;
3185using System.Net;
3186using System.Net.Sockets;
3187using System.Text;
3188using System.Runtime.InteropServices;
3189using System.Management.Automation;
3190
3191namespace ADRecon
3192{
3193 public class PingCastleScannersSMBScanner
3194 {
3195 [StructLayout(LayoutKind.Explicit)]
3196 struct SMB_Header {
3197 [FieldOffset(0)]
3198 public UInt32 Protocol;
3199 [FieldOffset(4)]
3200 public byte Command;
3201 [FieldOffset(5)]
3202 public int Status;
3203 [FieldOffset(9)]
3204 public byte Flags;
3205 [FieldOffset(10)]
3206 public UInt16 Flags2;
3207 [FieldOffset(12)]
3208 public UInt16 PIDHigh;
3209 [FieldOffset(14)]
3210 public UInt64 SecurityFeatures;
3211 [FieldOffset(22)]
3212 public UInt16 Reserved;
3213 [FieldOffset(24)]
3214 public UInt16 TID;
3215 [FieldOffset(26)]
3216 public UInt16 PIDLow;
3217 [FieldOffset(28)]
3218 public UInt16 UID;
3219 [FieldOffset(30)]
3220 public UInt16 MID;
3221 };
3222 // https://msdn.microsoft.com/en-us/library/cc246529.aspx
3223 [StructLayout(LayoutKind.Explicit)]
3224 struct SMB2_Header {
3225 [FieldOffset(0)]
3226 public UInt32 ProtocolId;
3227 [FieldOffset(4)]
3228 public UInt16 StructureSize;
3229 [FieldOffset(6)]
3230 public UInt16 CreditCharge;
3231 [FieldOffset(8)]
3232 public UInt32 Status; // to do SMB3
3233 [FieldOffset(12)]
3234 public UInt16 Command;
3235 [FieldOffset(14)]
3236 public UInt16 CreditRequest_Response;
3237 [FieldOffset(16)]
3238 public UInt32 Flags;
3239 [FieldOffset(20)]
3240 public UInt32 NextCommand;
3241 [FieldOffset(24)]
3242 public UInt64 MessageId;
3243 [FieldOffset(32)]
3244 public UInt32 Reserved;
3245 [FieldOffset(36)]
3246 public UInt32 TreeId;
3247 [FieldOffset(40)]
3248 public UInt64 SessionId;
3249 [FieldOffset(48)]
3250 public UInt64 Signature1;
3251 [FieldOffset(56)]
3252 public UInt64 Signature2;
3253 }
3254 [StructLayout(LayoutKind.Explicit)]
3255 struct SMB2_NegotiateRequest
3256 {
3257 [FieldOffset(0)]
3258 public UInt16 StructureSize;
3259 [FieldOffset(2)]
3260 public UInt16 DialectCount;
3261 [FieldOffset(4)]
3262 public UInt16 SecurityMode;
3263 [FieldOffset(6)]
3264 public UInt16 Reserved;
3265 [FieldOffset(8)]
3266 public UInt32 Capabilities;
3267 [FieldOffset(12)]
3268 public Guid ClientGuid;
3269 [FieldOffset(28)]
3270 public UInt64 ClientStartTime;
3271 [FieldOffset(36)]
3272 public UInt16 DialectToTest;
3273 }
3274 const int SMB_COM_NEGOTIATE = 0x72;
3275 const int SMB2_NEGOTIATE = 0;
3276 const int SMB_FLAGS_CASE_INSENSITIVE = 0x08;
3277 const int SMB_FLAGS_CANONICALIZED_PATHS = 0x10;
3278 const int SMB_FLAGS2_LONG_NAMES = 0x0001;
3279 const int SMB_FLAGS2_EAS = 0x0002;
3280 const int SMB_FLAGS2_SECURITY_SIGNATURE_REQUIRED = 0x0010 ;
3281 const int SMB_FLAGS2_IS_LONG_NAME = 0x0040;
3282 const int SMB_FLAGS2_ESS = 0x0800;
3283 const int SMB_FLAGS2_NT_STATUS = 0x4000;
3284 const int SMB_FLAGS2_UNICODE = 0x8000;
3285 const int SMB_DB_FORMAT_DIALECT = 0x02;
3286 static byte[] GenerateSmbHeaderFromCommand(byte command)
3287 {
3288 SMB_Header header = new SMB_Header();
3289 header.Protocol = 0x424D53FF;
3290 header.Command = command;
3291 header.Status = 0;
3292 header.Flags = SMB_FLAGS_CASE_INSENSITIVE | SMB_FLAGS_CANONICALIZED_PATHS;
3293 header.Flags2 = SMB_FLAGS2_LONG_NAMES | SMB_FLAGS2_EAS | SMB_FLAGS2_SECURITY_SIGNATURE_REQUIRED | SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_ESS | SMB_FLAGS2_NT_STATUS | SMB_FLAGS2_UNICODE;
3294 header.PIDHigh = 0;
3295 header.SecurityFeatures = 0;
3296 header.Reserved = 0;
3297 header.TID = 0xffff;
3298 header.PIDLow = 0xFEFF;
3299 header.UID = 0;
3300 header.MID = 0;
3301 return getBytes(header);
3302 }
3303 static byte[] GenerateSmb2HeaderFromCommand(byte command)
3304 {
3305 SMB2_Header header = new SMB2_Header();
3306 header.ProtocolId = 0x424D53FE;
3307 header.Command = command;
3308 header.StructureSize = 64;
3309 header.Command = command;
3310 header.MessageId = 0;
3311 header.Reserved = 0xFEFF;
3312 return getBytes(header);
3313 }
3314 static byte[] getBytes(object structure)
3315 {
3316 int size = Marshal.SizeOf(structure);
3317 byte[] arr = new byte[size];
3318 IntPtr ptr = Marshal.AllocHGlobal(size);
3319 Marshal.StructureToPtr(structure, ptr, true);
3320 Marshal.Copy(ptr, arr, 0, size);
3321 Marshal.FreeHGlobal(ptr);
3322 return arr;
3323 }
3324 static byte[] getDialect(string dialect)
3325 {
3326 byte[] dialectBytes = Encoding.ASCII.GetBytes(dialect);
3327 byte[] output = new byte[dialectBytes.Length + 2];
3328 output[0] = 2;
3329 output[output.Length - 1] = 0;
3330 Array.Copy(dialectBytes, 0, output, 1, dialectBytes.Length);
3331 return output;
3332 }
3333 static byte[] GetNegotiateMessage(byte[] dialect)
3334 {
3335 byte[] output = new byte[dialect.Length + 3];
3336 output[0] = 0;
3337 output[1] = (byte) dialect.Length;
3338 output[2] = 0;
3339 Array.Copy(dialect, 0, output, 3, dialect.Length);
3340 return output;
3341 }
3342 // MS-SMB2 2.2.3 SMB2 NEGOTIATE Request
3343 static byte[] GetNegotiateMessageSmbv2(int DialectToTest)
3344 {
3345 SMB2_NegotiateRequest request = new SMB2_NegotiateRequest();
3346 request.StructureSize = 36;
3347 request.DialectCount = 1;
3348 request.SecurityMode = 1; // signing enabled
3349 request.ClientGuid = Guid.NewGuid();
3350 request.DialectToTest = (UInt16) DialectToTest;
3351 return getBytes(request);
3352 }
3353 static byte[] GetNegotiatePacket(byte[] header, byte[] smbPacket)
3354 {
3355 byte[] output = new byte[smbPacket.Length + header.Length + 4];
3356 output[0] = 0;
3357 output[1] = 0;
3358 output[2] = 0;
3359 output[3] = (byte)(smbPacket.Length + header.Length);
3360 Array.Copy(header, 0, output, 4, header.Length);
3361 Array.Copy(smbPacket, 0, output, 4 + header.Length, smbPacket.Length);
3362 return output;
3363 }
3364 public static bool DoesServerSupportDialect(string server, string dialect)
3365 {
3366 Trace.WriteLine("Checking " + server + " for SMBV1 dialect " + dialect);
3367 TcpClient client = new TcpClient();
3368 try
3369 {
3370 client.Connect(server, 445);
3371 }
3372 catch (Exception)
3373 {
3374 throw new Exception("port 445 is closed on " + server);
3375 }
3376 try
3377 {
3378 NetworkStream stream = client.GetStream();
3379 byte[] header = GenerateSmbHeaderFromCommand(SMB_COM_NEGOTIATE);
3380 byte[] dialectEncoding = getDialect(dialect);
3381 byte[] negotiatemessage = GetNegotiateMessage(dialectEncoding);
3382 byte[] packet = GetNegotiatePacket(header, negotiatemessage);
3383 stream.Write(packet, 0, packet.Length);
3384 stream.Flush();
3385 byte[] netbios = new byte[4];
3386 if (stream.Read(netbios, 0, netbios.Length) != netbios.Length)
3387 {
3388 return false;
3389 }
3390 byte[] smbHeader = new byte[Marshal.SizeOf(typeof(SMB_Header))];
3391 if (stream.Read(smbHeader, 0, smbHeader.Length) != smbHeader.Length)
3392 {
3393 return false;
3394 }
3395 byte[] negotiateresponse = new byte[3];
3396 if (stream.Read(negotiateresponse, 0, negotiateresponse.Length) != negotiateresponse.Length)
3397 {
3398 return false;
3399 }
3400 if (negotiateresponse[1] == 0 && negotiateresponse[2] == 0)
3401 {
3402 Trace.WriteLine("Checking " + server + " for SMBV1 dialect " + dialect + " = Supported");
3403 return true;
3404 }
3405 Trace.WriteLine("Checking " + server + " for SMBV1 dialect " + dialect + " = Not supported");
3406 return false;
3407 }
3408 catch (Exception)
3409 {
3410 throw new ApplicationException("Smb1 is not supported on " + server);
3411 }
3412 }
3413 public static bool DoesServerSupportDialectWithSmbV2(string server, int dialect, bool checkSMBSigning)
3414 {
3415 Trace.WriteLine("Checking " + server + " for SMBV2 dialect 0x" + dialect.ToString("X2"));
3416 TcpClient client = new TcpClient();
3417 try
3418 {
3419 client.Connect(server, 445);
3420 }
3421 catch (Exception)
3422 {
3423 throw new Exception("port 445 is closed on " + server);
3424 }
3425 try
3426 {
3427 NetworkStream stream = client.GetStream();
3428 byte[] header = GenerateSmb2HeaderFromCommand(SMB2_NEGOTIATE);
3429 byte[] negotiatemessage = GetNegotiateMessageSmbv2(dialect);
3430 byte[] packet = GetNegotiatePacket(header, negotiatemessage);
3431 stream.Write(packet, 0, packet.Length);
3432 stream.Flush();
3433 byte[] netbios = new byte[4];
3434 if( stream.Read(netbios, 0, netbios.Length) != netbios.Length)
3435 {
3436 return false;
3437 }
3438 byte[] smbHeader = new byte[Marshal.SizeOf(typeof(SMB2_Header))];
3439 if (stream.Read(smbHeader, 0, smbHeader.Length) != smbHeader.Length)
3440 {
3441 return false;
3442 }
3443 if (smbHeader[8] != 0 || smbHeader[9] != 0 || smbHeader[10] != 0 || smbHeader[11] != 0)
3444 {
3445 Trace.WriteLine("Checking " + server + " for SMBV2 dialect 0x" + dialect.ToString("X2") + " = Not supported via error code");
3446 return false;
3447 }
3448 byte[] negotiateresponse = new byte[6];
3449 if (stream.Read(negotiateresponse, 0, negotiateresponse.Length) != negotiateresponse.Length)
3450 {
3451 return false;
3452 }
3453 if (checkSMBSigning)
3454 {
3455 // https://support.microsoft.com/en-in/help/887429/overview-of-server-message-block-signing
3456 // https://msdn.microsoft.com/en-us/library/cc246561.aspx
3457 if (negotiateresponse[2] == 3)
3458 {
3459 Trace.WriteLine("Checking " + server + " for SMBV2 SMB Signing dialect 0x" + dialect.ToString("X2") + " = Supported");
3460 return true;
3461 }
3462 else
3463 {
3464 return false;
3465 }
3466 }
3467 int selectedDialect = negotiateresponse[5] * 0x100 + negotiateresponse[4];
3468 if (selectedDialect == dialect)
3469 {
3470 Trace.WriteLine("Checking " + server + " for SMBV2 dialect 0x" + dialect.ToString("X2") + " = Supported");
3471 return true;
3472 }
3473 Trace.WriteLine("Checking " + server + " for SMBV2 dialect 0x" + dialect.ToString("X2") + " = Not supported via not returned dialect");
3474 return false;
3475 }
3476 catch (Exception)
3477 {
3478 throw new ApplicationException("Smb2 is not supported on " + server);
3479 }
3480 }
3481 public static bool SupportSMB1(string server)
3482 {
3483 try
3484 {
3485 return DoesServerSupportDialect(server, "NT LM 0.12");
3486 }
3487 catch (Exception)
3488 {
3489 return false;
3490 }
3491 }
3492 public static bool SupportSMB2(string server)
3493 {
3494 try
3495 {
3496 return (DoesServerSupportDialectWithSmbV2(server, 0x0202, false) || DoesServerSupportDialectWithSmbV2(server, 0x0210, false));
3497 }
3498 catch (Exception)
3499 {
3500 return false;
3501 }
3502 }
3503 public static bool SupportSMB3(string server)
3504 {
3505 try
3506 {
3507 return (DoesServerSupportDialectWithSmbV2(server, 0x0300, false) || DoesServerSupportDialectWithSmbV2(server, 0x0302, false) || DoesServerSupportDialectWithSmbV2(server, 0x0311, false));
3508 }
3509 catch (Exception)
3510 {
3511 return false;
3512 }
3513 }
3514 public static string Name { get { return "smb"; } }
3515 public static PSObject GetPSObject(string computer)
3516 {
3517 PSObject DCSMBObj = new PSObject();
3518 if (computer == "")
3519 {
3520 DCSMBObj.Members.Add(new PSNoteProperty("SMB Port Open", null));
3521 DCSMBObj.Members.Add(new PSNoteProperty("SMB1(NT LM 0.12)", null));
3522 DCSMBObj.Members.Add(new PSNoteProperty("SMB2(0x0202)", null));
3523 DCSMBObj.Members.Add(new PSNoteProperty("SMB2(0x0210)", null));
3524 DCSMBObj.Members.Add(new PSNoteProperty("SMB3(0x0300)", null));
3525 DCSMBObj.Members.Add(new PSNoteProperty("SMB3(0x0302)", null));
3526 DCSMBObj.Members.Add(new PSNoteProperty("SMB3(0x0311)", null));
3527 DCSMBObj.Members.Add(new PSNoteProperty("SMB Signing", null));
3528 return DCSMBObj;
3529 }
3530 bool isPortOpened = true;
3531 bool SMBv1 = false;
3532 bool SMBv2_0x0202 = false;
3533 bool SMBv2_0x0210 = false;
3534 bool SMBv3_0x0300 = false;
3535 bool SMBv3_0x0302 = false;
3536 bool SMBv3_0x0311 = false;
3537 bool SMBSigning = false;
3538 try
3539 {
3540 try
3541 {
3542 SMBv1 = DoesServerSupportDialect(computer, "NT LM 0.12");
3543 }
3544 catch (ApplicationException)
3545 {
3546 }
3547 try
3548 {
3549 SMBv2_0x0202 = DoesServerSupportDialectWithSmbV2(computer, 0x0202, false);
3550 SMBv2_0x0210 = DoesServerSupportDialectWithSmbV2(computer, 0x0210, false);
3551 SMBv3_0x0300 = DoesServerSupportDialectWithSmbV2(computer, 0x0300, false);
3552 SMBv3_0x0302 = DoesServerSupportDialectWithSmbV2(computer, 0x0302, false);
3553 SMBv3_0x0311 = DoesServerSupportDialectWithSmbV2(computer, 0x0311, false);
3554 }
3555 catch (ApplicationException)
3556 {
3557 }
3558 }
3559 catch (Exception)
3560 {
3561 isPortOpened = false;
3562 }
3563 if (SMBv3_0x0311)
3564 {
3565 SMBSigning = DoesServerSupportDialectWithSmbV2(computer, 0x0311, true);
3566 }
3567 else if (SMBv3_0x0302)
3568 {
3569 SMBSigning = DoesServerSupportDialectWithSmbV2(computer, 0x0302, true);
3570 }
3571 else if (SMBv3_0x0300)
3572 {
3573 SMBSigning = DoesServerSupportDialectWithSmbV2(computer, 0x0300, true);
3574 }
3575 else if (SMBv2_0x0210)
3576 {
3577 SMBSigning = DoesServerSupportDialectWithSmbV2(computer, 0x0210, true);
3578 }
3579 else if (SMBv2_0x0202)
3580 {
3581 SMBSigning = DoesServerSupportDialectWithSmbV2(computer, 0x0202, true);
3582 }
3583 DCSMBObj.Members.Add(new PSNoteProperty("SMB Port Open", isPortOpened));
3584 DCSMBObj.Members.Add(new PSNoteProperty("SMB1(NT LM 0.12)", SMBv1));
3585 DCSMBObj.Members.Add(new PSNoteProperty("SMB2(0x0202)", SMBv2_0x0202));
3586 DCSMBObj.Members.Add(new PSNoteProperty("SMB2(0x0210)", SMBv2_0x0210));
3587 DCSMBObj.Members.Add(new PSNoteProperty("SMB3(0x0300)", SMBv3_0x0300));
3588 DCSMBObj.Members.Add(new PSNoteProperty("SMB3(0x0302)", SMBv3_0x0302));
3589 DCSMBObj.Members.Add(new PSNoteProperty("SMB3(0x0311)", SMBv3_0x0311));
3590 DCSMBObj.Members.Add(new PSNoteProperty("SMB Signing", SMBSigning));
3591 return DCSMBObj;
3592 }
3593 }
3594}
3595"@
3596
3597# Import the LogonUser, ImpersonateLoggedOnUser and RevertToSelf Functions from advapi32.dll and the CloseHandle Function from kernel32.dll
3598# https://docs.microsoft.com/en-gb/powershell/module/Microsoft.PowerShell.Utility/Add-Type?view=powershell-5.1
3599# https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx
3600# https://msdn.microsoft.com/en-us/library/windows/desktop/aa378612(v=vs.85).aspx
3601# https://msdn.microsoft.com/en-us/library/windows/desktop/aa379317(v=vs.85).aspx
3602
3603$Advapi32Def = @'
3604 [DllImport("advapi32.dll", SetLastError = true)]
3605 public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
3606
3607 [DllImport("advapi32.dll", SetLastError = true)]
3608 public static extern bool ImpersonateLoggedOnUser(IntPtr hToken);
3609
3610 [DllImport("advapi32.dll", SetLastError = true)]
3611 public static extern bool RevertToSelf();
3612'@
3613
3614# https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx
3615
3616$Kernel32Def = @'
3617 [DllImport("kernel32.dll", SetLastError = true)]
3618 public static extern bool CloseHandle(IntPtr hObject);
3619'@
3620
3621Function Get-DateDiff
3622{
3623<#
3624.SYNOPSIS
3625 Get difference between two dates.
3626
3627.DESCRIPTION
3628 Returns the difference between two dates.
3629
3630.PARAMETER Date1
3631 [DateTime]
3632 Date
3633
3634.PARAMETER Date2
3635 [DateTime]
3636 Date
3637
3638.OUTPUTS
3639 [System.ValueType.TimeSpan]
3640 Returns the difference between the two dates.
3641#>
3642 param (
3643 [Parameter(Mandatory = $true)]
3644 [DateTime] $Date1,
3645
3646 [Parameter(Mandatory = $true)]
3647 [DateTime] $Date2
3648 )
3649
3650 If ($Date2 -gt $Date1)
3651 {
3652 $DDiff = $Date2 - $Date1
3653 }
3654 Else
3655 {
3656 $DDiff = $Date1 - $Date2
3657 }
3658 Return $DDiff
3659}
3660
3661Function Get-DNtoFQDN
3662{
3663<#
3664.SYNOPSIS
3665 Gets Domain Distinguished Name (DN) from the Fully Qualified Domain Name (FQDN).
3666
3667.DESCRIPTION
3668 Converts Domain Distinguished Name (DN) to Fully Qualified Domain Name (FQDN).
3669
3670.PARAMETER ADObjectDN
3671 [string]
3672 Domain Distinguished Name (DN)
3673
3674.OUTPUTS
3675 [String]
3676 Returns the Fully Qualified Domain Name (FQDN).
3677
3678.LINK
3679 https://adsecurity.org/?p=440
3680#>
3681 param(
3682 [Parameter(Mandatory = $true)]
3683 [string] $ADObjectDN
3684 )
3685
3686 $Index = $ADObjectDN.IndexOf('DC=')
3687 If ($Index)
3688 {
3689 $ADObjectDNDomainName = $($ADObjectDN.SubString($Index)) -replace 'DC=','' -replace ',','.'
3690 }
3691 Else
3692 {
3693 # Modified version from https://adsecurity.org/?p=440
3694 [array] $ADObjectDNArray = $ADObjectDN -Split ("DC=")
3695 $ADObjectDNArray | ForEach-Object {
3696 [array] $temp = $_ -Split (",")
3697 [string] $ADObjectDNArrayItemDomainName += $temp[0] + "."
3698 }
3699 $ADObjectDNDomainName = $ADObjectDNArrayItemDomainName.Substring(1, $ADObjectDNArrayItemDomainName.Length - 2)
3700 }
3701 Return $ADObjectDNDomainName
3702}
3703
3704Function Export-ADRCSV
3705{
3706<#
3707.SYNOPSIS
3708 Exports Object to a CSV file.
3709
3710.DESCRIPTION
3711 Exports Object to a CSV file using Export-CSV.
3712
3713.PARAMETER ADRObj
3714 [PSObject]
3715 ADRObj
3716
3717.PARAMETER ADFileName
3718 [String]
3719 Path to save the CSV File.
3720
3721.OUTPUTS
3722 CSV file.
3723#>
3724 param(
3725 [Parameter(Mandatory = $true)]
3726 [ValidateNotNullOrEmpty()]
3727 [PSObject] $ADRObj,
3728
3729 [Parameter(Mandatory = $true)]
3730 [ValidateNotNullOrEmpty()]
3731 [String] $ADFileName
3732 )
3733
3734 Try
3735 {
3736 $ADRObj | Export-Csv -Path $ADFileName -NoTypeInformation
3737 }
3738 Catch
3739 {
3740 Write-Warning "[Export-ADRCSV] Failed to export $($ADFileName)."
3741 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3742 }
3743}
3744
3745Function Export-ADRXML
3746{
3747<#
3748.SYNOPSIS
3749 Exports Object to a XML file.
3750
3751.DESCRIPTION
3752 Exports Object to a XML file using Export-Clixml.
3753
3754.PARAMETER ADRObj
3755 [PSObject]
3756 ADRObj
3757
3758.PARAMETER ADFileName
3759 [String]
3760 Path to save the XML File.
3761
3762.OUTPUTS
3763 XML file.
3764#>
3765 param(
3766 [Parameter(Mandatory = $true)]
3767 [ValidateNotNullOrEmpty()]
3768 [PSObject] $ADRObj,
3769
3770 [Parameter(Mandatory = $true)]
3771 [ValidateNotNullOrEmpty()]
3772 [String] $ADFileName
3773 )
3774
3775 Try
3776 {
3777 (ConvertTo-Xml -NoTypeInformation -InputObject $ADRObj).Save($ADFileName)
3778 }
3779 Catch
3780 {
3781 Write-Warning "[Export-ADRXML] Failed to export $($ADFileName)."
3782 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3783 }
3784}
3785
3786Function Export-ADRJSON
3787{
3788<#
3789.SYNOPSIS
3790 Exports Object to a JSON file.
3791
3792.DESCRIPTION
3793 Exports Object to a JSON file using ConvertTo-Json.
3794
3795.PARAMETER ADRObj
3796 [PSObject]
3797 ADRObj
3798
3799.PARAMETER ADFileName
3800 [String]
3801 Path to save the JSON File.
3802
3803.OUTPUTS
3804 JSON file.
3805#>
3806 param(
3807 [Parameter(Mandatory = $true)]
3808 [ValidateNotNullOrEmpty()]
3809 [PSObject] $ADRObj,
3810
3811 [Parameter(Mandatory = $true)]
3812 [ValidateNotNullOrEmpty()]
3813 [String] $ADFileName
3814 )
3815
3816 Try
3817 {
3818 ConvertTo-JSON -InputObject $ADRObj | Out-File -FilePath $ADFileName
3819 }
3820 Catch
3821 {
3822 Write-Warning "[Export-ADRJSON] Failed to export $($ADFileName)."
3823 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3824 }
3825}
3826
3827Function Export-ADRHTML
3828{
3829<#
3830.SYNOPSIS
3831 Exports Object to a HTML file.
3832
3833.DESCRIPTION
3834 Exports Object to a HTML file using ConvertTo-Html.
3835
3836.PARAMETER ADRObj
3837 [PSObject]
3838 ADRObj
3839
3840.PARAMETER ADFileName
3841 [String]
3842 Path to save the HTML File.
3843
3844.OUTPUTS
3845 HTML file.
3846#>
3847 param(
3848 [Parameter(Mandatory = $true)]
3849 [ValidateNotNullOrEmpty()]
3850 [PSObject] $ADRObj,
3851
3852 [Parameter(Mandatory = $true)]
3853 [ValidateNotNullOrEmpty()]
3854 [String] $ADFileName,
3855
3856 [Parameter(Mandatory = $false)]
3857 [String] $ADROutputDir = $null
3858 )
3859
3860$Header = @"
3861<style type="text/css">
3862th {
3863 color:white;
3864 background-color:blue;
3865}
3866td, th {
3867 border:0px solid black;
3868 border-collapse:collapse;
3869 white-space:pre;
3870}
3871tr:nth-child(2n+1) {
3872 background-color: #dddddd;
3873}
3874tr:hover td {
3875 background-color: #c1d5f8;
3876}
3877table, tr, td, th {
3878 padding: 0px;
3879 margin: 0px;
3880 white-space:pre;
3881}
3882table {
3883 margin-left:1px;
3884}
3885</style>
3886"@
3887 Try
3888 {
3889 If ($ADFileName.Contains("Index"))
3890 {
3891 $HTMLPath = -join($ADROutputDir,'\','HTML-Files')
3892 $HTMLPath = $((Convert-Path $HTMLPath).TrimEnd("\"))
3893 $HTMLFiles = Get-ChildItem -Path $HTMLPath -name
3894 $HTML = $HTMLFiles | ConvertTo-HTML -Title "ADRecon" -Property @{Label="Table of Contents";Expression={"<a href='$($_)'>$($_)</a>"}} -Head $Header
3895
3896 Add-Type -AssemblyName System.Web
3897 [System.Web.HttpUtility]::HtmlDecode($HTML) | Out-File -FilePath $ADFileName
3898 }
3899 Else
3900 {
3901 If ($ADRObj -is [array])
3902 {
3903 $ADRObj | Select-Object * | ConvertTo-HTML -As Table -Head $Header | Out-File -FilePath $ADFileName
3904 }
3905 Else
3906 {
3907 ConvertTo-HTML -InputObject $ADRObj -As Table -Head $Header | Out-File -FilePath $ADFileName
3908 }
3909 }
3910 }
3911 Catch
3912 {
3913 Write-Warning "[Export-ADRHTML] Failed to export $($ADFileName)."
3914 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
3915 }
3916}
3917
3918Function Export-ADR
3919{
3920<#
3921.SYNOPSIS
3922 Helper function for all output types supported.
3923
3924.DESCRIPTION
3925 Helper function for all output types supported.
3926
3927.PARAMETER ADObjectDN
3928 [PSObject]
3929 ADRObj
3930
3931.PARAMETER ADROutputDir
3932 [String]
3933 Path for ADRecon output folder.
3934
3935.PARAMETER OutputType
3936 [array]
3937 Output Type.
3938
3939.PARAMETER ADRModuleName
3940 [String]
3941 Module Name.
3942
3943.OUTPUTS
3944 STDOUT, CSV, XML, JSON and/or HTML file, etc.
3945#>
3946 param(
3947 [Parameter(Mandatory = $true)]
3948 [PSObject] $ADRObj,
3949
3950 [Parameter(Mandatory = $true)]
3951 [String] $ADROutputDir,
3952
3953 [Parameter(Mandatory = $true)]
3954 [array] $OutputType,
3955
3956 [Parameter(Mandatory = $true)]
3957 [String] $ADRModuleName
3958 )
3959
3960 Switch ($OutputType)
3961 {
3962 'STDOUT'
3963 {
3964 If ($ADRModuleName -ne "AboutADRecon")
3965 {
3966 If ($ADRObj -is [array])
3967 {
3968 # Fix for InvalidOperationException: The object of type "Microsoft.PowerShell.Commands.Internal.Format.FormatStartData" is not valid or not in the correct sequence.
3969 $ADRObj | Out-String -Stream
3970 }
3971 Else
3972 {
3973 # Fix for InvalidOperationException: The object of type "Microsoft.PowerShell.Commands.Internal.Format.FormatStartData" is not valid or not in the correct sequence.
3974 $ADRObj | Format-List | Out-String -Stream
3975 }
3976 }
3977 }
3978 'CSV'
3979 {
3980 $ADFileName = -join($ADROutputDir,'\','CSV-Files','\',$ADRModuleName,'.csv')
3981 Export-ADRCSV -ADRObj $ADRObj -ADFileName $ADFileName
3982 }
3983 'XML'
3984 {
3985 $ADFileName = -join($ADROutputDir,'\','XML-Files','\',$ADRModuleName,'.xml')
3986 Export-ADRXML -ADRObj $ADRObj -ADFileName $ADFileName
3987 }
3988 'JSON'
3989 {
3990 $ADFileName = -join($ADROutputDir,'\','JSON-Files','\',$ADRModuleName,'.json')
3991 Export-ADRJSON -ADRObj $ADRObj -ADFileName $ADFileName
3992 }
3993 'HTML'
3994 {
3995 $ADFileName = -join($ADROutputDir,'\','HTML-Files','\',$ADRModuleName,'.html')
3996 Export-ADRHTML -ADRObj $ADRObj -ADFileName $ADFileName -ADROutputDir $ADROutputDir
3997 }
3998 }
3999}
4000
4001Function Get-ADRExcelComObj
4002{
4003<#
4004.SYNOPSIS
4005 Creates a ComObject to interact with Microsoft Excel.
4006
4007.DESCRIPTION
4008 Creates a ComObject to interact with Microsoft Excel if installed, else warning is raised.
4009
4010.OUTPUTS
4011 [System.__ComObject] and [System.MarshalByRefObject]
4012 Creates global variables $excel and $workbook.
4013#>
4014
4015 #Check if Excel is installed.
4016 Try
4017 {
4018 # Suppress verbose output
4019 $SaveVerbosePreference = $script:VerbosePreference
4020 $script:VerbosePreference = 'SilentlyContinue'
4021 $global:excel = New-Object -ComObject excel.application
4022 If ($SaveVerbosePreference)
4023 {
4024 $script:VerbosePreference = $SaveVerbosePreference
4025 Remove-Variable SaveVerbosePreference
4026 }
4027 }
4028 Catch
4029 {
4030 If ($SaveVerbosePreference)
4031 {
4032 $script:VerbosePreference = $SaveVerbosePreference
4033 Remove-Variable SaveVerbosePreference
4034 }
4035 Write-Warning "[Get-ADRExcelComObj] Excel does not appear to be installed. Skipping generation of ADRecon-Report.xlsx. Use the -GenExcel parameter to generate the ADRecon-Report.xslx on a host with Microsoft Excel installed."
4036 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4037 Return $null
4038 }
4039 $excel.Visible = $true
4040 $excel.Interactive = $false
4041 $global:workbook = $excel.Workbooks.Add()
4042 If ($workbook.Worksheets.Count -eq 3)
4043 {
4044 $workbook.WorkSheets.Item(3).Delete()
4045 $workbook.WorkSheets.Item(2).Delete()
4046 }
4047}
4048
4049Function Get-ADRExcelComObjRelease
4050{
4051<#
4052.SYNOPSIS
4053 Releases the ComObject created to interact with Microsoft Excel.
4054
4055.DESCRIPTION
4056 Releases the ComObject created to interact with Microsoft Excel.
4057
4058.PARAMETER ComObjtoRelease
4059 ComObjtoRelease
4060
4061.PARAMETER Final
4062 Final
4063#>
4064 param(
4065 [Parameter(Mandatory = $true)]
4066 $ComObjtoRelease,
4067
4068 [Parameter(Mandatory = $false)]
4069 [bool] $Final = $false
4070 )
4071 # https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.releasecomobject(v=vs.110).aspx
4072 # https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.finalreleasecomobject(v=vs.110).aspx
4073 If ($Final)
4074 {
4075 [System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($ComObjtoRelease) | Out-Null
4076 }
4077 Else
4078 {
4079 [System.Runtime.InteropServices.Marshal]::ReleaseComObject($ComObjtoRelease) | Out-Null
4080 }
4081 [System.GC]::Collect()
4082 [System.GC]::WaitForPendingFinalizers()
4083}
4084
4085Function Get-ADRExcelWorkbook
4086{
4087<#
4088.SYNOPSIS
4089 Adds a WorkSheet to the Workbook.
4090
4091.DESCRIPTION
4092 Adds a WorkSheet to the Workbook using the $workboook global variable and assigns it a name.
4093
4094.PARAMETER name
4095 [string]
4096 Name of the WorkSheet.
4097#>
4098 param (
4099 [Parameter(Mandatory = $true)]
4100 [string] $name
4101 )
4102
4103 $workbook.Worksheets.Add() | Out-Null
4104 $worksheet = $workbook.Worksheets.Item(1)
4105 $worksheet.Name = $name
4106
4107 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
4108 Remove-Variable worksheet
4109}
4110
4111Function Get-ADRExcelImport
4112{
4113<#
4114.SYNOPSIS
4115 Helper to import CSV to the current WorkSheet.
4116
4117.DESCRIPTION
4118 Helper to import CSV to the current WorkSheet. Supports two methods.
4119
4120.PARAMETER ADFileName
4121 [string]
4122 Filename of the CSV file to import.
4123
4124.PARAMETER method
4125 [int]
4126 Method to use for the import.
4127
4128.PARAMETER row
4129 [int]
4130 Row.
4131
4132.PARAMETER column
4133 [int]
4134 Column.
4135#>
4136 param (
4137 [Parameter(Mandatory = $true)]
4138 [string] $ADFileName,
4139
4140 [Parameter(Mandatory = $false)]
4141 [int] $method = 1,
4142
4143 [Parameter(Mandatory = $false)]
4144 [int] $row = 1,
4145
4146 [Parameter(Mandatory = $false)]
4147 [int] $column = 1
4148 )
4149
4150 $excel.ScreenUpdating = $false
4151 If ($method -eq 1)
4152 {
4153 If (Test-Path $ADFileName)
4154 {
4155 $worksheet = $workbook.Worksheets.Item(1)
4156 $TxtConnector = ("TEXT;" + $ADFileName)
4157 $CellRef = $worksheet.Range("A1")
4158 #Build, use and remove the text file connector
4159 $Connector = $worksheet.QueryTables.add($TxtConnector, $CellRef)
4160
4161 #65001: Unicode (UTF-8)
4162 $worksheet.QueryTables.item($Connector.name).TextFilePlatform = 65001
4163 $worksheet.QueryTables.item($Connector.name).TextFileCommaDelimiter = $True
4164 $worksheet.QueryTables.item($Connector.name).TextFileParseType = 1
4165 $worksheet.QueryTables.item($Connector.name).Refresh() | Out-Null
4166 $worksheet.QueryTables.item($Connector.name).delete()
4167
4168 Get-ADRExcelComObjRelease -ComObjtoRelease $CellRef
4169 Remove-Variable CellRef
4170 Get-ADRExcelComObjRelease -ComObjtoRelease $Connector
4171 Remove-Variable Connector
4172
4173 $listObject = $worksheet.ListObjects.Add([Microsoft.Office.Interop.Excel.XlListObjectSourceType]::xlSrcRange, $worksheet.UsedRange, $null, [Microsoft.Office.Interop.Excel.XlYesNoGuess]::xlYes, $null)
4174 $listObject.TableStyle = "TableStyleLight2" # Style Cheat Sheet: https://msdn.microsoft.com/en-au/library/documentformat.openxml.spreadsheet.tablestyle.aspx
4175 $worksheet.UsedRange.EntireColumn.AutoFit() | Out-Null
4176 }
4177 Remove-Variable ADFileName
4178 }
4179 Elseif ($method -eq 2)
4180 {
4181 $worksheet = $workbook.Worksheets.Item(1)
4182 If (Test-Path $ADFileName)
4183 {
4184 $ADTemp = Import-Csv -Path $ADFileName
4185 $ADTemp | ForEach-Object {
4186 Foreach ($prop in $_.PSObject.Properties)
4187 {
4188 $worksheet.Cells.Item($row, $column) = $prop.Name
4189 $worksheet.Cells.Item($row, $column + 1) = $prop.Value
4190 $row++
4191 }
4192 }
4193 Remove-Variable ADTemp
4194 $listObject = $worksheet.ListObjects.Add([Microsoft.Office.Interop.Excel.XlListObjectSourceType]::xlSrcRange, $worksheet.UsedRange, $null, [Microsoft.Office.Interop.Excel.XlYesNoGuess]::xlYes, $null)
4195 $listObject.TableStyle = "TableStyleLight2" # Style Cheat Sheet: https://msdn.microsoft.com/en-au/library/documentformat.openxml.spreadsheet.tablestyle.aspx
4196 $usedRange = $worksheet.UsedRange
4197 $usedRange.EntireColumn.AutoFit() | Out-Null
4198 }
4199 Else
4200 {
4201 $worksheet.Cells.Item($row, $column) = "Error!"
4202 }
4203 Remove-Variable ADFileName
4204 }
4205 $excel.ScreenUpdating = $true
4206
4207 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
4208 Remove-Variable worksheet
4209}
4210
4211# Thanks Anant Shrivastava for the suggestion of using Pivot Tables for generation of the Stats sheets.
4212Function Get-ADRExcelPivotTable
4213{
4214<#
4215.SYNOPSIS
4216 Helper to add Pivot Table to the current WorkSheet.
4217
4218.DESCRIPTION
4219 Helper to add Pivot Table to the current WorkSheet.
4220
4221.PARAMETER SrcSheetName
4222 [string]
4223 Source Sheet Name.
4224
4225.PARAMETER PivotTableName
4226 [string]
4227 Pivot Table Name.
4228
4229.PARAMETER PivotRows
4230 [array]
4231 Row names from Source Sheet.
4232
4233.PARAMETER PivotColumns
4234 [array]
4235 Column names from Source Sheet.
4236
4237.PARAMETER PivotFilters
4238 [array]
4239 Row/Column names from Source Sheet to use as filters.
4240
4241.PARAMETER PivotValues
4242 [array]
4243 Row/Column names from Source Sheet to use for Values.
4244
4245.PARAMETER PivotPercentage
4246 [array]
4247 Row/Column names from Source Sheet to use for Percentage.
4248
4249.PARAMETER PivotLocation
4250 [array]
4251 Location of the Pivot Table in Row/Column.
4252#>
4253 param (
4254 [Parameter(Mandatory = $true)]
4255 [string] $SrcSheetName,
4256
4257 [Parameter(Mandatory = $true)]
4258 [string] $PivotTableName,
4259
4260 [Parameter(Mandatory = $false)]
4261 [array] $PivotRows,
4262
4263 [Parameter(Mandatory = $false)]
4264 [array] $PivotColumns,
4265
4266 [Parameter(Mandatory = $false)]
4267 [array] $PivotFilters,
4268
4269 [Parameter(Mandatory = $false)]
4270 [array] $PivotValues,
4271
4272 [Parameter(Mandatory = $false)]
4273 [array] $PivotPercentage,
4274
4275 [Parameter(Mandatory = $false)]
4276 [string] $PivotLocation = "R1C1"
4277 )
4278
4279 $excel.ScreenUpdating = $false
4280 $SrcWorksheet = $workbook.Sheets.Item($SrcSheetName)
4281 $workbook.ShowPivotTableFieldList = $false
4282
4283 # https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlpivottablesourcetype-enumeration-excel
4284 # https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlpivottableversionlist-enumeration-excel
4285 # https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlpivotfieldorientation-enumeration-excel
4286 # https://msdn.microsoft.com/en-us/vba/excel-vba/articles/constants-enumeration-excel
4287 # https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlsortorder-enumeration-excel
4288 # https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlpivotfiltertype-enumeration-excel
4289
4290 # xlDatabase = 1 # this just means local sheet data
4291 # xlPivotTableVersion12 = 3 # Excel 2007
4292 $PivotFailed = $false
4293 Try
4294 {
4295 $PivotCaches = $workbook.PivotCaches().Create([Microsoft.Office.Interop.Excel.XlPivotTableSourceType]::xlDatabase, $SrcWorksheet.UsedRange, [Microsoft.Office.Interop.Excel.XlPivotTableVersionList]::xlPivotTableVersion12)
4296 }
4297 Catch
4298 {
4299 $PivotFailed = $true
4300 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4301 }
4302 If ( $PivotFailed -eq $true )
4303 {
4304 $rows = $SrcWorksheet.UsedRange.Rows.Count
4305 If ($SrcSheetName -eq "Computer SPNs")
4306 {
4307 $PivotCols = "A1:B"
4308 }
4309 ElseIf ($SrcSheetName -eq "Users")
4310 {
4311 $PivotCols = "A1:AI"
4312 }
4313 $UsedRange = $SrcWorksheet.Range($PivotCols+$rows)
4314 $PivotCaches = $workbook.PivotCaches().Create([Microsoft.Office.Interop.Excel.XlPivotTableSourceType]::xlDatabase, $UsedRange, [Microsoft.Office.Interop.Excel.XlPivotTableVersionList]::xlPivotTableVersion12)
4315 Remove-Variable rows
4316 Remove-Variable PivotCols
4317 Remove-Variable UsedRange
4318 }
4319 Remove-Variable PivotFailed
4320 $PivotTable = $PivotCaches.CreatePivotTable($PivotLocation,$PivotTableName)
4321 # $workbook.ShowPivotTableFieldList = $true
4322
4323 If ($PivotRows)
4324 {
4325 ForEach ($Row in $PivotRows)
4326 {
4327 $PivotField = $PivotTable.PivotFields($Row)
4328 $PivotField.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlRowField
4329 }
4330 }
4331
4332 If ($PivotColumns)
4333 {
4334 ForEach ($Col in $PivotColumns)
4335 {
4336 $PivotField = $PivotTable.PivotFields($Col)
4337 $PivotField.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlColumnField
4338 }
4339 }
4340
4341 If ($PivotFilters)
4342 {
4343 ForEach ($Fil in $PivotFilters)
4344 {
4345 $PivotField = $PivotTable.PivotFields($Fil)
4346 $PivotField.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlPageField
4347 }
4348 }
4349
4350 If ($PivotValues)
4351 {
4352 ForEach ($Val in $PivotValues)
4353 {
4354 $PivotField = $PivotTable.PivotFields($Val)
4355 $PivotField.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlDataField
4356 }
4357 }
4358
4359 If ($PivotPercentage)
4360 {
4361 ForEach ($Val in $PivotPercentage)
4362 {
4363 $PivotField = $PivotTable.PivotFields($Val)
4364 $PivotField.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlDataField
4365 $PivotField.Calculation = [Microsoft.Office.Interop.Excel.XlPivotFieldCalculation]::xlPercentOfTotal
4366 $PivotTable.ShowValuesRow = $false
4367 }
4368 }
4369
4370 # $PivotFields.Caption = ""
4371 $excel.ScreenUpdating = $true
4372
4373 Get-ADRExcelComObjRelease -ComObjtoRelease $PivotField
4374 Remove-Variable PivotField
4375 Get-ADRExcelComObjRelease -ComObjtoRelease $PivotTable
4376 Remove-Variable PivotTable
4377 Get-ADRExcelComObjRelease -ComObjtoRelease $PivotCaches
4378 Remove-Variable PivotCaches
4379 Get-ADRExcelComObjRelease -ComObjtoRelease $SrcWorksheet
4380 Remove-Variable SrcWorksheet
4381}
4382
4383Function Get-ADRExcelAttributeStats
4384{
4385<#
4386.SYNOPSIS
4387 Helper to add Attribute Stats to the current WorkSheet.
4388
4389.DESCRIPTION
4390 Helper to add Attribute Stats to the current WorkSheet.
4391
4392.PARAMETER SrcSheetName
4393 [string]
4394 Source Sheet Name.
4395
4396.PARAMETER Title1
4397 [string]
4398 Title1.
4399
4400.PARAMETER Title2
4401 [string]
4402 Title2.
4403
4404.PARAMETER ObjAttributes
4405 [OrderedDictionary]
4406 Attributes.
4407#>
4408 param (
4409 [Parameter(Mandatory = $true)]
4410 [string] $SrcSheetName,
4411
4412 [Parameter(Mandatory = $true)]
4413 [string] $Title1,
4414
4415 [Parameter(Mandatory = $true)]
4416 [string] $Title2,
4417
4418 [Parameter(Mandatory = $true)]
4419 [System.Object] $ObjAttributes
4420 )
4421
4422 $excel.ScreenUpdating = $false
4423 $worksheet = $workbook.Worksheets.Item(1)
4424 $SrcWorksheet = $workbook.Sheets.Item($SrcSheetName)
4425
4426 $row = 1
4427 $column = 1
4428 $worksheet.Cells.Item($row, $column) = $Title1
4429 $worksheet.Cells.Item($row,$column).Style = "Heading 2"
4430 $worksheet.Cells.Item($row,$column).HorizontalAlignment = -4108
4431 $MergeCells = $worksheet.Range("A1:C1")
4432 $MergeCells.Select() | Out-Null
4433 $MergeCells.MergeCells = $true
4434 Remove-Variable MergeCells
4435
4436 Get-ADRExcelPivotTable -SrcSheetName $SrcSheetName -PivotTableName "User Status" -PivotRows @("Enabled") -PivotValues @("UserName") -PivotPercentage @("UserName") -PivotLocation "R2C1"
4437 $excel.ScreenUpdating = $false
4438
4439 $row = 2
4440 "Type","Count","Percentage" | ForEach-Object {
4441 $worksheet.Cells.Item($row, $column) = $_
4442 $worksheet.Cells.Item($row, $column).Font.Bold = $True
4443 $column++
4444 }
4445
4446 $row = 3
4447 $column = 1
4448 For($row = 3; $row -le 6; $row++)
4449 {
4450 $temptext = [string] $worksheet.Cells.Item($row, $column).Text
4451 switch ($temptext.ToUpper())
4452 {
4453 "TRUE" { $worksheet.Cells.Item($row, $column) = "Enabled" }
4454 "FALSE" { $worksheet.Cells.Item($row, $column) = "Disabled" }
4455 "GRAND TOTAL" { $worksheet.Cells.Item($row, $column) = "Total" }
4456 }
4457 }
4458
4459 $row = 1
4460 $column = 6
4461 $worksheet.Cells.Item($row, $column) = $Title2
4462 $worksheet.Cells.Item($row,$column).Style = "Heading 2"
4463 $worksheet.Cells.Item($row,$column).HorizontalAlignment = -4108
4464 $MergeCells = $worksheet.Range("F1:L1")
4465 $MergeCells.Select() | Out-Null
4466 $MergeCells.MergeCells = $true
4467 Remove-Variable MergeCells
4468
4469 $row++
4470 "Category","Enabled Count","Enabled Percentage","Disabled Count","Disabled Percentage","Total Count","Total Percentage" | ForEach-Object {
4471 $worksheet.Cells.Item($row, $column) = $_
4472 $worksheet.Cells.Item($row, $column).Font.Bold = $True
4473 $column++
4474 }
4475
4476 $ExcelColumn = ($SrcWorksheet.Columns.Find("Enabled"))
4477 $EnabledColAddress = "$($ExcelColumn.Address($false,$false).Substring(0,$ExcelColumn.Address($false,$false).Length-1)):$($ExcelColumn.Address($false,$false).Substring(0,$ExcelColumn.Address($false,$false).Length-1))"
4478
4479 $column = 6
4480 $i = 2
4481
4482 $ObjAttributes.keys | ForEach-Object {
4483 $ExcelColumn = ($SrcWorksheet.Columns.Find($_))
4484 $ColAddress = "$($ExcelColumn.Address($false,$false).Substring(0,$ExcelColumn.Address($false,$false).Length-1)):$($ExcelColumn.Address($false,$false).Substring(0,$ExcelColumn.Address($false,$false).Length-1))"
4485 $row++
4486 $i++
4487 If ($_ -eq "Delegation Typ")
4488 {
4489 $worksheet.Cells.Item($row, $column) = "Unconstrained Delegation"
4490 }
4491 ElseIf ($_ -eq "Delegation Type")
4492 {
4493 $worksheet.Cells.Item($row, $column) = "Constrained Delegation"
4494 }
4495 Else
4496 {
4497 $worksheet.Cells.Item($row, $column).Formula = '=' + $SrcWorksheet.Name + '!' + $ExcelColumn.Address($false,$false)
4498 }
4499 $worksheet.Cells.Item($row, $column+1).Formula = '=COUNTIFS(' + $SrcWorksheet.Name + '!' + $EnabledColAddress + ',"TRUE",' + $SrcWorksheet.Name + '!' + $ColAddress + ',' + $ObjAttributes[$_] + ')'
4500 $worksheet.Cells.Item($row, $column+2).Formula = '=IFERROR(G' + $i + '/VLOOKUP("Enabled",A3:B6,2,FALSE),0)'
4501 $worksheet.Cells.Item($row, $column+3).Formula = '=COUNTIFS(' + $SrcWorksheet.Name + '!' + $EnabledColAddress + ',"FALSE",' + $SrcWorksheet.Name + '!' + $ColAddress + ',' + $ObjAttributes[$_] + ')'
4502 $worksheet.Cells.Item($row, $column+4).Formula = '=IFERROR(I' + $i + '/VLOOKUP("Disabled",A3:B6,2,FALSE),0)'
4503 If ( ($_ -eq "SIDHistory") -or ($_ -eq "ms-ds-CreatorSid") )
4504 {
4505 $worksheet.Cells.Item($row, $column+5).Formula = '=COUNTIF(' + $SrcWorksheet.Name + '!' + $ColAddress + ',' + $ObjAttributes[$_] + ')-1'
4506 }
4507 Else
4508 {
4509 $worksheet.Cells.Item($row, $column+5).Formula = '=COUNTIF(' + $SrcWorksheet.Name + '!' + $ColAddress + ',' + $ObjAttributes[$_] + ')'
4510 }
4511 $worksheet.Cells.Item($row, $column+6).Formula = '=IFERROR(K' + $i + '/VLOOKUP("Total",A3:B6,2,FALSE),0)'
4512 }
4513
4514 # http://www.excelhowto.com/macros/formatting-a-range-of-cells-in-excel-vba/
4515 "H", "J" , "L" | ForEach-Object {
4516 $rng = $_ + $($row - $ObjAttributes.Count + 1) + ":" + $_ + $($row)
4517 $worksheet.Range($rng).NumberFormat = "0.00%"
4518 }
4519 $excel.ScreenUpdating = $true
4520
4521 Get-ADRExcelComObjRelease -ComObjtoRelease $SrcWorksheet
4522 Remove-Variable SrcWorksheet
4523 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
4524 Remove-Variable worksheet
4525}
4526
4527Function Get-ADRExcelChart
4528{
4529<#
4530.SYNOPSIS
4531 Helper to add charts to the current WorkSheet.
4532
4533.DESCRIPTION
4534 Helper to add charts to the current WorkSheet.
4535
4536.PARAMETER ChartType
4537 [int]
4538 Chart Type.
4539
4540.PARAMETER ChartLayout
4541 [int]
4542 Chart Layout.
4543
4544.PARAMETER ChartTitle
4545 [string]
4546 Title of the Chart.
4547
4548.PARAMETER RangetoCover
4549 WorkSheet Range to be covered by the Chart.
4550
4551.PARAMETER ChartData
4552 Data for the Chart.
4553
4554.PARAMETER StartRow
4555 Start row to calculate data for the Chart.
4556
4557.PARAMETER StartColumn
4558 Start column to calculate data for the Chart.
4559#>
4560 param (
4561 [Parameter(Mandatory = $true)]
4562 [string] $ChartType,
4563
4564 [Parameter(Mandatory = $true)]
4565 [int] $ChartLayout,
4566
4567 [Parameter(Mandatory = $true)]
4568 [string] $ChartTitle,
4569
4570 [Parameter(Mandatory = $true)]
4571 $RangetoCover,
4572
4573 [Parameter(Mandatory = $false)]
4574 $ChartData = $null,
4575
4576 [Parameter(Mandatory = $false)]
4577 $StartRow = $null,
4578
4579 [Parameter(Mandatory = $false)]
4580 $StartColumn = $null
4581 )
4582
4583 $excel.ScreenUpdating = $false
4584 $excel.DisplayAlerts = $false
4585 $worksheet = $workbook.Worksheets.Item(1)
4586 $chart = $worksheet.Shapes.AddChart().Chart
4587 # https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlcharttype-enumeration-excel
4588 $chart.chartType = [int]([Microsoft.Office.Interop.Excel.XLChartType]::$ChartType)
4589 $chart.ApplyLayout($ChartLayout)
4590 If ($null -eq $ChartData)
4591 {
4592 If ($null -eq $StartRow)
4593 {
4594 $start = $worksheet.Range("A1")
4595 }
4596 Else
4597 {
4598 $start = $worksheet.Range($StartRow)
4599 }
4600 # get the last cell
4601 $X = $worksheet.Range($start,$start.End([Microsoft.Office.Interop.Excel.XLDirection]::xlDown))
4602 If ($null -eq $StartColumn)
4603 {
4604 $start = $worksheet.Range("B1")
4605 }
4606 Else
4607 {
4608 $start = $worksheet.Range($StartColumn)
4609 }
4610 # get the last cell
4611 $Y = $worksheet.Range($start,$start.End([Microsoft.Office.Interop.Excel.XLDirection]::xlDown))
4612 $ChartData = $worksheet.Range($X,$Y)
4613
4614 Get-ADRExcelComObjRelease -ComObjtoRelease $X
4615 Remove-Variable X
4616 Get-ADRExcelComObjRelease -ComObjtoRelease $Y
4617 Remove-Variable Y
4618 Get-ADRExcelComObjRelease -ComObjtoRelease $start
4619 Remove-Variable start
4620 }
4621 $chart.SetSourceData($ChartData)
4622 # https://docs.microsoft.com/en-us/dotnet/api/microsoft.office.interop.excel.chartclass.plotby?redirectedfrom=MSDN&view=excel-pia#Microsoft_Office_Interop_Excel_ChartClass_PlotBy
4623 $chart.PlotBy = [Microsoft.Office.Interop.Excel.XlRowCol]::xlColumns
4624 $chart.seriesCollection(1).Select() | Out-Null
4625 $chart.SeriesCollection(1).ApplyDataLabels() | out-Null
4626 # modify the chart title
4627 $chart.HasTitle = $True
4628 $chart.ChartTitle.Text = $ChartTitle
4629 # Reposition the Chart
4630 $temp = $worksheet.Range($RangetoCover)
4631 # $chart.parent.placement = 3
4632 $chart.parent.top = $temp.Top
4633 $chart.parent.left = $temp.Left
4634 $chart.parent.width = $temp.Width
4635 If ($ChartTitle -ne "Privileged Groups in AD")
4636 {
4637 $chart.parent.height = $temp.Height
4638 }
4639 # $chart.Legend.Delete()
4640 $excel.ScreenUpdating = $true
4641 $excel.DisplayAlerts = $true
4642
4643 Get-ADRExcelComObjRelease -ComObjtoRelease $chart
4644 Remove-Variable chart
4645 Get-ADRExcelComObjRelease -ComObjtoRelease $ChartData
4646 Remove-Variable ChartData
4647 Get-ADRExcelComObjRelease -ComObjtoRelease $temp
4648 Remove-Variable temp
4649 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
4650 Remove-Variable worksheet
4651}
4652
4653Function Get-ADRExcelSort
4654{
4655<#
4656.SYNOPSIS
4657 Sorts a WorkSheet in the active Workbook.
4658
4659.DESCRIPTION
4660 Sorts a WorkSheet in the active Workbook.
4661
4662.PARAMETER ColumnName
4663 [string]
4664 Name of the Column.
4665#>
4666 param (
4667 [Parameter(Mandatory = $true)]
4668 [string] $ColumnName
4669 )
4670
4671 $worksheet = $workbook.Worksheets.Item(1)
4672 $worksheet.Activate();
4673
4674 $ExcelColumn = ($worksheet.Columns.Find($ColumnName))
4675 If ($ExcelColumn)
4676 {
4677 If ($ExcelColumn.Text -ne $ColumnName)
4678 {
4679 $BeginAddress = $ExcelColumn.Address(0,0,1,1)
4680 $End = $False
4681 Do {
4682 Write-Verbose "[Get-ADRExcelSort] $($ExcelColumn.Text) selected instead of $($ColumnName) in the $($worksheet.Name) worksheet."
4683 $ExcelColumn = ($worksheet.Columns.FindNext($ExcelColumn))
4684 $Address = $ExcelColumn.Address(0,0,1,1)
4685 If ( ($Address -eq $BeginAddress) -or ($ExcelColumn.Text -eq $ColumnName) )
4686 {
4687 $End = $True
4688 }
4689 } Until ($End -eq $True)
4690 }
4691 If ($ExcelColumn.Text -eq $ColumnName)
4692 {
4693 # Sort by Column
4694 $workSheet.ListObjects.Item(1).Sort.SortFields.Clear()
4695 $workSheet.ListObjects.Item(1).Sort.SortFields.Add($ExcelColumn) | Out-Null
4696 $worksheet.ListObjects.Item(1).Sort.Apply()
4697 }
4698 Else
4699 {
4700 Write-Verbose "[Get-ADRExcelSort] $($ColumnName) not found in the $($worksheet.Name) worksheet."
4701 }
4702 }
4703 Else
4704 {
4705 Write-Verbose "[Get-ADRExcelSort] $($ColumnName) not found in the $($worksheet.Name) worksheet."
4706 }
4707 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
4708 Remove-Variable worksheet
4709}
4710
4711Function Export-ADRExcel
4712{
4713<#
4714.SYNOPSIS
4715 Automates the generation of the ADRecon report.
4716
4717.DESCRIPTION
4718 Automates the generation of the ADRecon report. If specific files exist, they are imported into the ADRecon report.
4719
4720.PARAMETER ExcelPath
4721 [string]
4722 Path for ADRecon output folder containing the CSV files to generate the ADRecon-Report.xlsx
4723
4724.OUTPUTS
4725 Creates the ADRecon-Report.xlsx report in the folder.
4726#>
4727 param(
4728 [Parameter(Mandatory = $true)]
4729 [string] $ExcelPath
4730 )
4731
4732 $ExcelPath = $((Convert-Path $ExcelPath).TrimEnd("\"))
4733 $ReportPath = -join($ExcelPath,'\','CSV-Files')
4734 If (!(Test-Path $ReportPath))
4735 {
4736 Write-Warning "[Export-ADRExcel] Could not locate the CSV-Files directory ... Exiting"
4737 Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
4738 Return $null
4739 }
4740 Get-ADRExcelComObj
4741 If ($excel)
4742 {
4743 Write-Output "[*] Generating ADRecon-Report.xlsx"
4744
4745 $ADFileName = -join($ReportPath,'\','AboutADRecon.csv')
4746 If (Test-Path $ADFileName)
4747 {
4748 Get-ADRExcelImport -ADFileName $ADFileName
4749 Remove-Variable ADFileName
4750
4751 $workbook.Worksheets.Item(1).Name = "About ADRecon"
4752 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(3,2) , "https://github.com/sense-of-security/ADRecon", "" , "", "github.com/sense-of-security/ADRecon") | Out-Null
4753 $workbook.Worksheets.Item(1).UsedRange.EntireColumn.AutoFit() | Out-Null
4754 }
4755
4756 $ADFileName = -join($ReportPath,'\','Forest.csv')
4757 If (Test-Path $ADFileName)
4758 {
4759 Get-ADRExcelWorkbook -Name "Forest"
4760 Get-ADRExcelImport -ADFileName $ADFileName
4761 Remove-Variable ADFileName
4762 }
4763
4764 $ADFileName = -join($ReportPath,'\','Domain.csv')
4765 If (Test-Path $ADFileName)
4766 {
4767 Get-ADRExcelWorkbook -Name "Domain"
4768 Get-ADRExcelImport -ADFileName $ADFileName
4769 $DomainObj = Import-CSV -Path $ADFileName
4770 Remove-Variable ADFileName
4771 $DomainName = -join($DomainObj[0].Value,"-")
4772 Remove-Variable DomainObj
4773 }
4774
4775 $ADFileName = -join($ReportPath,'\','Trusts.csv')
4776 If (Test-Path $ADFileName)
4777 {
4778 Get-ADRExcelWorkbook -Name "Trusts"
4779 Get-ADRExcelImport -ADFileName $ADFileName
4780 Remove-Variable ADFileName
4781 }
4782
4783 $ADFileName = -join($ReportPath,'\','Subnets.csv')
4784 If (Test-Path $ADFileName)
4785 {
4786 Get-ADRExcelWorkbook -Name "Subnets"
4787 Get-ADRExcelImport -ADFileName $ADFileName
4788 Remove-Variable ADFileName
4789 }
4790
4791 $ADFileName = -join($ReportPath,'\','Sites.csv')
4792 If (Test-Path $ADFileName)
4793 {
4794 Get-ADRExcelWorkbook -Name "Sites"
4795 Get-ADRExcelImport -ADFileName $ADFileName
4796 Remove-Variable ADFileName
4797 }
4798
4799 $ADFileName = -join($ReportPath,'\','FineGrainedPasswordPolicy.csv')
4800 If (Test-Path $ADFileName)
4801 {
4802 Get-ADRExcelWorkbook -Name "Fine Grained Password Policy"
4803 Get-ADRExcelImport -ADFileName $ADFileName
4804 Remove-Variable ADFileName
4805 }
4806
4807 $ADFileName = -join($ReportPath,'\','DefaultPasswordPolicy.csv')
4808 If (Test-Path $ADFileName)
4809 {
4810 Get-ADRExcelWorkbook -Name "Default Password Policy"
4811 Get-ADRExcelImport -ADFileName $ADFileName
4812 Remove-Variable ADFileName
4813
4814 $excel.ScreenUpdating = $false
4815 $worksheet = $workbook.Worksheets.Item(1)
4816 # https://docs.microsoft.com/en-us/office/vba/api/excel.xlhalign
4817 $worksheet.Range("B2:G10").HorizontalAlignment = -4108
4818 # https://docs.microsoft.com/en-us/office/vba/api/excel.range.borderaround
4819
4820 "A2:B10", "C2:D10", "E2:F10", "G2:G10" | ForEach-Object {
4821 $worksheet.Range($_).BorderAround(1) | Out-Null
4822 }
4823
4824 # https://docs.microsoft.com/en-us/dotnet/api/microsoft.office.interop.excel.formatconditions.add?view=excel-pia
4825 # $worksheet.Range().FormatConditions.Add
4826 # http://dmcritchie.mvps.org/excel/colors.htm
4827 # Values for Font.ColorIndex
4828
4829 $ObjValues = @(
4830 # PCI Enforce password history (passwords)
4831 "C2", '=IF(B2<4,TRUE, FALSE)'
4832
4833 # PCI Maximum password age (days)
4834 "C3", '=IF(OR(B3=0,B3>90),TRUE, FALSE)'
4835
4836 # PCI Minimum password age (days)
4837
4838 # PCI Minimum password length (characters)
4839 "C5", '=IF(B5<7,TRUE, FALSE)'
4840
4841 # PCI Password must meet complexity requirements
4842 "C6", '=IF(B6<>TRUE,TRUE, FALSE)'
4843
4844 # PCI Store password using reversible encryption for all users in the domain
4845
4846 # PCI Account lockout duration (mins)
4847 "C8", '=IF(AND(B8>=1,B8<30),TRUE, FALSE)'
4848
4849 # PCI Account lockout threshold (attempts)
4850 "C9", '=IF(OR(B9=0,B9>6),TRUE, FALSE)'
4851
4852 # PCI Reset account lockout counter after (mins)
4853
4854 # ASD ISM Enforce password history (passwords)
4855 "E2", '=IF(B2<8,TRUE, FALSE)'
4856
4857 # ASD ISM Maximum password age (days)
4858 "E3", '=IF(OR(B3=0,B3>90),TRUE, FALSE)'
4859
4860 # ASD ISM Minimum password age (days)
4861 "E4", '=IF(B4=0,TRUE, FALSE)'
4862
4863 # ASD ISM Minimum password length (characters)
4864 "E5", '=IF(B5<13,TRUE, FALSE)'
4865
4866 # ASD ISM Password must meet complexity requirements
4867 "E6", '=IF(B6<>TRUE,TRUE, FALSE)'
4868
4869 # ASD ISM Store password using reversible encryption for all users in the domain
4870
4871 # ASD ISM Account lockout duration (mins)
4872
4873 # ASD ISM Account lockout threshold (attempts)
4874 "E9", '=IF(OR(B9=0,B9>5),TRUE, FALSE)'
4875
4876 # ASD ISM Reset account lockout counter after (mins)
4877
4878 # CIS Benchmark Enforce password history (passwords)
4879 "G2", '=IF(B2<24,TRUE, FALSE)'
4880
4881 # CIS Benchmark Maximum password age (days)
4882 "G3", '=IF(OR(B3=0,B3>60),TRUE, FALSE)'
4883
4884 # CIS Benchmark Minimum password age (days)
4885 "G4", '=IF(B4=0,TRUE, FALSE)'
4886
4887 # CIS Benchmark Minimum password length (characters)
4888 "G5", '=IF(B5<14,TRUE, FALSE)'
4889
4890 # CIS Benchmark Password must meet complexity requirements
4891 "G6", '=IF(B6<>TRUE,TRUE, FALSE)'
4892
4893 # CIS Benchmark Store password using reversible encryption for all users in the domain
4894 "G7", '=IF(B7<>FALSE,TRUE, FALSE)'
4895
4896 # CIS Benchmark Account lockout duration (mins)
4897 "G8", '=IF(AND(B8>=1,B8<15),TRUE, FALSE)'
4898
4899 # CIS Benchmark Account lockout threshold (attempts)
4900 "G9", '=IF(OR(B9=0,B9>10),TRUE, FALSE)'
4901
4902 # CIS Benchmark Reset account lockout counter after (mins)
4903 "G10", '=IF(B10<15,TRUE, FALSE)' )
4904
4905 For ($i = 0; $i -lt $($ObjValues.Count); $i++)
4906 {
4907 $worksheet.Range($ObjValues[$i]).FormatConditions.Add([Microsoft.Office.Interop.Excel.XlFormatConditionType]::xlExpression, 0, $ObjValues[$i+1]) | Out-Null
4908 $i++
4909 }
4910
4911 "C2", "C3" , "C5", "C6", "C8", "C9", "E2", "E3" , "E4", "E5", "E6", "E9", "G2", "G3", "G4", "G5", "G6", "G7", "G8", "G9", "G10" | ForEach-Object {
4912 $worksheet.Range($_).FormatConditions.Item(1).StopIfTrue = $false
4913 $worksheet.Range($_).FormatConditions.Item(1).Font.ColorIndex = 3
4914 }
4915
4916 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,4) , "https://www.pcisecuritystandards.org/document_library?category=pcidss&document=pci_dss", "" , "", "PCI DSS v3.2.1") | Out-Null
4917 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,6) , "https://acsc.gov.au/infosec/ism/", "" , "", "2018 ISM Controls") | Out-Null
4918 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,7) , "https://www.cisecurity.org/benchmark/microsoft_windows_server/", "" , "", "CIS Benchmark 2016") | Out-Null
4919
4920 $excel.ScreenUpdating = $true
4921 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
4922 Remove-Variable worksheet
4923 }
4924
4925 $ADFileName = -join($ReportPath,'\','DomainControllers.csv')
4926 If (Test-Path $ADFileName)
4927 {
4928 Get-ADRExcelWorkbook -Name "Domain Controllers"
4929 Get-ADRExcelImport -ADFileName $ADFileName
4930 Remove-Variable ADFileName
4931 }
4932
4933 $ADFileName = -join($ReportPath,'\','DACLs.csv')
4934 If (Test-Path $ADFileName)
4935 {
4936 Get-ADRExcelWorkbook -Name "DACLs"
4937 Get-ADRExcelImport -ADFileName $ADFileName
4938 Remove-Variable ADFileName
4939 }
4940
4941 $ADFileName = -join($ReportPath,'\','SACLs.csv')
4942 If (Test-Path $ADFileName)
4943 {
4944 Get-ADRExcelWorkbook -Name "SACLs"
4945 Get-ADRExcelImport -ADFileName $ADFileName
4946 Remove-Variable ADFileName
4947 }
4948
4949 $ADFileName = -join($ReportPath,'\','GPOs.csv')
4950 If (Test-Path $ADFileName)
4951 {
4952 Get-ADRExcelWorkbook -Name "GPOs"
4953 Get-ADRExcelImport -ADFileName $ADFileName
4954 Remove-Variable ADFileName
4955 }
4956
4957 $ADFileName = -join($ReportPath,'\','gPLinks.csv')
4958 If (Test-Path $ADFileName)
4959 {
4960 Get-ADRExcelWorkbook -Name "gPLinks"
4961 Get-ADRExcelImport -ADFileName $ADFileName
4962 Remove-Variable ADFileName
4963 }
4964
4965 $ADFileName = -join($ReportPath,'\','DNSNodes','.csv')
4966 If (Test-Path $ADFileName)
4967 {
4968 Get-ADRExcelWorkbook -Name "DNS Records"
4969 Get-ADRExcelImport -ADFileName $ADFileName
4970 Remove-Variable ADFileName
4971 }
4972
4973 $ADFileName = -join($ReportPath,'\','DNSZones.csv')
4974 If (Test-Path $ADFileName)
4975 {
4976 Get-ADRExcelWorkbook -Name "DNS Zones"
4977 Get-ADRExcelImport -ADFileName $ADFileName
4978 Remove-Variable ADFileName
4979 }
4980
4981 $ADFileName = -join($ReportPath,'\','Printers.csv')
4982 If (Test-Path $ADFileName)
4983 {
4984 Get-ADRExcelWorkbook -Name "Printers"
4985 Get-ADRExcelImport -ADFileName $ADFileName
4986 Remove-Variable ADFileName
4987 }
4988
4989 $ADFileName = -join($ReportPath,'\','BitLockerRecoveryKeys.csv')
4990 If (Test-Path $ADFileName)
4991 {
4992 Get-ADRExcelWorkbook -Name "BitLocker"
4993 Get-ADRExcelImport -ADFileName $ADFileName
4994 Remove-Variable ADFileName
4995 }
4996
4997 $ADFileName = -join($ReportPath,'\','LAPS.csv')
4998 If (Test-Path $ADFileName)
4999 {
5000 Get-ADRExcelWorkbook -Name "LAPS"
5001 Get-ADRExcelImport -ADFileName $ADFileName
5002 Remove-Variable ADFileName
5003 }
5004
5005 $ADFileName = -join($ReportPath,'\','ComputerSPNs.csv')
5006 If (Test-Path $ADFileName)
5007 {
5008 Get-ADRExcelWorkbook -Name "Computer SPNs"
5009 Get-ADRExcelImport -ADFileName $ADFileName
5010 Remove-Variable ADFileName
5011
5012 Get-ADRExcelSort -ColumnName "Name"
5013 }
5014
5015 $ADFileName = -join($ReportPath,'\','Computers.csv')
5016 If (Test-Path $ADFileName)
5017 {
5018 Get-ADRExcelWorkbook -Name "Computers"
5019 Get-ADRExcelImport -ADFileName $ADFileName
5020 Remove-Variable ADFileName
5021
5022 Get-ADRExcelSort -ColumnName "UserName"
5023
5024 $worksheet = $workbook.Worksheets.Item(1)
5025 # Freeze First Row and Column
5026 $worksheet.Select()
5027 $worksheet.Application.ActiveWindow.splitcolumn = 1
5028 $worksheet.Application.ActiveWindow.splitrow = 1
5029 $worksheet.Application.ActiveWindow.FreezePanes = $true
5030
5031 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
5032 Remove-Variable worksheet
5033 }
5034
5035 $ADFileName = -join($ReportPath,'\','OUs.csv')
5036 If (Test-Path $ADFileName)
5037 {
5038 Get-ADRExcelWorkbook -Name "OUs"
5039 Get-ADRExcelImport -ADFileName $ADFileName
5040 Remove-Variable ADFileName
5041 }
5042
5043 $ADFileName = -join($ReportPath,'\','UserSPNs.csv')
5044 If (Test-Path $ADFileName)
5045 {
5046 Get-ADRExcelWorkbook -Name "User SPNs"
5047 Get-ADRExcelImport -ADFileName $ADFileName
5048 Remove-Variable ADFileName
5049 }
5050
5051 $ADFileName = -join($ReportPath,'\','Groups.csv')
5052 If (Test-Path $ADFileName)
5053 {
5054 Get-ADRExcelWorkbook -Name "Groups"
5055 Get-ADRExcelImport -ADFileName $ADFileName
5056 Remove-Variable ADFileName
5057
5058 Get-ADRExcelSort -ColumnName "DistinguishedName"
5059 }
5060
5061 $ADFileName = -join($ReportPath,'\','GroupMembers.csv')
5062 If (Test-Path $ADFileName)
5063 {
5064 Get-ADRExcelWorkbook -Name "Group Members"
5065 Get-ADRExcelImport -ADFileName $ADFileName
5066 Remove-Variable ADFileName
5067
5068 Get-ADRExcelSort -ColumnName "Group Name"
5069 }
5070
5071 $ADFileName = -join($ReportPath,'\','Users.csv')
5072 If (Test-Path $ADFileName)
5073 {
5074 Get-ADRExcelWorkbook -Name "Users"
5075 Get-ADRExcelImport -ADFileName $ADFileName
5076 Remove-Variable ADFileName
5077
5078 Get-ADRExcelSort -ColumnName "UserName"
5079
5080 $worksheet = $workbook.Worksheets.Item(1)
5081
5082 # Freeze First Row and Column
5083 $worksheet.Select()
5084 $worksheet.Application.ActiveWindow.splitcolumn = 1
5085 $worksheet.Application.ActiveWindow.splitrow = 1
5086 $worksheet.Application.ActiveWindow.FreezePanes = $true
5087
5088 $worksheet.Cells.Item(1,3).Interior.ColorIndex = 5
5089 $worksheet.Cells.Item(1,3).font.ColorIndex = 2
5090 # Set Filter to Enabled Accounts only
5091 $worksheet.UsedRange.Select() | Out-Null
5092 $excel.Selection.AutoFilter(3,$true) | Out-Null
5093 $worksheet.Cells.Item(1,1).Select() | Out-Null
5094 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
5095 Remove-Variable worksheet
5096 }
5097
5098 # Computer Role Stats
5099 $ADFileName = -join($ReportPath,'\','ComputerSPNs.csv')
5100 If (Test-Path $ADFileName)
5101 {
5102 Get-ADRExcelWorkbook -Name "Computer Role Stats"
5103 Remove-Variable ADFileName
5104
5105 $worksheet = $workbook.Worksheets.Item(1)
5106 $PivotTableName = "Computer SPNs"
5107 Get-ADRExcelPivotTable -SrcSheetName "Computer SPNs" -PivotTableName $PivotTableName -PivotRows @("Service") -PivotValues @("Service")
5108
5109 $worksheet.Cells.Item(1,1) = "Computer Role"
5110 $worksheet.Cells.Item(1,2) = "Count"
5111
5112 # https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlsortorder-enumeration-excel
5113 $worksheet.PivotTables($PivotTableName).PivotFields("Service").AutoSort([Microsoft.Office.Interop.Excel.XlSortOrder]::xlDescending,"Count")
5114
5115 Get-ADRExcelChart -ChartType "xlColumnClustered" -ChartLayout 10 -ChartTitle "Computer Roles in AD" -RangetoCover "D2:U16"
5116 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,4) , "" , "'Computer SPNs'!A1", "", "Raw Data") | Out-Null
5117 $excel.Windows.Item(1).Displaygridlines = $false
5118 Remove-Variable PivotTableName
5119
5120 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
5121 Remove-Variable worksheet
5122 }
5123
5124 # Operating System Stats
5125 $ADFileName = -join($ReportPath,'\','Computers.csv')
5126 If (Test-Path $ADFileName)
5127 {
5128 Get-ADRExcelWorkbook -Name "Operating System Stats"
5129 Remove-Variable ADFileName
5130
5131 $worksheet = $workbook.Worksheets.Item(1)
5132 $PivotTableName = "Operating Systems"
5133 Get-ADRExcelPivotTable -SrcSheetName "Computers" -PivotTableName $PivotTableName -PivotRows @("Operating System") -PivotValues @("Operating System")
5134
5135 $worksheet.Cells.Item(1,1) = "Operating System"
5136 $worksheet.Cells.Item(1,2) = "Count"
5137
5138 # https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlsortorder-enumeration-excel
5139 $worksheet.PivotTables($PivotTableName).PivotFields("Operating System").AutoSort([Microsoft.Office.Interop.Excel.XlSortOrder]::xlDescending,"Count")
5140
5141 Get-ADRExcelChart -ChartType "xlColumnClustered" -ChartLayout 10 -ChartTitle "Operating Systems in AD" -RangetoCover "D2:S16"
5142 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,4) , "" , "Computers!A1", "", "Raw Data") | Out-Null
5143 $excel.Windows.Item(1).Displaygridlines = $false
5144 Remove-Variable PivotTableName
5145
5146 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
5147 Remove-Variable worksheet
5148 }
5149
5150 # Group Stats
5151 $ADFileName = -join($ReportPath,'\','GroupMembers.csv')
5152 If (Test-Path $ADFileName)
5153 {
5154 Get-ADRExcelWorkbook -Name "Privileged Group Stats"
5155 Remove-Variable ADFileName
5156
5157 $worksheet = $workbook.Worksheets.Item(1)
5158 $PivotTableName = "Group Members"
5159 Get-ADRExcelPivotTable -SrcSheetName "Group Members" -PivotTableName $PivotTableName -PivotRows @("Group Name")-PivotFilters @("AccountType") -PivotValues @("AccountType")
5160
5161 # Set the filter
5162 $worksheet.PivotTables($PivotTableName).PivotFields("AccountType").CurrentPage = "user"
5163
5164 $worksheet.Cells.Item(1,2).Interior.ColorIndex = 5
5165 $worksheet.Cells.Item(1,2).font.ColorIndex = 2
5166
5167 $worksheet.Cells.Item(3,1) = "Group Name"
5168 $worksheet.Cells.Item(3,2) = "Count (Not-Recursive)"
5169
5170 $excel.ScreenUpdating = $false
5171 # Create a copy of the Pivot Table
5172 $PivotTableTemp = ($workbook.PivotCaches().Item($workbook.PivotCaches().Count)).CreatePivotTable("R1C5","PivotTableTemp")
5173 $PivotFieldTemp = $PivotTableTemp.PivotFields("Group Name")
5174 # Set a filter
5175 $PivotFieldTemp.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlPageField
5176 Try
5177 {
5178 $PivotFieldTemp.CurrentPage = "Domain Admins"
5179 }
5180 Catch
5181 {
5182 # No Direct Domain Admins. Good Job!
5183 $NoDA = $true
5184 }
5185 If ($NoDA)
5186 {
5187 Try
5188 {
5189 $PivotFieldTemp.CurrentPage = "Administrators"
5190 }
5191 Catch
5192 {
5193 # No Direct Administrators
5194 }
5195 }
5196 # Create a Slicer
5197 $PivotSlicer = $workbook.SlicerCaches.Add($PivotTableTemp,$PivotFieldTemp)
5198 # Add Original Pivot Table to the Slicer
5199 $PivotSlicer.PivotTables.AddPivotTable($worksheet.PivotTables($PivotTableName))
5200 # Delete the Slicer
5201 $PivotSlicer.Delete()
5202 # Delete the Pivot Table Copy
5203 $PivotTableTemp.TableRange2.Delete() | Out-Null
5204
5205 Get-ADRExcelComObjRelease -ComObjtoRelease $PivotFieldTemp
5206 Get-ADRExcelComObjRelease -ComObjtoRelease $PivotSlicer
5207 Get-ADRExcelComObjRelease -ComObjtoRelease $PivotTableTemp
5208
5209 Remove-Variable PivotFieldTemp
5210 Remove-Variable PivotSlicer
5211 Remove-Variable PivotTableTemp
5212
5213 "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 {
5214 Try
5215 {
5216 $worksheet.PivotTables($PivotTableName).PivotFields("Group Name").PivotItems($_).Visible = $true
5217 }
5218 Catch
5219 {
5220 # when PivotItem is not found
5221 }
5222 }
5223
5224 # https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlsortorder-enumeration-excel
5225 $worksheet.PivotTables($PivotTableName).PivotFields("Group Name").AutoSort([Microsoft.Office.Interop.Excel.XlSortOrder]::xlDescending,"Count (Not-Recursive)")
5226
5227 $worksheet.Cells.Item(3,1).Interior.ColorIndex = 5
5228 $worksheet.Cells.Item(3,1).font.ColorIndex = 2
5229
5230 $excel.ScreenUpdating = $true
5231
5232 Get-ADRExcelChart -ChartType "xlColumnClustered" -ChartLayout 10 -ChartTitle "Privileged Groups in AD" -RangetoCover "D2:P16" -StartRow "A3" -StartColumn "B3"
5233 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,4) , "" , "'Group Members'!A1", "", "Raw Data") | Out-Null
5234 $excel.Windows.Item(1).Displaygridlines = $false
5235
5236 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
5237 Remove-Variable worksheet
5238 }
5239
5240 # Computer Stats
5241 $ADFileName = -join($ReportPath,'\','Computers.csv')
5242 If (Test-Path $ADFileName)
5243 {
5244 Get-ADRExcelWorkbook -Name "Computer Stats"
5245 Remove-Variable ADFileName
5246
5247 $ObjAttributes = New-Object System.Collections.Specialized.OrderedDictionary
5248 $ObjAttributes.Add("Delegation Typ",'"Unconstrained"')
5249 $ObjAttributes.Add("Delegation Type",'"Constrained"')
5250 $ObjAttributes.Add("SIDHistory",'"*"')
5251 $ObjAttributes.Add("Dormant",'"TRUE"')
5252 $ObjAttributes.Add("Password Age (> ",'"TRUE"')
5253 $ObjAttributes.Add("ms-ds-CreatorSid",'"*"')
5254
5255 Get-ADRExcelAttributeStats -SrcSheetName "Computers" -Title1 "Computer Accounts in AD" -Title2 "Status of Computer Accounts" -ObjAttributes $ObjAttributes
5256 Remove-Variable ObjAttributes
5257
5258 Get-ADRExcelChart -ChartType "xlPie" -ChartLayout 3 -ChartTitle "Computer Accounts in AD" -RangetoCover "A11:D23" -ChartData $workbook.Worksheets.Item(1).Range("A3:A4,B3:B4")
5259 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(10,1) , "" , "Computers!A1", "", "Raw Data") | Out-Null
5260
5261 Get-ADRExcelChart -ChartType "xlBarClustered" -ChartLayout 1 -ChartTitle "Status of Computer Accounts" -RangetoCover "F11:L23" -ChartData $workbook.Worksheets.Item(1).Range("F2:F8,G2:G8")
5262 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(10,6) , "" , "Computers!A1", "", "Raw Data") | Out-Null
5263
5264 $workbook.Worksheets.Item(1).UsedRange.EntireColumn.AutoFit() | Out-Null
5265 $excel.Windows.Item(1).Displaygridlines = $false
5266 }
5267
5268 # User Stats
5269 $ADFileName = -join($ReportPath,'\','Users.csv')
5270 If (Test-Path $ADFileName)
5271 {
5272 Get-ADRExcelWorkbook -Name "User Stats"
5273 Remove-Variable ADFileName
5274
5275 $ObjAttributes = New-Object System.Collections.Specialized.OrderedDictionary
5276 $ObjAttributes.Add("Must Change Password at Logon",'"TRUE"')
5277 $ObjAttributes.Add("Cannot Change Password",'"TRUE"')
5278 $ObjAttributes.Add("Password Never Expires",'"TRUE"')
5279 $ObjAttributes.Add("Reversible Password Encryption",'"TRUE"')
5280 $ObjAttributes.Add("Smartcard Logon Required",'"TRUE"')
5281 $ObjAttributes.Add("Delegation Permitted",'"TRUE"')
5282 $ObjAttributes.Add("Kerberos DES Only",'"TRUE"')
5283 $ObjAttributes.Add("Kerberos RC4",'"TRUE"')
5284 $ObjAttributes.Add("Does Not Require Pre Auth",'"TRUE"')
5285 $ObjAttributes.Add("Password Age (> ",'"TRUE"')
5286 $ObjAttributes.Add("Account Locked Out",'"TRUE"')
5287 $ObjAttributes.Add("Never Logged in",'"TRUE"')
5288 $ObjAttributes.Add("Dormant",'"TRUE"')
5289 $ObjAttributes.Add("Password Not Required",'"TRUE"')
5290 $ObjAttributes.Add("Delegation Typ",'"Unconstrained"')
5291 $ObjAttributes.Add("SIDHistory",'"*"')
5292
5293 Get-ADRExcelAttributeStats -SrcSheetName "Users" -Title1 "User Accounts in AD" -Title2 "Status of User Accounts" -ObjAttributes $ObjAttributes
5294 Remove-Variable ObjAttributes
5295
5296 Get-ADRExcelChart -ChartType "xlPie" -ChartLayout 3 -ChartTitle "User Accounts in AD" -RangetoCover "A21:D33" -ChartData $workbook.Worksheets.Item(1).Range("A3:A4,B3:B4")
5297 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(20,1) , "" , "Users!A1", "", "Raw Data") | Out-Null
5298
5299 Get-ADRExcelChart -ChartType "xlBarClustered" -ChartLayout 1 -ChartTitle "Status of User Accounts" -RangetoCover "F21:L43" -ChartData $workbook.Worksheets.Item(1).Range("F2:F18,G2:G18")
5300 $workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(20,6) , "" , "Users!A1", "", "Raw Data") | Out-Null
5301
5302 $workbook.Worksheets.Item(1).UsedRange.EntireColumn.AutoFit() | Out-Null
5303 $excel.Windows.Item(1).Displaygridlines = $false
5304 }
5305
5306 # Create Table of Contents
5307 Get-ADRExcelWorkbook -Name "Table of Contents"
5308 $worksheet = $workbook.Worksheets.Item(1)
5309
5310 $excel.ScreenUpdating = $false
5311 # Image format and properties
5312 # $path = "C:\SOS_Logo.jpg"
5313 # $base64sos = [convert]::ToBase64String((Get-Content $path -Encoding byte))
5314
5315 $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="
5316
5317 $bytes = [System.Convert]::FromBase64String($base64sos)
5318 Remove-Variable base64sos
5319
5320 $CompanyLogo = -join($ReportPath,'\','SOS_Logo.jpg')
5321 $p = New-Object IO.MemoryStream($bytes, 0, $bytes.length)
5322 $p.Write($bytes, 0, $bytes.length)
5323 Add-Type -AssemblyName System.Drawing
5324 $picture = [System.Drawing.Image]::FromStream($p, $true)
5325 $picture.Save($CompanyLogo)
5326
5327 Remove-Variable bytes
5328 Remove-Variable p
5329 Remove-Variable picture
5330
5331 $LinkToFile = $false
5332 $SaveWithDocument = $true
5333 $Left = 0
5334 $Top = 0
5335 $Width = 135
5336 $Height = 50
5337
5338 # Add image to the Sheet
5339 $worksheet.Shapes.AddPicture($CompanyLogo, $LinkToFile, $SaveWithDocument, $Left, $Top, $Width, $Height) | Out-Null
5340
5341 Remove-Variable LinkToFile
5342 Remove-Variable SaveWithDocument
5343 Remove-Variable Left
5344 Remove-Variable Top
5345 Remove-Variable Width
5346 Remove-Variable Height
5347
5348 If (Test-Path -Path $CompanyLogo)
5349 {
5350 Remove-Item $CompanyLogo
5351 }
5352 Remove-Variable CompanyLogo
5353
5354 $row = 5
5355 $column = 1
5356 $worksheet.Cells.Item($row,$column)= "Table of Contents"
5357 $worksheet.Cells.Item($row,$column).Style = "Heading 2"
5358 $row++
5359
5360 For($i=2; $i -le $workbook.Worksheets.Count; $i++)
5361 {
5362 $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
5363 $row++
5364 }
5365
5366 $row++
5367 $worksheet.Cells.Item($row, 1) = "© Sense of Security 2018"
5368 $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
5369
5370 $worksheet.UsedRange.EntireColumn.AutoFit() | Out-Null
5371
5372 $excel.Windows.Item(1).Displaygridlines = $false
5373 $excel.ScreenUpdating = $true
5374 $ADStatFileName = -join($ExcelPath,'\',$DomainName,'ADRecon-Report.xlsx')
5375 Try
5376 {
5377 # Disable prompt if file exists
5378 $excel.DisplayAlerts = $False
5379 $workbook.SaveAs($ADStatFileName)
5380 Write-Output "[+] Excelsheet Saved to: $ADStatFileName"
5381 }
5382 Catch
5383 {
5384 Write-Error "[EXCEPTION] $($_.Exception.Message)"
5385 }
5386 $excel.Quit()
5387 Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet -Final $true
5388 Remove-Variable worksheet
5389 Get-ADRExcelComObjRelease -ComObjtoRelease $workbook -Final $true
5390 Remove-Variable -Name workbook -Scope Global
5391 Get-ADRExcelComObjRelease -ComObjtoRelease $excel -Final $true
5392 Remove-Variable -Name excel -Scope Global
5393 }
5394}