ADRecon
ADRecon
.SYNOPSIS
ADRecon is a tool which gathers information about the Active Directory and generates a report which
.DESCRIPTION
ADRecon is a tool which extracts and combines various artefacts (as highlighted below) out of an AD
The tool is useful to various classes of security professionals like auditors, DFIR, students, administra
It can be run from any workstation that is connected to the environment, even hosts that are not dom
Fine Grained Password Policy, LAPS and BitLocker may require Privileged user accounts.
The tool will use Microsoft Remote Server Administration Tools (RSAT) if available, otherwise it will c
The following information is gathered by the tool:
* Forest;
* Domain;
* Trusts;
* Sites;
* Subnets;
* Schema History;
* Default and Fine Grained Password Policy (if implemented);
* Domain Controllers, SMB versions, whether SMB Signing is supported and FSMO roles;
* Users and their attributes;
* Service Principal Names (SPNs);
* Groups, memberships and changes;
* Organizational Units (OUs);
* GroupPolicy objects and gPLink details;
* DNS Zones and Records;
* Printers;
* Computers and their attributes;
* PasswordAttributes (Experimental);
* LAPS passwords (if implemented);
* BitLocker Recovery Keys (if implemented);
* ACLs (DACLs and SACLs) for the Domain, OUs, Root Containers, GPO, Users, Computers and Gr
* GPOReport (requires RSAT);
* Kerberoast (not included in the default collection method); and
* Domain accounts used for service accounts (requires privileged account and not included in the def
Author : Prashant Mahajan
.NOTES
The following commands can be used to turn off ExecutionPolicy: (Requires Admin Privs)
PS > $ExecPolicy = Get-ExecutionPolicy
PS > Set-ExecutionPolicy bypass
PS > .\ADRecon.ps1
PS > Set-ExecutionPolicy $ExecPolicy
OR
Start the PowerShell as follows:
powershell.exe -ep bypass
OR
Already have a PowerShell open ?
PS > $Env:PSExecutionPolicyPreference = 'Bypass'
OR
powershell.exe -nologo -executionpolicy bypass -noprofile -file ADRecon.ps1
.PARAMETER Method
■Which method to use; ADWS (default), LDAP
.PARAMETER DomainController
■Domain Controller IP Address or Domain FQDN.
.PARAMETER Credential
■Domain Credentials.
.PARAMETER GenExcel
■Path for ADRecon output folder containing the CSV files to generate the ADRecon-Report.xlsx. Use it
.PARAMETER OutputDir
■Path for ADRecon output folder to save the files and the ADRecon-Report.xlsx. (The folder specified w
.PARAMETER Collect
Which modules to run; Comma separated; e.g Forest,Domain (Default all except Kerberoast, Domain
Valid values include: Forest, Domain, Trusts, Sites, Subnets, SchemaHistory, PasswordPolicy, FineG
.PARAMETER OutputType
Output Type; Comma seperated; e.g STDOUT,CSV,XML,JSON,HTML,Excel (Default STDOUT with
Valid values include: STDOUT, CSV, XML, JSON, HTML, Excel, All (excludes STDOUT).
.PARAMETER DormantTimeSpan
Timespan for Dormant accounts. (Default 90 days)
.PARAMETER PassMaxAge
Maximum machine account password age. (Default 30 days)
.PARAMETER PageSize
The PageSize to set for the LDAP searcher object.
.PARAMETER Threads
The number of threads to use during processing objects. (Default 10)
.PARAMETER OnlyEnabled
Only collect details for enabled objects. (Default $false)
.PARAMETER Log
Create ADRecon Log using Start-Transcript
.PARAMETER Logo
Which Logo to use in the excel file? (Default ADRecon)
Values include ADRecon, CyberCX, Payatu.
.EXAMPLE
■.\ADRecon.ps1 -GenExcel C:\ADRecon-Report-<timestamp>
[*] ADRecon <version> by Prashant Mahajan (@prashant3535)
[*] Generating ADRecon-Report.xlsx
[+] Excelsheet Saved to: C:\ADRecon-Report-<timestamp>\<domain>-ADRecon-Report.xlsx
.EXAMPLE
■.\ADRecon.ps1 -DomainController <IP or FQDN> -Credential <domain\username>
[*] ADRecon <version> by Prashant Mahajan (@prashant3535)
■[*] Running on <domain>\<hostname> - Member Workstation as <user>
<snip>
Example output from Domain Member with Alternate Credentials.
.EXAMPLE
■.\ADRecon.ps1 -DomainController <IP or FQDN> -Credential <domain\username> -Collect DomainCo
[*] ADRecon <version> by Prashant Mahajan (@prashant3535)
[*] Running on WORKGROUP\<hostname> - Standalone Workstation as <user>
[*] Commencing - <timestamp>
[-] Domain Controllers
[*] Total Execution Time (mins): <minutes>
[*] Generating ADRecon-Report.xlsx
[+] Excelsheet Saved to: C:\ADRecon-Report-<timestamp>\<domain>-ADRecon-Report.xlsx
[*] Completed.
[*] Output Directory: C:\ADRecon-Report-<timestamp>
Example output from from a Non-Member using RSAT to only enumerate Domain Controllers.
.EXAMPLE
.\ADRecon.ps1 -Method ADWS -DomainController <IP or FQDN> -Credential <domain\username>
[*] ADRecon <version> by Prashant Mahajan (@prashant3535)
[*] Running on WORKGROUP\<hostname> - Standalone Workstation as <user>
[*] Commencing - <timestamp>
[-] Domain
[-] Forest
[-] Trusts
[-] Sites
[-] Subnets
[-] SchemaHistory - May take some time
[-] Default Password Policy
[-] Fine Grained Password Policy - May need a Privileged Account
[-] Domain Controllers
[-] Users and SPNs - May take some time
[-] PasswordAttributes - Experimental
[-] Groups and Membership Changes - May take some time
[-] Group Memberships - May take some time
[-] OrganizationalUnits (OUs)
[-] GPOs
[-] gPLinks - Scope of Management (SOM)
[-] DNS Zones and Records
[-] Printers
[-] Computers and SPNs - May take some time
[-] LAPS - Needs Privileged Account
WARNING: [*] LAPS is not implemented.
[-] BitLocker Recovery Keys - Needs Privileged Account
[-] GPOReport - May take some time
WARNING: [*] Run the tool using RUNAS.
WARNING: [*] runas /user:<Domain FQDN>\<Username> /netonly powershell.exe
[*] Total Execution Time (mins): <minutes>
[*] Output Directory: C:\ADRecon-Report-<timestamp>
[*] Generating ADRecon-Report.xlsx
[+] Excelsheet Saved to: C:\ADRecon-Report-<timestamp>\<domain>-ADRecon-Report.xlsx
Example output from a Non-Member using RSAT.
.EXAMPLE
.\ADRecon.ps1 -Method LDAP -DomainController <IP or FQDN> -Credential <domain\username>
[*] ADRecon <version> by Prashant Mahajan (@prashant3535)
[*] Running on WORKGROUP\<hostname> - Standalone Workstation as <user>
[*] LDAP bind Successful
[*] Commencing - <timestamp>
[-] Domain
[-] Forest
[-] Trusts
[-] Sites
[-] Subnets
[-] SchemaHistory - May take some time
[-] Default Password Policy
[-] Fine Grained Password Policy - May need a Privileged Account
[-] Domain Controllers
[-] Users and SPNs - May take some time
[-] PasswordAttributes - Experimental
[-] Groups and Membership Changes - May take some time
[-] Group Memberships - May take some time
[-] OrganizationalUnits (OUs)
[-] GPOs
[-] gPLinks - Scope of Management (SOM)
[-] DNS Zones and Records
[-] Printers
[-] Computers and SPNs - May take some time
[-] LAPS - Needs Privileged Account
WARNING: [*] LAPS is not implemented.
[-] BitLocker Recovery Keys - Needs Privileged Account
[-] GPOReport - May take some time
WARNING: [*] Currently, the module is only supported with ADWS.
[*] Total Execution Time (mins): <minutes>
[*] Output Directory: C:\ADRecon-Report-<timestamp>
[*] Generating ADRecon-Report.xlsx
[+] Excelsheet Saved to: C:\ADRecon-Report-<timestamp>\<domain>-ADRecon-Report.xlsx
Example output from a Non-Member using LDAP.
.LINK
https://github.com/adrecon/ADRecon
#>
[CmdletBinding()]
param
(
[Parameter(Mandatory = $false, HelpMessage = "Which method to use; ADWS (default), LDAP")]
[ValidateSet('ADWS', 'LDAP')]
[string] $Method = 'ADWS',
[Parameter(Mandatory = $false, HelpMessage = "Domain Controller IP Address or Domain FQDN.")]
[string] $DomainController = '',
[Parameter(Mandatory = $false, HelpMessage = "Domain Credentials.")]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Em
[Parameter(Mandatory = $false, HelpMessage = "Path for ADRecon output folder containing the CSV
[string] $GenExcel,
[Parameter(Mandatory = $false, HelpMessage = "Path for ADRecon output folder to save the CSV/XM
[string] $OutputDir,
[Parameter(Mandatory = $false, HelpMessage = "Which modules to run; Comma separated; e.g Fore
port, Kerberoast, DomainAccountsusedforServiceLogon")]
[ValidateSet('Forest', 'Domain', 'Trusts', 'Sites', 'Subnets', 'SchemaHistory', 'PasswordPolicy', 'FineGra
[array] $Collect = 'Default',
[Parameter(Mandatory = $false, HelpMessage = "Output type; Comma seperated; e.g STDOUT,CSV
[ValidateSet('STDOUT', 'CSV', 'XML', 'JSON', 'EXCEL', 'HTML', 'All', 'Default')]
[array] $OutputType = 'Default',
[Parameter(Mandatory = $false, HelpMessage = "Timespan for Dormant accounts. Default 90 days")
[ValidateRange(1,1000)]
[int] $DormantTimeSpan = 90,
[Parameter(Mandatory = $false, HelpMessage = "Maximum machine account password age. Default
[ValidateRange(1,1000)]
[int] $PassMaxAge = 30,
[Parameter(Mandatory = $false, HelpMessage = "The PageSize to set for the LDAP searcher object.
[ValidateRange(1,10000)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false, HelpMessage = "The number of threads to use during processing of
[ValidateRange(1,100)]
[int] $Threads = 10,
[Parameter(Mandatory = $false, HelpMessage = "Only collect details for enabled objects. Default `$fa
[bool] $OnlyEnabled = $false,
[Parameter(Mandatory = $false, HelpMessage = "Create ADRecon Log using Start-Transcript.")]
[switch] $Log,
[Parameter(Mandatory = $false, HelpMessage = "Which Logo to use in the excel file? Default ADRec
[ValidateSet('ADRecon', 'CyberCX', 'Payatu')]
[string] $Logo = "ADRecon"
)
$ADWSSource = @"
// Thanks Dennis Albuquerque for the C# multithreading code
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Threading;
using System.DirectoryServices;
//using System.Security.Principal;
using System.Security.AccessControl;
using System.Management.Automation;
using System.Diagnostics;
//using System.IO;
//using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Runtime.InteropServices;
namespace ADRecon
{
public static class ADWSClass
{
private static DateTime Date1;
private static int PassMaxAge;
private static int DormantTimeSpan;
private static Dictionary<string, string> AdGroupDictionary = new Dictionary<string, string>();
private static string DomainSID;
private static Dictionary<string, string> AdGPODictionary = new Dictionary<string, string>();
private static Hashtable GUIDs = new Hashtable();
private static Dictionary<string, string> AdSIDDictionary = new Dictionary<string, string>();
private static readonly HashSet<string> Groups = new HashSet<string> ( new string[] {"268435456
private static readonly HashSet<string> Users = new HashSet<string> ( new string[] { "805306368
private static readonly HashSet<string> Computers = new HashSet<string> ( new string[] { "80530
private static readonly HashSet<string> TrustAccounts = new HashSet<string> ( new string[] { "805
[Flags]
//Values taken from https://support.microsoft.com/en-au/kb/305144
public enum UACFlags
{
SCRIPT = 1, // 0x1
ACCOUNTDISABLE = 2, // 0x2
HOMEDIR_REQUIRED = 8, // 0x8
LOCKOUT = 16, // 0x10
PASSWD_NOTREQD = 32, // 0x20
PASSWD_CANT_CHANGE = 64, // 0x40
ENCRYPTED_TEXT_PASSWORD_ALLOWED = 128, // 0x80
TEMP_DUPLICATE_ACCOUNT = 256, // 0x100
NORMAL_ACCOUNT = 512, // 0x200
INTERDOMAIN_TRUST_ACCOUNT = 2048, // 0x800
WORKSTATION_TRUST_ACCOUNT = 4096, // 0x1000
SERVER_TRUST_ACCOUNT = 8192, // 0x2000
DONT_EXPIRE_PASSWD = 65536, // 0x10000
MNS_LOGON_ACCOUNT = 131072, // 0x20000
SMARTCARD_REQUIRED = 262144, // 0x40000
TRUSTED_FOR_DELEGATION = 524288, // 0x80000
NOT_DELEGATED = 1048576, // 0x100000
USE_DES_KEY_ONLY = 2097152, // 0x200000
DONT_REQUIRE_PREAUTH = 4194304, // 0x400000
PASSWORD_EXPIRED = 8388608, // 0x800000
TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 16777216, // 0x1000000
PARTIAL_SECRETS_ACCOUNT = 67108864 // 0x04000000
}
[Flags]
//Values taken from https://blogs.msdn.microsoft.com/openspecification/2011/05/30/windows-confi
public enum KerbEncFlags
{
ZERO = 0,
DES_CBC_CRC = 1, // 0x1
DES_CBC_MD5 = 2, // 0x2
RC4_HMAC = 4, // 0x4
AES128_CTS_HMAC_SHA1_96 = 8, // 0x18
AES256_CTS_HMAC_SHA1_96 = 16 // 0x10
}
■■private static readonly Dictionary<string, string> Replacements = new Dictionary<string, string>()
{
//{System.Environment.NewLine, ""},
//{",", ";"},
{"\"", "'"}
};
public static string CleanString(Object StringtoClean)
{
// Remove extra spaces and new lines
string CleanedString = string.Join(" ", ((Convert.ToString(StringtoClean)).Split((string[]) null, Strin
foreach (string Replacement in Replacements.Keys)
{
CleanedString = CleanedString.Replace(Replacement, Replacements[Replacement]);
}
return CleanedString;
}
public static int ObjectCount(Object[] ADRObject)
{
return ADRObject.Length;
}
public static Object[] DomainControllerParser(Object[] AdDomainControllers, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdDomainControllers, numOfThreads, "DomainControllers");
return ADRObj;
}
public static Object[] SchemaParser(Object[] AdSchemas, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdSchemas, numOfThreads, "SchemaHistory");
return ADRObj;
}
public static Object[] UserParser(Object[] AdUsers, DateTime Date1, int DormantTimeSpan, int Pa
{
ADWSClass.Date1 = Date1;
ADWSClass.DormantTimeSpan = DormantTimeSpan;
ADWSClass.PassMaxAge = PassMaxAge;
Object[] ADRObj = runProcessor(AdUsers, numOfThreads, "Users");
return ADRObj;
}
public static Object[] UserSPNParser(Object[] AdUsers, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdUsers, numOfThreads, "UserSPNs");
return ADRObj;
}
public static Object[] GroupParser(Object[] AdGroups, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdGroups, numOfThreads, "Groups");
return ADRObj;
}
public static Object[] GroupChangeParser(Object[] AdGroups, DateTime Date1, int numOfThreads
{
ADWSClass.Date1 = Date1;
Object[] ADRObj = runProcessor(AdGroups, numOfThreads, "GroupChanges");
return ADRObj;
}
public static Object[] GroupMemberParser(Object[] AdGroups, Object[] AdGroupMembers, string D
{
ADWSClass.AdGroupDictionary = new Dictionary<string, string>();
runProcessor(AdGroups, numOfThreads, "GroupsDictionary");
ADWSClass.DomainSID = DomainSID;
Object[] ADRObj = runProcessor(AdGroupMembers, numOfThreads, "GroupMembers");
return ADRObj;
}
public static Object[] OUParser(Object[] AdOUs, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdOUs, numOfThreads, "OUs");
return ADRObj;
}
public static Object[] GPOParser(Object[] AdGPOs, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdGPOs, numOfThreads, "GPOs");
return ADRObj;
}
public static Object[] SOMParser(Object[] AdGPOs, Object[] AdSOMs, int numOfThreads)
{
ADWSClass.AdGPODictionary = new Dictionary<string, string>();
runProcessor(AdGPOs, numOfThreads, "GPOsDictionary");
Object[] ADRObj = runProcessor(AdSOMs, numOfThreads, "SOMs");
return ADRObj;
}
public static Object[] PrinterParser(Object[] ADPrinters, int numOfThreads)
{
Object[] ADRObj = runProcessor(ADPrinters, numOfThreads, "Printers");
return ADRObj;
}
public static Object[] ComputerParser(Object[] AdComputers, DateTime Date1, int DormantTimeSp
{
ADWSClass.Date1 = Date1;
ADWSClass.DormantTimeSpan = DormantTimeSpan;
ADWSClass.PassMaxAge = PassMaxAge;
Object[] ADRObj = runProcessor(AdComputers, numOfThreads, "Computers");
return ADRObj;
}
public static Object[] ComputerSPNParser(Object[] AdComputers, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdComputers, numOfThreads, "ComputerSPNs");
return ADRObj;
}
public static Object[] LAPSParser(Object[] AdComputers, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdComputers, numOfThreads, "LAPS");
return ADRObj;
}
public static Object[] DACLParser(Object[] ADObjects, Object PSGUIDs, int numOfThreads)
{
ADWSClass.AdSIDDictionary = new Dictionary<string, string>();
runProcessor(ADObjects, numOfThreads, "SIDDictionary");
ADWSClass.GUIDs = (Hashtable) PSGUIDs;
Object[] ADRObj = runProcessor(ADObjects, numOfThreads, "DACLs");
return ADRObj;
}
public static Object[] SACLParser(Object[] ADObjects, Object PSGUIDs, int numOfThreads)
{
ADWSClass.GUIDs = (Hashtable) PSGUIDs;
Object[] ADRObj = runProcessor(ADObjects, numOfThreads, "SACLs");
return ADRObj;
}
static Object[] runProcessor(Object[] arrayToProcess, int numOfThreads, string processorType)
{
int totalRecords = arrayToProcess.Length;
IRecordProcessor recordProcessor = recordProcessorFactory(processorType);
IResultsHandler resultsHandler = new SimpleResultsHandler ();
int numberOfRecordsPerThread = totalRecords / numOfThreads;
int remainders = totalRecords % numOfThreads;
Thread[] threads = new Thread[numOfThreads];
for (int i = 0; i < numOfThreads; i++)
{
int numberOfRecordsToProcess = numberOfRecordsPerThread;
if (i == (numOfThreads - 1))
{
//last thread, do the remaining records
numberOfRecordsToProcess += remainders;
}
//split the full array into chunks to be given to different threads
Object[] sliceToProcess = new Object[numberOfRecordsToProcess];
Array.Copy(arrayToProcess, i * numberOfRecordsPerThread, sliceToProcess, 0, numberOfR
ProcessorThread processorThread = new ProcessorThread(i, recordProcessor, resultsHandle
threads[i] = new Thread(processorThread.processThreadRecords);
threads[i].Start();
}
foreach (Thread t in threads)
{
t.Join();
}
return resultsHandler.finalise();
}
static IRecordProcessor recordProcessorFactory(string name)
{
switch (name)
{
case "DomainControllers":
return new DomainControllerRecordProcessor();
case "SchemaHistory":
return new SchemaRecordProcessor();
case "Users":
return new UserRecordProcessor();
case "UserSPNs":
return new UserSPNRecordProcessor();
case "Groups":
return new GroupRecordProcessor();
case "GroupChanges":
return new GroupChangeRecordProcessor();
case "GroupsDictionary":
return new GroupRecordDictionaryProcessor();
case "GroupMembers":
return new GroupMemberRecordProcessor();
case "OUs":
return new OURecordProcessor();
case "GPOs":
return new GPORecordProcessor();
case "GPOsDictionary":
return new GPORecordDictionaryProcessor();
case "SOMs":
return new SOMRecordProcessor();
case "Printers":
return new PrinterRecordProcessor();
case "Computers":
return new ComputerRecordProcessor();
case "ComputerSPNs":
return new ComputerSPNRecordProcessor();
case "LAPS":
return new LAPSRecordProcessor();
case "SIDDictionary":
return new SIDRecordDictionaryProcessor();
case "DACLs":
return new DACLRecordProcessor();
case "SACLs":
return new SACLRecordProcessor();
}
throw new ArgumentException("Invalid processor type " + name);
}
class ProcessorThread
{
readonly int id;
readonly IRecordProcessor recordProcessor;
readonly IResultsHandler resultsHandler;
readonly Object[] objectsToBeProcessed;
public ProcessorThread(int id, IRecordProcessor recordProcessor, IResultsHandler resultsHand
{
this.recordProcessor = recordProcessor;
this.id = id;
this.resultsHandler = resultsHandler;
this.objectsToBeProcessed = objectsToBeProcessed;
}
public void processThreadRecords()
{
for (int i = 0; i < objectsToBeProcessed.Length; i++)
{
Object[] result = recordProcessor.processRecord(objectsToBeProcessed[i]);
resultsHandler.processResults(result); //this is a thread safe operation
}
}
}
//The interface and implmentation class used to process a record (this implemmentation just return
interface IRecordProcessor
{
PSObject[] processRecord(Object record);
}
class DomainControllerRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdDC = (PSObject) record;
bool Infra = false;
bool Naming = false;
bool Schema = false;
bool RID = false;
bool PDC = false;
PSObject DCSMBObj = new PSObject();
string OperatingSystem = CleanString((AdDC.Members["OperatingSystem"].Value != null ?
foreach (var OperationMasterRole in (Microsoft.ActiveDirectory.Management.ADPropertyV
{
switch (OperationMasterRole.ToString())
{
case "InfrastructureMaster":
Infra = true;
break;
case "DomainNamingMaster":
Naming = true;
break;
case "SchemaMaster":
Schema = true;
break;
case "RIDMaster":
RID = true;
break;
case "PDCEmulator":
PDC = true;
break;
}
}
PSObject DCObj = new PSObject();
DCObj.Members.Add(new PSNoteProperty("Domain", AdDC.Members["Domain"].Value));
DCObj.Members.Add(new PSNoteProperty("Site", AdDC.Members["Site"].Value));
DCObj.Members.Add(new PSNoteProperty("Name", AdDC.Members["Name"].Value));
DCObj.Members.Add(new PSNoteProperty("IPv4Address", AdDC.Members["IPv4Address"
DCObj.Members.Add(new PSNoteProperty("Operating System", OperatingSystem));
DCObj.Members.Add(new PSNoteProperty("Hostname", AdDC.Members["HostName"].Va
DCObj.Members.Add(new PSNoteProperty("Infra", Infra));
DCObj.Members.Add(new PSNoteProperty("Naming", Naming));
DCObj.Members.Add(new PSNoteProperty("Schema", Schema));
DCObj.Members.Add(new PSNoteProperty("RID", RID));
DCObj.Members.Add(new PSNoteProperty("PDC", PDC));
if (AdDC.Members["IPv4Address"].Value != null)
{
DCSMBObj = GetPSObject(AdDC.Members["IPv4Address"].Value);
}
else
{
DCSMBObj = new PSObject();
DCSMBObj.Members.Add(new PSNoteProperty("SMB Port Open", false));
}
foreach (PSPropertyInfo psPropertyInfo in DCSMBObj.Properties)
{
if (Convert.ToString(psPropertyInfo.Name) == "SMB Port Open" && (bool) psPropertyInf
{
DCObj.Members.Add(new PSNoteProperty(psPropertyInfo.Name, psPropertyInfo.Val
DCObj.Members.Add(new PSNoteProperty("SMB1(NT LM 0.12)", null));
DCObj.Members.Add(new PSNoteProperty("SMB2(0x0202)", null));
DCObj.Members.Add(new PSNoteProperty("SMB2(0x0210)", null));
DCObj.Members.Add(new PSNoteProperty("SMB3(0x0300)", null));
DCObj.Members.Add(new PSNoteProperty("SMB3(0x0302)", null));
DCObj.Members.Add(new PSNoteProperty("SMB3(0x0311)", null));
DCObj.Members.Add(new PSNoteProperty("SMB Signing", null));
break;
}
else
{
DCObj.Members.Add(new PSNoteProperty(psPropertyInfo.Name, psPropertyInfo.Val
}
}
return new PSObject[] { DCObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
return new PSObject[] { };
}
}
}
class SchemaRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdSchema = (PSObject) record;
PSObject SchemaObj = new PSObject();
SchemaObj.Members.Add(new PSNoteProperty("ObjectClass", AdSchema.Members["Obj
SchemaObj.Members.Add(new PSNoteProperty("Name", AdSchema.Members["Name"].Va
SchemaObj.Members.Add(new PSNoteProperty("whenCreated", AdSchema.Members["wh
SchemaObj.Members.Add(new PSNoteProperty("whenChanged", AdSchema.Members["w
SchemaObj.Members.Add(new PSNoteProperty("DistinguishedName", AdSchema.Membe
return new PSObject[] { SchemaObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class UserRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdUser = (PSObject) record;
bool? Enabled = null;
bool MustChangePasswordatLogon = false;
bool PasswordNotChangedafterMaxAge = false;
bool NeverLoggedIn = false;
int? DaysSinceLastLogon = null;
int? DaysSinceLastPasswordChange = null;
int? AccountExpirationNumofDays = null;
bool Dormant = false;
string SIDHistory = "";
bool? KerberosRC4 = null;
bool? KerberosAES128 = null;
bool? KerberosAES256 = null;
string DelegationType = null;
string DelegationProtocol = null;
string DelegationServices = null;
DateTime? LastLogonDate = null;
DateTime? PasswordLastSet = null;
DateTime? AccountExpires = null;
bool? AccountNotDelegated = null;
bool? HasSPN = null;
try
{
// The Enabled field can be blank which raises an exception. This may occur when the u
Enabled = (bool) AdUser.Members["Enabled"].Value;
}
catch //(Exception e)
{
//Console.WriteLine("Exception caught: {0}", e);
}
if (AdUser.Members["lastLogonTimeStamp"].Value != null)
{
//LastLogonDate = DateTime.FromFileTime((long)(AdUser.Members["lastLogonTimeSta
// LastLogonDate is lastLogonTimeStamp converted to local time
LastLogonDate = Convert.ToDateTime(AdUser.Members["LastLogonDate"].Value);
DaysSinceLastLogon = Math.Abs((Date1 - (DateTime)LastLogonDate).Days);
if (DaysSinceLastLogon > DormantTimeSpan)
{
Dormant = true;
}
}
else
{
NeverLoggedIn = true;
}
if (Convert.ToString(AdUser.Members["pwdLastSet"].Value) == "0")
{
if ((bool) AdUser.Members["PasswordNeverExpires"].Value == false)
{
MustChangePasswordatLogon = true;
}
}
if (AdUser.Members["PasswordLastSet"].Value != null)
{
//PasswordLastSet = DateTime.FromFileTime((long)(AdUser.Members["pwdLastSet"].Va
// PasswordLastSet is pwdLastSet converted to local time
PasswordLastSet = Convert.ToDateTime(AdUser.Members["PasswordLastSet"].Value);
DaysSinceLastPasswordChange = Math.Abs((Date1 - (DateTime)PasswordLastSet).Da
if (DaysSinceLastPasswordChange > PassMaxAge)
{
PasswordNotChangedafterMaxAge = true;
}
}
//https://msdn.microsoft.com/en-us/library/ms675098(v=vs.85).aspx
//if ((Int64) AdUser.Members["accountExpires"].Value != (Int64) 9223372036854775807)
//{
//if ((Int64) AdUser.Members["accountExpires"].Value != (Int64) 0)
if (AdUser.Members["AccountExpirationDate"].Value != null)
{
try
{
//AccountExpires = DateTime.FromFileTime((long)(AdUser.Members["accountExpir
// AccountExpirationDate is accountExpires converted to local time
AccountExpires = Convert.ToDateTime(AdUser.Members["AccountExpirationDate"
AccountExpirationNumofDays = ((int)((DateTime)AccountExpires - Date1).Days);
}
catch //(Exception e)
{
//Console.WriteLine("Exception caught: {0}", e);
}
}
//}
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection history = (Microsoft.Act
string sids = "";
foreach (var value in history)
{
sids = sids + "," + Convert.ToString(value);
}
SIDHistory = sids.TrimStart(',');
if (AdUser.Members["msDS-SupportedEncryptionTypes"].Value != null)
{
var userKerbEncFlags = (KerbEncFlags) AdUser.Members["msDS-SupportedEncryption
if (userKerbEncFlags != KerbEncFlags.ZERO)
{
KerberosRC4 = (userKerbEncFlags & KerbEncFlags.RC4_HMAC) == KerbEncFlags.R
KerberosAES128 = (userKerbEncFlags & KerbEncFlags.AES128_CTS_HMAC_SHA1
KerberosAES256 = (userKerbEncFlags & KerbEncFlags.AES256_CTS_HMAC_SHA1
}
}
if (AdUser.Members["UserAccountControl"].Value != null)
{
AccountNotDelegated = !((bool) AdUser.Members["AccountNotDelegated"].Value);
if ((bool) AdUser.Members["TrustedForDelegation"].Value)
{
DelegationType = "Unconstrained";
DelegationServices = "Any";
}
if (AdUser.Members["msDS-AllowedToDelegateTo"] != null)
{
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection delegateto = (Micr
if (delegateto.Value != null)
{
DelegationType = "Constrained";
foreach (var value in delegateto)
{
DelegationServices = DelegationServices + "," + Convert.ToString(value);
}
DelegationServices = DelegationServices.TrimStart(',');
}
}
if ((bool) AdUser.Members["TrustedToAuthForDelegation"].Value == true)
{
DelegationProtocol = "Any";
}
else if (DelegationType != null)
{
DelegationProtocol = "Kerberos";
}
}
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection SPNs = (Microsoft.Activ
if (SPNs.Value == null)
{
HasSPN = false;
}
else
{
HasSPN = true;
}
PSObject UserObj = new PSObject();
UserObj.Members.Add(new PSNoteProperty("UserName", CleanString(AdUser.Members["
UserObj.Members.Add(new PSNoteProperty("Name", CleanString(AdUser.Members["Nam
UserObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
UserObj.Members.Add(new PSNoteProperty("Must Change Password at Logon", MustCha
UserObj.Members.Add(new PSNoteProperty("Cannot Change Password", AdUser.Membe
UserObj.Members.Add(new PSNoteProperty("Password Never Expires", AdUser.Members
UserObj.Members.Add(new PSNoteProperty("Reversible Password Encryption", AdUser.M
UserObj.Members.Add(new PSNoteProperty("Smartcard Logon Required", AdUser.Membe
UserObj.Members.Add(new PSNoteProperty("Delegation Permitted", AccountNotDelegate
UserObj.Members.Add(new PSNoteProperty("Kerberos DES Only", AdUser.Members["Use
UserObj.Members.Add(new PSNoteProperty("Kerberos RC4", KerberosRC4));
UserObj.Members.Add(new PSNoteProperty("Kerberos AES-128bit", KerberosAES128));
UserObj.Members.Add(new PSNoteProperty("Kerberos AES-256bit", KerberosAES256));
UserObj.Members.Add(new PSNoteProperty("Does Not Require Pre Auth", AdUser.Memb
UserObj.Members.Add(new PSNoteProperty("Never Logged in", NeverLoggedIn));
UserObj.Members.Add(new PSNoteProperty("Logon Age (days)", DaysSinceLastLogon));
UserObj.Members.Add(new PSNoteProperty("Password Age (days)", DaysSinceLastPassw
UserObj.Members.Add(new PSNoteProperty("Dormant (> " + DormantTimeSpan + " days)
UserObj.Members.Add(new PSNoteProperty("Password Age (> " + PassMaxAge + " days)
UserObj.Members.Add(new PSNoteProperty("Account Locked Out", AdUser.Members["Lo
UserObj.Members.Add(new PSNoteProperty("Password Expired", AdUser.Members["Pass
UserObj.Members.Add(new PSNoteProperty("Password Not Required", AdUser.Members[
UserObj.Members.Add(new PSNoteProperty("Delegation Type", DelegationType));
UserObj.Members.Add(new PSNoteProperty("Delegation Protocol", DelegationProtocol));
UserObj.Members.Add(new PSNoteProperty("Delegation Services", DelegationServices));
UserObj.Members.Add(new PSNoteProperty("Logon Workstations", AdUser.Members["Log
UserObj.Members.Add(new PSNoteProperty("AdminCount", AdUser.Members["AdminCou
UserObj.Members.Add(new PSNoteProperty("Primary GroupID", AdUser.Members["primar
UserObj.Members.Add(new PSNoteProperty("SID", AdUser.Members["SID"].Value));
UserObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
UserObj.Members.Add(new PSNoteProperty("HasSPN", HasSPN));
UserObj.Members.Add(new PSNoteProperty("Description", CleanString(AdUser.Members[
UserObj.Members.Add(new PSNoteProperty("Title", CleanString(AdUser.Members["Title"].
UserObj.Members.Add(new PSNoteProperty("Department", CleanString(AdUser.Members
UserObj.Members.Add(new PSNoteProperty("Company", CleanString(AdUser.Members["C
UserObj.Members.Add(new PSNoteProperty("Manager", CleanString(AdUser.Members["M
UserObj.Members.Add(new PSNoteProperty("Info", CleanString(AdUser.Members["Info"].V
UserObj.Members.Add(new PSNoteProperty("Last Logon Date", LastLogonDate));
UserObj.Members.Add(new PSNoteProperty("Password LastSet", PasswordLastSet));
UserObj.Members.Add(new PSNoteProperty("Account Expiration Date", AccountExpires));
UserObj.Members.Add(new PSNoteProperty("Account Expiration (days)", AccountExpiratio
UserObj.Members.Add(new PSNoteProperty("Mobile", CleanString(AdUser.Members["Mob
UserObj.Members.Add(new PSNoteProperty("Email", CleanString(AdUser.Members["mail"
UserObj.Members.Add(new PSNoteProperty("HomeDirectory", AdUser.Members["homeDi
UserObj.Members.Add(new PSNoteProperty("ProfilePath", AdUser.Members["profilePath"]
UserObj.Members.Add(new PSNoteProperty("ScriptPath", AdUser.Members["ScriptPath"].
UserObj.Members.Add(new PSNoteProperty("UserAccountControl", AdUser.Members["Us
UserObj.Members.Add(new PSNoteProperty("First Name", CleanString(AdUser.Members[
UserObj.Members.Add(new PSNoteProperty("Middle Name", CleanString(AdUser.Member
UserObj.Members.Add(new PSNoteProperty("Last Name", CleanString(AdUser.Members[
UserObj.Members.Add(new PSNoteProperty("Country", CleanString(AdUser.Members["c"]
UserObj.Members.Add(new PSNoteProperty("whenCreated", AdUser.Members["whenCrea
UserObj.Members.Add(new PSNoteProperty("whenChanged", AdUser.Members["whenCh
UserObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdUser.M
UserObj.Members.Add(new PSNoteProperty("CanonicalName", CleanString(AdUser.Mem
return new PSObject[] { UserObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class UserSPNRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdUser = (PSObject) record;
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection SPNs = (Microsoft.Activ
if (SPNs.Value == null)
{
return new PSObject[] { };
}
List<PSObject> SPNList = new List<PSObject>();
bool? Enabled = null;
string Memberof = null;
DateTime? PasswordLastSet = null;
// When the user is not allowed to query the UserAccountControl attribute.
if (AdUser.Members["userAccountControl"].Value != null)
{
var userFlags = (UACFlags) AdUser.Members["userAccountControl"].Value;
Enabled = !((userFlags & UACFlags.ACCOUNTDISABLE) == UACFlags.ACCOUNTDIS
}
if (Convert.ToString(AdUser.Members["pwdLastSet"].Value) != "0")
{
PasswordLastSet = DateTime.FromFileTime((long)AdUser.Members["pwdLastSet"].Valu
}
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection MemberOfAttribute = (M
if (MemberOfAttribute.Value != null)
{
foreach (string Member in MemberOfAttribute)
{
Memberof = Memberof + "," + ((Convert.ToString(Member)).Split(',')[0]).Split('=')[1];
}
Memberof = Memberof.TrimStart(',');
}
string Description = CleanString(AdUser.Members["Description"].Value);
string PrimaryGroupID = Convert.ToString(AdUser.Members["primaryGroupID"].Value);
foreach (string SPN in SPNs)
{
string[] SPNArray = SPN.Split('/');
PSObject UserSPNObj = new PSObject();
UserSPNObj.Members.Add(new PSNoteProperty("Username", CleanString(AdUser.Mem
UserSPNObj.Members.Add(new PSNoteProperty("Name", CleanString(AdUser.Member
UserSPNObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
UserSPNObj.Members.Add(new PSNoteProperty("Service", SPNArray[0]));
UserSPNObj.Members.Add(new PSNoteProperty("Host", SPNArray[1]));
UserSPNObj.Members.Add(new PSNoteProperty("Password Last Set", PasswordLastS
UserSPNObj.Members.Add(new PSNoteProperty("Description", Description));
UserSPNObj.Members.Add(new PSNoteProperty("Primary GroupID", PrimaryGroupID))
UserSPNObj.Members.Add(new PSNoteProperty("Memberof", Memberof));
SPNList.Add( UserSPNObj );
}
return SPNList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class GroupRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdGroup = (PSObject) record;
string ManagedByValue = Convert.ToString(AdGroup.Members["managedBy"].Value);
string ManagedBy = "";
string SIDHistory = "";
if (AdGroup.Members["managedBy"].Value != null)
{
ManagedBy = (ManagedByValue.Split(new string[] { "CN=" },StringSplitOptions.Remove
}
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection history = (Microsoft.Act
string sids = "";
foreach (var value in history)
{
sids = sids + "," + Convert.ToString(value);
}
SIDHistory = sids.TrimStart(',');
PSObject GroupObj = new PSObject();
GroupObj.Members.Add(new PSNoteProperty("Name", AdGroup.Members["SamAccountN
GroupObj.Members.Add(new PSNoteProperty("AdminCount", AdGroup.Members["AdminC
GroupObj.Members.Add(new PSNoteProperty("GroupCategory", AdGroup.Members["Grou
GroupObj.Members.Add(new PSNoteProperty("GroupScope", AdGroup.Members["GroupS
GroupObj.Members.Add(new PSNoteProperty("ManagedBy", ManagedBy));
GroupObj.Members.Add(new PSNoteProperty("SID", AdGroup.Members["sid"].Value));
GroupObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
GroupObj.Members.Add(new PSNoteProperty("Description", CleanString(AdGroup.Membe
GroupObj.Members.Add(new PSNoteProperty("whenCreated", AdGroup.Members["whenC
GroupObj.Members.Add(new PSNoteProperty("whenChanged", AdGroup.Members["when
GroupObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdGroup
GroupObj.Members.Add(new PSNoteProperty("CanonicalName", AdGroup.Members["Can
return new PSObject[] { GroupObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class GroupChangeRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdGroup = (PSObject) record;
string Action = null;
int? DaysSinceAdded = null;
int? DaysSinceRemoved = null;
DateTime? AddedDate = null;
DateTime? RemovedDate = null;
List<PSObject> GroupChangesList = new List<PSObject>();
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection ReplValueMetaData =
if (ReplValueMetaData.Value != null)
{
foreach (string ReplData in ReplValueMetaData)
{
XmlDocument ReplXML = new XmlDocument();
ReplXML.LoadXml(ReplData.Replace("\x00", "").Replace("&","&"));
if (ReplXML.SelectSingleNode("DS_REPL_VALUE_META_DATA")["ftimeDeleted"].In
{
Action = "Removed";
AddedDate = DateTime.Parse(ReplXML.SelectSingleNode("DS_REPL_VALUE_ME
DaysSinceAdded = Math.Abs((Date1 - (DateTime) AddedDate).Days);
RemovedDate = DateTime.Parse(ReplXML.SelectSingleNode("DS_REPL_VALUE_
DaysSinceRemoved = Math.Abs((Date1 - (DateTime) RemovedDate).Days);
}
else
{
Action = "Added";
AddedDate = DateTime.Parse(ReplXML.SelectSingleNode("DS_REPL_VALUE_ME
DaysSinceAdded = Math.Abs((Date1 - (DateTime) AddedDate).Days);
RemovedDate = null;
DaysSinceRemoved = null;
}
PSObject GroupChangeObj = new PSObject();
GroupChangeObj.Members.Add(new PSNoteProperty("Group Name", AdGroup.Mem
GroupChangeObj.Members.Add(new PSNoteProperty("Group DistinguishedName", C
GroupChangeObj.Members.Add(new PSNoteProperty("Member DistinguishedName"
GroupChangeObj.Members.Add(new PSNoteProperty("Action", Action));
GroupChangeObj.Members.Add(new PSNoteProperty("Added Age (Days)", DaysSinc
GroupChangeObj.Members.Add(new PSNoteProperty("Removed Age (Days)", DaysS
GroupChangeObj.Members.Add(new PSNoteProperty("Added Date", AddedDate));
GroupChangeObj.Members.Add(new PSNoteProperty("Removed Date", RemovedDa
GroupChangeObj.Members.Add(new PSNoteProperty("ftimeCreated", ReplXML.Sele
GroupChangeObj.Members.Add(new PSNoteProperty("ftimeDeleted", ReplXML.Selec
GroupChangesList.Add( GroupChangeObj );
}
}
return GroupChangesList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class GroupRecordDictionaryProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdGroup = (PSObject) record;
ADWSClass.AdGroupDictionary.Add((Convert.ToString(AdGroup.Properties["SID"].Value))
return new PSObject[] { };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class GroupMemberRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
// based on https://github.com/BloodHoundAD/BloodHound/blob/master/PowerShell/BloodH
PSObject AdGroup = (PSObject) record;
List<PSObject> GroupsList = new List<PSObject>();
string SamAccountType = Convert.ToString(AdGroup.Members["samaccounttype"].Value);
string ObjectClass = Convert.ToString(AdGroup.Members["ObjectClass"].Value);
string AccountType = "";
string GroupName = "";
string MemberUserName = "-";
string MemberName = "";
string PrimaryGroupID = "";
PSObject GroupMemberObj = new PSObject();
if (ObjectClass == "foreignSecurityPrincipal")
{
AccountType = "foreignSecurityPrincipal";
MemberUserName = ((Convert.ToString(AdGroup.Members["DistinguishedName"].Valu
MemberName = null;
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection MemberGroups = (M
if (MemberGroups.Value != null)
{
foreach (string GroupMember in MemberGroups)
{
GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", Mem
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberN
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", AdGroup.Me
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountTyp
GroupsList.Add( GroupMemberObj );
}
}
}
if (Groups.Contains(SamAccountType))
{
AccountType = "group";
MemberName = ((Convert.ToString(AdGroup.Members["DistinguishedName"].Value)).S
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection MemberGroups = (M
if (MemberGroups.Value != null)
{
foreach (string GroupMember in MemberGroups)
{
GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", Mem
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberN
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", AdGroup.Me
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountTyp
GroupsList.Add( GroupMemberObj );
}
}
}
if (Users.Contains(SamAccountType))
{
AccountType = "user";
MemberName = ((Convert.ToString(AdGroup.Members["DistinguishedName"].Value)).S
MemberUserName = Convert.ToString(AdGroup.Members["sAMAccountName"].Value);
PrimaryGroupID = Convert.ToString(AdGroup.Members["primaryGroupID"].Value);
try
{
GroupName = ADWSClass.AdGroupDictionary[ADWSClass.DomainSID + "-" + Prima
}
catch //(Exception e)
{
//Console.WriteLine("Exception caught: {0}", e);
GroupName = PrimaryGroupID;
}
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberU
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName)
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", AdGroup.Membe
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection MemberGroups = (M
if (MemberGroups.Value != null)
{
foreach (string GroupMember in MemberGroups)
{
GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", Mem
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberN
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", AdGroup.Me
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountTyp
GroupsList.Add( GroupMemberObj );
}
}
}
if (Computers.Contains(SamAccountType))
{
AccountType = "computer";
MemberName = ((Convert.ToString(AdGroup.Members["DistinguishedName"].Value)).S
MemberUserName = Convert.ToString(AdGroup.Members["sAMAccountName"].Value);
PrimaryGroupID = Convert.ToString(AdGroup.Members["primaryGroupID"].Value);
try
{
GroupName = ADWSClass.AdGroupDictionary[ADWSClass.DomainSID + "-" + Prima
}
catch //(Exception e)
{
//Console.WriteLine("Exception caught: {0}", e);
GroupName = PrimaryGroupID;
}
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberU
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName)
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", AdGroup.Membe
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection MemberGroups = (M
if (MemberGroups.Value != null)
{
foreach (string GroupMember in MemberGroups)
{
GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", Mem
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberN
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", AdGroup.Me
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountTyp
GroupsList.Add( GroupMemberObj );
}
}
}
if (TrustAccounts.Contains(SamAccountType))
{
AccountType = "trust";
MemberName = ((Convert.ToString(AdGroup.Members["DistinguishedName"].Value)).S
MemberUserName = Convert.ToString(AdGroup.Members["sAMAccountName"].Value);
PrimaryGroupID = Convert.ToString(AdGroup.Members["primaryGroupID"].Value);
try
{
GroupName = ADWSClass.AdGroupDictionary[ADWSClass.DomainSID + "-" + Prima
}
catch //(Exception e)
{
//Console.WriteLine("Exception caught: {0}", e);
GroupName = PrimaryGroupID;
}
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberU
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName)
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", AdGroup.Membe
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
}
return GroupsList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class OURecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdOU = (PSObject) record;
PSObject OUObj = new PSObject();
OUObj.Members.Add(new PSNoteProperty("Name", AdOU.Members["Name"].Value));
OUObj.Members.Add(new PSNoteProperty("Depth", ((Convert.ToString(AdOU.Members["D
OUObj.Members.Add(new PSNoteProperty("Description", AdOU.Members["Description"].V
OUObj.Members.Add(new PSNoteProperty("whenCreated", AdOU.Members["whenCreate
OUObj.Members.Add(new PSNoteProperty("whenChanged", AdOU.Members["whenChang
OUObj.Members.Add(new PSNoteProperty("DistinguishedName", AdOU.Members["Disting
return new PSObject[] { OUObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class GPORecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdGPO = (PSObject) record;
PSObject GPOObj = new PSObject();
GPOObj.Members.Add(new PSNoteProperty("DisplayName", CleanString(AdGPO.Membe
GPOObj.Members.Add(new PSNoteProperty("GUID", CleanString(AdGPO.Members["Nam
GPOObj.Members.Add(new PSNoteProperty("whenCreated", AdGPO.Members["whenCre
GPOObj.Members.Add(new PSNoteProperty("whenChanged", AdGPO.Members["whenCh
GPOObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdGPO.M
GPOObj.Members.Add(new PSNoteProperty("FilePath", AdGPO.Members["gPCFileSysPa
return new PSObject[] { GPOObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class GPORecordDictionaryProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdGPO = (PSObject) record;
ADWSClass.AdGPODictionary.Add((Convert.ToString(AdGPO.Members["DistinguishedNa
return new PSObject[] { };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class SOMRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdSOM = (PSObject) record;
List<PSObject> SOMsList = new List<PSObject>();
int Depth = 0;
bool BlockInheritance = false;
bool? LinkEnabled = null;
bool? Enforced = null;
string gPLink = Convert.ToString(AdSOM.Members["gPLink"].Value);
string GPOName = null;
Depth = (Convert.ToString(AdSOM.Members["DistinguishedName"].Value).Split(new string
if (AdSOM.Members["gPOptions"].Value != null && (int) AdSOM.Members["gPOptions"].Va
{
BlockInheritance = true;
}
var GPLinks = gPLink.Split(']', '[').Where(x => x.StartsWith("LDAP"));
int Order = (GPLinks.ToArray()).Length;
if (Order == 0)
{
PSObject SOMObj = new PSObject();
SOMObj.Members.Add(new PSNoteProperty("Name", AdSOM.Members["Name"].Value
SOMObj.Members.Add(new PSNoteProperty("Depth", Depth));
SOMObj.Members.Add(new PSNoteProperty("DistinguishedName", AdSOM.Members["
SOMObj.Members.Add(new PSNoteProperty("Link Order", null));
SOMObj.Members.Add(new PSNoteProperty("GPO", GPOName));
SOMObj.Members.Add(new PSNoteProperty("Enforced", Enforced));
SOMObj.Members.Add(new PSNoteProperty("Link Enabled", LinkEnabled));
SOMObj.Members.Add(new PSNoteProperty("BlockInheritance", BlockInheritance));
SOMObj.Members.Add(new PSNoteProperty("gPLink", gPLink));
SOMObj.Members.Add(new PSNoteProperty("gPOptions", AdSOM.Members["gPOption
SOMsList.Add( SOMObj );
}
foreach (string link in GPLinks)
{
string[] linksplit = link.Split('/', ';');
if (!Convert.ToBoolean((Convert.ToInt32(linksplit[3]) & 1)))
{
LinkEnabled = true;
}
else
{
LinkEnabled = false;
}
if (Convert.ToBoolean((Convert.ToInt32(linksplit[3]) & 2)))
{
Enforced = true;
}
else
{
Enforced = false;
}
GPOName = ADWSClass.AdGPODictionary.ContainsKey(linksplit[2].ToUpper()) ? ADW
PSObject SOMObj = new PSObject();
SOMObj.Members.Add(new PSNoteProperty("Name", AdSOM.Members["Name"].Value
SOMObj.Members.Add(new PSNoteProperty("Depth", Depth));
SOMObj.Members.Add(new PSNoteProperty("DistinguishedName", AdSOM.Members["
SOMObj.Members.Add(new PSNoteProperty("Link Order", Order));
SOMObj.Members.Add(new PSNoteProperty("GPO", GPOName));
SOMObj.Members.Add(new PSNoteProperty("Enforced", Enforced));
SOMObj.Members.Add(new PSNoteProperty("Link Enabled", LinkEnabled));
SOMObj.Members.Add(new PSNoteProperty("BlockInheritance", BlockInheritance));
SOMObj.Members.Add(new PSNoteProperty("gPLink", gPLink));
SOMObj.Members.Add(new PSNoteProperty("gPOptions", AdSOM.Members["gPOption
SOMsList.Add( SOMObj );
Order--;
}
return SOMsList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class PrinterRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdPrinter = (PSObject) record;
PSObject PrinterObj = new PSObject();
PrinterObj.Members.Add(new PSNoteProperty("Name", AdPrinter.Members["Name"].Value
PrinterObj.Members.Add(new PSNoteProperty("ServerName", AdPrinter.Members["server
PrinterObj.Members.Add(new PSNoteProperty("ShareName", ((Microsoft.ActiveDirectory.M
PrinterObj.Members.Add(new PSNoteProperty("DriverName", AdPrinter.Members["driverN
PrinterObj.Members.Add(new PSNoteProperty("DriverVersion", AdPrinter.Members["driver
PrinterObj.Members.Add(new PSNoteProperty("PortName", ((Microsoft.ActiveDirectory.Ma
PrinterObj.Members.Add(new PSNoteProperty("URL", ((Microsoft.ActiveDirectory.Manage
PrinterObj.Members.Add(new PSNoteProperty("whenCreated", AdPrinter.Members["whenC
PrinterObj.Members.Add(new PSNoteProperty("whenChanged", AdPrinter.Members["when
return new PSObject[] { PrinterObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class ComputerRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdComputer = (PSObject) record;
int? DaysSinceLastLogon = null;
int? DaysSinceLastPasswordChange = null;
bool Dormant = false;
bool PasswordNotChangedafterMaxAge = false;
string SIDHistory = "";
string DelegationType = null;
string DelegationProtocol = null;
string DelegationServices = null;
DateTime? LastLogonDate = null;
DateTime? PasswordLastSet = null;
if (AdComputer.Members["LastLogonDate"].Value != null)
{
//LastLogonDate = DateTime.FromFileTime((long)(AdComputer.Members["lastLogonTim
// LastLogonDate is lastLogonTimeStamp converted to local time
LastLogonDate = Convert.ToDateTime(AdComputer.Members["LastLogonDate"].Value)
DaysSinceLastLogon = Math.Abs((Date1 - (DateTime)LastLogonDate).Days);
if (DaysSinceLastLogon > DormantTimeSpan)
{
Dormant = true;
}
}
if (AdComputer.Members["PasswordLastSet"].Value != null)
{
//PasswordLastSet = DateTime.FromFileTime((long)(AdComputer.Members["pwdLastSe
// PasswordLastSet is pwdLastSet converted to local time
PasswordLastSet = Convert.ToDateTime(AdComputer.Members["PasswordLastSet"].Va
DaysSinceLastPasswordChange = Math.Abs((Date1 - (DateTime)PasswordLastSet).Da
if (DaysSinceLastPasswordChange > PassMaxAge)
{
PasswordNotChangedafterMaxAge = true;
}
}
if ( ((bool) AdComputer.Members["TrustedForDelegation"].Value) && ((int) AdComputer.Me
{
DelegationType = "Unconstrained";
DelegationServices = "Any";
}
if (AdComputer.Members["msDS-AllowedToDelegateTo"] != null)
{
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection delegateto = (Micros
if (delegateto.Value != null)
{
DelegationType = "Constrained";
foreach (var value in delegateto)
{
DelegationServices = DelegationServices + "," + Convert.ToString(value);
}
DelegationServices = DelegationServices.TrimStart(',');
}
}
if ((bool) AdComputer.Members["TrustedToAuthForDelegation"].Value)
{
DelegationProtocol = "Any";
}
else if (DelegationType != null)
{
DelegationProtocol = "Kerberos";
}
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection history = (Microsoft.Act
string sids = "";
foreach (var value in history)
{
sids = sids + "," + Convert.ToString(value);
}
SIDHistory = sids.TrimStart(',');
string OperatingSystem = CleanString((AdComputer.Members["OperatingSystem"].Value !
PSObject ComputerObj = new PSObject();
ComputerObj.Members.Add(new PSNoteProperty("UserName", CleanString(AdComputer.
ComputerObj.Members.Add(new PSNoteProperty("Name", CleanString(AdComputer.Mem
ComputerObj.Members.Add(new PSNoteProperty("DNSHostName", AdComputer.Member
ComputerObj.Members.Add(new PSNoteProperty("Enabled", AdComputer.Members["Enab
ComputerObj.Members.Add(new PSNoteProperty("IPv4Address", AdComputer.Members["
ComputerObj.Members.Add(new PSNoteProperty("Operating System", OperatingSystem))
ComputerObj.Members.Add(new PSNoteProperty("Logon Age (days)", DaysSinceLastLog
ComputerObj.Members.Add(new PSNoteProperty("Password Age (days)", DaysSinceLast
ComputerObj.Members.Add(new PSNoteProperty("Dormant (> " + DormantTimeSpan + " d
ComputerObj.Members.Add(new PSNoteProperty("Password Age (> " + PassMaxAge + "
ComputerObj.Members.Add(new PSNoteProperty("Delegation Type", DelegationType));
ComputerObj.Members.Add(new PSNoteProperty("Delegation Protocol", DelegationProtoc
ComputerObj.Members.Add(new PSNoteProperty("Delegation Services", DelegationServic
ComputerObj.Members.Add(new PSNoteProperty("Primary Group ID", AdComputer.Memb
ComputerObj.Members.Add(new PSNoteProperty("SID", AdComputer.Members["SID"].Val
ComputerObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
ComputerObj.Members.Add(new PSNoteProperty("Description", CleanString(AdComputer
ComputerObj.Members.Add(new PSNoteProperty("ms-ds-CreatorSid", AdComputer.Memb
ComputerObj.Members.Add(new PSNoteProperty("Last Logon Date", LastLogonDate));
ComputerObj.Members.Add(new PSNoteProperty("Password LastSet", PasswordLastSet)
ComputerObj.Members.Add(new PSNoteProperty("UserAccountControl", AdComputer.Me
ComputerObj.Members.Add(new PSNoteProperty("whenCreated", AdComputer.Members[
ComputerObj.Members.Add(new PSNoteProperty("whenChanged", AdComputer.Members
ComputerObj.Members.Add(new PSNoteProperty("Distinguished Name", AdComputer.Me
return new PSObject[] { ComputerObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class ComputerSPNRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdComputer = (PSObject) record;
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection SPNs = (Microsoft.Activ
if (SPNs.Value == null)
{
return new PSObject[] { };
}
List<PSObject> SPNList = new List<PSObject>();
foreach (string SPN in SPNs)
{
bool flag = true;
string[] SPNArray = SPN.Split('/');
foreach (PSObject Obj in SPNList)
{
if ( (string) Obj.Members["Service"].Value == SPNArray[0] )
{
Obj.Members["Host"].Value = string.Join(",", (Obj.Members["Host"].Value + "," + SP
flag = false;
}
}
if (flag)
{
PSObject ComputerSPNObj = new PSObject();
ComputerSPNObj.Members.Add(new PSNoteProperty("UserName", CleanString(AdC
ComputerSPNObj.Members.Add(new PSNoteProperty("Name", CleanString(AdComp
ComputerSPNObj.Members.Add(new PSNoteProperty("Service", SPNArray[0]));
ComputerSPNObj.Members.Add(new PSNoteProperty("Host", SPNArray[1]));
SPNList.Add( ComputerSPNObj );
}
}
return SPNList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class LAPSRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdComputer = (PSObject) record;
bool? Enabled = null;
bool PasswordStored = false;
DateTime? CurrentExpiration = null;
// When the user is not allowed to query the UserAccountControl attribute.
if (AdComputer.Members["userAccountControl"].Value != null)
{
var userFlags = (UACFlags) AdComputer.Members["userAccountControl"].Value;
Enabled = !((userFlags & UACFlags.ACCOUNTDISABLE) == UACFlags.ACCOUNTDIS
}
try
{
CurrentExpiration = DateTime.FromFileTime((long)(AdComputer.Members["ms-Mcs-Adm
PasswordStored = true;
}
catch //(Exception e)
{
//Console.WriteLine("Exception caught: {0}", e);
}
PSObject LAPSObj = new PSObject();
LAPSObj.Members.Add(new PSNoteProperty("Hostname", (AdComputer.Members["DNSH
LAPSObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
LAPSObj.Members.Add(new PSNoteProperty("Stored", PasswordStored));
LAPSObj.Members.Add(new PSNoteProperty("Readable", (AdComputer.Members["ms-Mc
LAPSObj.Members.Add(new PSNoteProperty("Password", AdComputer.Members["ms-Mc
LAPSObj.Members.Add(new PSNoteProperty("Expiration", CurrentExpiration));
return new PSObject[] { LAPSObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class SIDRecordDictionaryProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdObject = (PSObject) record;
switch (Convert.ToString(AdObject.Members["ObjectClass"].Value))
{
case "user":
case "computer":
case "group":
ADWSClass.AdSIDDictionary.Add(Convert.ToString(AdObject.Members["objectsid"].V
break;
}
return new PSObject[] { };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class DACLRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdObject = (PSObject) record;
string Name = null;
string Type = null;
List<PSObject> DACLList = new List<PSObject>();
Name = Convert.ToString(AdObject.Members["Name"].Value);
switch (Convert.ToString(AdObject.Members["objectClass"].Value))
{
case "user":
Type = "User";
break;
case "computer":
Type = "Computer";
break;
case "group":
Type = "Group";
break;
case "container":
Type = "Container";
break;
case "groupPolicyContainer":
Type = "GPO";
Name = Convert.ToString(AdObject.Members["DisplayName"].Value);
break;
case "organizationalUnit":
Type = "OU";
break;
case "domainDNS":
Type = "Domain";
break;
default:
Type = Convert.ToString(AdObject.Members["objectClass"].Value);
break;
}
// When the user is not allowed to query the ntsecuritydescriptor attribute.
if (AdObject.Members["ntsecuritydescriptor"] != null)
{
DirectoryObjectSecurity DirObjSec = (DirectoryObjectSecurity) AdObject.Members["ntse
AuthorizationRuleCollection AccessRules = (AuthorizationRuleCollection) DirObjSec.Ge
foreach (ActiveDirectoryAccessRule Rule in AccessRules)
{
string IdentityReference = Convert.ToString(Rule.IdentityReference);
string Owner = Convert.ToString(DirObjSec.GetOwner(typeof(System.Security.Princip
PSObject ObjectObj = new PSObject();
ObjectObj.Members.Add(new PSNoteProperty("Name", CleanString(Name)));
ObjectObj.Members.Add(new PSNoteProperty("Type", Type));
ObjectObj.Members.Add(new PSNoteProperty("ObjectTypeName", ADWSClass.GUID
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectTypeName", ADWSCl
ObjectObj.Members.Add(new PSNoteProperty("ActiveDirectoryRights", Rule.ActiveDi
ObjectObj.Members.Add(new PSNoteProperty("AccessControlType", Rule.AccessCo
ObjectObj.Members.Add(new PSNoteProperty("IdentityReferenceName", ADWSClas
ObjectObj.Members.Add(new PSNoteProperty("OwnerName", ADWSClass.AdSIDDic
ObjectObj.Members.Add(new PSNoteProperty("Inherited", Rule.IsInherited));
ObjectObj.Members.Add(new PSNoteProperty("ObjectFlags", Rule.ObjectFlags));
ObjectObj.Members.Add(new PSNoteProperty("InheritanceFlags", Rule.InheritanceFl
ObjectObj.Members.Add(new PSNoteProperty("InheritanceType", Rule.InheritanceTy
ObjectObj.Members.Add(new PSNoteProperty("PropagationFlags", Rule.Propagation
ObjectObj.Members.Add(new PSNoteProperty("ObjectType", Rule.ObjectType));
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectType", Rule.InheritedO
ObjectObj.Members.Add(new PSNoteProperty("IdentityReference", Rule.IdentityRefe
ObjectObj.Members.Add(new PSNoteProperty("Owner", Owner));
ObjectObj.Members.Add(new PSNoteProperty("DistinguishedName", AdObject.Memb
DACLList.Add( ObjectObj );
}
}
return DACLList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class SACLRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdObject = (PSObject) record;
string Name = null;
string Type = null;
List<PSObject> SACLList = new List<PSObject>();
Name = Convert.ToString(AdObject.Members["Name"].Value);
switch (Convert.ToString(AdObject.Members["objectClass"].Value))
{
case "user":
Type = "User";
break;
case "computer":
Type = "Computer";
break;
case "group":
Type = "Group";
break;
case "container":
Type = "Container";
break;
case "groupPolicyContainer":
Type = "GPO";
Name = Convert.ToString(AdObject.Members["DisplayName"].Value);
break;
case "organizationalUnit":
Type = "OU";
break;
case "domainDNS":
Type = "Domain";
break;
default:
Type = Convert.ToString(AdObject.Members["objectClass"].Value);
break;
}
// When the user is not allowed to query the ntsecuritydescriptor attribute.
if (AdObject.Members["ntsecuritydescriptor"] != null)
{
DirectoryObjectSecurity DirObjSec = (DirectoryObjectSecurity) AdObject.Members["ntse
AuthorizationRuleCollection AuditRules = (AuthorizationRuleCollection) DirObjSec.GetA
foreach (ActiveDirectoryAuditRule Rule in AuditRules)
{
PSObject ObjectObj = new PSObject();
ObjectObj.Members.Add(new PSNoteProperty("Name", CleanString(Name)));
ObjectObj.Members.Add(new PSNoteProperty("Type", Type));
ObjectObj.Members.Add(new PSNoteProperty("ObjectTypeName", ADWSClass.GUID
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectTypeName", ADWSCl
ObjectObj.Members.Add(new PSNoteProperty("ActiveDirectoryRights", Rule.ActiveDi
ObjectObj.Members.Add(new PSNoteProperty("IdentityReference", Rule.IdentityRefe
ObjectObj.Members.Add(new PSNoteProperty("AuditFlags", Rule.AuditFlags));
ObjectObj.Members.Add(new PSNoteProperty("ObjectFlags", Rule.ObjectFlags));
ObjectObj.Members.Add(new PSNoteProperty("InheritanceFlags", Rule.InheritanceFl
ObjectObj.Members.Add(new PSNoteProperty("InheritanceType", Rule.InheritanceTy
ObjectObj.Members.Add(new PSNoteProperty("Inherited", Rule.IsInherited));
ObjectObj.Members.Add(new PSNoteProperty("PropagationFlags", Rule.Propagation
ObjectObj.Members.Add(new PSNoteProperty("ObjectType", Rule.ObjectType));
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectType", Rule.InheritedO
SACLList.Add( ObjectObj );
}
}
return SACLList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
//The interface and implmentation class used to handle the results (this implementation just writes
interface IResultsHandler
{
void processResults(Object[] t);
Object[] finalise();
}
class SimpleResultsHandler : IResultsHandler
{
private Object lockObj = new Object();
private List<Object> processed = new List<Object>();
public SimpleResultsHandler()
{
}
public void processResults(Object[] results)
{
lock (lockObj)
{
if (results.Length != 0)
{
for (var i = 0; i < results.Length; i++)
{
processed.Add((PSObject)results[i]);
}
}
}
}
public Object[] finalise()
{
return processed.ToArray();
}
}
"@
$LDAPSource = @"
// Thanks Dennis Albuquerque for the C# multithreading code
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Net;
using System.Threading;
using System.DirectoryServices;
using System.Security.Principal;
using System.Security.AccessControl;
using System.Management.Automation;
using System.Diagnostics;
//using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Runtime.InteropServices;
namespace ADRecon
{
public static class LDAPClass
{
private static DateTime Date1;
private static int PassMaxAge;
private static int DormantTimeSpan;
private static Dictionary<string, string> AdGroupDictionary = new Dictionary<string, string>();
private static string DomainSID;
private static Dictionary<string, string> AdGPODictionary = new Dictionary<string, string>();
private static Hashtable GUIDs = new Hashtable();
private static Dictionary<string, string> AdSIDDictionary = new Dictionary<string, string>();
private static readonly HashSet<string> Groups = new HashSet<string> ( new string[] {"268435456
private static readonly HashSet<string> Users = new HashSet<string> ( new string[] { "805306368
private static readonly HashSet<string> Computers = new HashSet<string> ( new string[] { "80530
private static readonly HashSet<string> TrustAccounts = new HashSet<string> ( new string[] { "805
[Flags]
//Values taken from https://support.microsoft.com/en-au/kb/305144
public enum UACFlags
{
SCRIPT = 1, // 0x1
ACCOUNTDISABLE = 2, // 0x2
HOMEDIR_REQUIRED = 8, // 0x8
LOCKOUT = 16, // 0x10
PASSWD_NOTREQD = 32, // 0x20
PASSWD_CANT_CHANGE = 64, // 0x40
ENCRYPTED_TEXT_PASSWORD_ALLOWED = 128, // 0x80
TEMP_DUPLICATE_ACCOUNT = 256, // 0x100
NORMAL_ACCOUNT = 512, // 0x200
INTERDOMAIN_TRUST_ACCOUNT = 2048, // 0x800
WORKSTATION_TRUST_ACCOUNT = 4096, // 0x1000
SERVER_TRUST_ACCOUNT = 8192, // 0x2000
DONT_EXPIRE_PASSWD = 65536, // 0x10000
MNS_LOGON_ACCOUNT = 131072, // 0x20000
SMARTCARD_REQUIRED = 262144, // 0x40000
TRUSTED_FOR_DELEGATION = 524288, // 0x80000
NOT_DELEGATED = 1048576, // 0x100000
USE_DES_KEY_ONLY = 2097152, // 0x200000
DONT_REQUIRE_PREAUTH = 4194304, // 0x400000
PASSWORD_EXPIRED = 8388608, // 0x800000
TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 16777216, // 0x1000000
PARTIAL_SECRETS_ACCOUNT = 67108864 // 0x04000000
}
[Flags]
//Values taken from https://blogs.msdn.microsoft.com/openspecification/2011/05/30/windows-confi
public enum KerbEncFlags
{
ZERO = 0,
DES_CBC_CRC = 1, // 0x1
DES_CBC_MD5 = 2, // 0x2
RC4_HMAC = 4, // 0x4
AES128_CTS_HMAC_SHA1_96 = 8, // 0x18
AES256_CTS_HMAC_SHA1_96 = 16 // 0x10
}
[Flags]
//Values taken from https://support.microsoft.com/en-au/kb/305144
public enum GroupTypeFlags
{
GLOBAL_GROUP = 2, // 0x00000002
DOMAIN_LOCAL_GROUP = 4, // 0x00000004
LOCAL_GROUP = 4, // 0x00000004
UNIVERSAL_GROUP = 8, // 0x00000008
SECURITY_ENABLED = -2147483648 // 0x80000000
}
■■private static readonly Dictionary<string, string> Replacements = new Dictionary<string, string>()
{
//{System.Environment.NewLine, ""},
//{",", ";"},
{"\"", "'"}
};
public static string CleanString(Object StringtoClean)
{
// Remove extra spaces and new lines
string CleanedString = string.Join(" ", ((Convert.ToString(StringtoClean)).Split((string[]) null, Strin
foreach (string Replacement in Replacements.Keys)
{
CleanedString = CleanedString.Replace(Replacement, Replacements[Replacement]);
}
return CleanedString;
}
public static int ObjectCount(Object[] ADRObject)
{
return ADRObject.Length;
}
public static Object[] DomainControllerParser(Object[] AdDomainControllers, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdDomainControllers, numOfThreads, "DomainControllers");
return ADRObj;
}
public static Object[] SchemaParser(Object[] AdSchemas, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdSchemas, numOfThreads, "SchemaHistory");
return ADRObj;
}
public static Object[] UserParser(Object[] AdUsers, DateTime Date1, int DormantTimeSpan, int Pa
{
LDAPClass.Date1 = Date1;
LDAPClass.DormantTimeSpan = DormantTimeSpan;
LDAPClass.PassMaxAge = PassMaxAge;
Object[] ADRObj = runProcessor(AdUsers, numOfThreads, "Users");
return ADRObj;
}
public static Object[] UserSPNParser(Object[] AdUsers, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdUsers, numOfThreads, "UserSPNs");
return ADRObj;
}
public static Object[] GroupParser(Object[] AdGroups, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdGroups, numOfThreads, "Groups");
return ADRObj;
}
public static Object[] GroupChangeParser(Object[] AdGroups, DateTime Date1, int numOfThreads
{
LDAPClass.Date1 = Date1;
Object[] ADRObj = runProcessor(AdGroups, numOfThreads, "GroupChanges");
return ADRObj;
}
public static Object[] GroupMemberParser(Object[] AdGroups, Object[] AdGroupMembers, string D
{
LDAPClass.AdGroupDictionary = new Dictionary<string, string>();
runProcessor(AdGroups, numOfThreads, "GroupsDictionary");
LDAPClass.DomainSID = DomainSID;
Object[] ADRObj = runProcessor(AdGroupMembers, numOfThreads, "GroupMembers");
return ADRObj;
}
public static Object[] OUParser(Object[] AdOUs, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdOUs, numOfThreads, "OUs");
return ADRObj;
}
public static Object[] GPOParser(Object[] AdGPOs, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdGPOs, numOfThreads, "GPOs");
return ADRObj;
}
public static Object[] SOMParser(Object[] AdGPOs, Object[] AdSOMs, int numOfThreads)
{
LDAPClass.AdGPODictionary = new Dictionary<string, string>();
runProcessor(AdGPOs, numOfThreads, "GPOsDictionary");
Object[] ADRObj = runProcessor(AdSOMs, numOfThreads, "SOMs");
return ADRObj;
}
public static Object[] PrinterParser(Object[] ADPrinters, int numOfThreads)
{
Object[] ADRObj = runProcessor(ADPrinters, numOfThreads, "Printers");
return ADRObj;
}
public static Object[] ComputerParser(Object[] AdComputers, DateTime Date1, int DormantTimeSp
{
LDAPClass.Date1 = Date1;
LDAPClass.DormantTimeSpan = DormantTimeSpan;
LDAPClass.PassMaxAge = PassMaxAge;
Object[] ADRObj = runProcessor(AdComputers, numOfThreads, "Computers");
return ADRObj;
}
public static Object[] ComputerSPNParser(Object[] AdComputers, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdComputers, numOfThreads, "ComputerSPNs");
return ADRObj;
}
public static Object[] LAPSParser(Object[] AdComputers, int numOfThreads)
{
Object[] ADRObj = runProcessor(AdComputers, numOfThreads, "LAPS");
return ADRObj;
}
public static Object[] DACLParser(Object[] ADObjects, Object PSGUIDs, int numOfThreads)
{
LDAPClass.AdSIDDictionary = new Dictionary<string, string>();
runProcessor(ADObjects, numOfThreads, "SIDDictionary");
LDAPClass.GUIDs = (Hashtable) PSGUIDs;
Object[] ADRObj = runProcessor(ADObjects, numOfThreads, "DACLs");
return ADRObj;
}
public static Object[] SACLParser(Object[] ADObjects, Object PSGUIDs, int numOfThreads)
{
LDAPClass.GUIDs = (Hashtable) PSGUIDs;
Object[] ADRObj = runProcessor(ADObjects, numOfThreads, "SACLs");
return ADRObj;
}
static Object[] runProcessor(Object[] arrayToProcess, int numOfThreads, string processorType)
{
int totalRecords = arrayToProcess.Length;
IRecordProcessor recordProcessor = recordProcessorFactory(processorType);
IResultsHandler resultsHandler = new SimpleResultsHandler ();
int numberOfRecordsPerThread = totalRecords / numOfThreads;
int remainders = totalRecords % numOfThreads;
Thread[] threads = new Thread[numOfThreads];
for (int i = 0; i < numOfThreads; i++)
{
int numberOfRecordsToProcess = numberOfRecordsPerThread;
if (i == (numOfThreads - 1))
{
//last thread, do the remaining records
numberOfRecordsToProcess += remainders;
}
//split the full array into chunks to be given to different threads
Object[] sliceToProcess = new Object[numberOfRecordsToProcess];
Array.Copy(arrayToProcess, i * numberOfRecordsPerThread, sliceToProcess, 0, numberOfR
ProcessorThread processorThread = new ProcessorThread(i, recordProcessor, resultsHandle
threads[i] = new Thread(processorThread.processThreadRecords);
threads[i].Start();
}
foreach (Thread t in threads)
{
t.Join();
}
return resultsHandler.finalise();
}
static IRecordProcessor recordProcessorFactory(string name)
{
switch (name)
{
case "DomainControllers":
return new DomainControllerRecordProcessor();
case "SchemaHistory":
return new SchemaRecordProcessor();
case "Users":
return new UserRecordProcessor();
case "UserSPNs":
return new UserSPNRecordProcessor();
case "Groups":
return new GroupRecordProcessor();
case "GroupChanges":
return new GroupChangeRecordProcessor();
case "GroupsDictionary":
return new GroupRecordDictionaryProcessor();
case "GroupMembers":
return new GroupMemberRecordProcessor();
case "OUs":
return new OURecordProcessor();
case "GPOs":
return new GPORecordProcessor();
case "GPOsDictionary":
return new GPORecordDictionaryProcessor();
case "SOMs":
return new SOMRecordProcessor();
case "Printers":
return new PrinterRecordProcessor();
case "Computers":
return new ComputerRecordProcessor();
case "ComputerSPNs":
return new ComputerSPNRecordProcessor();
case "LAPS":
return new LAPSRecordProcessor();
case "SIDDictionary":
return new SIDRecordDictionaryProcessor();
case "DACLs":
return new DACLRecordProcessor();
case "SACLs":
return new SACLRecordProcessor();
}
throw new ArgumentException("Invalid processor type " + name);
}
class ProcessorThread
{
readonly int id;
readonly IRecordProcessor recordProcessor;
readonly IResultsHandler resultsHandler;
readonly Object[] objectsToBeProcessed;
public ProcessorThread(int id, IRecordProcessor recordProcessor, IResultsHandler resultsHand
{
this.recordProcessor = recordProcessor;
this.id = id;
this.resultsHandler = resultsHandler;
this.objectsToBeProcessed = objectsToBeProcessed;
}
public void processThreadRecords()
{
for (int i = 0; i < objectsToBeProcessed.Length; i++)
{
Object[] result = recordProcessor.processRecord(objectsToBeProcessed[i]);
resultsHandler.processResults(result); //this is a thread safe operation
}
}
}
//The interface and implmentation class used to process a record (this implemmentation just return
interface IRecordProcessor
{
PSObject[] processRecord(Object record);
}
class DomainControllerRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
System.DirectoryServices.ActiveDirectory.DomainController AdDC = (System.DirectorySer
bool? Infra = false;
bool? Naming = false;
bool? Schema = false;
bool? RID = false;
bool? PDC = false;
string Domain = null;
string Site = null;
string OperatingSystem = null;
PSObject DCSMBObj = new PSObject();
try
{
Domain = AdDC.Domain.ToString();
foreach (var OperationMasterRole in (System.DirectoryServices.ActiveDirectory.ActiveD
{
switch (OperationMasterRole.ToString())
{
case "InfrastructureRole":
Infra = true;
break;
case "NamingRole":
Naming = true;
break;
case "SchemaRole":
Schema = true;
break;
case "RidRole":
RID = true;
break;
case "PdcRole":
PDC = true;
break;
}
}
Site = AdDC.SiteName;
OperatingSystem = AdDC.OSVersion.ToString();
}
catch (System.DirectoryServices.ActiveDirectory.ActiveDirectoryServerDownException)// e
{
//Console.WriteLine("Exception caught: {0}", e);
Infra = null;
Naming = null;
Schema = null;
RID = null;
PDC = null;
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
}
PSObject DCObj = new PSObject();
DCObj.Members.Add(new PSNoteProperty("Domain", Domain));
DCObj.Members.Add(new PSNoteProperty("Site", Site));
DCObj.Members.Add(new PSNoteProperty("Name", Convert.ToString(AdDC.Name).Split('
DCObj.Members.Add(new PSNoteProperty("IPv4Address", AdDC.IPAddress));
DCObj.Members.Add(new PSNoteProperty("Operating System", OperatingSystem));
DCObj.Members.Add(new PSNoteProperty("Hostname", AdDC.Name));
DCObj.Members.Add(new PSNoteProperty("Infra", Infra));
DCObj.Members.Add(new PSNoteProperty("Naming", Naming));
DCObj.Members.Add(new PSNoteProperty("Schema", Schema));
DCObj.Members.Add(new PSNoteProperty("RID", RID));
DCObj.Members.Add(new PSNoteProperty("PDC", PDC));
if (AdDC.IPAddress != null)
{
DCSMBObj = GetPSObject(AdDC.IPAddress);
}
else
{
DCSMBObj = new PSObject();
DCSMBObj.Members.Add(new PSNoteProperty("SMB Port Open", false));
}
foreach (PSPropertyInfo psPropertyInfo in DCSMBObj.Properties)
{
if (Convert.ToString(psPropertyInfo.Name) == "SMB Port Open" && (bool) psPropertyInf
{
DCObj.Members.Add(new PSNoteProperty(psPropertyInfo.Name, psPropertyInfo.Val
DCObj.Members.Add(new PSNoteProperty("SMB1(NT LM 0.12)", null));
DCObj.Members.Add(new PSNoteProperty("SMB2(0x0202)", null));
DCObj.Members.Add(new PSNoteProperty("SMB2(0x0210)", null));
DCObj.Members.Add(new PSNoteProperty("SMB3(0x0300)", null));
DCObj.Members.Add(new PSNoteProperty("SMB3(0x0302)", null));
DCObj.Members.Add(new PSNoteProperty("SMB3(0x0311)", null));
DCObj.Members.Add(new PSNoteProperty("SMB Signing", null));
break;
}
else
{
DCObj.Members.Add(new PSNoteProperty(psPropertyInfo.Name, psPropertyInfo.Val
}
}
return new PSObject[] { DCObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class SchemaRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdSchema = (SearchResult) record;
PSObject SchemaObj = new PSObject();
SchemaObj.Members.Add(new PSNoteProperty("ObjectClass", AdSchema.Properties["obj
SchemaObj.Members.Add(new PSNoteProperty("Name", AdSchema.Properties["name"][0
SchemaObj.Members.Add(new PSNoteProperty("whenCreated", AdSchema.Properties["w
SchemaObj.Members.Add(new PSNoteProperty("whenChanged", AdSchema.Properties["w
SchemaObj.Members.Add(new PSNoteProperty("DistinguishedName", AdSchema.Propert
return new PSObject[] { SchemaObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class UserRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdUser = (SearchResult) record;
bool? Enabled = null;
bool? CannotChangePassword = null;
bool? PasswordNeverExpires = null;
bool? AccountLockedOut = null;
bool? PasswordExpired = null;
bool? ReversiblePasswordEncryption = null;
bool? DelegationPermitted = null;
bool? SmartcardRequired = null;
bool? UseDESKeyOnly = null;
bool? PasswordNotRequired = null;
bool? TrustedforDelegation = null;
bool? TrustedtoAuthforDelegation = null;
bool? DoesNotRequirePreAuth = null;
bool? KerberosRC4 = null;
bool? KerberosAES128 = null;
bool? KerberosAES256 = null;
string DelegationType = null;
string DelegationProtocol = null;
string DelegationServices = null;
bool MustChangePasswordatLogon = false;
int? DaysSinceLastLogon = null;
int? DaysSinceLastPasswordChange = null;
int? AccountExpirationNumofDays = null;
bool PasswordNotChangedafterMaxAge = false;
bool NeverLoggedIn = false;
bool Dormant = false;
DateTime? LastLogonDate = null;
DateTime? PasswordLastSet = null;
DateTime? AccountExpires = null;
byte[] ntSecurityDescriptor = null;
bool DenyEveryone = false;
bool DenySelf = false;
string SIDHistory = "";
bool? HasSPN = null;
// When the user is not allowed to query the UserAccountControl attribute.
if (AdUser.Properties["useraccountcontrol"].Count != 0)
{
var userFlags = (UACFlags) AdUser.Properties["useraccountcontrol"][0];
Enabled = !((userFlags & UACFlags.ACCOUNTDISABLE) == UACFlags.ACCOUNTDIS
PasswordNeverExpires = (userFlags & UACFlags.DONT_EXPIRE_PASSWD) == UACF
AccountLockedOut = (userFlags & UACFlags.LOCKOUT) == UACFlags.LOCKOUT;
DelegationPermitted = !((userFlags & UACFlags.NOT_DELEGATED) == UACFlags.NOT
SmartcardRequired = (userFlags & UACFlags.SMARTCARD_REQUIRED) == UACFlag
ReversiblePasswordEncryption = (userFlags & UACFlags.ENCRYPTED_TEXT_PASSW
UseDESKeyOnly = (userFlags & UACFlags.USE_DES_KEY_ONLY) == UACFlags.USE
PasswordNotRequired = (userFlags & UACFlags.PASSWD_NOTREQD) == UACFlags.P
PasswordExpired = (userFlags & UACFlags.PASSWORD_EXPIRED) == UACFlags.PAS
TrustedforDelegation = (userFlags & UACFlags.TRUSTED_FOR_DELEGATION) == UA
TrustedtoAuthforDelegation = (userFlags & UACFlags.TRUSTED_TO_AUTHENTICATE
DoesNotRequirePreAuth = (userFlags & UACFlags.DONT_REQUIRE_PREAUTH) == U
}
if (AdUser.Properties["msds-supportedencryptiontypes"].Count != 0)
{
var userKerbEncFlags = (KerbEncFlags) AdUser.Properties["msds-supportedencryption
if (userKerbEncFlags != KerbEncFlags.ZERO)
{
KerberosRC4 = (userKerbEncFlags & KerbEncFlags.RC4_HMAC) == KerbEncFlags.R
KerberosAES128 = (userKerbEncFlags & KerbEncFlags.AES128_CTS_HMAC_SHA1
KerberosAES256 = (userKerbEncFlags & KerbEncFlags.AES256_CTS_HMAC_SHA1
}
}
// When the user is not allowed to query the ntsecuritydescriptor attribute.
if (AdUser.Properties["ntsecuritydescriptor"].Count != 0)
{
ntSecurityDescriptor = (byte[]) AdUser.Properties["ntsecuritydescriptor"][0];
}
else
{
DirectoryEntry AdUserEntry = ((SearchResult)record).GetDirectoryEntry();
ntSecurityDescriptor = (byte[]) AdUserEntry.ObjectSecurity.GetSecurityDescriptorBinary
}
if (ntSecurityDescriptor != null)
{
DirectoryObjectSecurity DirObjSec = new ActiveDirectorySecurity();
DirObjSec.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor);
AuthorizationRuleCollection AccessRules = (AuthorizationRuleCollection) DirObjSec.Ge
foreach (ActiveDirectoryAccessRule Rule in AccessRules)
{
if ((Convert.ToString(Rule.ObjectType)).Equals("ab721a53-1e2f-11d0-9819-00aa0040
{
if (Rule.AccessControlType.ToString() == "Deny")
{
string ObjectName = Convert.ToString(Rule.IdentityReference);
if (ObjectName == "Everyone")
{
DenyEveryone = true;
}
if (ObjectName == "NT AUTHORITY\\SELF")
{
DenySelf = true;
}
}
}
}
if (DenyEveryone && DenySelf)
{
CannotChangePassword = true;
}
else
{
CannotChangePassword = false;
}
}
if (AdUser.Properties["lastlogontimestamp"].Count != 0)
{
LastLogonDate = DateTime.FromFileTime((long)(AdUser.Properties["lastlogontimestam
DaysSinceLastLogon = Math.Abs((Date1 - (DateTime)LastLogonDate).Days);
if (DaysSinceLastLogon > DormantTimeSpan)
{
Dormant = true;
}
}
else
{
NeverLoggedIn = true;
}
if (AdUser.Properties["pwdLastSet"].Count != 0)
{
if (Convert.ToString(AdUser.Properties["pwdlastset"][0]) == "0")
{
if ((bool) PasswordNeverExpires == false)
{
MustChangePasswordatLogon = true;
}
}
else
{
PasswordLastSet = DateTime.FromFileTime((long)(AdUser.Properties["pwdlastset"][0
DaysSinceLastPasswordChange = Math.Abs((Date1 - (DateTime)PasswordLastSet).D
if (DaysSinceLastPasswordChange > PassMaxAge)
{
PasswordNotChangedafterMaxAge = true;
}
}
}
if (AdUser.Properties["accountExpires"].Count != 0)
{
if ((Int64) AdUser.Properties["accountExpires"][0] != (Int64) 9223372036854775807)
{
if ((Int64) AdUser.Properties["accountExpires"][0] != (Int64) 0)
{
try
{
//https://msdn.microsoft.com/en-us/library/ms675098(v=vs.85).aspx
AccountExpires = DateTime.FromFileTime((long)(AdUser.Properties["accountEx
AccountExpirationNumofDays = ((int)((DateTime)AccountExpires - Date1).Days)
}
catch //(Exception e)
{
// Console.WriteLine("Exception caught: {0}", e);
}
}
}
}
if (AdUser.Properties["useraccountcontrol"].Count != 0)
{
if ((bool) TrustedforDelegation)
{
DelegationType = "Unconstrained";
DelegationServices = "Any";
}
if (AdUser.Properties["msDS-AllowedToDelegateTo"].Count >= 1)
{
DelegationType = "Constrained";
for (int i = 0; i < AdUser.Properties["msDS-AllowedToDelegateTo"].Count; i++)
{
var delegateto = AdUser.Properties["msDS-AllowedToDelegateTo"][i];
DelegationServices = DelegationServices + "," + Convert.ToString(delegateto);
}
DelegationServices = DelegationServices.TrimStart(',');
}
if ((bool) TrustedtoAuthforDelegation)
{
DelegationProtocol = "Any";
}
else if (DelegationType != null)
{
DelegationProtocol = "Kerberos";
}
}
if (AdUser.Properties["sidhistory"].Count >= 1)
{
string sids = "";
for (int i = 0; i < AdUser.Properties["sidhistory"].Count; i++)
{
var history = AdUser.Properties["sidhistory"][i];
sids = sids + "," + Convert.ToString(new SecurityIdentifier((byte[])history, 0));
}
SIDHistory = sids.TrimStart(',');
}
if (AdUser.Properties["serviceprincipalname"].Count == 0)
{
HasSPN = false;
}
else if (AdUser.Properties["serviceprincipalname"].Count > 0)
{
HasSPN = true;
}
PSObject UserObj = new PSObject();
UserObj.Members.Add(new PSNoteProperty("UserName", (AdUser.Properties["samaccou
UserObj.Members.Add(new PSNoteProperty("Name", (AdUser.Properties["name"].Count !=
UserObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
UserObj.Members.Add(new PSNoteProperty("Must Change Password at Logon", MustCha
UserObj.Members.Add(new PSNoteProperty("Cannot Change Password", CannotChangeP
UserObj.Members.Add(new PSNoteProperty("Password Never Expires", PasswordNeverE
UserObj.Members.Add(new PSNoteProperty("Reversible Password Encryption", Reversibl
UserObj.Members.Add(new PSNoteProperty("Smartcard Logon Required", SmartcardReq
UserObj.Members.Add(new PSNoteProperty("Delegation Permitted", DelegationPermitted)
UserObj.Members.Add(new PSNoteProperty("Kerberos DES Only", UseDESKeyOnly));
UserObj.Members.Add(new PSNoteProperty("Kerberos RC4", KerberosRC4));
UserObj.Members.Add(new PSNoteProperty("Kerberos AES-128bit", KerberosAES128));
UserObj.Members.Add(new PSNoteProperty("Kerberos AES-256bit", KerberosAES256));
UserObj.Members.Add(new PSNoteProperty("Does Not Require Pre Auth", DoesNotRequi
UserObj.Members.Add(new PSNoteProperty("Never Logged in", NeverLoggedIn));
UserObj.Members.Add(new PSNoteProperty("Logon Age (days)", DaysSinceLastLogon));
UserObj.Members.Add(new PSNoteProperty("Password Age (days)", DaysSinceLastPassw
UserObj.Members.Add(new PSNoteProperty("Dormant (> " + DormantTimeSpan + " days)
UserObj.Members.Add(new PSNoteProperty("Password Age (> " + PassMaxAge + " days)
UserObj.Members.Add(new PSNoteProperty("Account Locked Out", AccountLockedOut));
UserObj.Members.Add(new PSNoteProperty("Password Expired", PasswordExpired));
UserObj.Members.Add(new PSNoteProperty("Password Not Required", PasswordNotRequ
UserObj.Members.Add(new PSNoteProperty("Delegation Type", DelegationType));
UserObj.Members.Add(new PSNoteProperty("Delegation Protocol", DelegationProtocol));
UserObj.Members.Add(new PSNoteProperty("Delegation Services", DelegationServices));
UserObj.Members.Add(new PSNoteProperty("Logon Workstations", (AdUser.Properties["u
UserObj.Members.Add(new PSNoteProperty("AdminCount", (AdUser.Properties["admincou
UserObj.Members.Add(new PSNoteProperty("Primary GroupID", (AdUser.Properties["prim
UserObj.Members.Add(new PSNoteProperty("SID", Convert.ToString(new SecurityIdentifie
UserObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
UserObj.Members.Add(new PSNoteProperty("HasSPN", HasSPN));
UserObj.Members.Add(new PSNoteProperty("Description", (AdUser.Properties["Descriptio
UserObj.Members.Add(new PSNoteProperty("Title", (AdUser.Properties["Title"].Count != 0
UserObj.Members.Add(new PSNoteProperty("Department", (AdUser.Properties["Departme
UserObj.Members.Add(new PSNoteProperty("Company", (AdUser.Properties["Company"].
UserObj.Members.Add(new PSNoteProperty("Manager", (AdUser.Properties["Manager"].C
UserObj.Members.Add(new PSNoteProperty("Info", (AdUser.Properties["info"].Count != 0 ?
UserObj.Members.Add(new PSNoteProperty("Last Logon Date", LastLogonDate));
UserObj.Members.Add(new PSNoteProperty("Password LastSet", PasswordLastSet));
UserObj.Members.Add(new PSNoteProperty("Account Expiration Date", AccountExpires));
UserObj.Members.Add(new PSNoteProperty("Account Expiration (days)", AccountExpiratio
UserObj.Members.Add(new PSNoteProperty("Mobile", (AdUser.Properties["mobile"].Count
UserObj.Members.Add(new PSNoteProperty("Email", (AdUser.Properties["mail"].Count !=
UserObj.Members.Add(new PSNoteProperty("HomeDirectory", (AdUser.Properties["homed
UserObj.Members.Add(new PSNoteProperty("ProfilePath", (AdUser.Properties["profilepath
UserObj.Members.Add(new PSNoteProperty("ScriptPath", (AdUser.Properties["scriptpath"]
UserObj.Members.Add(new PSNoteProperty("UserAccountControl", (AdUser.Properties["u
UserObj.Members.Add(new PSNoteProperty("First Name", (AdUser.Properties["givenNam
UserObj.Members.Add(new PSNoteProperty("Middle Name", (AdUser.Properties["middleN
UserObj.Members.Add(new PSNoteProperty("Last Name", (AdUser.Properties["sn"].Count
UserObj.Members.Add(new PSNoteProperty("Country", (AdUser.Properties["c"].Count != 0
UserObj.Members.Add(new PSNoteProperty("whenCreated", (AdUser.Properties["whencre
UserObj.Members.Add(new PSNoteProperty("whenChanged", (AdUser.Properties["whenc
UserObj.Members.Add(new PSNoteProperty("DistinguishedName", (AdUser.Properties["di
UserObj.Members.Add(new PSNoteProperty("CanonicalName", (AdUser.Properties["canon
return new PSObject[] { UserObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class UserSPNRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdUser = (SearchResult) record;
if (AdUser.Properties["serviceprincipalname"].Count == 0)
{
return new PSObject[] { };
}
List<PSObject> SPNList = new List<PSObject>();
bool? Enabled = null;
string Memberof = null;
DateTime? PasswordLastSet = null;
if (AdUser.Properties["pwdlastset"].Count != 0)
{
if (Convert.ToString(AdUser.Properties["pwdlastset"][0]) != "0")
{
PasswordLastSet = DateTime.FromFileTime((long)(AdUser.Properties["pwdLastSet"][
}
}
// When the user is not allowed to query the UserAccountControl attribute.
if (AdUser.Properties["useraccountcontrol"].Count != 0)
{
var userFlags = (UACFlags) AdUser.Properties["useraccountcontrol"][0];
Enabled = !((userFlags & UACFlags.ACCOUNTDISABLE) == UACFlags.ACCOUNTDIS
}
string Description = (AdUser.Properties["Description"].Count != 0 ? CleanString(AdUser.Pro
string PrimaryGroupID = (AdUser.Properties["primarygroupid"].Count != 0 ? Convert.ToStri
if (AdUser.Properties["memberof"].Count != 0)
{
foreach (string Member in AdUser.Properties["memberof"])
{
Memberof = Memberof + "," + ((Convert.ToString(Member)).Split(',')[0]).Split('=')[1];
}
Memberof = Memberof.TrimStart(',');
}
foreach (string SPN in AdUser.Properties["serviceprincipalname"])
{
string[] SPNArray = SPN.Split('/');
PSObject UserSPNObj = new PSObject();
UserSPNObj.Members.Add(new PSNoteProperty("UserName", (AdUser.Properties["sam
UserSPNObj.Members.Add(new PSNoteProperty("Name", (AdUser.Properties["name"].C
UserSPNObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
UserSPNObj.Members.Add(new PSNoteProperty("Service", SPNArray[0]));
UserSPNObj.Members.Add(new PSNoteProperty("Host", SPNArray[1]));
UserSPNObj.Members.Add(new PSNoteProperty("Password Last Set", PasswordLastS
UserSPNObj.Members.Add(new PSNoteProperty("Description", Description));
UserSPNObj.Members.Add(new PSNoteProperty("Primary GroupID", PrimaryGroupID))
UserSPNObj.Members.Add(new PSNoteProperty("Memberof", Memberof));
SPNList.Add( UserSPNObj );
}
return SPNList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class GroupRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdGroup = (SearchResult) record;
string ManagedByValue = AdGroup.Properties["managedby"].Count != 0 ? Convert.ToStrin
string ManagedBy = "";
string GroupCategory = null;
string GroupScope = null;
string SIDHistory = "";
if (AdGroup.Properties["managedBy"].Count != 0)
{
ManagedBy = (ManagedByValue.Split(new string[] { "CN=" },StringSplitOptions.Remove
}
if (AdGroup.Properties["grouptype"].Count != 0)
{
var groupTypeFlags = (GroupTypeFlags) AdGroup.Properties["grouptype"][0];
GroupCategory = (groupTypeFlags & GroupTypeFlags.SECURITY_ENABLED) == Grou
if ((groupTypeFlags & GroupTypeFlags.UNIVERSAL_GROUP) == GroupTypeFlags.UNI
{
GroupScope = "Universal";
}
else if ((groupTypeFlags & GroupTypeFlags.GLOBAL_GROUP) == GroupTypeFlags.GL
{
GroupScope = "Global";
}
else if ((groupTypeFlags & GroupTypeFlags.DOMAIN_LOCAL_GROUP) == GroupType
{
GroupScope = "DomainLocal";
}
}
if (AdGroup.Properties["sidhistory"].Count >= 1)
{
string sids = "";
for (int i = 0; i < AdGroup.Properties["sidhistory"].Count; i++)
{
var history = AdGroup.Properties["sidhistory"][i];
sids = sids + "," + Convert.ToString(new SecurityIdentifier((byte[])history, 0));
}
SIDHistory = sids.TrimStart(',');
}
PSObject GroupObj = new PSObject();
GroupObj.Members.Add(new PSNoteProperty("Name", AdGroup.Properties["samaccountn
GroupObj.Members.Add(new PSNoteProperty("AdminCount", (AdGroup.Properties["admin
GroupObj.Members.Add(new PSNoteProperty("GroupCategory", GroupCategory));
GroupObj.Members.Add(new PSNoteProperty("GroupScope", GroupScope));
GroupObj.Members.Add(new PSNoteProperty("ManagedBy", ManagedBy));
GroupObj.Members.Add(new PSNoteProperty("SID", Convert.ToString(new SecurityIdentif
GroupObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
GroupObj.Members.Add(new PSNoteProperty("Description", (AdGroup.Properties["Descrip
GroupObj.Members.Add(new PSNoteProperty("whenCreated", AdGroup.Properties["when
GroupObj.Members.Add(new PSNoteProperty("whenChanged", AdGroup.Properties["when
GroupObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdGroup
GroupObj.Members.Add(new PSNoteProperty("CanonicalName", AdGroup.Properties["can
return new PSObject[] { GroupObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class GroupChangeRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdGroup = (SearchResult) record;
string Action = null;
int? DaysSinceAdded = null;
int? DaysSinceRemoved = null;
DateTime? AddedDate = null;
DateTime? RemovedDate = null;
List<PSObject> GroupChangesList = new List<PSObject>();
System.DirectoryServices.ResultPropertyValueCollection ReplValueMetaData = (System.D
if (ReplValueMetaData.Count != 0)
{
foreach (string ReplData in ReplValueMetaData)
{
XmlDocument ReplXML = new XmlDocument();
ReplXML.LoadXml(ReplData.Replace("\x00", "").Replace("&","&"));
if (ReplXML.SelectSingleNode("DS_REPL_VALUE_META_DATA")["ftimeDeleted"].In
{
Action = "Removed";
AddedDate = DateTime.Parse(ReplXML.SelectSingleNode("DS_REPL_VALUE_ME
DaysSinceAdded = Math.Abs((Date1 - (DateTime) AddedDate).Days);
RemovedDate = DateTime.Parse(ReplXML.SelectSingleNode("DS_REPL_VALUE_
DaysSinceRemoved = Math.Abs((Date1 - (DateTime) RemovedDate).Days);
}
else
{
Action = "Added";
AddedDate = DateTime.Parse(ReplXML.SelectSingleNode("DS_REPL_VALUE_ME
DaysSinceAdded = Math.Abs((Date1 - (DateTime) AddedDate).Days);
RemovedDate = null;
DaysSinceRemoved = null;
}
PSObject GroupChangeObj = new PSObject();
GroupChangeObj.Members.Add(new PSNoteProperty("Group Name", AdGroup.Prop
GroupChangeObj.Members.Add(new PSNoteProperty("Group DistinguishedName", C
GroupChangeObj.Members.Add(new PSNoteProperty("Member DistinguishedName"
GroupChangeObj.Members.Add(new PSNoteProperty("Action", Action));
GroupChangeObj.Members.Add(new PSNoteProperty("Added Age (Days)", DaysSinc
GroupChangeObj.Members.Add(new PSNoteProperty("Removed Age (Days)", DaysS
GroupChangeObj.Members.Add(new PSNoteProperty("Added Date", AddedDate));
GroupChangeObj.Members.Add(new PSNoteProperty("Removed Date", RemovedDa
GroupChangeObj.Members.Add(new PSNoteProperty("ftimeCreated", ReplXML.Sele
GroupChangeObj.Members.Add(new PSNoteProperty("ftimeDeleted", ReplXML.Selec
GroupChangesList.Add( GroupChangeObj );
}
}
return GroupChangesList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class GroupRecordDictionaryProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdGroup = (SearchResult) record;
LDAPClass.AdGroupDictionary.Add((Convert.ToString(new SecurityIdentifier((byte[])AdGro
return new PSObject[] { };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class GroupMemberRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
// https://github.com/BloodHoundAD/BloodHound/blob/master/PowerShell/BloodHound.ps1
SearchResult AdGroup = (SearchResult) record;
List<PSObject> GroupsList = new List<PSObject>();
string SamAccountType = AdGroup.Properties["samaccounttype"].Count != 0 ? Convert.To
string ObjectClass = Convert.ToString(AdGroup.Properties["objectclass"][AdGroup.Propert
string AccountType = "";
string GroupName = "";
string MemberUserName = "-";
string MemberName = "";
string PrimaryGroupID = "";
PSObject GroupMemberObj = new PSObject();
if (ObjectClass == "foreignSecurityPrincipal")
{
AccountType = "foreignSecurityPrincipal";
MemberName = null;
MemberUserName = ((Convert.ToString(AdGroup.Properties["DistinguishedName"][0])).
foreach (string GroupMember in AdGroup.Properties["memberof"])
{
GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", Membe
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberNam
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", Convert.ToStri
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType)
GroupsList.Add( GroupMemberObj );
}
}
if (Groups.Contains(SamAccountType))
{
AccountType = "group";
MemberName = ((Convert.ToString(AdGroup.Properties["DistinguishedName"][0])).Split
foreach (string GroupMember in AdGroup.Properties["memberof"])
{
GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", Membe
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberNam
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", Convert.ToStri
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType)
GroupsList.Add( GroupMemberObj );
}
}
if (Users.Contains(SamAccountType))
{
AccountType = "user";
MemberName = ((Convert.ToString(AdGroup.Properties["DistinguishedName"][0])).Split
MemberUserName = Convert.ToString(AdGroup.Properties["sAMAccountName"][0]);
PrimaryGroupID = Convert.ToString(AdGroup.Properties["primaryGroupID"][0]);
try
{
GroupName = LDAPClass.AdGroupDictionary[LDAPClass.DomainSID + "-" + Primary
}
catch //(Exception e)
{
//Console.WriteLine("Exception caught: {0}", e);
GroupName = PrimaryGroupID;
}
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberU
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName)
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", Convert.ToString
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
foreach (string GroupMember in AdGroup.Properties["memberof"])
{
GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", Membe
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberNam
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", Convert.ToStri
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType)
GroupsList.Add( GroupMemberObj );
}
}
if (Computers.Contains(SamAccountType))
{
AccountType = "computer";
MemberName = ((Convert.ToString(AdGroup.Properties["DistinguishedName"][0])).Split
MemberUserName = Convert.ToString(AdGroup.Properties["sAMAccountName"][0]);
PrimaryGroupID = Convert.ToString(AdGroup.Properties["primaryGroupID"][0]);
try
{
GroupName = LDAPClass.AdGroupDictionary[LDAPClass.DomainSID + "-" + Primary
}
catch //(Exception e)
{
//Console.WriteLine("Exception caught: {0}", e);
GroupName = PrimaryGroupID;
}
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberU
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName)
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", Convert.ToString
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
foreach (string GroupMember in AdGroup.Properties["memberof"])
{
GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", Membe
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberNam
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", Convert.ToStri
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType)
GroupsList.Add( GroupMemberObj );
}
}
if (TrustAccounts.Contains(SamAccountType))
{
AccountType = "trust";
MemberName = ((Convert.ToString(AdGroup.Properties["DistinguishedName"][0])).Split
MemberUserName = Convert.ToString(AdGroup.Properties["sAMAccountName"][0]);
PrimaryGroupID = Convert.ToString(AdGroup.Properties["primaryGroupID"][0]);
try
{
GroupName = LDAPClass.AdGroupDictionary[LDAPClass.DomainSID + "-" + Primary
}
catch //(Exception e)
{
//Console.WriteLine("Exception caught: {0}", e);
GroupName = PrimaryGroupID;
}
GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberU
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName)
GroupMemberObj.Members.Add(new PSNoteProperty("Member SID", Convert.ToString
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
}
return GroupsList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class OURecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdOU = (SearchResult) record;
PSObject OUObj = new PSObject();
OUObj.Members.Add(new PSNoteProperty("Name", AdOU.Properties["name"][0]));
OUObj.Members.Add(new PSNoteProperty("Depth", ((Convert.ToString(AdOU.Properties[
OUObj.Members.Add(new PSNoteProperty("Description", (AdOU.Properties["description"].
OUObj.Members.Add(new PSNoteProperty("whenCreated", AdOU.Properties["whencreate
OUObj.Members.Add(new PSNoteProperty("whenChanged", AdOU.Properties["whenchan
OUObj.Members.Add(new PSNoteProperty("DistinguishedName", AdOU.Properties["distin
return new PSObject[] { OUObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class GPORecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdGPO = (SearchResult) record;
PSObject GPOObj = new PSObject();
GPOObj.Members.Add(new PSNoteProperty("DisplayName", CleanString(AdGPO.Propert
GPOObj.Members.Add(new PSNoteProperty("GUID", CleanString(AdGPO.Properties["nam
GPOObj.Members.Add(new PSNoteProperty("whenCreated", AdGPO.Properties["whenCre
GPOObj.Members.Add(new PSNoteProperty("whenChanged", AdGPO.Properties["whenC
GPOObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdGPO.P
GPOObj.Members.Add(new PSNoteProperty("FilePath", AdGPO.Properties["gpcfilesyspat
return new PSObject[] { GPOObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class GPORecordDictionaryProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdGPO = (SearchResult) record;
LDAPClass.AdGPODictionary.Add((Convert.ToString(AdGPO.Properties["distinguishednam
return new PSObject[] { };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class SOMRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdSOM = (SearchResult) record;
List<PSObject> SOMsList = new List<PSObject>();
int Depth = 0;
bool BlockInheritance = false;
bool? LinkEnabled = null;
bool? Enforced = null;
string gPLink = (AdSOM.Properties["gPLink"].Count != 0 ? Convert.ToString(AdSOM.Prope
string GPOName = null;
Depth = ((Convert.ToString(AdSOM.Properties["distinguishedname"][0]).Split(new string[] {
if (AdSOM.Properties["gPOptions"].Count != 0)
{
if ((int) AdSOM.Properties["gPOptions"][0] == 1)
{
BlockInheritance = true;
}
}
var GPLinks = gPLink.Split(']', '[').Where(x => x.StartsWith("LDAP"));
int Order = (GPLinks.ToArray()).Length;
if (Order == 0)
{
PSObject SOMObj = new PSObject();
SOMObj.Members.Add(new PSNoteProperty("Name", AdSOM.Properties["name"][0]));
SOMObj.Members.Add(new PSNoteProperty("Depth", Depth));
SOMObj.Members.Add(new PSNoteProperty("DistinguishedName", AdSOM.Properties[
SOMObj.Members.Add(new PSNoteProperty("Link Order", null));
SOMObj.Members.Add(new PSNoteProperty("GPO", GPOName));
SOMObj.Members.Add(new PSNoteProperty("Enforced", Enforced));
SOMObj.Members.Add(new PSNoteProperty("Link Enabled", LinkEnabled));
SOMObj.Members.Add(new PSNoteProperty("BlockInheritance", BlockInheritance));
SOMObj.Members.Add(new PSNoteProperty("gPLink", gPLink));
SOMObj.Members.Add(new PSNoteProperty("gPOptions", (AdSOM.Properties["gpoptio
SOMsList.Add( SOMObj );
}
foreach (string link in GPLinks)
{
string[] linksplit = link.Split('/', ';');
if (!Convert.ToBoolean((Convert.ToInt32(linksplit[3]) & 1)))
{
LinkEnabled = true;
}
else
{
LinkEnabled = false;
}
if (Convert.ToBoolean((Convert.ToInt32(linksplit[3]) & 2)))
{
Enforced = true;
}
else
{
Enforced = false;
}
GPOName = LDAPClass.AdGPODictionary.ContainsKey(linksplit[2].ToUpper()) ? LDAP
PSObject SOMObj = new PSObject();
SOMObj.Members.Add(new PSNoteProperty("Name", AdSOM.Properties["name"][0]));
SOMObj.Members.Add(new PSNoteProperty("Depth", Depth));
SOMObj.Members.Add(new PSNoteProperty("DistinguishedName", AdSOM.Properties[
SOMObj.Members.Add(new PSNoteProperty("Link Order", Order));
SOMObj.Members.Add(new PSNoteProperty("GPO", GPOName));
SOMObj.Members.Add(new PSNoteProperty("Enforced", Enforced));
SOMObj.Members.Add(new PSNoteProperty("Link Enabled", LinkEnabled));
SOMObj.Members.Add(new PSNoteProperty("BlockInheritance", BlockInheritance));
SOMObj.Members.Add(new PSNoteProperty("gPLink", gPLink));
SOMObj.Members.Add(new PSNoteProperty("gPOptions", (AdSOM.Properties["gpoptio
SOMsList.Add( SOMObj );
Order--;
}
return SOMsList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class PrinterRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdPrinter = (SearchResult) record;
PSObject PrinterObj = new PSObject();
PrinterObj.Members.Add(new PSNoteProperty("Name", AdPrinter.Properties["Name"][0]));
PrinterObj.Members.Add(new PSNoteProperty("ServerName", AdPrinter.Properties["serve
PrinterObj.Members.Add(new PSNoteProperty("ShareName", AdPrinter.Properties["printSh
PrinterObj.Members.Add(new PSNoteProperty("DriverName", AdPrinter.Properties["driverN
PrinterObj.Members.Add(new PSNoteProperty("DriverVersion", AdPrinter.Properties["drive
PrinterObj.Members.Add(new PSNoteProperty("PortName", AdPrinter.Properties["portNam
PrinterObj.Members.Add(new PSNoteProperty("URL", AdPrinter.Properties["url"][0]));
PrinterObj.Members.Add(new PSNoteProperty("whenCreated", AdPrinter.Properties["when
PrinterObj.Members.Add(new PSNoteProperty("whenChanged", AdPrinter.Properties["whe
return new PSObject[] { PrinterObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class ComputerRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdComputer = (SearchResult) record;
bool Dormant = false;
bool? Enabled = null;
bool PasswordNotChangedafterMaxAge = false;
bool? TrustedforDelegation = null;
bool? TrustedtoAuthforDelegation = null;
string DelegationType = null;
string DelegationProtocol = null;
string DelegationServices = null;
string StrIPAddress = null;
int? DaysSinceLastLogon = null;
int? DaysSinceLastPasswordChange = null;
DateTime? LastLogonDate = null;
DateTime? PasswordLastSet = null;
if (AdComputer.Properties["dnshostname"].Count != 0)
{
try
{
StrIPAddress = Convert.ToString(Dns.GetHostEntry(Convert.ToString(AdComputer.P
}
catch
{
StrIPAddress = null;
}
}
// When the user is not allowed to query the UserAccountControl attribute.
if (AdComputer.Properties["useraccountcontrol"].Count != 0)
{
var userFlags = (UACFlags) AdComputer.Properties["useraccountcontrol"][0];
Enabled = !((userFlags & UACFlags.ACCOUNTDISABLE) == UACFlags.ACCOUNTDIS
TrustedforDelegation = (userFlags & UACFlags.TRUSTED_FOR_DELEGATION) == UA
TrustedtoAuthforDelegation = (userFlags & UACFlags.TRUSTED_TO_AUTHENTICATE
}
if (AdComputer.Properties["lastlogontimestamp"].Count != 0)
{
LastLogonDate = DateTime.FromFileTime((long)(AdComputer.Properties["lastlogontime
DaysSinceLastLogon = Math.Abs((Date1 - (DateTime)LastLogonDate).Days);
if (DaysSinceLastLogon > DormantTimeSpan)
{
Dormant = true;
}
}
if (AdComputer.Properties["pwdlastset"].Count != 0)
{
PasswordLastSet = DateTime.FromFileTime((long)(AdComputer.Properties["pwdlastset"
DaysSinceLastPasswordChange = Math.Abs((Date1 - (DateTime)PasswordLastSet).Da
if (DaysSinceLastPasswordChange > PassMaxAge)
{
PasswordNotChangedafterMaxAge = true;
}
}
if ( ((bool) TrustedforDelegation) && ((int) AdComputer.Properties["primarygroupid"][0] == 5
{
DelegationType = "Unconstrained";
DelegationServices = "Any";
}
if (AdComputer.Properties["msDS-AllowedToDelegateTo"].Count >= 1)
{
DelegationType = "Constrained";
for (int i = 0; i < AdComputer.Properties["msDS-AllowedToDelegateTo"].Count; i++)
{
var delegateto = AdComputer.Properties["msDS-AllowedToDelegateTo"][i];
DelegationServices = DelegationServices + "," + Convert.ToString(delegateto);
}
DelegationServices = DelegationServices.TrimStart(',');
}
if ((bool) TrustedtoAuthforDelegation)
{
DelegationProtocol = "Any";
}
else if (DelegationType != null)
{
DelegationProtocol = "Kerberos";
}
string SIDHistory = "";
if (AdComputer.Properties["sidhistory"].Count >= 1)
{
string sids = "";
for (int i = 0; i < AdComputer.Properties["sidhistory"].Count; i++)
{
var history = AdComputer.Properties["sidhistory"][i];
sids = sids + "," + Convert.ToString(new SecurityIdentifier((byte[])history, 0));
}
SIDHistory = sids.TrimStart(',');
}
string OperatingSystem = CleanString((AdComputer.Properties["operatingsystem"].Count !
puter.Properties["operatingsystemversion"][0] : " "));
PSObject ComputerObj = new PSObject();
ComputerObj.Members.Add(new PSNoteProperty("UserName", (AdComputer.Properties["
ComputerObj.Members.Add(new PSNoteProperty("Name", (AdComputer.Properties["name
ComputerObj.Members.Add(new PSNoteProperty("DNSHostName", (AdComputer.Propert
ComputerObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
ComputerObj.Members.Add(new PSNoteProperty("IPv4Address", StrIPAddress));
ComputerObj.Members.Add(new PSNoteProperty("Operating System", OperatingSystem))
ComputerObj.Members.Add(new PSNoteProperty("Logon Age (days)", DaysSinceLastLog
ComputerObj.Members.Add(new PSNoteProperty("Password Age (days)", DaysSinceLast
ComputerObj.Members.Add(new PSNoteProperty("Dormant (> " + DormantTimeSpan + " d
ComputerObj.Members.Add(new PSNoteProperty("Password Age (> " + PassMaxAge + "
ComputerObj.Members.Add(new PSNoteProperty("Delegation Type", DelegationType));
ComputerObj.Members.Add(new PSNoteProperty("Delegation Protocol", DelegationProtoc
ComputerObj.Members.Add(new PSNoteProperty("Delegation Services", DelegationServic
ComputerObj.Members.Add(new PSNoteProperty("Primary Group ID", (AdComputer.Prope
ComputerObj.Members.Add(new PSNoteProperty("SID", Convert.ToString(new SecurityIde
ComputerObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
ComputerObj.Members.Add(new PSNoteProperty("Description", (AdComputer.Properties["
ComputerObj.Members.Add(new PSNoteProperty("ms-ds-CreatorSid", (AdComputer.Prop
ComputerObj.Members.Add(new PSNoteProperty("Last Logon Date", LastLogonDate));
ComputerObj.Members.Add(new PSNoteProperty("Password LastSet", PasswordLastSet)
ComputerObj.Members.Add(new PSNoteProperty("UserAccountControl", (AdComputer.Pr
ComputerObj.Members.Add(new PSNoteProperty("whenCreated", AdComputer.Properties
ComputerObj.Members.Add(new PSNoteProperty("whenChanged", AdComputer.Propertie
ComputerObj.Members.Add(new PSNoteProperty("Distinguished Name", AdComputer.Pro
return new PSObject[] { ComputerObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class ComputerSPNRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdComputer = (SearchResult) record;
if (AdComputer.Properties["serviceprincipalname"].Count == 0)
{
return new PSObject[] { };
}
List<PSObject> SPNList = new List<PSObject>();
foreach (string SPN in AdComputer.Properties["serviceprincipalname"])
{
string[] SPNArray = SPN.Split('/');
bool flag = true;
foreach (PSObject Obj in SPNList)
{
if ( (string) Obj.Members["Service"].Value == SPNArray[0] )
{
Obj.Members["Host"].Value = string.Join(",", (Obj.Members["Host"].Value + "," + SP
flag = false;
}
}
if (flag)
{
PSObject ComputerSPNObj = new PSObject();
ComputerSPNObj.Members.Add(new PSNoteProperty("UserName", (AdComputer.Pr
ComputerSPNObj.Members.Add(new PSNoteProperty("Name", (AdComputer.Proper
ComputerSPNObj.Members.Add(new PSNoteProperty("Service", SPNArray[0]));
ComputerSPNObj.Members.Add(new PSNoteProperty("Host", SPNArray[1]));
SPNList.Add( ComputerSPNObj );
}
}
return SPNList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class LAPSRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdComputer = (SearchResult) record;
bool? Enabled = null;
bool PasswordStored = false;
DateTime? CurrentExpiration = null;
// When the user is not allowed to query the UserAccountControl attribute.
if (AdComputer.Properties["useraccountcontrol"].Count != 0)
{
var userFlags = (UACFlags) AdComputer.Properties["useraccountcontrol"][0];
Enabled = !((userFlags & UACFlags.ACCOUNTDISABLE) == UACFlags.ACCOUNTDIS
}
if (AdComputer.Properties["ms-mcs-admpwdexpirationtime"].Count != 0)
{
CurrentExpiration = DateTime.FromFileTime((long)(AdComputer.Properties["ms-mcs-ad
PasswordStored = true;
}
PSObject LAPSObj = new PSObject();
LAPSObj.Members.Add(new PSNoteProperty("Hostname", (AdComputer.Properties["dnsh
LAPSObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
LAPSObj.Members.Add(new PSNoteProperty("Stored", PasswordStored));
LAPSObj.Members.Add(new PSNoteProperty("Readable", (AdComputer.Properties["ms-m
LAPSObj.Members.Add(new PSNoteProperty("Password", (AdComputer.Properties["ms-m
LAPSObj.Members.Add(new PSNoteProperty("Expiration", CurrentExpiration));
return new PSObject[] { LAPSObj };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class SIDRecordDictionaryProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdObject = (SearchResult) record;
switch (Convert.ToString(AdObject.Properties["objectclass"][AdObject.Properties["objectcla
{
case "user":
case "computer":
case "group":
LDAPClass.AdSIDDictionary.Add(Convert.ToString(new SecurityIdentifier((byte[])AdO
break;
}
return new PSObject[] { };
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class DACLRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdObject = (SearchResult) record;
byte[] ntSecurityDescriptor = null;
string Name = null;
string Type = null;
List<PSObject> DACLList = new List<PSObject>();
Name = Convert.ToString(AdObject.Properties["name"][0]);
switch (Convert.ToString(AdObject.Properties["objectclass"][AdObject.Properties["objectcla
{
case "user":
Type = "User";
break;
case "computer":
Type = "Computer";
break;
case "group":
Type = "Group";
break;
case "container":
Type = "Container";
break;
case "groupPolicyContainer":
Type = "GPO";
Name = Convert.ToString(AdObject.Properties["displayname"][0]);
break;
case "organizationalUnit":
Type = "OU";
break;
case "domainDNS":
Type = "Domain";
break;
default:
Type = Convert.ToString(AdObject.Properties["objectclass"][AdObject.Properties["obje
break;
}
// When the user is not allowed to query the ntsecuritydescriptor attribute.
if (AdObject.Properties["ntsecuritydescriptor"].Count != 0)
{
ntSecurityDescriptor = (byte[]) AdObject.Properties["ntsecuritydescriptor"][0];
}
else
{
DirectoryEntry AdObjectEntry = ((SearchResult)record).GetDirectoryEntry();
ntSecurityDescriptor = (byte[]) AdObjectEntry.ObjectSecurity.GetSecurityDescriptorBina
}
if (ntSecurityDescriptor != null)
{
DirectoryObjectSecurity DirObjSec = new ActiveDirectorySecurity();
DirObjSec.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor);
AuthorizationRuleCollection AccessRules = (AuthorizationRuleCollection) DirObjSec.Ge
foreach (ActiveDirectoryAccessRule Rule in AccessRules)
{
string IdentityReference = Convert.ToString(Rule.IdentityReference);
string Owner = Convert.ToString(DirObjSec.GetOwner(typeof(System.Security.Princip
PSObject ObjectObj = new PSObject();
ObjectObj.Members.Add(new PSNoteProperty("Name", CleanString(Name)));
ObjectObj.Members.Add(new PSNoteProperty("Type", Type));
ObjectObj.Members.Add(new PSNoteProperty("ObjectTypeName", LDAPClass.GUID
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectTypeName", LDAPCla
ObjectObj.Members.Add(new PSNoteProperty("ActiveDirectoryRights", Rule.ActiveDi
ObjectObj.Members.Add(new PSNoteProperty("AccessControlType", Rule.AccessCo
ObjectObj.Members.Add(new PSNoteProperty("IdentityReferenceName", LDAPClass
ObjectObj.Members.Add(new PSNoteProperty("OwnerName", LDAPClass.AdSIDDict
ObjectObj.Members.Add(new PSNoteProperty("Inherited", Rule.IsInherited));
ObjectObj.Members.Add(new PSNoteProperty("ObjectFlags", Rule.ObjectFlags));
ObjectObj.Members.Add(new PSNoteProperty("InheritanceFlags", Rule.InheritanceFl
ObjectObj.Members.Add(new PSNoteProperty("InheritanceType", Rule.InheritanceTy
ObjectObj.Members.Add(new PSNoteProperty("PropagationFlags", Rule.Propagation
ObjectObj.Members.Add(new PSNoteProperty("ObjectType", Rule.ObjectType));
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectType", Rule.InheritedO
ObjectObj.Members.Add(new PSNoteProperty("IdentityReference", Rule.IdentityRefe
ObjectObj.Members.Add(new PSNoteProperty("Owner", Owner));
ObjectObj.Members.Add(new PSNoteProperty("DistinguishedName", AdObject.Prope
DACLList.Add( ObjectObj );
}
}
return DACLList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
class SACLRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdObject = (SearchResult) record;
byte[] ntSecurityDescriptor = null;
string Name = null;
string Type = null;
List<PSObject> SACLList = new List<PSObject>();
Name = Convert.ToString(AdObject.Properties["name"][0]);
switch (Convert.ToString(AdObject.Properties["objectclass"][AdObject.Properties["objectcla
{
case "user":
Type = "User";
break;
case "computer":
Type = "Computer";
break;
case "group":
Type = "Group";
break;
case "container":
Type = "Container";
break;
case "groupPolicyContainer":
Type = "GPO";
Name = Convert.ToString(AdObject.Properties["displayname"][0]);
break;
case "organizationalUnit":
Type = "OU";
break;
case "domainDNS":
Type = "Domain";
break;
default:
Type = Convert.ToString(AdObject.Properties["objectclass"][AdObject.Properties["obje
break;
}
// When the user is not allowed to query the ntsecuritydescriptor attribute.
if (AdObject.Properties["ntsecuritydescriptor"].Count != 0)
{
ntSecurityDescriptor = (byte[]) AdObject.Properties["ntsecuritydescriptor"][0];
}
else
{
DirectoryEntry AdObjectEntry = ((SearchResult)record).GetDirectoryEntry();
ntSecurityDescriptor = (byte[]) AdObjectEntry.ObjectSecurity.GetSecurityDescriptorBina
}
if (ntSecurityDescriptor != null)
{
DirectoryObjectSecurity DirObjSec = new ActiveDirectorySecurity();
DirObjSec.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor);
AuthorizationRuleCollection AuditRules = (AuthorizationRuleCollection) DirObjSec.GetA
foreach (ActiveDirectoryAuditRule Rule in AuditRules)
{
string IdentityReference = Convert.ToString(Rule.IdentityReference);
PSObject ObjectObj = new PSObject();
ObjectObj.Members.Add(new PSNoteProperty("Name", CleanString(Name)));
ObjectObj.Members.Add(new PSNoteProperty("Type", Type));
ObjectObj.Members.Add(new PSNoteProperty("ObjectTypeName", LDAPClass.GUID
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectTypeName", LDAPCla
ObjectObj.Members.Add(new PSNoteProperty("ActiveDirectoryRights", Rule.ActiveDi
ObjectObj.Members.Add(new PSNoteProperty("IdentityReferenceName", LDAPClass
ObjectObj.Members.Add(new PSNoteProperty("AuditFlags", Rule.AuditFlags));
ObjectObj.Members.Add(new PSNoteProperty("ObjectFlags", Rule.ObjectFlags));
ObjectObj.Members.Add(new PSNoteProperty("InheritanceFlags", Rule.InheritanceFl
ObjectObj.Members.Add(new PSNoteProperty("InheritanceType", Rule.InheritanceTy
ObjectObj.Members.Add(new PSNoteProperty("Inherited", Rule.IsInherited));
ObjectObj.Members.Add(new PSNoteProperty("PropagationFlags", Rule.Propagation
ObjectObj.Members.Add(new PSNoteProperty("ObjectType", Rule.ObjectType));
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectType", Rule.InheritedO
ObjectObj.Members.Add(new PSNoteProperty("IdentityReference", Rule.IdentityRefe
SACLList.Add( ObjectObj );
}
}
return SACLList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Exception caught: {0}", e);
return new PSObject[] { };
}
}
}
//The interface and implmentation class used to handle the results (this implementation just writes
interface IResultsHandler
{
void processResults(Object[] t);
Object[] finalise();
}
class SimpleResultsHandler : IResultsHandler
{
private Object lockObj = new Object();
private List<Object> processed = new List<Object>();
public SimpleResultsHandler()
{
}
public void processResults(Object[] results)
{
lock (lockObj)
{
if (results.Length != 0)
{
for (var i = 0; i < results.Length; i++)
{
processed.Add((PSObject)results[i]);
}
}
}
}
public Object[] finalise()
{
return processed.ToArray();
}
}
"@
# modified version from https://github.com/vletoux/SmbScanner/blob/master/smbscanner.ps1
$PingCastleSMBScannerSource = @"
[StructLayout(LayoutKind.Explicit)]
■■struct SMB_Header {
■■■[FieldOffset(0)]
■■■public UInt32 Protocol;
■■■[FieldOffset(4)]
■■■public byte Command;
■■■[FieldOffset(5)]
■■■public int Status;
■■■[FieldOffset(9)]
■■■public byte Flags;
■■■[FieldOffset(10)]
■■■public UInt16 Flags2;
■■■[FieldOffset(12)]
■■■public UInt16 PIDHigh;
■■■[FieldOffset(14)]
■■■public UInt64 SecurityFeatures;
■■■[FieldOffset(22)]
■■■public UInt16 Reserved;
■■■[FieldOffset(24)]
■■■public UInt16 TID;
■■■[FieldOffset(26)]
■■■public UInt16 PIDLow;
■■■[FieldOffset(28)]
■■■public UInt16 UID;
■■■[FieldOffset(30)]
■■■public UInt16 MID;
■■};
■■// https://msdn.microsoft.com/en-us/library/cc246529.aspx
■■[StructLayout(LayoutKind.Explicit)]
■■struct SMB2_Header {
■■■[FieldOffset(0)]
■■■public UInt32 ProtocolId;
■■■[FieldOffset(4)]
■■■public UInt16 StructureSize;
■■■[FieldOffset(6)]
■■■public UInt16 CreditCharge;
■■■[FieldOffset(8)]
■■■public UInt32 Status; // to do SMB3
■■■[FieldOffset(12)]
■■■public UInt16 Command;
■■■[FieldOffset(14)]
■■■public UInt16 CreditRequest_Response;
■■■[FieldOffset(16)]
■■■public UInt32 Flags;
■■■[FieldOffset(20)]
■■■public UInt32 NextCommand;
■■■[FieldOffset(24)]
■■■public UInt64 MessageId;
■■■[FieldOffset(32)]
■■■public UInt32 Reserved;
■■■[FieldOffset(36)]
■■■public UInt32 TreeId;
■■■[FieldOffset(40)]
■■■public UInt64 SessionId;
■■■[FieldOffset(48)]
■■■public UInt64 Signature1;
■■■[FieldOffset(56)]
■■■public UInt64 Signature2;
■■}
[StructLayout(LayoutKind.Explicit)]
■■struct SMB2_NegotiateRequest
■■{
■■■[FieldOffset(0)]
■■■public UInt16 StructureSize;
■■■[FieldOffset(2)]
■■■public UInt16 DialectCount;
■■■[FieldOffset(4)]
■■■public UInt16 SecurityMode;
■■■[FieldOffset(6)]
■■■public UInt16 Reserved;
■■■[FieldOffset(8)]
■■■public UInt32 Capabilities;
■■■[FieldOffset(12)]
■■■public Guid ClientGuid;
■■■[FieldOffset(28)]
■■■public UInt64 ClientStartTime;
■■■[FieldOffset(36)]
■■■public UInt16 DialectToTest;
■■}
■■const int SMB_COM_NEGOTIATE■= 0x72;
■■const int SMB2_NEGOTIATE = 0;
■■const int SMB_FLAGS_CASE_INSENSITIVE = 0x08;
■■const int SMB_FLAGS_CANONICALIZED_PATHS = 0x10;
■■const int SMB_FLAGS2_LONG_NAMES■■■■■= 0x0001;
■■const int SMB_FLAGS2_EAS■■■■■■■= 0x0002;
■■const int SMB_FLAGS2_SECURITY_SIGNATURE_REQUIRED■= 0x0010■;
■■const int SMB_FLAGS2_IS_LONG_NAME■■■■■= 0x0040;
■■const int SMB_FLAGS2_ESS■■■■■■■= 0x0800;
■■const int SMB_FLAGS2_NT_STATUS■■■■■= 0x4000;
■■const int SMB_FLAGS2_UNICODE■■■■■■= 0x8000;
■■const int SMB_DB_FORMAT_DIALECT = 0x02;
■■static byte[] GenerateSmbHeaderFromCommand(byte command)
■■{
■■■SMB_Header header = new SMB_Header();
■■■header.Protocol = 0x424D53FF;
■■■header.Command = command;
■■■header.Status = 0;
■■■header.Flags = SMB_FLAGS_CASE_INSENSITIVE | SMB_FLAGS_CANONICALIZED_PATHS;
■■■header.Flags2 = SMB_FLAGS2_LONG_NAMES | SMB_FLAGS2_EAS | SMB_FLAGS2_SECURI
■■■header.PIDHigh = 0;
■■■header.SecurityFeatures = 0;
■■■header.Reserved = 0;
■■■header.TID = 0xffff;
■■■header.PIDLow = 0xFEFF;
■■■header.UID = 0;
■■■header.MID = 0;
■■■return getBytes(header);
■■}
■■static byte[] GenerateSmb2HeaderFromCommand(byte command)
■■{
■■■SMB2_Header header = new SMB2_Header();
■■■header.ProtocolId = 0x424D53FE;
■■■header.Command = command;
■■■header.StructureSize = 64;
■■■header.Command = command;
■■■header.MessageId = 0;
■■■header.Reserved = 0xFEFF;
■■■return getBytes(header);
■■}
■■static byte[] getBytes(object structure)
■■{
■■■int size = Marshal.SizeOf(structure);
■■■byte[] arr = new byte[size];
■■■IntPtr ptr = Marshal.AllocHGlobal(size);
■■■Marshal.StructureToPtr(structure, ptr, true);
■■■Marshal.Copy(ptr, arr, 0, size);
■■■Marshal.FreeHGlobal(ptr);
■■■return arr;
■■}
■■static byte[] getDialect(string dialect)
■■{
■■■byte[] dialectBytes = Encoding.ASCII.GetBytes(dialect);
■■■byte[] output = new byte[dialectBytes.Length + 2];
■■■output[0] = 2;
■■■output[output.Length - 1] = 0;
■■■Array.Copy(dialectBytes, 0, output, 1, dialectBytes.Length);
■■■return output;
■■}
■■static byte[] GetNegotiateMessage(byte[] dialect)
■■{
■■■byte[] output = new byte[dialect.Length + 3];
■■■output[0] = 0;
■■■output[1] = (byte) dialect.Length;
■■■output[2] = 0;
■■■Array.Copy(dialect, 0, output, 3, dialect.Length);
■■■return output;
■■}
■■// MS-SMB2 2.2.3 SMB2 NEGOTIATE Request
■■static byte[] GetNegotiateMessageSmbv2(int DialectToTest)
■■{
■■■SMB2_NegotiateRequest request = new SMB2_NegotiateRequest();
■■■request.StructureSize = 36;
■■■request.DialectCount = 1;
■■■request.SecurityMode = 1; // signing enabled
■■■request.ClientGuid = Guid.NewGuid();
■■■request.DialectToTest = (UInt16) DialectToTest;
■■■return getBytes(request);
■■}
■■static byte[] GetNegotiatePacket(byte[] header, byte[] smbPacket)
■■{
■■■byte[] output = new byte[smbPacket.Length + header.Length + 4];
■■■output[0] = 0;
■■■output[1] = 0;
■■■output[2] = 0;
■■■output[3] = (byte)(smbPacket.Length + header.Length);
■■■Array.Copy(header, 0, output, 4, header.Length);
■■■Array.Copy(smbPacket, 0, output, 4 + header.Length, smbPacket.Length);
■■■return output;
■■}
■■public static bool DoesServerSupportDialect(string server, string dialect)
■■{
■■■Trace.WriteLine("Checking " + server + " for SMBV1 dialect " + dialect);
■■■TcpClient client = new TcpClient();
■■■try
■■■{
■■■■client.Connect(server, 445);
■■■}
■■■catch (Exception)
■■■{
■■■■throw new Exception("port 445 is closed on " + server);
■■■}
■■■try
■■■{
■■■■NetworkStream stream = client.GetStream();
■■■■byte[] header = GenerateSmbHeaderFromCommand(SMB_COM_NEGOTIATE);
■■■■byte[] dialectEncoding = getDialect(dialect);
■■■■byte[] negotiatemessage = GetNegotiateMessage(dialectEncoding);
■■■■byte[] packet = GetNegotiatePacket(header, negotiatemessage);
■■■■stream.Write(packet, 0, packet.Length);
■■■■stream.Flush();
■■■■byte[] netbios = new byte[4];
■■■■if (stream.Read(netbios, 0, netbios.Length) != netbios.Length)
{
return false;
}
■■■■byte[] smbHeader = new byte[Marshal.SizeOf(typeof(SMB_Header))];
■■■■if (stream.Read(smbHeader, 0, smbHeader.Length) != smbHeader.Length)
{
return false;
}
■■■■byte[] negotiateresponse = new byte[3];
■■■■if (stream.Read(negotiateresponse, 0, negotiateresponse.Length) != negotiateresponse.Length)
{
return false;
}
■■■■if (negotiateresponse[1] == 0 && negotiateresponse[2] == 0)
■■■■{
■■■■■Trace.WriteLine("Checking " + server + " for SMBV1 dialect " + dialect + " = Supported");
■■■■■return true;
■■■■}
■■■■Trace.WriteLine("Checking " + server + " for SMBV1 dialect " + dialect + " = Not supported");
■■■■return false;
■■■}
■■■catch (Exception)
■■■{
■■■■throw new ApplicationException("Smb1 is not supported on " + server);
■■■}
■■}
■■public static bool DoesServerSupportDialectWithSmbV2(string server, int dialect, bool checkSMBSig
■■{
■■■Trace.WriteLine("Checking " + server + " for SMBV2 dialect 0x" + dialect.ToString("X2"));
■■■TcpClient client = new TcpClient();
■■■try
■■■{
■■■■client.Connect(server, 445);
■■■}
■■■catch (Exception)
■■■{
■■■■throw new Exception("port 445 is closed on " + server);
■■■}
■■■try
■■■{
■■■■NetworkStream stream = client.GetStream();
■■■■byte[] header = GenerateSmb2HeaderFromCommand(SMB2_NEGOTIATE);
■■■■byte[] negotiatemessage = GetNegotiateMessageSmbv2(dialect);
■■■■byte[] packet = GetNegotiatePacket(header, negotiatemessage);
■■■■stream.Write(packet, 0, packet.Length);
■■■■stream.Flush();
■■■■byte[] netbios = new byte[4];
■■■■if( stream.Read(netbios, 0, netbios.Length) != netbios.Length)
{
return false;
}
■■■■byte[] smbHeader = new byte[Marshal.SizeOf(typeof(SMB2_Header))];
■■■■if (stream.Read(smbHeader, 0, smbHeader.Length) != smbHeader.Length)
{
return false;
}
■■■■if (smbHeader[8] != 0 || smbHeader[9] != 0 || smbHeader[10] != 0 || smbHeader[11] != 0)
■■■■{
■■■■■Trace.WriteLine("Checking " + server + " for SMBV2 dialect 0x" + dialect.ToString("X2") + " = N
■■■■■return false;
■■■■}
■■■■byte[] negotiateresponse = new byte[6];
■■■■if (stream.Read(negotiateresponse, 0, negotiateresponse.Length) != negotiateresponse.Length)
{
return false;
}
if (checkSMBSigning)
{
// https://support.microsoft.com/en-in/help/887429/overview-of-server-message-block-signi
// https://msdn.microsoft.com/en-us/library/cc246561.aspx
■■■■ if (negotiateresponse[2] == 3)
■■■■ {
■■■■■ Trace.WriteLine("Checking " + server + " for SMBV2 SMB Signing dialect 0x" + dialect.ToStr
■■■■■ return true;
■■■■ }
else
{
return false;
}
}
■■■■int selectedDialect = negotiateresponse[5] * 0x100 + negotiateresponse[4];
■■■■if (selectedDialect == dialect)
■■■■{
■■■■■Trace.WriteLine("Checking " + server + " for SMBV2 dialect 0x" + dialect.ToString("X2") + " = S
■■■■■return true;
■■■■}
■■■■Trace.WriteLine("Checking " + server + " for SMBV2 dialect 0x" + dialect.ToString("X2") + " = No
■■■■return false;
■■■}
■■■catch (Exception)
■■■{
■■■■throw new ApplicationException("Smb2 is not supported on " + server);
■■■}
■■}
■■public static bool SupportSMB1(string server)
■■{
■■■try
■■■{
■■■■return DoesServerSupportDialect(server, "NT LM 0.12");
■■■}
■■■catch (Exception)
■■■{
■■■■return false;
■■■}
■■}
■■public static bool SupportSMB2(string server)
■■{
■■■try
■■■{
■■■■return (DoesServerSupportDialectWithSmbV2(server, 0x0202, false) || DoesServerSupportDiale
■■■}
■■■catch (Exception)
■■■{
■■■■return false;
■■■}
■■}
■■public static bool SupportSMB3(string server)
■■{
■■■try
■■■{
■■■■return (DoesServerSupportDialectWithSmbV2(server, 0x0300, false) || DoesServerSupportDiale
■■■}
■■■catch (Exception)
■■■{
■■■■return false;
■■■}
■■}
■■public static string Name { get { return "smb"; } }
■■public static PSObject GetPSObject(Object IPv4Address)
■■{
string computer = Convert.ToString(IPv4Address);
PSObject DCSMBObj = new PSObject();
if (computer == "")
{
DCSMBObj.Members.Add(new PSNoteProperty("SMB Port Open", null));
DCSMBObj.Members.Add(new PSNoteProperty("SMB1(NT LM 0.12)", null));
DCSMBObj.Members.Add(new PSNoteProperty("SMB2(0x0202)", null));
DCSMBObj.Members.Add(new PSNoteProperty("SMB2(0x0210)", null));
DCSMBObj.Members.Add(new PSNoteProperty("SMB3(0x0300)", null));
DCSMBObj.Members.Add(new PSNoteProperty("SMB3(0x0302)", null));
DCSMBObj.Members.Add(new PSNoteProperty("SMB3(0x0311)", null));
DCSMBObj.Members.Add(new PSNoteProperty("SMB Signing", null));
return DCSMBObj;
}
bool isPortOpened = true;
■■■bool SMBv1 = false;
■■■bool SMBv2_0x0202 = false;
■■■bool SMBv2_0x0210 = false;
■■■bool SMBv3_0x0300 = false;
■■■bool SMBv3_0x0302 = false;
■■■bool SMBv3_0x0311 = false;
bool SMBSigning = false;
■■■try
■■■{
■■■■try
■■■■{
■■■■■SMBv1 = DoesServerSupportDialect(computer, "NT LM 0.12");
■■■■}
■■■■catch (ApplicationException)
■■■■{
■■■■}
■■■■try
■■■■{
■■■■■SMBv2_0x0202 = DoesServerSupportDialectWithSmbV2(computer, 0x0202, false);
■■■■■SMBv2_0x0210 = DoesServerSupportDialectWithSmbV2(computer, 0x0210, false);
■■■■■SMBv3_0x0300 = DoesServerSupportDialectWithSmbV2(computer, 0x0300, false);
■■■■■SMBv3_0x0302 = DoesServerSupportDialectWithSmbV2(computer, 0x0302, false);
■■■■■SMBv3_0x0311 = DoesServerSupportDialectWithSmbV2(computer, 0x0311, false);
■■■■}
■■■■catch (ApplicationException)
■■■■{
■■■■}
■■■}
■■■catch (Exception)
■■■{
■■■■isPortOpened = false;
■■■}
■■■if (SMBv3_0x0311)
■■■{
■■■■SMBSigning = DoesServerSupportDialectWithSmbV2(computer, 0x0311, true);
■■■}
■■■else if (SMBv3_0x0302)
■■■{
■■■■SMBSigning = DoesServerSupportDialectWithSmbV2(computer, 0x0302, true);
■■■}
■■■else if (SMBv3_0x0300)
■■■{
■■■■SMBSigning = DoesServerSupportDialectWithSmbV2(computer, 0x0300, true);
■■■}
■■■else if (SMBv2_0x0210)
■■■{
■■■■SMBSigning = DoesServerSupportDialectWithSmbV2(computer, 0x0210, true);
■■■}
■■■else if (SMBv2_0x0202)
■■■{
■■■■SMBSigning = DoesServerSupportDialectWithSmbV2(computer, 0x0202, true);
■■■}
DCSMBObj.Members.Add(new PSNoteProperty("SMB Port Open", isPortOpened));
DCSMBObj.Members.Add(new PSNoteProperty("SMB1(NT LM 0.12)", SMBv1));
DCSMBObj.Members.Add(new PSNoteProperty("SMB2(0x0202)", SMBv2_0x0202));
DCSMBObj.Members.Add(new PSNoteProperty("SMB2(0x0210)", SMBv2_0x0210));
DCSMBObj.Members.Add(new PSNoteProperty("SMB3(0x0300)", SMBv3_0x0300));
DCSMBObj.Members.Add(new PSNoteProperty("SMB3(0x0302)", SMBv3_0x0302));
DCSMBObj.Members.Add(new PSNoteProperty("SMB3(0x0311)", SMBv3_0x0311));
DCSMBObj.Members.Add(new PSNoteProperty("SMB Signing", SMBSigning));
return DCSMBObj;
■■}
■}
}
"@
# Import the LogonUser, ImpersonateLoggedOnUser and RevertToSelf Functions from advapi32.dll and
# https://docs.microsoft.com/en-gb/powershell/module/Microsoft.PowerShell.Utility/Add-Type?view=pow
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa378612(v=vs.85).aspx
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa379317(v=vs.85).aspx
$Advapi32Def = @'
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, in
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool ImpersonateLoggedOnUser(IntPtr hToken);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool RevertToSelf();
'@
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx
$Kernel32Def = @'
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
'@
Function Get-DateDiff
{
<#
.SYNOPSIS
Get difference between two dates.
.DESCRIPTION
Returns the difference between two dates.
.PARAMETER Date1
[DateTime]
Date
.PARAMETER Date2
[DateTime]
Date
.OUTPUTS
[System.ValueType.TimeSpan]
Returns the difference between the two dates.
#>
param (
[Parameter(Mandatory = $true)]
[DateTime] $Date1,
[Parameter(Mandatory = $true)]
[DateTime] $Date2
)
If ($Date2 -gt $Date1)
{
$DDiff = $Date2 - $Date1
}
Else
{
$DDiff = $Date1 - $Date2
}
Return $DDiff
}
Function Get-DNtoFQDN
{
<#
.SYNOPSIS
Gets Domain Distinguished Name (DN) from the Fully Qualified Domain Name (FQDN).
.DESCRIPTION
Converts Domain Distinguished Name (DN) to Fully Qualified Domain Name (FQDN).
.PARAMETER ADObjectDN
[string]
Domain Distinguished Name (DN)
.OUTPUTS
[String]
Returns the Fully Qualified Domain Name (FQDN).
.LINK
https://adsecurity.org/?p=440
#>
param(
[Parameter(Mandatory = $true)]
[string] $ADObjectDN
)
$Index = $ADObjectDN.IndexOf('DC=')
If ($Index)
{
$ADObjectDNDomainName = $($ADObjectDN.SubString($Index)) -replace 'DC=','' -replace ',','.'
}
Else
{
# Modified version from https://adsecurity.org/?p=440
[array] $ADObjectDNArray = $ADObjectDN -Split ("DC=")
$ADObjectDNArray | ForEach-Object {
[array] $temp = $_ -Split (",")
[string] $ADObjectDNArrayItemDomainName += $temp[0] + "."
}
$ADObjectDNDomainName = $ADObjectDNArrayItemDomainName.Substring(1, $ADObjectDNAr
}
Return $ADObjectDNDomainName
}
Function Export-ADRCSV
{
<#
.SYNOPSIS
Exports Object to a CSV file.
.DESCRIPTION
Exports Object to a CSV file using Export-CSV.
.PARAMETER ADRObj
[PSObject]
ADRObj
.PARAMETER ADFileName
[String]
Path to save the CSV File.
.OUTPUTS
CSV file.
#>
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[PSObject] $ADRObj,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String] $ADFileName
)
Try
{
$ADRObj | Export-Csv -Path $ADFileName -NoTypeInformation -Encoding Default
}
Catch
{
Write-Warning "[Export-ADRCSV] Failed to export $($ADFileName)."
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
Function Export-ADRXML
{
<#
.SYNOPSIS
Exports Object to a XML file.
.DESCRIPTION
Exports Object to a XML file using Export-Clixml.
.PARAMETER ADRObj
[PSObject]
ADRObj
.PARAMETER ADFileName
[String]
Path to save the XML File.
.OUTPUTS
XML file.
#>
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[PSObject] $ADRObj,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String] $ADFileName
)
Try
{
(ConvertTo-Xml -NoTypeInformation -InputObject $ADRObj).Save($ADFileName)
}
Catch
{
Write-Warning "[Export-ADRXML] Failed to export $($ADFileName)."
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
Function Export-ADRJSON
{
<#
.SYNOPSIS
Exports Object to a JSON file.
.DESCRIPTION
Exports Object to a JSON file using ConvertTo-Json.
.PARAMETER ADRObj
[PSObject]
ADRObj
.PARAMETER ADFileName
[String]
Path to save the JSON File.
.OUTPUTS
JSON file.
#>
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[PSObject] $ADRObj,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String] $ADFileName
)
Try
{
ConvertTo-JSON -InputObject $ADRObj | Out-File -FilePath $ADFileName
}
Catch
{
Write-Warning "[Export-ADRJSON] Failed to export $($ADFileName)."
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
Function Export-ADRHTML
{
<#
.SYNOPSIS
Exports Object to a HTML file.
.DESCRIPTION
Exports Object to a HTML file using ConvertTo-Html.
.PARAMETER ADRObj
[PSObject]
ADRObj
.PARAMETER ADFileName
[String]
Path to save the HTML File.
.OUTPUTS
HTML file.
#>
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[PSObject] $ADRObj,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String] $ADFileName,
[Parameter(Mandatory = $false)]
[String] $ADROutputDir = $null
)
$Header = @"
<style type="text/css">
th {
■color:white;
■background-color:blue;
}
td, th {
■border:0px solid black;
■border-collapse:collapse;
■white-space:pre;
}
tr:nth-child(2n+1) {
background-color: #dddddd;
}
tr:hover td {
background-color: #c1d5f8;
}
table, tr, td, th {
■padding: 0px;
■margin: 0px;
■white-space:pre;
}
table {
■margin-left:1px;
}
</style>
"@
Try
{
If ($ADFileName.Contains("Index"))
{
$HTMLPath = -join($ADROutputDir,'\','HTML-Files')
$HTMLPath = $((Convert-Path $HTMLPath).TrimEnd("\"))
$HTMLFiles = Get-ChildItem -Path $HTMLPath -name
$HTML = $HTMLFiles | ConvertTo-HTML -Title "ADRecon" -Property @{Label="Table of Conten
Add-Type -AssemblyName System.Web
[System.Web.HttpUtility]::HtmlDecode($HTML) | Out-File -FilePath $ADFileName
}
Else
{
If ($ADRObj -is [array])
{
$ADRObj | Select-Object * | ConvertTo-HTML -As Table -Head $Header | Out-File -FilePath $
}
Else
{
ConvertTo-HTML -InputObject $ADRObj -As Table -Head $Header | Out-File -FilePath $ADF
}
}
}
Catch
{
Write-Warning "[Export-ADRHTML] Failed to export $($ADFileName)."
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
Function Export-ADR
{
<#
.SYNOPSIS
Helper function for all output types supported.
.DESCRIPTION
Helper function for all output types supported.
.PARAMETER ADObjectDN
[PSObject]
ADRObj
.PARAMETER ADROutputDir
[String]
Path for ADRecon output folder.
.PARAMETER OutputType
[array]
Output Type.
.PARAMETER ADRModuleName
[String]
Module Name.
.OUTPUTS
STDOUT, CSV, XML, JSON and/or HTML file, etc.
#>
param(
[Parameter(Mandatory = $true)]
[PSObject] $ADRObj,
[Parameter(Mandatory = $true)]
[String] $ADROutputDir,
[Parameter(Mandatory = $true)]
[array] $OutputType,
[Parameter(Mandatory = $true)]
[String] $ADRModuleName
)
Switch ($OutputType)
{
'STDOUT'
{
If ($ADRModuleName -ne "AboutADRecon")
{
If ($ADRObj -is [array])
{
# Fix for InvalidOperationException: The object of type "Microsoft.PowerShell.Commands.I
$ADRObj | Out-String -Stream
}
Else
{
# Fix for InvalidOperationException: The object of type "Microsoft.PowerShell.Commands.I
$ADRObj | Format-List | Out-String -Stream
}
}
}
'CSV'
{
$ADFileName = -join($ADROutputDir,'\','CSV-Files','\',$ADRModuleName,'.csv')
Export-ADRCSV -ADRObj $ADRObj -ADFileName $ADFileName
}
'XML'
{
$ADFileName = -join($ADROutputDir,'\','XML-Files','\',$ADRModuleName,'.xml')
Export-ADRXML -ADRObj $ADRObj -ADFileName $ADFileName
}
'JSON'
{
$ADFileName = -join($ADROutputDir,'\','JSON-Files','\',$ADRModuleName,'.json')
Export-ADRJSON -ADRObj $ADRObj -ADFileName $ADFileName
}
'HTML'
{
$ADFileName = -join($ADROutputDir,'\','HTML-Files','\',$ADRModuleName,'.html')
Export-ADRHTML -ADRObj $ADRObj -ADFileName $ADFileName -ADROutputDir $ADROutpu
}
}
}
Function Get-ADRExcelComObj
{
<#
.SYNOPSIS
Creates a ComObject to interact with Microsoft Excel.
.DESCRIPTION
Creates a ComObject to interact with Microsoft Excel if installed, else warning is raised.
.OUTPUTS
[System.__ComObject] and [System.MarshalByRefObject]
Creates global variables $excel and $workbook.
#>
#Check if Excel is installed.
Try
{
# Suppress verbose output
$SaveVerbosePreference = $script:VerbosePreference
$script:VerbosePreference = 'SilentlyContinue'
$global:excel = New-Object -ComObject excel.application
If ($SaveVerbosePreference)
{
$script:VerbosePreference = $SaveVerbosePreference
Remove-Variable SaveVerbosePreference
}
}
Catch
{
If ($SaveVerbosePreference)
{
$script:VerbosePreference = $SaveVerbosePreference
Remove-Variable SaveVerbosePreference
}
Write-Warning "[Get-ADRExcelComObj] Excel does not appear to be installed. Skipping generatio
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$excel.Visible = $true
$excel.Interactive = $false
$global:workbook = $excel.Workbooks.Add()
If ($workbook.Worksheets.Count -eq 3)
{
$workbook.WorkSheets.Item(3).Delete()
$workbook.WorkSheets.Item(2).Delete()
}
}
Function Get-ADRExcelComObjRelease
{
<#
.SYNOPSIS
Releases the ComObject created to interact with Microsoft Excel.
.DESCRIPTION
Releases the ComObject created to interact with Microsoft Excel.
.PARAMETER ComObjtoRelease
ComObjtoRelease
.PARAMETER Final
Final
#>
param(
[Parameter(Mandatory = $true)]
$ComObjtoRelease,
[Parameter(Mandatory = $false)]
[bool] $Final = $false
)
# https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.releasecomobject
# https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.finalreleasecomob
If ($Final)
{
[System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($ComObjtoRelease) | Out-N
}
Else
{
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($ComObjtoRelease) | Out-Null
}
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
Function Get-ADRExcelWorkbook
{
<#
.SYNOPSIS
Adds a WorkSheet to the Workbook.
.DESCRIPTION
Adds a WorkSheet to the Workbook using the $workboook global variable and assigns it a name.
.PARAMETER name
[string]
Name of the WorkSheet.
#>
param (
[Parameter(Mandatory = $true)]
[string] $name
)
$workbook.Worksheets.Add() | Out-Null
$worksheet = $workbook.Worksheets.Item(1)
$worksheet.Name = $name
Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
Remove-Variable worksheet
}
Function Get-ADRExcelImport
{
<#
.SYNOPSIS
Helper to import CSV to the current WorkSheet.
.DESCRIPTION
Helper to import CSV to the current WorkSheet. Supports two methods.
.PARAMETER ADFileName
[string]
Filename of the CSV file to import.
.PARAMETER method
[int]
Method to use for the import.
3 - Prints data horizontally. Headers column 1, then first data row in column 2, etc.
.PARAMETER row
[int]
Row.
.PARAMETER column
[int]
Column.
#>
param (
[Parameter(Mandatory = $true)]
[string] $ADFileName,
[Parameter(Mandatory = $false)]
[int] $Method = 1,
[Parameter(Mandatory = $false)]
[int] $row = 1,
[Parameter(Mandatory = $false)]
[int] $column = 1
)
$excel.ScreenUpdating = $false
If ($Method -eq 1)
{
If (Test-Path $ADFileName)
{
$worksheet = $workbook.Worksheets.Item(1)
$TxtConnector = ("TEXT;" + $ADFileName)
$CellRef = $worksheet.Range("A1")
#Build, use and remove the text file connector
$Connector = $worksheet.QueryTables.add($TxtConnector, $CellRef)
#65001: Unicode (UTF-8)
$worksheet.QueryTables.item($Connector.name).TextFilePlatform = 65001
$worksheet.QueryTables.item($Connector.name).TextFileCommaDelimiter = $True
$worksheet.QueryTables.item($Connector.name).TextFileParseType = 1
$worksheet.QueryTables.item($Connector.name).Refresh() | Out-Null
$worksheet.QueryTables.item($Connector.name).delete()
Get-ADRExcelComObjRelease -ComObjtoRelease $CellRef
Remove-Variable CellRef
Get-ADRExcelComObjRelease -ComObjtoRelease $Connector
Remove-Variable Connector
$listObject = $worksheet.ListObjects.Add([Microsoft.Office.Interop.Excel.XlListObjectSourceTyp
$listObject.TableStyle = "TableStyleLight2" # Style Cheat Sheet: https://msdn.microsoft.com/en-
$worksheet.UsedRange.EntireColumn.AutoFit() | Out-Null
}
Remove-Variable ADFileName
}
Elseif ($Method -eq 2)
{
$worksheet = $workbook.Worksheets.Item(1)
If (Test-Path $ADFileName)
{
$ADTemp = Import-Csv -Path $ADFileName
$ADTemp | ForEach-Object {
Foreach ($prop in $_.PSObject.Properties)
{
$worksheet.Cells.Item($row, $column) = $prop.Name
$worksheet.Cells.Item($row, $column + 1) = $prop.Value
$row++
}
}
Remove-Variable ADTemp
$listObject = $worksheet.ListObjects.Add([Microsoft.Office.Interop.Excel.XlListObjectSourceTyp
$listObject.TableStyle = "TableStyleLight2" # Style Cheat Sheet: https://msdn.microsoft.com/en-
$usedRange = $worksheet.UsedRange
$usedRange.EntireColumn.AutoFit() | Out-Null
}
Else
{
$worksheet.Cells.Item($row, $column) = "Error!"
}
Remove-Variable ADFileName
}
Elseif ($Method -eq 3)
{
$worksheet = $workbook.Worksheets.Item(1)
If (Test-Path $ADFileName)
{
$CsvData = Import-Csv -Path $ADFileName
$row_output = $row
$CsvData[0].PsObject.Properties.Name | ForEach {
$worksheet.Cells.Item($row_output, $column) = $_
$row_output++
}
Remove-Variable row_output
$column_output = $column + 1
$CsvData | ForEach-Object {
$row_output = $row
ForEach ($prop_value in $_.PSObject.Properties.Value)
{
$worksheet.Cells.Item($row_output, $column_output) = $prop_value
$row_output++
}
$column_output++
}
Remove-Variable column_output
Remove-Variable row_output
Remove-Variable CsvData
$listObject = $worksheet.ListObjects.Add([Microsoft.Office.Interop.Excel.XlListObjectSourceTyp
$listObject.TableStyle = "TableStyleLight2" # Style Cheat Sheet: https://msdn.microsoft.com/en-
$usedRange = $worksheet.UsedRange
$usedRange.EntireColumn.AutoFit() | Out-Null
}
Else
{
$worksheet.Cells.Item($row, $column) = "Error!"
}
Remove-Variable ADFileName
}
$excel.ScreenUpdating = $true
Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
Remove-Variable worksheet
}
# Thanks Anant Shrivastava for the suggestion of using Pivot Tables for generation of the Stats sheets.
Function Get-ADRExcelPivotTable
{
<#
.SYNOPSIS
Helper to add Pivot Table to the current WorkSheet.
.DESCRIPTION
Helper to add Pivot Table to the current WorkSheet.
.PARAMETER SrcSheetName
[string]
Source Sheet Name.
.PARAMETER PivotTableName
[string]
Pivot Table Name.
.PARAMETER PivotRows
[array]
Row names from Source Sheet.
.PARAMETER PivotColumns
[array]
Column names from Source Sheet.
.PARAMETER PivotFilters
[array]
Row/Column names from Source Sheet to use as filters.
.PARAMETER PivotValues
[array]
Row/Column names from Source Sheet to use for Values.
.PARAMETER PivotPercentage
[array]
Row/Column names from Source Sheet to use for Percentage.
.PARAMETER PivotLocation
[array]
Location of the Pivot Table in Row/Column.
#>
param (
[Parameter(Mandatory = $true)]
[string] $SrcSheetName,
[Parameter(Mandatory = $true)]
[string] $PivotTableName,
[Parameter(Mandatory = $false)]
[array] $PivotRows,
[Parameter(Mandatory = $false)]
[array] $PivotColumns,
[Parameter(Mandatory = $false)]
[array] $PivotFilters,
[Parameter(Mandatory = $false)]
[array] $PivotValues,
[Parameter(Mandatory = $false)]
[array] $PivotPercentage,
[Parameter(Mandatory = $false)]
[string] $PivotLocation = "R1C1"
)
$excel.ScreenUpdating = $false
$SrcWorksheet = $workbook.Sheets.Item($SrcSheetName)
$workbook.ShowPivotTableFieldList = $false
# https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlpivottablesourcetype-enumeration-excel
# https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlpivottableversionlist-enumeration-excel
# https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlpivotfieldorientation-enumeration-excel
# https://msdn.microsoft.com/en-us/vba/excel-vba/articles/constants-enumeration-excel
# https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlsortorder-enumeration-excel
# https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlpivotfiltertype-enumeration-excel
# xlDatabase = 1 # this just means local sheet data
# xlPivotTableVersion12 = 3 # Excel 2007
$PivotFailed = $false
Try
{
$PivotCaches = $workbook.PivotCaches().Create([Microsoft.Office.Interop.Excel.XlPivotTableSou
}
Catch
{
$PivotFailed = $true
Write-Verbose "[PivotCaches().Create] Failed"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ( $PivotFailed -eq $true )
{
$rows = $SrcWorksheet.UsedRange.Rows.Count
If ($SrcSheetName -eq "Computer SPNs")
{
$PivotCols = "A1:C"
}
ElseIf ($SrcSheetName -eq "Computers")
{
$PivotCols = "A1:F"
}
ElseIf ($SrcSheetName -eq "Users")
{
$PivotCols = "A1:C"
}
$UsedRange = $SrcWorksheet.Range($PivotCols+$rows)
$PivotCaches = $workbook.PivotCaches().Create([Microsoft.Office.Interop.Excel.XlPivotTableSou
Remove-Variable rows
■ Remove-Variable PivotCols
Remove-Variable UsedRange
}
Remove-Variable PivotFailed
$PivotTable = $PivotCaches.CreatePivotTable($PivotLocation,$PivotTableName)
# $workbook.ShowPivotTableFieldList = $true
If ($PivotRows)
{
ForEach ($Row in $PivotRows)
{
$PivotField = $PivotTable.PivotFields($Row)
$PivotField.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlRowField
}
}
If ($PivotColumns)
{
ForEach ($Col in $PivotColumns)
{
$PivotField = $PivotTable.PivotFields($Col)
$PivotField.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlColumnField
}
}
If ($PivotFilters)
{
ForEach ($Fil in $PivotFilters)
{
$PivotField = $PivotTable.PivotFields($Fil)
$PivotField.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlPageField
}
}
If ($PivotValues)
{
ForEach ($Val in $PivotValues)
{
$PivotField = $PivotTable.PivotFields($Val)
$PivotField.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlDataField
}
}
If ($PivotPercentage)
{
ForEach ($Val in $PivotPercentage)
{
$PivotField = $PivotTable.PivotFields($Val)
$PivotField.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlDataField
$PivotField.Calculation = [Microsoft.Office.Interop.Excel.XlPivotFieldCalculation]::xlPercentOfTo
$PivotTable.ShowValuesRow = $false
}
}
# $PivotFields.Caption = ""
$excel.ScreenUpdating = $true
Get-ADRExcelComObjRelease -ComObjtoRelease $PivotField
Remove-Variable PivotField
Get-ADRExcelComObjRelease -ComObjtoRelease $PivotTable
Remove-Variable PivotTable
Get-ADRExcelComObjRelease -ComObjtoRelease $PivotCaches
Remove-Variable PivotCaches
Get-ADRExcelComObjRelease -ComObjtoRelease $SrcWorksheet
Remove-Variable SrcWorksheet
}
Function Get-ADRExcelAttributeStats
{
<#
.SYNOPSIS
Helper to add Attribute Stats to the current WorkSheet.
.DESCRIPTION
Helper to add Attribute Stats to the current WorkSheet.
.PARAMETER SrcSheetName
[string]
Source Sheet Name.
.PARAMETER Title1
[string]
Title1.
.PARAMETER PivotTableName
[string]
PivotTableName.
.PARAMETER PivotRows
[string]
PivotRows.
.PARAMETER PivotValues
[string]
PivotValues.
.PARAMETER PivotPercentage
[string]
PivotPercentage.
.PARAMETER Title2
[string]
Title2.
.PARAMETER ObjAttributes
[OrderedDictionary]
Attributes.
#>
param (
[Parameter(Mandatory = $true)]
[string] $SrcSheetName,
[Parameter(Mandatory = $true)]
[string] $Title1,
[Parameter(Mandatory = $true)]
[string] $PivotTableName,
[Parameter(Mandatory = $true)]
[string] $PivotRows,
[Parameter(Mandatory = $true)]
[string] $PivotValues,
[Parameter(Mandatory = $true)]
[string] $PivotPercentage,
[Parameter(Mandatory = $true)]
[string] $Title2,
[Parameter(Mandatory = $true)]
[System.Object] $ObjAttributes
)
$excel.ScreenUpdating = $false
$worksheet = $workbook.Worksheets.Item(1)
$SrcWorksheet = $workbook.Sheets.Item($SrcSheetName)
$row = 1
$column = 1
$worksheet.Cells.Item($row, $column) = $Title1
$worksheet.Cells.Item($row,$column).Style = "Heading 2"
$worksheet.Cells.Item($row,$column).HorizontalAlignment = -4108
$MergeCells = $worksheet.Range("A1:C1")
$MergeCells.Select() | Out-Null
$MergeCells.MergeCells = $true
Remove-Variable MergeCells
Get-ADRExcelPivotTable -SrcSheetName $SrcSheetName -PivotTableName $PivotTableName -Piv
$excel.ScreenUpdating = $false
$row = 2
"Type","Count","Percentage" | ForEach-Object {
$worksheet.Cells.Item($row, $column) = $_
$worksheet.Cells.Item($row, $column).Font.Bold = $True
$column++
}
$row = 3
$column = 1
For($row = 3; $row -le 6; $row++)
{
$temptext = [string] $worksheet.Cells.Item($row, $column).Text
switch ($temptext.ToUpper())
{
"TRUE" { $worksheet.Cells.Item($row, $column) = "Enabled" }
"FALSE" { $worksheet.Cells.Item($row, $column) = "Disabled" }
"GRAND TOTAL" { $worksheet.Cells.Item($row, $column) = "Total" }
}
}
If ($ObjAttributes)
{
$row = 1
$column = 6
$worksheet.Cells.Item($row, $column) = $Title2
$worksheet.Cells.Item($row,$column).Style = "Heading 2"
$worksheet.Cells.Item($row,$column).HorizontalAlignment = -4108
$MergeCells = $worksheet.Range("F1:L1")
$MergeCells.Select() | Out-Null
$MergeCells.MergeCells = $true
Remove-Variable MergeCells
$row++
"Category","Enabled Count","Enabled Percentage","Disabled Count","Disabled Percentage","Total
$worksheet.Cells.Item($row, $column) = $_
$worksheet.Cells.Item($row, $column).Font.Bold = $True
$column++
}
$ExcelColumn = ($SrcWorksheet.Columns.Find("Enabled"))
$EnabledColAddress = "$($ExcelColumn.Address($false,$false).Substring(0,$ExcelColumn.Addre
$column = 6
$i = 2
$ObjAttributes.keys | ForEach-Object {
$ExcelColumn = ($SrcWorksheet.Columns.Find($_))
$ColAddress = "$($ExcelColumn.Address($false,$false).Substring(0,$ExcelColumn.Address($fa
$row++
$i++
If ($_ -eq "Delegation Typ")
{
$worksheet.Cells.Item($row, $column) = "Unconstrained Delegation"
}
ElseIf ($_ -eq "Delegation Type")
{
$worksheet.Cells.Item($row, $column) = "Constrained Delegation"
}
Else
{
$worksheet.Cells.Item($row, $column).Formula = "='" + $SrcWorksheet.Name + "'!" + $ExcelC
}
$worksheet.Cells.Item($row, $column+1).Formula = "=COUNTIFS('" + $SrcWorksheet.Name +
$worksheet.Cells.Item($row, $column+2).Formula = '=IFERROR(G' + $i + '/VLOOKUP("Enabled
$worksheet.Cells.Item($row, $column+3).Formula = "=COUNTIFS('" + $SrcWorksheet.Name +
$worksheet.Cells.Item($row, $column+4).Formula = '=IFERROR(I' + $i + '/VLOOKUP("Disabled
If ( ($_ -eq "SIDHistory") -or ($_ -eq "ms-ds-CreatorSid") )
{
# Remove count of FieldName
$worksheet.Cells.Item($row, $column+5).Formula = "=COUNTIF('" + $SrcWorksheet.Name +
}
Else
{
$worksheet.Cells.Item($row, $column+5).Formula = "=COUNTIF('" + $SrcWorksheet.Name +
}
$worksheet.Cells.Item($row, $column+6).Formula = '=IFERROR(K' + $i + '/VLOOKUP("Total",A
}
# http://www.excelhowto.com/macros/formatting-a-range-of-cells-in-excel-vba/
"H", "J" , "L" | ForEach-Object {
$rng = $_ + $($row - $ObjAttributes.Count + 1) + ":" + $_ + $($row)
$worksheet.Range($rng).NumberFormat = "0.00%"
}
}
$excel.ScreenUpdating = $true
Get-ADRExcelComObjRelease -ComObjtoRelease $SrcWorksheet
Remove-Variable SrcWorksheet
Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
Remove-Variable worksheet
}
Function Get-ADRExcelChart
{
<#
.SYNOPSIS
Helper to add charts to the current WorkSheet.
.DESCRIPTION
Helper to add charts to the current WorkSheet.
.PARAMETER ChartType
[int]
Chart Type.
.PARAMETER ChartLayout
[int]
Chart Layout.
.PARAMETER ChartTitle
[string]
Title of the Chart.
.PARAMETER RangetoCover
WorkSheet Range to be covered by the Chart.
.PARAMETER ChartData
Data for the Chart.
.PARAMETER StartRow
Start row to calculate data for the Chart.
.PARAMETER StartColumn
Start column to calculate data for the Chart.
#>
param (
[Parameter(Mandatory = $true)]
[string] $ChartType,
[Parameter(Mandatory = $true)]
[int] $ChartLayout,
[Parameter(Mandatory = $true)]
[string] $ChartTitle,
[Parameter(Mandatory = $true)]
$RangetoCover,
[Parameter(Mandatory = $false)]
$ChartData = $null,
[Parameter(Mandatory = $false)]
$StartRow = $null,
[Parameter(Mandatory = $false)]
$StartColumn = $null
)
$excel.ScreenUpdating = $false
$excel.DisplayAlerts = $false
$worksheet = $workbook.Worksheets.Item(1)
$chart = $worksheet.Shapes.AddChart().Chart
# https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlcharttype-enumeration-excel
$chart.chartType = [int]([Microsoft.Office.Interop.Excel.XLChartType]::$ChartType)
$chart.ApplyLayout($ChartLayout)
If ($null -eq $ChartData)
{
If ($null -eq $StartRow)
{
$start = $worksheet.Range("A1")
}
Else
{
$start = $worksheet.Range($StartRow)
}
# get the last cell
$X = $worksheet.Range($start,$start.End([Microsoft.Office.Interop.Excel.XLDirection]::xlDown))
If ($null -eq $StartColumn)
{
$start = $worksheet.Range("B1")
}
Else
{
$start = $worksheet.Range($StartColumn)
}
# get the last cell
$Y = $worksheet.Range($start,$start.End([Microsoft.Office.Interop.Excel.XLDirection]::xlDown))
$ChartData = $worksheet.Range($X,$Y)
Get-ADRExcelComObjRelease -ComObjtoRelease $X
Remove-Variable X
Get-ADRExcelComObjRelease -ComObjtoRelease $Y
Remove-Variable Y
Get-ADRExcelComObjRelease -ComObjtoRelease $start
Remove-Variable start
}
$chart.SetSourceData($ChartData)
# https://docs.microsoft.com/en-us/dotnet/api/microsoft.office.interop.excel.chartclass.plotby?redirect
$chart.PlotBy = [Microsoft.Office.Interop.Excel.XlRowCol]::xlColumns
$chart.seriesCollection(1).Select() | Out-Null
$chart.SeriesCollection(1).ApplyDataLabels() | out-Null
# modify the chart title
$chart.HasTitle = $True
$chart.ChartTitle.Text = $ChartTitle
# Reposition the Chart
$temp = $worksheet.Range($RangetoCover)
# $chart.parent.placement = 3
$chart.parent.top = $temp.Top
$chart.parent.left = $temp.Left
$chart.parent.width = $temp.Width
If ($ChartTitle -ne "Privileged Groups in AD")
{
$chart.parent.height = $temp.Height
}
# $chart.Legend.Delete()
$excel.ScreenUpdating = $true
$excel.DisplayAlerts = $true
Get-ADRExcelComObjRelease -ComObjtoRelease $chart
Remove-Variable chart
Get-ADRExcelComObjRelease -ComObjtoRelease $ChartData
Remove-Variable ChartData
Get-ADRExcelComObjRelease -ComObjtoRelease $temp
Remove-Variable temp
Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
Remove-Variable worksheet
}
Function Get-ADRExcelSort
{
<#
.SYNOPSIS
Sorts a WorkSheet in the active Workbook.
.DESCRIPTION
Sorts a WorkSheet in the active Workbook.
.PARAMETER ColumnName
[string]
Name of the Column.
#>
param (
[Parameter(Mandatory = $true)]
[string] $ColumnName
)
$worksheet = $workbook.Worksheets.Item(1)
$worksheet.Activate();
$ExcelColumn = ($worksheet.Columns.Find($ColumnName))
If ($ExcelColumn)
{
If ($ExcelColumn.Text -ne $ColumnName)
{
$BeginAddress = $ExcelColumn.Address(0,0,1,1)
$End = $False
Do {
#Write-Verbose "[Get-ADRExcelSort] $($ExcelColumn.Text) selected instead of $($ColumnN
$ExcelColumn = ($worksheet.Columns.FindNext($ExcelColumn))
$Address = $ExcelColumn.Address(0,0,1,1)
If ( ($Address -eq $BeginAddress) -or ($ExcelColumn.Text -eq $ColumnName) )
{
$End = $True
}
} Until ($End -eq $True)
}
If ($ExcelColumn.Text -eq $ColumnName)
{
# Sort by Column
$workSheet.ListObjects.Item(1).Sort.SortFields.Clear()
$workSheet.ListObjects.Item(1).Sort.SortFields.Add($ExcelColumn) | Out-Null
$worksheet.ListObjects.Item(1).Sort.Apply()
}
Else
{
Write-Verbose "[Get-ADRExcelSort] $($ColumnName) not found in the $($worksheet.Name) wo
}
}
Else
{
Write-Verbose "[Get-ADRExcelSort] $($ColumnName) not found in the $($worksheet.Name) works
}
Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
Remove-Variable worksheet
}
Function Export-ADRExcel
{
<#
.SYNOPSIS
Automates the generation of the ADRecon report.
.DESCRIPTION
Automates the generation of the ADRecon report. If specific files exist, they are imported into the AD
.PARAMETER ExcelPath
[string]
Path for ADRecon output folder containing the CSV files to generate the ADRecon-Report.xlsx
.PARAMETER Logo
[string]
Which Logo to use in the excel file? (Default ADRecon)
.OUTPUTS
Creates the ADRecon-Report.xlsx report in the folder.
#>
param(
[Parameter(Mandatory = $true)]
[string] $ExcelPath,
[Parameter(Mandatory = $false)]
[string] $Logo = "ADRecon"
)
If ($PSVersionTable.PSEdition -eq "Core")
{
If ($PSVersionTable.Platform -eq "Win32NT")
{
$returndir = Get-Location
Set-Location C:\Windows\assembly\
$refFolder = (Get-ChildItem -Recurse Microsoft.Office.Interop.Excel.dll).Directory
Set-Location $refFolder
Add-Type -AssemblyName "Microsoft.Office.Interop.Excel"
Set-Location $returndir
Remove-Variable returndir
Remove-Variable refFolder
}
}
$ExcelPath = $((Convert-Path $ExcelPath).TrimEnd("\"))
$ReportPath = -join($ExcelPath,'\','CSV-Files')
If (!(Test-Path $ReportPath))
{
Write-Warning "[Export-ADRExcel] Could not locate the CSV-Files directory ... Exiting"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
Get-ADRExcelComObj
If ($excel)
{
Write-Output "[*] Generating ADRecon-Report.xlsx"
$ADFileName = -join($ReportPath,'\','AboutADRecon.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
$workbook.Worksheets.Item(1).Name = "About ADRecon"
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(3,2) ,
$workbook.Worksheets.Item(1).UsedRange.EntireColumn.AutoFit() | Out-Null
}
$ADFileName = -join($ReportPath,'\','Forest.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Forest"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','Domain.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Domain"
Get-ADRExcelImport -ADFileName $ADFileName
$DomainObj = Import-CSV -Path $ADFileName
Remove-Variable ADFileName
$DomainName = -join($DomainObj[0].Value,"-")
Remove-Variable DomainObj
}
$ADFileName = -join($ReportPath,'\','Trusts.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Trusts"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','Subnets.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Subnets"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','Sites.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Sites"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','SchemaHistory.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "SchemaHistory"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','FineGrainedPasswordPolicy.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Fine Grained Password Policy"
Get-ADRExcelImport -ADFileName $ADFileName -Method 3
Remove-Variable ADFileName
$worksheet = $workbook.Worksheets.Item(1)
$usedRange = $worksheet.UsedRange
$usedRange.Rows(2).WrapText = $True
$usedRange.Columns | ForEach-Object {
$_.ColumnWidth = 60
}
$usedRange.Rows(2).AutoFit() | Out-Null
$usedRange.Columns["A"].AutoFit() | Out-Null
}
$ADFileName = -join($ReportPath,'\','DefaultPasswordPolicy.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Default Password Policy"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
$excel.ScreenUpdating = $false
$worksheet = $workbook.Worksheets.Item(1)
# https://docs.microsoft.com/en-us/office/vba/api/excel.xlhalign
$worksheet.Range("C1:D1").HorizontalAlignment = -4108
$workbook.Worksheets.Item(1).Cells.Item(1,7).HorizontalAlignment = -4108
$worksheet.Range("B2:H10").HorizontalAlignment = -4108
# https://docs.microsoft.com/en-us/office/vba/api/excel.range.borderaround
"A2:B10", "C2:E10", "F2:G10", "H2:H10" | ForEach-Object {
$worksheet.Range($_).BorderAround(1) | Out-Null
}
# https://docs.microsoft.com/en-us/dotnet/api/microsoft.office.interop.excel.formatconditions.add
# $worksheet.Range().FormatConditions.Add
# http://dmcritchie.mvps.org/excel/colors.htm
# Values for Font.ColorIndex
$ObjValues = @(
# PCI v3.2.1 Enforce password history (passwords)
"C2", '=IF(B2<4,TRUE, FALSE)'
# PCI v3.2.1 Maximum password age (days)
"C3", '=IF(OR(B3=0,B3>90),TRUE, FALSE)'
# PCI v3.2.1 Minimum password age (days)
# PCI v3.2.1 Minimum password length (characters)
"C5", '=IF(B5<7,TRUE, FALSE)'
# PCI v3.2.1 Password must meet complexity requirements
"C6", '=IF(B6<>TRUE,TRUE, FALSE)'
# PCI v3.2.1 Store password using reversible encryption for all users in the domain
# PCI v3.2.1 Account lockout duration (mins)
"C8", '=IF(AND(B8>=1,B8<30),TRUE, FALSE)'
# PCI v3.2.1 Account lockout threshold (attempts)
"C9", '=IF(OR(B9=0,B9>6),TRUE, FALSE)'
# PCI v3.2.1 Reset account lockout counter after (mins)
# PCI v4.0 Enforce password history (passwords)
"D2", '=IF(B2<4,TRUE, FALSE)'
# PCI v4.0 Maximum password age (days)
"D3", '=IF(OR(B3=0,B3>90),TRUE, FALSE)'
# PCI v4.0 Minimum password age (days)
# PCI v4.0 Minimum password length (characters)
"D5", '=IF(B5<12,TRUE, FALSE)'
# PCI v4.0 Password must meet complexity requirements
"D6", '=IF(B6<>TRUE,TRUE, FALSE)'
# PCI v4.0 Store password using reversible encryption for all users in the domain
# PCI v4.0 Account lockout duration (mins)
"D8", '=IF(AND(B8>=1,B8<30),TRUE, FALSE)'
# PCI v4.0 Account lockout threshold (attempts)
"D9", '=IF(OR(B9=0,B9>10),TRUE, FALSE)'
# PCI v4.0 Reset account lockout counter after (mins)
# ACSC ISM Enforce password history (passwords)
#"F2", '=IF(B2<8,TRUE, FALSE)'
# ACSC ISM Maximum password age (days)
"F3", '=IF(OR(B3=0,B3>365),TRUE, FALSE)'
# ACSC ISM Minimum password age (days)
#"F4", '=IF(B4=0,TRUE, FALSE)'
# ACSC ISM Minimum password length (characters)
"F5", '=IF(B5<14,TRUE, FALSE)'
# ACSC ISM Password must meet complexity requirements
#"F6", '=IF(B6<>TRUE,TRUE, FALSE)'
# ACSC ISM Store password using reversible encryption for all users in the domain
# ACSC ISM Account lockout duration (mins)
# ACSC ISM Account lockout threshold (attempts)
"F9", '=IF(OR(B9=0,B9>5),TRUE, FALSE)'
# ACSC ISM Reset account lockout counter after (mins)
# CIS Benchmark Enforce password history (passwords)
"H2", '=IF(B2<24,TRUE, FALSE)'
# CIS Benchmark Maximum password age (days)
"H3", '=IF(OR(B3=0,B3>365),TRUE, FALSE)'
# CIS Benchmark Minimum password age (days)
"H4", '=IF(B4=0,TRUE, FALSE)'
# CIS Benchmark Minimum password length (characters)
"H5", '=IF(B5<14,TRUE, FALSE)'
# CIS Benchmark Password must meet complexity requirements
"H6", '=IF(B6<>TRUE,TRUE, FALSE)'
# CIS Benchmark Store password using reversible encryption for all users in the domain
"H7", '=IF(B7<>FALSE,TRUE, FALSE)'
# CIS Benchmark Account lockout duration (mins)
"H8", '=IF(AND(B8>=1,B8<15),TRUE, FALSE)'
# CIS Benchmark Account lockout threshold (attempts)
"H9", '=IF(OR(B9=0,B9>5),TRUE, FALSE)'
# CIS Benchmark Reset account lockout counter after (mins)
"H10", '=IF(B10<15,TRUE, FALSE)' )
For ($i = 0; $i -lt $($ObjValues.Count); $i++)
{
$worksheet.Range($ObjValues[$i]).FormatConditions.Add([Microsoft.Office.Interop.Excel.XlF
$i++
}
"C2", "C3" , "C5", "C6", "C8", "C9", "D2", "D3" , "D5", "D6", "D8", "D9", "F5", "F9", "H2", "H3", "H
$worksheet.Range($_).FormatConditions.Item(1).StopIfTrue = $false
$worksheet.Range($_).FormatConditions.Item(1).Font.ColorIndex = 3
}
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,5) ,
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1, 7)
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,8) ,
$excel.ScreenUpdating = $true
Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
Remove-Variable worksheet
}
$ADFileName = -join($ReportPath,'\','DomainControllers.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Domain Controllers"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','GroupChanges.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Group Changes"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
Get-ADRExcelSort -ColumnName "Group Name"
}
$ADFileName = -join($ReportPath,'\','DACLs.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "DACLs"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','SACLs.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "SACLs"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','GPOs.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "GPOs"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','gPLinks.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "gPLinks"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','DNSNodes','.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "DNS Records"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','DNSZones.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "DNS Zones"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','Printers.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Printers"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','BitLockerRecoveryKeys.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "BitLocker"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','LAPS.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "LAPS"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','ComputerSPNs.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Computer SPNs"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
Get-ADRExcelSort -ColumnName "UserName"
}
$ADFileName = -join($ReportPath,'\','Computers.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Computers"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
Get-ADRExcelSort -ColumnName "UserName"
$worksheet = $workbook.Worksheets.Item(1)
# Freeze First Row and Column
$worksheet.Select()
$worksheet.Application.ActiveWindow.splitcolumn = 1
$worksheet.Application.ActiveWindow.splitrow = 1
$worksheet.Application.ActiveWindow.FreezePanes = $true
Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
Remove-Variable worksheet
}
$ADFileName = -join($ReportPath,'\','OUs.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "OUs"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','Groups.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Groups"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
Get-ADRExcelSort -ColumnName "DistinguishedName"
}
$ADFileName = -join($ReportPath,'\','GroupMembers.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Group Members"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
Get-ADRExcelSort -ColumnName "Group Name"
}
$ADFileName = -join($ReportPath,'\','UserSPNs.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "User SPNs"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$ADFileName = -join($ReportPath,'\','Users.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Users"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
Get-ADRExcelSort -ColumnName "UserName"
$worksheet = $workbook.Worksheets.Item(1)
# Freeze First Row and Column
$worksheet.Select()
$worksheet.Application.ActiveWindow.splitcolumn = 1
$worksheet.Application.ActiveWindow.splitrow = 1
$worksheet.Application.ActiveWindow.FreezePanes = $true
$worksheet.Cells.Item(1,3).Interior.ColorIndex = 5
$worksheet.Cells.Item(1,3).font.ColorIndex = 2
# Set Filter to Enabled Accounts only
$worksheet.UsedRange.Select() | Out-Null
$excel.Selection.AutoFilter(3,$true) | Out-Null
$worksheet.Cells.Item(1,1).Select() | Out-Null
Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
Remove-Variable worksheet
}
# Computer Role Stats
$ADFileName = -join($ReportPath,'\','ComputerSPNs.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Computer Role Stats"
Remove-Variable ADFileName
$worksheet = $workbook.Worksheets.Item(1)
$PivotTableName = "Computer SPNs"
Get-ADRExcelPivotTable -SrcSheetName "Computer SPNs" -PivotTableName $PivotTableNam
$worksheet.Cells.Item(1,1) = "Computer Role"
$worksheet.Cells.Item(1,2) = "Count"
# https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlsortorder-enumeration-excel
$worksheet.PivotTables($PivotTableName).PivotFields("Service").AutoSort([Microsoft.Office.Int
Get-ADRExcelChart -ChartType "xlColumnClustered" -ChartLayout 10 -ChartTitle "Computer Ro
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,4) ,
$excel.Windows.Item(1).Displaygridlines = $false
Remove-Variable PivotTableName
Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
Remove-Variable worksheet
}
# Operating System Stats
$ADFileName = -join($ReportPath,'\','Computers.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Operating System Stats"
Remove-Variable ADFileName
$worksheet = $workbook.Worksheets.Item(1)
$PivotTableName = "Operating Systems"
Get-ADRExcelPivotTable -SrcSheetName "Computers" -PivotTableName $PivotTableName -Pi
$worksheet.Cells.Item(1,1) = "Operating System"
$worksheet.Cells.Item(1,2) = "Count"
# https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlsortorder-enumeration-excel
$worksheet.PivotTables($PivotTableName).PivotFields("Operating System").AutoSort([Microsof
Get-ADRExcelChart -ChartType "xlColumnClustered" -ChartLayout 10 -ChartTitle "Operating Sy
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,4) ,
$excel.Windows.Item(1).Displaygridlines = $false
Remove-Variable PivotTableName
Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
Remove-Variable worksheet
}
# Group Stats
$ADFileName = -join($ReportPath,'\','GroupMembers.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Privileged Group Stats"
Remove-Variable ADFileName
$worksheet = $workbook.Worksheets.Item(1)
$PivotTableName = "Group Members"
Get-ADRExcelPivotTable -SrcSheetName "Group Members" -PivotTableName $PivotTableNam
# Set the filter
$worksheet.PivotTables($PivotTableName).PivotFields("AccountType").CurrentPage = "user"
$worksheet.Cells.Item(1,2).Interior.ColorIndex = 5
$worksheet.Cells.Item(1,2).font.ColorIndex = 2
$worksheet.Cells.Item(3,1) = "Group Name"
$worksheet.Cells.Item(3,2) = "Count (Not-Recursive)"
$excel.ScreenUpdating = $false
# Create a copy of the Pivot Table
$PivotTableTemp = ($workbook.PivotCaches().Item($workbook.PivotCaches().Count)).CreateP
$PivotFieldTemp = $PivotTableTemp.PivotFields("Group Name")
# Set a filter
$PivotFieldTemp.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlPageFi
Try
{
$PivotFieldTemp.CurrentPage = "Domain Admins"
}
Catch
{
# No Direct Domain Admins. Good Job!
$NoDA = $true
}
If ($NoDA)
{
Try
{
$PivotFieldTemp.CurrentPage = "Administrators"
}
Catch
{
# No Direct Administrators
}
}
# Create a Slicer
$PivotSlicer = $workbook.SlicerCaches.Add($PivotTableTemp,$PivotFieldTemp)
# Add Original Pivot Table to the Slicer
$PivotSlicer.PivotTables.AddPivotTable($worksheet.PivotTables($PivotTableName))
# Delete the Slicer
$PivotSlicer.Delete()
# Delete the Pivot Table Copy
$PivotTableTemp.TableRange2.Delete() | Out-Null
Get-ADRExcelComObjRelease -ComObjtoRelease $PivotFieldTemp
Get-ADRExcelComObjRelease -ComObjtoRelease $PivotSlicer
Get-ADRExcelComObjRelease -ComObjtoRelease $PivotTableTemp
Remove-Variable PivotFieldTemp
Remove-Variable PivotSlicer
Remove-Variable PivotTableTemp
"Account Operators","Administrators","Backup Operators","Cert Publishers","Crypto Operators",
Try
{
$worksheet.PivotTables($PivotTableName).PivotFields("Group Name").PivotItems($_).Vis
}
Catch
{
# when PivotItem is not found
}
}
# https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlsortorder-enumeration-excel
$worksheet.PivotTables($PivotTableName).PivotFields("Group Name").AutoSort([Microsoft.Offi
$worksheet.Cells.Item(3,1).Interior.ColorIndex = 5
$worksheet.Cells.Item(3,1).font.ColorIndex = 2
$excel.ScreenUpdating = $true
Get-ADRExcelChart -ChartType "xlColumnClustered" -ChartLayout 10 -ChartTitle "Privileged G
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,4) ,
$excel.Windows.Item(1).Displaygridlines = $false
Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet
Remove-Variable worksheet
}
# Computer Stats
$ADFileName = -join($ReportPath,'\','Computers.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Computer Stats"
Remove-Variable ADFileName
$ObjAttributes = New-Object System.Collections.Specialized.OrderedDictionary
$ObjAttributes.Add("Delegation Typ",'"Unconstrained"')
$ObjAttributes.Add("Delegation Type",'"Constrained"')
$ObjAttributes.Add("SIDHistory",'"*"')
$ObjAttributes.Add("Dormant",'"TRUE"')
$ObjAttributes.Add("Password Age (> ",'"TRUE"')
$ObjAttributes.Add("ms-ds-CreatorSid",'"*"')
Get-ADRExcelAttributeStats -SrcSheetName "Computers" -Title1 "Computer Accounts in AD" -P
Remove-Variable ObjAttributes
#Todo: Replace with a better way to include the LAPS Stats
For($i = 1 ; $i -le $workbook.Sheets.count ; $i++)
{
$SrcSheetName = "LAPS"
If ($workbook.Worksheets.item($i).name -eq $SrcSheetName)
{
$ADRLAPSCheck = $true
break
}
Else
{
$ADRLAPSCheck = $false
}
}
If ($ADRLAPSCheck)
{
$worksheet = $workbook.Worksheets.Item(1)
$ExcelColumn = $workbook.Sheets.Item("LAPS").Columns.Find("Stored")
$ColAddress = "$($ExcelColumn.Address($false,$false).Substring(0,$ExcelColumn.Address(
$i = 9
$row = 9
$column = 6
$worksheet.Cells.Item($row, $column) = "LAPS"
$worksheet.Cells.Item($row, $column+1).Formula = "=COUNTIFS('" + $SrcSheetName + "'!"
$worksheet.Cells.Item($row, $column+2).Formula = '=IFERROR(G' + $i + '/VLOOKUP("Enab
$worksheet.Cells.Item($row, $column+3).Formula = "=COUNTIFS('" + $SrcSheetName + "'!"
$worksheet.Cells.Item($row, $column+4).Formula = '=IFERROR(I' + $i + '/VLOOKUP("Disabl
$worksheet.Cells.Item($row, $column+5).Formula = "=COUNTIF('" + $SrcSheetName + "'!" +
$worksheet.Cells.Item($row, $column+6).Formula = '=IFERROR(K' + $i + '/VLOOKUP("Total"
# http://www.excelhowto.com/macros/formatting-a-range-of-cells-in-excel-vba/
"H", "J" , "L" | ForEach-Object {
$rng = $_ + "9" + ":" + $_ + $($row)
$worksheet.Range($rng).NumberFormat = "0.00%"
}
Get-ADRExcelChart -ChartType "xlPie" -ChartLayout 3 -ChartTitle "Computer Accounts in AD
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(11
Get-ADRExcelChart -ChartType "xlBarClustered" -ChartLayout 1 -ChartTitle "Status of Comp
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(11
}
Else
{
Get-ADRExcelChart -ChartType "xlPie" -ChartLayout 3 -ChartTitle "Computer Accounts in AD
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(10
Get-ADRExcelChart -ChartType "xlBarClustered" -ChartLayout 1 -ChartTitle "Status of Comp
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(10
}
$workbook.Worksheets.Item(1).UsedRange.EntireColumn.AutoFit() | Out-Null
$excel.Windows.Item(1).Displaygridlines = $false
}
# User Stats
$ADFileName = -join($ReportPath,'\','Users.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "User Stats"
Remove-Variable ADFileName
$ObjAttributes = New-Object System.Collections.Specialized.OrderedDictionary
$ObjAttributes.Add("Must Change Password at Logon",'"TRUE"')
$ObjAttributes.Add("Cannot Change Password",'"TRUE"')
$ObjAttributes.Add("Password Never Expires",'"TRUE"')
$ObjAttributes.Add("Reversible Password Encryption",'"TRUE"')
$ObjAttributes.Add("Smartcard Logon Required",'"TRUE"')
$ObjAttributes.Add("Delegation Permitted",'"TRUE"')
$ObjAttributes.Add("Kerberos DES Only",'"TRUE"')
$ObjAttributes.Add("Kerberos RC4",'"TRUE"')
$ObjAttributes.Add("Does Not Require Pre Auth",'"TRUE"')
$ObjAttributes.Add("Password Age (> ",'"TRUE"')
$ObjAttributes.Add("Account Locked Out",'"TRUE"')
$ObjAttributes.Add("Never Logged in",'"TRUE"')
$ObjAttributes.Add("Dormant",'"TRUE"')
$ObjAttributes.Add("Password Not Required",'"TRUE"')
$ObjAttributes.Add("Delegation Typ",'"Unconstrained"')
$ObjAttributes.Add("SIDHistory",'"*"')
Get-ADRExcelAttributeStats -SrcSheetName "Users" -Title1 "User Accounts in AD" -PivotTableN
Remove-Variable ObjAttributes
Get-ADRExcelChart -ChartType "xlPie" -ChartLayout 3 -ChartTitle "User Accounts in AD" -Rang
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(20,1)
Get-ADRExcelChart -ChartType "xlBarClustered" -ChartLayout 1 -ChartTitle "Status of User Acc
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(20,6)
$workbook.Worksheets.Item(1).UsedRange.EntireColumn.AutoFit() | Out-Null
$excel.Windows.Item(1).Displaygridlines = $false
}
# Create Table of Contents
Get-ADRExcelWorkbook -Name "Table of Contents"
$worksheet = $workbook.Worksheets.Item(1)
$excel.ScreenUpdating = $false
# Image format and properties
# $path = "C:\ADRecon_Logo.jpg"
# $base64adrecon = [convert]::ToBase64String((Get-Content $path -Encoding byte))
If ($Logo -eq "CyberCX")
{
$base64adrecon = "/9j/4AAQSkZJRgABAQAASABIAAD/4QCGRXhpZgAATU0AKgAAAAgAAw
ItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Im
pEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjlFMDI3ODg5MDFFNjExRUJBQUMz
AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
Bob3Rvc2hvcCAzLjAAOEJJTQQEAAAAAAAAOEJJTQQlAAAAAAAQ1B2M2Y8AsgTpgAmY7PhCfv/AA
VictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eX
HfKyuodCCD0Ip1cXbXc9ocxn5c8gjjB7+1dNaahBdDaDtf+6f6VhKDQy9RRRUDCiiigAooooAKKKKACiii
MFLkMygj5uCQD0zjrW8jrIu5DkVxSoWAUDJIKcAHkcjGD+tX7UXcTb4QF3YOCSB7jB7+9ZygnsO51FF
}
ElseIf ($Logo -eq "Payatu")
{
$base64adrecon = "/9j/4AAQSkZJRgABAQAAkACQAAD/4QCMRXhpZgAATU0AKgAAAAgABQ
VGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsr
kJCQsLCwsLCwsLCwv/2wBDAQICAgMDAwUDAwULCAYICwsLCwsLCwsLCwsLCwsLCwsLCwsLCw
n7aX7ZHxvupLv4pfE/xLq6ytuMD6jNHbKf9mCNlhT6Kgr5N1LS3vpmurtmllblnclmJ9STzXZhFFMeFWGK
m+6QJMeZ9plXEcK8lRr6Sez6P/ACZ/N/HPg5jMsoTzHLk50Y6yjvKK7r+aK69UtdVdr+n+iiivqD8PCiiigAo
71iw0q6uLKNF+8DOkbR8fxHd8o64zRQozqS5YIjOc4w2WYWWKxMklsrtK8nslfq/8AgnzbrmqHxLqP2a0
8OtL/aD+MHiiLwP8JbfxD4o1qdHkj0/SEub66dIxudlihDuQoGWIHA5NfRX/DFn/BTf/ok3xP/APBDq3/xmv
PA5SRCNnBVgQaAP6maK/lJ/wCIqz4H/wDRJNd/8GFv/wDEV0Ph/wD4Oo/2ZLm7RPFPwx8T2cBYB3t
8AwYW//wARQB/VtRX8qll/wdU/ACScLqPwp8QRR92jvbaRvyIQfrX6p/sMf8FkP2M/2+PEY+Hnw2vr7w/
hN/wDhDPCV94W/4Qn+zfO+23Ec/n/2l9p27dijGz7Mc567h6UAfq3RXxH8Xv8AgpD+wx8A/iJqHwm+MX
qSSejZP1arl1KU4NzhFN2+1ZeXX5W9D9yv+CQn/BIHW/2udUs/2gv2hLSbT/hlZyh7W1bMU2uSRnlUPD
vhVuf1ommrooXKsvzocFeQRwQa/wBRr9kn4oT/ABr/AGW/h18XLyTzLnxH4b0zULk5zi4nt0aUE+okLA+
aT482GwtI1iiD7SQHcKZHAZgHcgEgCv0etf+Dan/gpLcTCKZfC0Cn+N9VYgf8AfMLH9K/R/wDY6/4Nf/8A
sGX8R7P9uz/gszqdpFqOn+NfiNcQTqJI5Yo7lkdW5BUiPBBHQjiuU+I37X3/BWrxX4A1rwz8UfFPj+58N6
CWIgumfmCSBkDcgZJNfnh/xDdf8Ezv+gf4i/8ABs3/AMRX5s/8FeP+C+Hxq+G3xx1z9mL9iq9tdGh8LTtY
wxAwvlwx4X6nvX+gX+yd/yax8NP+xU0b/0kir+LD/g6N/5P/8ACH/ZPtP/APTlqdAHvH/BF7/gj/8Asbftx/seT
4GBui8lxzlmZvSvwJilBFfi2aYV0MVUpPo393T8D/Urw/4gp5vkGCx8HdzhG/lJK0l8pJotUjHAzTfMH+f/wB
XzeaZvVx1Xnnolsui/4Pdn7zwD4bZZwrl31PCrmqys6lRr3pv9Ir7MenW7bb4S6W805jcWDbD3HY/UVNo
mQPcwNGmEQljliMntX9hFABRRRQB+af/BYqx8R6j/wTI+Mlv4WEhuV0FpX8oEt9milje46fw+Sr7v9nNf5
m1uYwx9vM8tfqwr8sP2Gf+CFP/AAUP+BP7Yvwz+MvxD0DS4NC8MeIrDUb+SLVLeV0t4JVZyqK2WIA
l/8Es/Bd5cqVXUNR1m4jz3QXssWR+MZoA/z+Pj3YeJtK+OnjTS/GokGs22vajFfiX/AFn2pLhxLu/2t4Ofev
o6JoOm2F0iMHVZ7a3jjcBhwQGU4I61+d//AAVm/wCCWnhn/gpZ8LtJs9P1ZPDnjbwo88ujalNG0tu6XAX
8ABSj4YWOj6tff8I54x8OmSTRdcSLzvLEoHmW88eVMkEhCnhgyMAyn7yt/Jf4v/wCDaz/gpF4d1CS10F
H4G8RX3g7xnZXGlatpk7215Z3cbQzwTRna6SIwDKykYIIBrg73XpLg+VB8xNfnqw0r2Z/XmK4so+zvCV
KACvxI/wCC4n7fPx7/AOCf/wAD/Bvj/wCADacuoa5rj6fc/wBo2xuU8lbd5BtAZcHco5z0r9t64nxv8Nfhz8Tb
cPk2vifSsRX6BR8qzD7lzGuAAsoJUZCMmSa/jb/a3/AODdz9vz9nK4uNZ+GOmxfFPw9GxKXOgAi/VB0
76EJY2Mo5DWlqSwDqeksrO4I3J5Z4r9vqKK+zw2Eo4eHs6MbI/mTO8/wAwzjEvGZlWdSo+r6eSS0S8k
}
Else
{
■■ $base64adrecon = "/9j/4AAQSkZJRgABAQAASABIAAD/4QBMRXhpZgAATU0AKgAAAAgAAgES
WgAAAdAAAAAUYlhZWgAAAeQAAAAUZ1hZWgAAAfgAAAAUclRSQwAAAgwAAAAgZ1RSQwAAAiw
cmEAAAAAAAMAAAACZmYAAPKnAAANWQAAE9AAAApbcGFyYQAAAAAAAwAAAAJmZgAA8qcAA
BwgJCgv/xADDEQACAgEDAwMCAwUCBQIEBIcBAAIRAxASIQQgMUETBTAiMlEUQAYzI2FCFXFSN
jbVttW21bTqjbVtOqNtWnFj2dP3RuXfy/wBc8p7JltuB5/0A3n/ovL93R63zjqgeYbdbzW2TafWaxH84lRtc2
afP+/wDCqVI3bxYpKt5hiXIpHh7bCjd9ksreyUlSTDEuRSPD22FH9Hdre82kVruGxbTb3cX9Hdre77JZW9
qt4N02y6sr9HJ8Mu/k2mzt7u4lupvCl7a21lMay+GporfdfE08Vxuv8Av9//2gAIAQMRAT8B/YDEeGPEbbE
/bcH9ntHtEwAipxHFqt4ySkDzd1/t+TX/aLjkWClFD1Hg1lKgRiODGKFEV1oGkm71p+0GqW2mMsnkkGr
AjWOujJPmxLCrFY82i73CVPvYPEtKriaNRTw1ftx/i5LOKWtqD0D4drhE8yUFXCrWR+00Wd/L/FKcC1C
X2Dt/fd9X2Boc4s/9D4HIJnurZYZPTKL52bJlJYoh7v7f+f/AMHufCD+rAxmaP6lIXmKqus1UhsJfikYEjw00
AP8A/8QAMxEBAQEAAwABAgUFAQEAAQEJAQARITEQQVFhIHHwkYGhsdHB4fEwQFBgcICQoLDA
i9cvQc40E7gNJ4rfuw+P+/i56/5H/IfH/Iv+b/8Ag/fxf8X4f8Mf4rz/AMDw8HXTEcFloORd/T7pZHfaZmifl/yvg/
GzFtwBcE8ji81AkZB9ySk2hBPIWfi/8A0P8AusW0BlGPd/f/AMqPT7uNWPd/x/u/7Xld82P+B1e8d380Cvt
}
$bytes = [System.Convert]::FromBase64String($base64adrecon)
Remove-Variable base64adrecon
$CompanyLogo = -join($ReportPath,'\','ADRecon_Logo.jpg')
■■$p = New-Object IO.MemoryStream($bytes, 0, $bytes.length)
■■$p.Write($bytes, 0, $bytes.length)
Add-Type -AssemblyName System.Drawing
■■$picture = [System.Drawing.Image]::FromStream($p, $true)
■■$picture.Save($CompanyLogo)
Remove-Variable bytes
Remove-Variable p
Remove-Variable picture
$LinkToFile = $false
$SaveWithDocument = $true
$Left = 0
$Top = 0
$Width = 150
$Height = 50
# Add image to the Sheet
$worksheet.Shapes.AddPicture($CompanyLogo, $LinkToFile, $SaveWithDocument, $Left, $Top, $
Remove-Variable LinkToFile
Remove-Variable SaveWithDocument
Remove-Variable Left
Remove-Variable Top
Remove-Variable Width
Remove-Variable Height
If (Test-Path -Path $CompanyLogo)
{
Remove-Item $CompanyLogo
}
Remove-Variable CompanyLogo
$row = 5
$column = 1
$worksheet.Cells.Item($row,$column)= "Table of Contents"
$worksheet.Cells.Item($row,$column).Style = "Heading 2"
$row++
For($i=2; $i -le $workbook.Worksheets.Count; $i++)
{
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item($row
$row++
}
$row++
■■$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item($row,1) ,
$worksheet.UsedRange.EntireColumn.AutoFit() | Out-Null
$excel.Windows.Item(1).Displaygridlines = $false
$excel.ScreenUpdating = $true
$ADStatFileName = -join($ExcelPath,'\',$DomainName,'ADRecon-Report.xlsx')
Try
{
# Disable prompt if file exists
$excel.DisplayAlerts = $False
$workbook.SaveAs($ADStatFileName)
Write-Output "[+] Excelsheet Saved to: $ADStatFileName"
}
Catch
{
Write-Error "[EXCEPTION] $($_.Exception.Message)"
}
$excel.Quit()
Get-ADRExcelComObjRelease -ComObjtoRelease $worksheet -Final $true
Remove-Variable worksheet
Get-ADRExcelComObjRelease -ComObjtoRelease $workbook -Final $true
Remove-Variable -Name workbook -Scope Global
Get-ADRExcelComObjRelease -ComObjtoRelease $excel -Final $true
Remove-Variable -Name excel -Scope Global
}
}
Function Get-ADRDomain
{
<#
.SYNOPSIS
Returns information of the current (or specified) domain.
.DESCRIPTION
Returns information of the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER objDomainRootDSE
[DirectoryServices.DirectoryEntry]
RootDSE Directory Entry object.
.PARAMETER DomainController
[string]
IP Address of the Domain Controller.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomainRootDSE,
[Parameter(Mandatory = $false)]
[string] $DomainController,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
)
If ($Method -eq 'ADWS')
{
Try
{
$ADDomain = Get-ADDomain
}
Catch
{
Write-Warning "[Get-ADRDomain] Error getting Domain Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADDomain)
{
$DomainObj = @()
# Values taken from https://technet.microsoft.com/en-us/library/hh852281(v=wps.630).aspx
$FLAD = @{
■ 0 = "Windows2000";
■ 1 = "Windows2003/Interim";
■ 2 = "Windows2003";
■ 3 = "Windows2008";
■ 4 = "Windows2008R2";
■ 5 = "Windows2012";
■ 6 = "Windows2012R2";
■ 7 = "Windows2016"
}
$DomainMode = $FLAD[[convert]::ToInt32($ADDomain.DomainMode)] + "Domain"
Remove-Variable FLAD
If (-Not $DomainMode)
{
$DomainMode = $ADDomain.DomainMode
}
$ObjValues = @("Name", $ADDomain.DNSRoot, "NetBIOS", $ADDomain.NetBIOSName, "Fun
For ($i = 0; $i -lt $($ObjValues.Count); $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value $ObjValues[$i]
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ObjValues[$i+1]
$i++
$DomainObj += $Obj
}
Remove-Variable DomainMode
For($i=0; $i -lt $ADDomain.ReplicaDirectoryServers.Count; $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Domain Controlle
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADDomain.ReplicaD
$DomainObj += $Obj
}
For($i=0; $i -lt $ADDomain.ReadOnlyReplicaDirectoryServers.Count; $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Read Only Doma
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADDomain.ReadOn
$DomainObj += $Obj
}
Try
{
$ADForest = Get-ADForest $ADDomain.Forest
}
Catch
{
Write-Verbose "[Get-ADRDomain] Error getting Forest Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If (-Not $ADForest)
{
Try
{
$ADForest = Get-ADForest -Server $DomainController
}
Catch
{
Write-Warning "[Get-ADRDomain] Error getting Forest Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
If ($ADForest)
{
$DomainCreation = Get-ADObject -SearchBase "$($ADForest.PartitionsContainer)" -LDAPFi
If (-Not $DomainCreation)
{
$DomainCreation = Get-ADObject -SearchBase "$($ADForest.PartitionsContainer)" -LDAP
}
Remove-Variable ADForest
}
# Get RIDAvailablePool
Try
{
$RIDManager = Get-ADObject -Identity "CN=RID Manager$,CN=System,$($ADDomain.Disti
$RIDproperty = $RIDManager.rIDAvailablePool
[int32] $totalSIDS = $($RIDproperty) / ([math]::Pow(2,32))
[int64] $temp64val = $totalSIDS * ([math]::Pow(2,32))
$RIDsIssued = [int32]($($RIDproperty) - $temp64val)
$RIDsRemaining = $totalSIDS - $RIDsIssued
Remove-Variable RIDManager
Remove-Variable RIDproperty
Remove-Variable totalSIDS
Remove-Variable temp64val
}
Catch
{
Write-Warning "[Get-ADRDomain] Error accessing CN=RID Manager$,CN=System,$($ADDo
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($DomainCreation)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Creation Date"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $DomainCreation.whe
$DomainObj += $Obj
Remove-Variable DomainCreation
}
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "ms-DS-MachineAcc
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $((Get-ADObject -Identi
$DomainObj += $Obj
If ($RIDsIssued)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "RIDs Issued"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $RIDsIssued
$DomainObj += $Obj
Remove-Variable RIDsIssued
}
If ($RIDsRemaining)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "RIDs Remaining"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $RIDsRemaining
$DomainObj += $Obj
Remove-Variable RIDsRemaining
}
}
}
If ($Method -eq 'LDAP')
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Do
Try
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainCon
}
Catch
{
Write-Warning "[Get-ADRDomain] Error getting Domain Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
Remove-Variable DomainContext
# Get RIDAvailablePool
Try
{
$SearchPath = "CN=RID Manager$,CN=System"
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainC
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPat
$objSearcherPath.PropertiesToLoad.AddRange(("ridavailablepool"))
$objSearcherResult = $objSearcherPath.FindAll()
$RIDproperty = $objSearcherResult.Properties.ridavailablepool
[int32] $totalSIDS = $($RIDproperty) / ([math]::Pow(2,32))
[int64] $temp64val = $totalSIDS * ([math]::Pow(2,32))
$RIDsIssued = [int32]($($RIDproperty) - $temp64val)
$RIDsRemaining = $totalSIDS - $RIDsIssued
Remove-Variable SearchPath
$objSearchPath.Dispose()
$objSearcherPath.Dispose()
$objSearcherResult.Dispose()
Remove-Variable RIDproperty
Remove-Variable totalSIDS
Remove-Variable temp64val
}
Catch
{
Write-Warning "[Get-ADRDomain] Error accessing CN=RID Manager$,CN=System,$($Searc
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
Try
{
$ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Fo
$ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
}
Catch
{
Write-Warning "[Get-ADRDomain] Error getting Forest Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($ForestContext)
{
Remove-Variable ForestContext
}
If ($ADForest)
{
$GlobalCatalog = $ADForest.FindGlobalCatalog()
}
If ($GlobalCatalog)
{
$DN = "GC://$($GlobalCatalog.IPAddress)/$($objDomain.distinguishedname)"
Try
{
$ADObject = New-Object -TypeName System.DirectoryServices.DirectoryEntry -Argument
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($ADObject.obje
$ADObject.Dispose()
}
Catch
{
Write-Warning "[Get-ADRDomain] Error retrieving Domain SID using the GlobalCatalog $($
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.obj
}
}
Else
{
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objec
}
}
Else
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
Try
{
$GlobalCatalog = $ADForest.FindGlobalCatalog()
$DN = "GC://$($GlobalCatalog)/$($objDomain.distinguishedname)"
$ADObject = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentLis
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($ADObject.objectS
$ADObject.dispose()
}
Catch
{
Write-Warning "[Get-ADRDomain] Error retrieving Domain SID using the GlobalCatalog $($G
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objec
}
# Get RIDAvailablePool
Try
{
$RIDManager = ([ADSI]"LDAP://CN=RID Manager$,CN=System,$($objDomain.distinguished
$RIDproperty = $ObjDomain.ConvertLargeIntegerToInt64($RIDManager.Properties.rIDAvaila
[int32] $totalSIDS = $($RIDproperty) / ([math]::Pow(2,32))
[int64] $temp64val = $totalSIDS * ([math]::Pow(2,32))
$RIDsIssued = [int32]($($RIDproperty) - $temp64val)
$RIDsRemaining = $totalSIDS - $RIDsIssued
Remove-Variable RIDManager
Remove-Variable RIDproperty
Remove-Variable totalSIDS
Remove-Variable temp64val
}
Catch
{
Write-Warning "[Get-ADRDomain] Error accessing CN=RID Manager$,CN=System,$($Searc
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
If ($ADDomain)
{
$DomainObj = @()
# Values taken from https://technet.microsoft.com/en-us/library/hh852281(v=wps.630).aspx
$FLAD = @{
■ 0 = "Windows2000";
■ 1 = "Windows2003/Interim";
■ 2 = "Windows2003";
■ 3 = "Windows2008";
■ 4 = "Windows2008R2";
■ 5 = "Windows2012";
■ 6 = "Windows2012R2";
■ 7 = "Windows2016"
}
$DomainMode = $FLAD[[convert]::ToInt32($objDomainRootDSE.domainFunctionality,10)] + "Do
Remove-Variable FLAD
$ObjValues = @("Name", $ADDomain.Name, "NetBIOS", $objDomain.dc.value, "Functional Lev
For ($i = 0; $i -lt $($ObjValues.Count); $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value $ObjValues[$i]
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ObjValues[$i+1]
$i++
$DomainObj += $Obj
}
Remove-Variable DomainMode
For($i=0; $i -lt $ADDomain.DomainControllers.Count; $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Domain Controlle
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADDomain.DomainC
$DomainObj += $Obj
}
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Creation Date"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $objDomain.whencreate
$DomainObj += $Obj
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "ms-DS-MachineAcc
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $objDomain.'ms-DS-Ma
$DomainObj += $Obj
If ($RIDsIssued)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "RIDs Issued"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $RIDsIssued
$DomainObj += $Obj
Remove-Variable RIDsIssued
}
If ($RIDsRemaining)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "RIDs Remaining"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $RIDsRemaining
$DomainObj += $Obj
Remove-Variable RIDsRemaining
}
}
}
If ($DomainObj)
{
Return $DomainObj
}
Else
{
Return $null
}
}
Function Get-ADRForest
{
<#
.SYNOPSIS
Returns information of the current (or specified) forest.
.DESCRIPTION
Returns information of the current (or specified) forest.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER objDomainRootDSE
[DirectoryServices.DirectoryEntry]
RootDSE Directory Entry object.
.PARAMETER DomainController
[string]
IP Address of the Domain Controller.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomainRootDSE,
[Parameter(Mandatory = $false)]
[string] $DomainController,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
)
If ($Method -eq 'ADWS')
{
Try
{
$ADDomain = Get-ADDomain
}
Catch
{
Write-Warning "[Get-ADRForest] Error getting Domain Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
Try
{
$ADForest = Get-ADForest $ADDomain.Forest
}
Catch
{
Write-Verbose "[Get-ADRForest] Error getting Forest Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
Remove-Variable ADDomain
If (-Not $ADForest)
{
Try
{
$ADForest = Get-ADForest -Server $DomainController
}
Catch
{
Write-Warning "[Get-ADRForest] Error getting Forest Context using Server parameter"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
}
If ($ADForest)
{
# Get Tombstone Lifetime
Try
{
$ADForestCNC = (Get-ADRootDSE).configurationNamingContext
$ADForestDSCP = Get-ADObject -Identity "CN=Directory Service,CN=Windows NT,CN=Serv
$ADForestTombstoneLifetime = $ADForestDSCP.tombstoneLifetime
Remove-Variable ADForestCNC
Remove-Variable ADForestDSCP
}
Catch
{
Write-Warning "[Get-ADRForest] Error retrieving Tombstone Lifetime"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
# Check Recycle Bin Feature Status
If ([convert]::ToInt32($ADForest.ForestMode) -ge 4)
{
Try
{
$ADRecycleBin = Get-ADOptionalFeature -Identity "Recycle Bin Feature" -Properties when
}
Catch
{
Write-Warning "[Get-ADRForest] Error retrieving Recycle Bin Feature"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
# Check Privileged Access Management Feature status
If ([convert]::ToInt32($ADForest.ForestMode) -ge 7)
{
Try
{
$PrivilegedAccessManagement = Get-ADOptionalFeature -Identity "Privileged Access Man
}
Catch
{
Write-Warning "[Get-ADRForest] Error retrieving Privileged Acceess Management Feature
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
$ForestObj = @()
# Values taken from https://technet.microsoft.com/en-us/library/hh852281(v=wps.630).aspx
$FLAD = @{
0 = "Windows2000";
1 = "Windows2003/Interim";
2 = "Windows2003";
3 = "Windows2008";
4 = "Windows2008R2";
5 = "Windows2012";
6 = "Windows2012R2";
7 = "Windows2016"
}
$ForestMode = $FLAD[[convert]::ToInt32($ADForest.ForestMode)] + "Forest"
Remove-Variable FLAD
If (-Not $ForestMode)
{
$ForestMode = $ADForest.ForestMode
}
# LAPS Check
$ADRLAPSCheck = Get-ADRLAPSCheck -Method ADWS
$ObjValues = @("Name", $ADForest.Name, "Functional Level", $ForestMode, "Domain Naming
For ($i = 0; $i -lt $($ObjValues.Count); $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value $ObjValues[$i]
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ObjValues[$i+1]
$i++
$ForestObj += $Obj
}
Remove-Variable ForestMode
For($i=0; $i -lt $ADForest.Domains.Count; $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Domain"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForest.Domains[$
$ForestObj += $Obj
}
For($i=0; $i -lt $ADForest.Sites.Count; $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Site"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForest.Sites[$i]
$ForestObj += $Obj
}
For($i=0; $i -lt $ADForest.GlobalCatalogs.Count; $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "GlobalCatalog"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForest.GlobalCat
$ForestObj += $Obj
}
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Tombstone Lifetime
If ($ADForestTombstoneLifetime)
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForestTombstone
Remove-Variable ADForestTombstoneLifetime
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Not Retrieved"
}
$ForestObj += $Obj
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Recycle Bin (2008 R
If ($ADRecycleBin)
{
If ($ADRecycleBin.EnabledScopes.Count -gt 0)
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Enabled"
$ForestObj += $Obj
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Recycle Bin En
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADRecycleBin.wh
$ForestObj += $Obj
For($i=0; $i -lt $($ADRecycleBin.EnabledScopes.Count); $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Enabled Sco
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADRecycleBin.
$ForestObj += $Obj
}
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
$ForestObj += $Obj
}
Remove-Variable ADRecycleBin
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
$ForestObj += $Obj
}
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Privileged Access M
If ($PrivilegedAccessManagement)
{
If ($PrivilegedAccessManagement.EnabledScopes.Count -gt 0)
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Enabled"
$ForestObj += $Obj
For($i=0; $i -lt $($PrivilegedAccessManagement.EnabledScopes.Count); $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Enabled Sco
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $PrivilegedAcce
$ForestObj += $Obj
}
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
$ForestObj += $Obj
}
Remove-Variable PrivilegedAccessManagement
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
$ForestObj += $Obj
}
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "LAPS"
If ($ADRLAPSCheck)
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Enabled"
$ForestObj += $Obj
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "LAPS Installed D
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $((Get-ADObject "CN
$ForestObj += $Obj
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
$ForestObj += $Obj
}
Remove-Variable ADForest
}
}
If ($Method -eq 'LDAP')
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Do
Try
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainCon
}
Catch
{
Write-Warning "[Get-ADRForest] Error getting Domain Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
Remove-Variable DomainContext
$ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Fore
Remove-Variable ADDomain
Try
{
$ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
}
Catch
{
Write-Warning "[Get-ADRForest] Error getting Forest Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
Remove-Variable ForestContext
# Get Tombstone Lifetime
Try
{
$SearchPath = "CN=Directory Service,CN=Windows NT,CN=Services"
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainC
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPat
$objSearcherPath.Filter="(name=Directory Service)"
$objSearcherResult = $objSearcherPath.FindAll()
$ADForestTombstoneLifetime = $objSearcherResult.Properties.tombstoneLifetime
Remove-Variable SearchPath
$objSearchPath.Dispose()
$objSearcherPath.Dispose()
$objSearcherResult.Dispose()
}
Catch
{
Write-Warning "[Get-ADRForest] Error retrieving Tombstone Lifetime"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
# Check Recycle Bin Feature Status
If ([convert]::ToInt32($objDomainRootDSE.forestFunctionality,10) -ge 4)
{
Try
{
$SearchPath = "CN=Recycle Bin Feature,CN=Optional Features,CN=Directory Service,CN
$ADRecycleBin = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($Domai
Remove-Variable SearchPath
}
Catch
{
Write-Warning "[Get-ADRForest] Error retrieving Recycle Bin Feature"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
# Check Privileged Access Management Feature status
If ([convert]::ToInt32($objDomainRootDSE.forestFunctionality,10) -ge 7)
{
Try
{
$SearchPath = "CN=Privileged Access Management Feature,CN=Optional Features,CN=D
$PrivilegedAccessManagement = New-Object System.DirectoryServices.DirectoryEntry "L
Remove-Variable SearchPath
}
Catch
{
Write-Warning "[Get-ADRForest] Error retrieving Privileged Access Management Feature"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
}
Else
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
# Get Tombstone Lifetime
$ADForestTombstoneLifetime = ([ADSI]"LDAP://CN=Directory Service,CN=Windows NT,CN=Se
# Check Recycle Bin Feature Status
If ([convert]::ToInt32($objDomainRootDSE.forestFunctionality,10) -ge 4)
{
$ADRecycleBin = ([ADSI]"LDAP://CN=Recycle Bin Feature,CN=Optional Features,CN=Direc
}
# Check Privileged Access Management Feature Status
If ([convert]::ToInt32($objDomainRootDSE.forestFunctionality,10) -ge 7)
{
$PrivilegedAccessManagement = ([ADSI]"LDAP://CN=Privileged Access Management Featu
}
}
# LAPS Check
$ADRLAPSCheck = Get-ADRLAPSCheck -Method LDAP -objDomainRootDSE $objDomainRootD
If ($ADForest)
{
$ForestObj = @()
# Values taken from https://technet.microsoft.com/en-us/library/hh852281(v=wps.630).aspx
$FLAD = @{
■ 0 = "Windows2000";
■ 1 = "Windows2003/Interim";
■ 2 = "Windows2003";
■ 3 = "Windows2008";
■ 4 = "Windows2008R2";
■ 5 = "Windows2012";
■ 6 = "Windows2012R2";
7 = "Windows2016"
}
$ForestMode = $FLAD[[convert]::ToInt32($objDomainRootDSE.forestFunctionality,10)] + "Fores
Remove-Variable FLAD
$ObjValues = @("Name", $ADForest.Name, "Functional Level", $ForestMode, "Domain Naming
For ($i = 0; $i -lt $($ObjValues.Count); $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value $ObjValues[$i]
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ObjValues[$i+1]
$i++
$ForestObj += $Obj
}
Remove-Variable ForestMode
For($i=0; $i -lt $ADForest.Domains.Count; $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Domain"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForest.Domains[$
$ForestObj += $Obj
}
For($i=0; $i -lt $ADForest.Sites.Count; $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Site"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForest.Sites[$i]
$ForestObj += $Obj
}
For($i=0; $i -lt $ADForest.GlobalCatalogs.Count; $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "GlobalCatalog"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForest.GlobalCat
$ForestObj += $Obj
}
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Tombstone Lifetime
If ($ADForestTombstoneLifetime)
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADForestTombstone
Remove-Variable ADForestTombstoneLifetime
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Not Retrieved"
}
$ForestObj += $Obj
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Recycle Bin (2008 R
If ($ADRecycleBin)
{
If ($ADRecycleBin.Properties.'msds-enabledfeaturebl'.Count -gt 0)
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Enabled"
$ForestObj += $Obj
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Recycle Bin En
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADRecycleBin.wh
$ForestObj += $Obj
For($i=0; $i -lt $($ADRecycleBin.Properties.'msds-enabledfeaturebl'.Count); $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Enabled Sco
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADRecycleBin.
$ForestObj += $Obj
}
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
$ForestObj += $Obj
}
$ADRecycleBin.Dispose()
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
$ForestObj += $Obj
}
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Privileged Access M
If ($PrivilegedAccessManagement)
{
If ($PrivilegedAccessManagement.Properties.'msDS-EnabledFeatureBL'.Count -gt 0)
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Enabled"
$ForestObj += $Obj
For($i=0; $i -lt $($PrivilegedAccessManagement.Properties.'msDS-EnabledFeatureBL'.Cou
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Enabled Sco
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $PrivilegedAcce
$ForestObj += $Obj
}
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
$ForestObj += $Obj
}
$PrivilegedAccessManagement.dispose()
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Disabled"
$ForestObj += $Obj
}
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "LAPS"
If ($ADRLAPSCheck)
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Enabled"
$ForestObj += $Obj
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "LAPS Installed D
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$ADRLAPSInstalledDate = (New-Object System.DirectoryServices.DirectoryEntry "LDAP:/
}
Else
{
$ADRLAPSInstalledDate = ([ADSI]("LDAP://CN=ms-Mcs-AdmPwd,$($objDomainRootDSE
}
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADRLAPSInstalledD
$ForestObj += $Obj
Remove-Variable ADRLAPSInstalledDate
}
Remove-Variable ADForest
}
}
If ($ForestObj)
{
Return $ForestObj
}
Else
{
Return $null
}
}
Function Get-ADRTrust
{
<#
.SYNOPSIS
Returns the Trusts of the current (or specified) domain.
.DESCRIPTION
Returns the Trusts of the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain
)
# Values taken from https://msdn.microsoft.com/en-us/library/cc223768.aspx
$TDAD = @{
0 = "Disabled";
1 = "Inbound";
2 = "Outbound";
3 = "BiDirectional";
}
# Values taken from https://msdn.microsoft.com/en-us/library/cc223771.aspx
$TTAD = @{
1 = "Downlevel";
2 = "Uplevel";
3 = "MIT";
4 = "DCE";
}
If ($Method -eq 'ADWS')
{
Try
{
$ADTrusts = Get-ADObject -LDAPFilter "(objectClass=trustedDomain)" -Properties Distinguishe
}
Catch
{
Write-Warning "[Get-ADRTrust] Error while enumerating trustedDomain Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADTrusts)
{
Write-Verbose "[*] Total Trusts: $([ADRecon.ADWSClass]::ObjectCount($ADTrusts))"
# Trust Info
$ADTrustObj = @()
$ADTrusts | ForEach-Object {
# Create the object for each instance.
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Source Domain" -Value (Get-DNtoF
$Obj | Add-Member -MemberType NoteProperty -Name "Target Domain" -Value $_.trustPartn
$TrustDirection = [string] $TDAD[$_.trustdirection]
$Obj | Add-Member -MemberType NoteProperty -Name "Trust Direction" -Value $TrustDirect
$TrustType = [string] $TTAD[$_.trusttype]
$Obj | Add-Member -MemberType NoteProperty -Name "Trust Type" -Value $TrustType
$TrustAttributes = $null
If ([int32] $_.TrustAttributes -band 0x00000001) { $TrustAttributes += "Non Transitive," }
If ([int32] $_.TrustAttributes -band 0x00000002) { $TrustAttributes += "UpLevel," }
If ([int32] $_.TrustAttributes -band 0x00000004) { $TrustAttributes += "Quarantined," } #SID F
If ([int32] $_.TrustAttributes -band 0x00000008) { $TrustAttributes += "Forest Transitive," }
If ([int32] $_.TrustAttributes -band 0x00000010) { $TrustAttributes += "Cross Organization," }
If ([int32] $_.TrustAttributes -band 0x00000020) { $TrustAttributes += "Within Forest," }
If ([int32] $_.TrustAttributes -band 0x00000040) { $TrustAttributes += "Treat as External," }
If ([int32] $_.TrustAttributes -band 0x00000080) { $TrustAttributes += "Uses RC4 Encryption,"
If ([int32] $_.TrustAttributes -band 0x00000200) { $TrustAttributes += "No TGT Delegation," }
If ([int32] $_.TrustAttributes -band 0x00000400) { $TrustAttributes += "PIM Trust," }
If ($TrustAttributes)
{
$TrustAttributes = $TrustAttributes.TrimEnd(",")
}
$Obj | Add-Member -MemberType NoteProperty -Name "Attributes" -Value $TrustAttributes
$Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value ([DateTime] $
$Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value ([DateTime]
$ADTrustObj += $Obj
}
Remove-Variable ADTrusts
}
}
If ($Method -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectClass=trustedDomain)"
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","trustpartner","trustdirection","tru
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADTrusts = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRTrust] Error while enumerating trustedDomain Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADTrusts)
{
Write-Verbose "[*] Total Trusts: $([ADRecon.LDAPClass]::ObjectCount($ADTrusts))"
# Trust Info
$ADTrustObj = @()
$ADTrusts | ForEach-Object {
# Create the object for each instance.
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Source Domain" -Value $(Get-DNto
$Obj | Add-Member -MemberType NoteProperty -Name "Target Domain" -Value $([string] $_.
$TrustDirection = [string] $TDAD[$_.Properties.trustdirection]
$Obj | Add-Member -MemberType NoteProperty -Name "Trust Direction" -Value $TrustDirect
$TrustType = [string] $TTAD[$_.Properties.trusttype]
$Obj | Add-Member -MemberType NoteProperty -Name "Trust Type" -Value $TrustType
$TrustAttributes = $null
If ([int32] $_.Properties.trustattributes[0] -band 0x00000001) { $TrustAttributes += "Non Trans
If ([int32] $_.Properties.trustattributes[0] -band 0x00000002) { $TrustAttributes += "UpLevel,"
If ([int32] $_.Properties.trustattributes[0] -band 0x00000004) { $TrustAttributes += "Quarantine
If ([int32] $_.Properties.trustattributes[0] -band 0x00000008) { $TrustAttributes += "Forest Tra
If ([int32] $_.Properties.trustattributes[0] -band 0x00000010) { $TrustAttributes += "Cross Org
If ([int32] $_.Properties.trustattributes[0] -band 0x00000020) { $TrustAttributes += "Within For
If ([int32] $_.Properties.trustattributes[0] -band 0x00000040) { $TrustAttributes += "Treat as E
If ([int32] $_.Properties.trustattributes[0] -band 0x00000080) { $TrustAttributes += "Uses RC4
If ([int32] $_.Properties.trustattributes[0] -band 0x00000200) { $TrustAttributes += "No TGT D
If ([int32] $_.Properties.trustattributes[0] -band 0x00000400) { $TrustAttributes += "PIM Trust,
If ($TrustAttributes)
{
$TrustAttributes = $TrustAttributes.TrimEnd(",")
}
$Obj | Add-Member -MemberType NoteProperty -Name "Attributes" -Value $TrustAttributes
$Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value ([DateTime] $
$Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value ([DateTime]
$ADTrustObj += $Obj
}
Remove-Variable ADTrusts
}
}
If ($ADTrustObj)
{
Return $ADTrustObj
}
Else
{
Return $null
}
}
Function Get-ADRSite
{
<#
.SYNOPSIS
Returns the Sites of the current (or specified) domain.
.DESCRIPTION
Returns the Sites of the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER objDomainRootDSE
[DirectoryServices.DirectoryEntry]
RootDSE Directory Entry object.
.PARAMETER DomainController
[string]
IP Address of the Domain Controller.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomainRootDSE,
[Parameter(Mandatory = $false)]
[string] $DomainController,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
)
If ($Method -eq 'ADWS')
{
Try
{
$SearchPath = "CN=Sites"
$ADSites = Get-ADObject -SearchBase "$SearchPath,$((Get-ADRootDSE).configurationNamin
}
Catch
{
Write-Warning "[Get-ADRSite] Error while enumerating Site Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADSites)
{
Write-Verbose "[*] Total Sites: $([ADRecon.ADWSClass]::ObjectCount($ADSites))"
# Sites Info
$ADSiteObj = @()
$ADSites | ForEach-Object {
# Create the object for each instance.
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $_.Name
$Obj | Add-Member -MemberType NoteProperty -Name "Description" -Value $_.Description
$Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value $_.whenCreat
$Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value $_.whenCha
$ADSiteObj += $Obj
}
Remove-Variable ADSites
}
}
If ($Method -eq 'LDAP')
{
$SearchPath = "CN=Sites"
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainCon
}
Else
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$SearchPath,$
}
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$ObjSearcher.Filter = "(objectClass=site)"
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADSites = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRSite] Error while enumerating Site Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADSites)
{
Write-Verbose "[*] Total Sites: $([ADRecon.LDAPClass]::ObjectCount($ADSites))"
# Site Info
$ADSiteObj = @()
$ADSites | ForEach-Object {
# Create the object for each instance.
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $([string] $_.Propertie
$Obj | Add-Member -MemberType NoteProperty -Name "Description" -Value $([string] $_.Pro
$Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value ([DateTime] $
$Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value ([DateTime]
$ADSiteObj += $Obj
}
Remove-Variable ADSites
}
}
If ($ADSiteObj)
{
Return $ADSiteObj
}
Else
{
Return $null
}
}
Function Get-ADRSubnet
{
<#
.SYNOPSIS
Returns the Subnets of the current (or specified) domain.
.DESCRIPTION
Returns the Subnets of the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER objDomainRootDSE
[DirectoryServices.DirectoryEntry]
RootDSE Directory Entry object.
.PARAMETER DomainController
[string]
IP Address of the Domain Controller.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomainRootDSE,
[Parameter(Mandatory = $false)]
[string] $DomainController,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
)
If ($Method -eq 'ADWS')
{
Try
{
$SearchPath = "CN=Subnets,CN=Sites"
$ADSubnets = Get-ADObject -SearchBase "$SearchPath,$((Get-ADRootDSE).configurationNam
}
Catch
{
Write-Warning "[Get-ADRSubnet] Error while enumerating Subnet Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADSubnets)
{
Write-Verbose "[*] Total Subnets: $([ADRecon.ADWSClass]::ObjectCount($ADSubnets))"
# Subnets Info
$ADSubnetObj = @()
$ADSubnets | ForEach-Object {
# Create the object for each instance.
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Site" -Value $(($_.siteObject -Split "
$Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $_.Name
$Obj | Add-Member -MemberType NoteProperty -Name "Description" -Value $_.Description
$Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value $_.whenCreat
$Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value $_.whenCha
$ADSubnetObj += $Obj
}
Remove-Variable ADSubnets
}
}
If ($Method -eq 'LDAP')
{
$SearchPath = "CN=Subnets,CN=Sites"
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainCon
}
Else
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$SearchPath,$
}
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$ObjSearcher.Filter = "(objectClass=subnet)"
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADSubnets = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRSubnet] Error while enumerating Subnet Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADSubnets)
{
Write-Verbose "[*] Total Subnets: $([ADRecon.LDAPClass]::ObjectCount($ADSubnets))"
# Subnets Info
$ADSubnetObj = @()
$ADSubnets | ForEach-Object {
# Create the object for each instance.
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Site" -Value $((([string] $_.Propertie
$Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $([string] $_.Propertie
$Obj | Add-Member -MemberType NoteProperty -Name "Description" -Value $([string] $_.Pro
$Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value ([DateTime] $
$Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value ([DateTime]
$ADSubnetObj += $Obj
}
Remove-Variable ADSubnets
}
}
If ($ADSubnetObj)
{
Return $ADSubnetObj
}
Else
{
Return $null
}
}
# based on https://blogs.technet.microsoft.com/heyscriptingguy/2012/01/05/how-to-find-active-directory
Function Get-ADRSchemaHistory
{
<#
.SYNOPSIS
Returns the Schema History of the current (or specified) domain.
.DESCRIPTION
Returns the Schema History of the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER objDomainRootDSE
[DirectoryServices.DirectoryEntry]
RootDSE Directory Entry object.
.PARAMETER DomainController
[string]
IP Address of the Domain Controller.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomainRootDSE,
[Parameter(Mandatory = $false)]
[string] $DomainController,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
)
If ($Method -eq 'ADWS')
{
Try
{
$ADSchemaHistory = @( Get-ADObject -SearchBase ((Get-ADRootDSE).schemaNamingConte
}
Catch
{
Write-Warning "[Get-ADRSchemaHistory] Error while enumerating Schema Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADSchemaHistory)
{
Write-Verbose "[*] Total Schema Objects: $([ADRecon.ADWSClass]::ObjectCount($ADSchema
$ADSchemaObj = [ADRecon.ADWSClass]::SchemaParser($ADSchemaHistory, $Threads)
Remove-Variable ADSchemaHistory
}
}
If ($Method -eq 'LDAP')
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainCon
}
Else
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($objDomain
}
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$ObjSearcher.Filter = "(objectClass=*)"
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","name","objectclass","whenchan
$ObjSearcher.SearchScope = "OneLevel"
Try
{
$ADSchemaHistory = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRSchemaHistory] Error while enumerating Schema Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADSchemaHistory)
{
Write-Verbose "[*] Total Schema Objects: $([ADRecon.LDAPClass]::ObjectCount($ADSchemaH
$ADSchemaObj = [ADRecon.LDAPClass]::SchemaParser($ADSchemaHistory, $Threads)
Remove-Variable ADSchemaHistory
}
}
If ($ADSchemaObj)
{
Return $ADSchemaObj
}
Else
{
Return $null
}
}
Function Get-ADRDefaultPasswordPolicy
{
<#
.SYNOPSIS
Returns the Default Password Policy of the current (or specified) domain.
.DESCRIPTION
Returns the Default Password Policy of the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain
)
If ($Method -eq 'ADWS')
{
Try
{
$ADpasspolicy = Get-ADDefaultDomainPasswordPolicy
}
Catch
{
Write-Warning "[Get-ADRDefaultPasswordPolicy] Error while enumerating the Default Password
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADpasspolicy)
{
$ObjValues = @( "Enforce password history (passwords)", $ADpasspolicy.PasswordHistoryCou
"Maximum password age (days)", $ADpasspolicy.MaxPasswordAge.days, "90", "90", "Req. 8.2.
"Minimum password age (days)", $ADpasspolicy.MinPasswordAge.days, "N/A", "N/A", "-", "N/A"
"Minimum password length (characters)", $ADpasspolicy.MinPasswordLength, "7", "12", "Req. 8
"Password must meet complexity requirements", $ADpasspolicy.ComplexityEnabled, $true, $tru
"Store password using reversible encryption for all users in the domain", $ADpasspolicy.Revers
"Account lockout duration (mins)", $ADpasspolicy.LockoutDuration.minutes, "0 (manual unlock)
"Account lockout threshold (attempts)", $ADpasspolicy.LockoutThreshold, "1 to 6", "1 to 10", "Re
"Reset account lockout counter after (mins)", $ADpasspolicy.LockoutObservationWindow.minut
Remove-Variable ADpasspolicy
}
}
If ($Method -eq 'LDAP')
{
If ($ObjDomain)
{
#Value taken from https://msdn.microsoft.com/en-us/library/ms679431(v=vs.85).aspx
$pwdProperties = @{
"DOMAIN_PASSWORD_COMPLEX" = 1;
"DOMAIN_PASSWORD_NO_ANON_CHANGE" = 2;
"DOMAIN_PASSWORD_NO_CLEAR_CHANGE" = 4;
"DOMAIN_LOCKOUT_ADMINS" = 8;
"DOMAIN_PASSWORD_STORE_CLEARTEXT" = 16;
"DOMAIN_REFUSE_PASSWORD_CHANGE" = 32
}
If (($ObjDomain.pwdproperties.value -band $pwdProperties["DOMAIN_PASSWORD_COMPLE
{
$ComplexPasswords = $true
}
Else
{
$ComplexPasswords = $false
}
If (($ObjDomain.pwdproperties.value -band $pwdProperties["DOMAIN_PASSWORD_STORE_C
{
$ReversibleEncryption = $true
}
Else
{
$ReversibleEncryption = $false
}
$LockoutDuration = $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.lockoutduration.va
If ($LockoutDuration -gt 99999)
{
$LockoutDuration = 0
}
$ObjValues = @( "Enforce password history (passwords)", $ObjDomain.PwdHistoryLength.valu
"Maximum password age (days)", $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.m
"Minimum password age (days)", $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.minp
"Minimum password length (characters)", $ObjDomain.MinPwdLength.value, "7", "12", "Req. 8.2
"Password must meet complexity requirements", $ComplexPasswords, $true, $true, "Req. 8.2.3
"Store password using reversible encryption for all users in the domain", $ReversibleEncryption
"Account lockout duration (mins)", $LockoutDuration, "0 (manual unlock) or 30", "0 (manual unlo
"Account lockout threshold (attempts)", $ObjDomain.LockoutThreshold.value, "1 to 6", "1 to 10",
"Reset account lockout counter after (mins)", $($ObjDomain.ConvertLargeIntegerToInt64($ObjD
Remove-Variable pwdProperties
Remove-Variable ComplexPasswords
Remove-Variable ReversibleEncryption
}
}
If ($ObjValues)
{
$ADPassPolObj = @()
For ($i = 0; $i -lt $($ObjValues.Count); $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Policy" -Value $ObjValues[$i]
$Obj | Add-Member -MemberType NoteProperty -Name "Current Value" -Value $ObjValues[$i+1
$Obj | Add-Member -MemberType NoteProperty -Name "PCI DSS v3.2.1" -Value $ObjValues[$i
$Obj | Add-Member -MemberType NoteProperty -Name "PCI DSS v4.0" -Value $ObjValues[$i+3
$Obj | Add-Member -MemberType NoteProperty -Name "PCI DSS Requirement" -Value $ObjVa
$Obj | Add-Member -MemberType NoteProperty -Name "ACSC ISM" -Value $ObjValues[$i+5]
$Obj | Add-Member -MemberType NoteProperty -Name "ISM Controls 16Jun2022" -Value $Obj
$Obj | Add-Member -MemberType NoteProperty -Name "CIS Benchmark 2022" -Value $ObjVal
$i += 7
$ADPassPolObj += $Obj
}
Remove-Variable ObjValues
Return $ADPassPolObj
}
Else
{
Return $null
}
}
Function Get-ADRFineGrainedPasswordPolicy
{
<#
.SYNOPSIS
Returns the Fine Grained Password Policy of the current (or specified) domain.
.DESCRIPTION
Returns the Fine Grained Password Policy of the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain
)
If ($Method -eq 'ADWS')
{
Try
{
$ADFinepasspolicy = Get-ADFineGrainedPasswordPolicy -Filter *
}
Catch
{
Write-Warning "[Get-ADRFineGrainedPasswordPolicy] Error while enumerating the Fine Graine
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADFinepasspolicy)
{
$FgppPassPolObj = @()
$ADFinepasspolicy | ForEach-Object {
$AppliesTo = ""
$AppliesTo = $_.AppliesTo -join ", "
$FgppValues = [ordered]@{
"Name" = $($_.Name)
"Applies To" = $AppliesTo
"Enforce password history" = $_.PasswordHistoryCount
"Maximum password age (days)" = $_.MaxPasswordAge.days
"Minimum password age (days)" = $_.MinPasswordAge.days
"Minimum password length" = $_.MinPasswordLength
"Password must meet complexity requirements" = $_.ComplexityEnabled
"Store password using reversible encryption" = $_.ReversibleEncryptionEnabled
"Account lockout duration (mins)" = $_.LockoutDuration.minutes
"Account lockout threshold" = $_.LockoutThreshold
"Reset account lockout counter after (mins)" = $_.LockoutObservationWindow.minutes
"Precedence" = $($_.Precedence)
}
$FgppObj = New-Object -TypeName PsObject -Property $FgppValues
$FgppPassPolObj += $FgppObj
}
Remove-Variable ADFinepasspolicy
}
}
If ($Method -eq 'LDAP')
{
If ($ObjDomain)
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectClass=msDS-PasswordSettings)"
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADFinepasspolicy = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRFineGrainedPasswordPolicy] Error while enumerating the Fine Grai
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADFinepasspolicy)
{
If ([ADRecon.LDAPClass]::ObjectCount($ADFinepasspolicy) -ge 1)
{
$FgppPassPolObj = @()
$ADFinepasspolicy | ForEach-Object {
$AppliesTo = ""
$AppliesTo = $_.Properties.'msds-psoappliesto' -join ", "
$FgppValues = [ordered]@{
"Name" = $($_.Properties.name)
"Applies To" = $AppliesTo
"Enforce password history" = $($_.Properties.'msds-passwordhistorylength
"Maximum password age (days)" = $($($_.Properties.'msds-maximumpassw
"Minimum password age (days)" = $($($_.Properties.'msds-minimumpasswo
"Minimum password length" = $($_.Properties.'msds-minimumpasswordle
"Password must meet complexity requirements" = $($_.Properties.'msds-passwordco
"Store password using reversible encryption" = $($_.Properties.'msds-passwordrevers
"Account lockout duration (mins)" = $($($_.Properties.'msds-lockoutduration')/-
"Account lockout threshold" = $($_.Properties.'msds-lockoutthreshold')
"Reset account lockout counter after (mins)" = $($($_.Properties.'msds-lockoutobserv
"Precedence" = $($_.Properties.'msds-passwordsettingspreceden
}
$FgppObj = New-Object -TypeName PsObject -Property $FgppValues
$FgppPassPolObj += $FgppObj
}
}
Remove-Variable ADFinepasspolicy
}
}
}
If ($FgppPassPolObj)
{
Return $FgppPassPolObj
}
Else
{
Return $null
}
}
Function Get-ADRDomainController
{
<#
.SYNOPSIS
Returns the domain controllers for the current (or specified) forest.
.DESCRIPTION
Returns the domain controllers for the current (or specified) forest.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
)
If ($Method -eq 'ADWS')
{
Try
{
$ADDomainControllers = @( Get-ADDomainController -Filter * )
}
Catch
{
Write-Warning "[Get-ADRDomainController] Error while enumerating DomainController Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
# DC Info
If ($ADDomainControllers)
{
Write-Verbose "[*] Total Domain Controllers: $([ADRecon.ADWSClass]::ObjectCount($ADDoma
$DCObj = [ADRecon.ADWSClass]::DomainControllerParser($ADDomainControllers, $Threads)
Remove-Variable ADDomainControllers
}
}
If ($Method -eq 'LDAP')
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Do
Try
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainCon
}
Catch
{
Write-Warning "[Get-ADRDomainController] Error getting Domain Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
Remove-Variable DomainContext
}
Else
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
}
If ($ADDomain.DomainControllers)
{
Write-Verbose "[*] Total Domain Controllers: $([ADRecon.LDAPClass]::ObjectCount($ADDomai
$DCObj = [ADRecon.LDAPClass]::DomainControllerParser($ADDomain.DomainControllers, $T
Remove-Variable ADDomain
}
}
If ($DCObj)
{
Return $DCObj
}
Else
{
Return $null
}
}
Function Get-ADRUser
{
<#
.SYNOPSIS
Returns all users and/or service principal name (SPN) in the current (or specified) domain.
.DESCRIPTION
Returns all users and/or service principal name (SPN) in the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER date
[DateTime]
Date when ADRecon was executed.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER DormantTimeSpan
[int]
Timespan for Dormant accounts. Default 90 days.
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.PARAMETER Threads
[int]
The number of threads to use during processing of objects. Default 10.
.PARAMETER ADRUsers
[bool]
.PARAMETER ADRUserSPNs
[bool]
.PARAMETER OnlyEnabled
[bool]
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $true)]
[DateTime] $date,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $DormantTimeSpan = 90,
[Parameter(Mandatory = $true)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false)]
[int] $Threads = 10,
[Parameter(Mandatory = $false)]
[int] $ADRUsers = $true,
[Parameter(Mandatory = $false)]
[int] $ADRUserSPNs = $false,
[Parameter(Mandatory = $false)]
[int] $OnlyEnabled = $false
)
If ($Method -eq 'ADWS')
{
If (!$ADRUsers)
{
Try
{
If ($OnlyEnabled)
{
$ADUsers = @( Get-ADObject -LDAPFilter "(&(samAccountType=805306368)(servicePrin
}
Else
{
$ADUsers = @( Get-ADObject -LDAPFilter "(&(samAccountType=805306368)(servicePrin
}
}
Catch
{
Write-Warning "[Get-ADRUser] Error while enumerating UserSPN Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
}
Else
{
Try
{
If ($OnlyEnabled)
{
$ADUsers = @( Get-ADUser -Filter 'enabled -eq $true' -ResultPageSize $PageSize -Prope
dEncryptionTypes',Name,PasswordExpired,PasswordLastSet,PasswordNeverExpires,PasswordNotReq
}
Else
{
$ADUsers = @( Get-ADUser -Filter * -ResultPageSize $PageSize -Properties AccountExp
Name,PasswordExpired,PasswordLastSet,PasswordNeverExpires,PasswordNotRequired,primaryGrou
}
}
Catch
{
Write-Warning "[Get-ADRUser] Error while enumerating User Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
}
If ($ADUsers)
{
Write-Verbose "[*] Total Users: $([ADRecon.ADWSClass]::ObjectCount($ADUsers))"
If ($ADRUsers)
{
Try
{
$ADpasspolicy = Get-ADDefaultDomainPasswordPolicy
$PassMaxAge = $ADpasspolicy.MaxPasswordAge.days
Remove-Variable ADpasspolicy
}
Catch
{
Write-Warning "[Get-ADRUser] Error retrieving Max Password Age from the Default Passw
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
$PassMaxAge = 90
}
$UserObj = [ADRecon.ADWSClass]::UserParser($ADUsers, $date, $DormantTimeSpan, $Pa
}
If ($ADRUserSPNs)
{
$UserSPNObj = [ADRecon.ADWSClass]::UserSPNParser($ADUsers, $Threads)
}
Remove-Variable ADUsers
}
}
If ($Method -eq 'LDAP')
{
If (!$ADRUsers)
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
If ($OnlyEnabled)
{
$ObjSearcher.Filter = "(&(samAccountType=805306368)(servicePrincipalName=*)(!userAcco
}
Else
{
$ObjSearcher.Filter = "(&(samAccountType=805306368)(servicePrincipalName=*))"
}
$ObjSearcher.PropertiesToLoad.AddRange(("name","description","memberof","samaccountnam
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADUsers = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRUser] Error while enumerating UserSPN Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
}
Else
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
If ($OnlyEnabled)
{
$ObjSearcher.Filter = "(&(samAccountType=805306368)(!userAccountControl:1.2.840.11355
}
Else
{
$ObjSearcher.Filter = "(samAccountType=805306368)"
}
# https://msdn.microsoft.com/en-us/library/system.directoryservices.securitymasks(v=vs.110)
$ObjSearcher.SecurityMasks = [System.DirectoryServices.SecurityMasks]'Dacl'
$ObjSearcher.PropertiesToLoad.AddRange(("accountExpires","admincount","c","canonicalname
tcontrol","userworkstations","whenchanged","whencreated"))
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADUsers = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRUser] Error while enumerating User Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
}
If ($ADUsers)
{
Write-Verbose "[*] Total Users: $([ADRecon.LDAPClass]::ObjectCount($ADUsers))"
If ($ADRUsers)
{
$PassMaxAge = $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.maxpwdage.value
If (-Not $PassMaxAge)
{
Write-Warning "[Get-ADRUser] Error retrieving Max Password Age from the Default Passw
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
$PassMaxAge = 90
}
$UserObj = [ADRecon.LDAPClass]::UserParser($ADUsers, $date, $DormantTimeSpan, $Pas
}
If ($ADRUserSPNs)
{
$UserSPNObj = [ADRecon.LDAPClass]::UserSPNParser($ADUsers, $Threads)
}
Remove-Variable ADUsers
}
}
If ($UserObj)
{
Export-ADR -ADRObj $UserObj -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRM
Remove-Variable UserObj
}
If ($UserSPNObj)
{
Export-ADR -ADRObj $UserSPNObj -ADROutputDir $ADROutputDir -OutputType $OutputType -A
Remove-Variable UserSPNObj
}
}
#TODO
Function Get-ADRPasswordAttributes
{
<#
.SYNOPSIS
Returns all objects with plaintext passwords in the current (or specified) domain.
.DESCRIPTION
Returns all objects with plaintext passwords in the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.OUTPUTS
PSObject.
.LINK
https://www.ibm.com/support/knowledgecenter/en/ssw_aix_71/com.ibm.aix.security/ad_password_at
https://msdn.microsoft.com/en-us/library/cc223248.aspx
https://msdn.microsoft.com/en-us/library/cc223249.aspx
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize
)
If ($Method -eq 'ADWS')
{
Try
{
$ADUsers = Get-ADObject -LDAPFilter '(|(UserPassword=*)(UnixUserPassword=*)(unicodePwd
}
Catch
{
Write-Warning "[Get-ADRPasswordAttributes] Error while enumerating Password Attributes"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADUsers)
{
Write-Warning "[*] Total PasswordAttribute Objects: $([ADRecon.ADWSClass]::ObjectCount($A
$UserObj = $ADUsers
Remove-Variable ADUsers
}
}
If ($Method -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(|(UserPassword=*)(UnixUserPassword=*)(unicodePwd=*)(msSFU30Passw
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADUsers = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRPasswordAttributes] Error while enumerating Password Attributes"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADUsers)
{
$cnt = [ADRecon.LDAPClass]::ObjectCount($ADUsers)
If ($cnt -gt 0)
{
Write-Warning "[*] Total PasswordAttribute Objects: $cnt"
}
$UserObj = $ADUsers
Remove-Variable ADUsers
}
}
If ($UserObj)
{
Return $UserObj
}
Else
{
Return $null
}
}
Function Get-ADRGroup
{
<#
.SYNOPSIS
Returns all groups and/or membership changes in the current (or specified) domain.
.DESCRIPTION
Returns all groups and/or membership changes in the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER date
[DateTime]
Date when ADRecon was executed.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.PARAMETER Threads
[int]
The number of threads to use during processing of objects. Default 10.
.PARAMETER ADROutputDir
[string]
Path for ADRecon output folder.
.PARAMETER OutputType
[array]
Output Type.
.PARAMETER ADRGroups
[bool]
.PARAMETER ADRGroupChanges
[bool]
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $true)]
[DateTime] $date,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false)]
[int] $Threads = 10,
[Parameter(Mandatory = $true)]
[string] $ADROutputDir,
[Parameter(Mandatory = $true)]
[array] $OutputType,
[Parameter(Mandatory = $false)]
[bool] $ADRGroups = $true,
[Parameter(Mandatory = $false)]
[bool] $ADRGroupChanges = $false
)
If ($Method -eq 'ADWS')
{
Try
{
$ADGroups = @( Get-ADGroup -Filter * -ResultPageSize $PageSize -Properties AdminCount,C
}
Catch
{
Write-Warning "[Get-ADRGroup] Error while enumerating Group Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADGroups)
{
Write-Verbose "[*] Total Groups: $([ADRecon.ADWSClass]::ObjectCount($ADGroups))"
If ($ADRGroups)
{
$GroupObj = [ADRecon.ADWSClass]::GroupParser($ADGroups, $Threads)
}
If ($ADRGroupChanges)
{
$GroupChangesObj = [ADRecon.ADWSClass]::GroupChangeParser($ADGroups, $date, $Th
}
Remove-Variable ADGroups
Remove-Variable ADRGroups
Remove-Variable ADRGroupChanges
}
}
If ($Method -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectClass=group)"
$ObjSearcher.PropertiesToLoad.AddRange(("admincount","canonicalname", "distinguishedname"
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADGroups = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRGroup] Error while enumerating Group Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADGroups)
{
Write-Verbose "[*] Total Groups: $([ADRecon.LDAPClass]::ObjectCount($ADGroups))"
If ($ADRGroups)
{
$GroupObj = [ADRecon.LDAPClass]::GroupParser($ADGroups, $Threads)
}
If ($ADRGroupChanges)
{
$GroupChangesObj = [ADRecon.LDAPClass]::GroupChangeParser($ADGroups, $date, $Thr
}
Remove-Variable ADGroups
Remove-Variable ADRGroups
Remove-Variable ADRGroupChanges
}
}
If ($GroupObj)
{
Export-ADR -ADRObj $GroupObj -ADROutputDir $ADROutputDir -OutputType $OutputType -ADR
Remove-Variable GroupObj
}
If ($GroupChangesObj)
{
Export-ADR -ADRObj $GroupChangesObj -ADROutputDir $ADROutputDir -OutputType $OutputT
Remove-Variable GroupChangesObj
}
}
Function Get-ADRGroupMember
{
<#
.SYNOPSIS
Returns all groups and their members in the current (or specified) domain.
.DESCRIPTION
Returns all groups and their members in the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.PARAMETER Threads
[int]
The number of threads to use during processing of objects. Default 10.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Method -eq 'ADWS')
{
Try
{
$ADDomain = Get-ADDomain
$ADDomainSID = $ADDomain.DomainSID.Value
Remove-Variable ADDomain
}
Catch
{
Write-Warning "[Get-ADRGroupMember] Error getting Domain Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
Try
{
$ADGroups = $ADGroups = @( Get-ADGroup -Filter * -ResultPageSize $PageSize -Properties
}
Catch
{
Write-Warning "[Get-ADRGroupMember] Error while enumerating Group Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
Try
{
$ADGroupMembers = @( Get-ADObject -LDAPFilter '(|(memberof=*)(primarygroupid=*))' -Prope
}
Catch
{
Write-Warning "[Get-ADRGroupMember] Error while enumerating GroupMember Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ( ($ADDomainSID) -and ($ADGroups) -and ($ADGroupMembers) )
{
Write-Verbose "[*] Total GroupMember Objects: $([ADRecon.ADWSClass]::ObjectCount($ADGr
$GroupMemberObj = [ADRecon.ADWSClass]::GroupMemberParser($ADGroups, $ADGroupMe
Remove-Variable ADGroups
Remove-Variable ADGroupMembers
Remove-Variable ADDomainSID
}
}
If ($Method -eq 'LDAP')
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Do
Try
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainCon
}
Catch
{
Write-Warning "[Get-ADRGroupMember] Error getting Domain Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
Remove-Variable DomainContext
Try
{
$ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Fo
$ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
}
Catch
{
Write-Warning "[Get-ADRGroupMember] Error getting Forest Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($ForestContext)
{
Remove-Variable ForestContext
}
If ($ADForest)
{
$GlobalCatalog = $ADForest.FindGlobalCatalog()
}
If ($GlobalCatalog)
{
$DN = "GC://$($GlobalCatalog.IPAddress)/$($objDomain.distinguishedname)"
Try
{
$ADObject = New-Object -TypeName System.DirectoryServices.DirectoryEntry -Argument
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($ADObject.obje
$ADObject.Dispose()
}
Catch
{
Write-Warning "[Get-ADRGroupMember] Error retrieving Domain SID using the GlobalCata
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.obj
}
}
Else
{
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objec
}
}
Else
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
Try
{
$GlobalCatalog = $ADForest.FindGlobalCatalog()
$DN = "GC://$($GlobalCatalog)/$($objDomain.distinguishedname)"
$ADObject = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentLis
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($ADObject.objectS
$ADObject.dispose()
}
Catch
{
Write-Warning "[Get-ADRGroupMember] Error retrieving Domain SID using the GlobalCatalo
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objec
}
}
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectClass=group)"
$ObjSearcher.PropertiesToLoad.AddRange(("samaccountname", "objectsid"))
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADGroups = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRGroupMember] Error while enumerating Group Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(|(memberof=*)(primarygroupid=*))"
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname", "dnshostname", "objectclass", "
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADGroupMembers = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRGroupMember] Error while enumerating GroupMember Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ( ($ADDomainSID) -and ($ADGroups) -and ($ADGroupMembers) )
{
Write-Verbose "[*] Total GroupMember Objects: $([ADRecon.LDAPClass]::ObjectCount($ADGro
$GroupMemberObj = [ADRecon.LDAPClass]::GroupMemberParser($ADGroups, $ADGroupMem
Remove-Variable ADGroups
Remove-Variable ADGroupMembers
Remove-Variable ADDomainSID
}
}
If ($GroupMemberObj)
{
Return $GroupMemberObj
}
Else
{
Return $null
}
}
Function Get-ADROU
{
<#
.SYNOPSIS
Returns all Organizational Units (OU) in the current (or specified) domain.
.DESCRIPTION
Returns all Organizational Units (OU) in the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.PARAMETER Threads
[int]
The number of threads to use during processing of objects. Default 10.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Method -eq 'ADWS')
{
Try
{
$ADOUs = @( Get-ADOrganizationalUnit -Filter * -Properties DistinguishedName,Description,N
}
Catch
{
Write-Warning "[Get-ADROU] Error while enumerating OU Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADOUs)
{
Write-Verbose "[*] Total OUs: $([ADRecon.ADWSClass]::ObjectCount($ADOUs))"
$OUObj = [ADRecon.ADWSClass]::OUParser($ADOUs, $Threads)
Remove-Variable ADOUs
}
}
If ($Method -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectclass=organizationalunit)"
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","description","name","whencrea
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADOUs = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADROU] Error while enumerating OU Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADOUs)
{
Write-Verbose "[*] Total OUs: $([ADRecon.LDAPClass]::ObjectCount($ADOUs))"
$OUObj = [ADRecon.LDAPClass]::OUParser($ADOUs, $Threads)
Remove-Variable ADOUs
}
}
If ($OUObj)
{
Return $OUObj
}
Else
{
Return $null
}
}
Function Get-ADRGPO
{
<#
.SYNOPSIS
Returns all Group Policy Objects (GPO) in the current (or specified) domain.
.DESCRIPTION
Returns all Group Policy Objects (GPO) in the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.PARAMETER Threads
[int]
The number of threads to use during processing of objects. Default 10.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Method -eq 'ADWS')
{
Try
{
$ADGPOs = @( Get-ADObject -LDAPFilter '(objectCategory=groupPolicyContainer)' -Properties
}
Catch
{
Write-Warning "[Get-ADRGPO] Error while enumerating groupPolicyContainer Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADGPOs)
{
Write-Verbose "[*] Total GPOs: $([ADRecon.ADWSClass]::ObjectCount($ADGPOs))"
$GPOsObj = [ADRecon.ADWSClass]::GPOParser($ADGPOs, $Threads)
Remove-Variable ADGPOs
}
}
If ($Method -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectCategory=groupPolicyContainer)"
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADGPOs = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRGPO] Error while enumerating groupPolicyContainer Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADGPOs)
{
Write-Verbose "[*] Total GPOs: $([ADRecon.LDAPClass]::ObjectCount($ADGPOs))"
$GPOsObj = [ADRecon.LDAPClass]::GPOParser($ADGPOs, $Threads)
Remove-Variable ADGPOs
}
}
If ($GPOsObj)
{
Return $GPOsObj
}
Else
{
Return $null
}
}
# based on https://github.com/GoateePFE/GPLinkReport/blob/master/gPLinkReport.ps1
Function Get-ADRGPLink
{
<#
.SYNOPSIS
Returns all group policy links (gPLink) applied to Scope of Management (SOM) in the current (or spe
.DESCRIPTION
Returns all group policy links (gPLink) applied to Scope of Management (SOM) in the current (or spe
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.PARAMETER Threads
[int]
The number of threads to use during processing of objects. Default 10.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Method -eq 'ADWS')
{
Try
{
$ADSOMs = @( Get-ADObject -LDAPFilter '(|(objectclass=domain)(objectclass=organizationalU
$ADSOMs += @( Get-ADObject -SearchBase "CN=Sites,$((Get-ADRootDSE).configurationNam
}
Catch
{
Write-Warning "[Get-ADRGPLink] Error while enumerating SOM Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
Try
{
$ADGPOs = @( Get-ADObject -LDAPFilter '(objectCategory=groupPolicyContainer)' -Properties
}
Catch
{
Write-Warning "[Get-ADRGPLink] Error while enumerating groupPolicyContainer Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ( ($ADSOMs) -and ($ADGPOs) )
{
Write-Verbose "[*] Total SOMs: $([ADRecon.ADWSClass]::ObjectCount($ADSOMs))"
$SOMObj = [ADRecon.ADWSClass]::SOMParser($ADGPOs, $ADSOMs, $Threads)
Remove-Variable ADSOMs
Remove-Variable ADGPOs
}
}
If ($Method -eq 'LDAP')
{
$ADSOMs = @()
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(|(objectclass=domain)(objectclass=organizationalUnit))"
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","name","gplink","gpoptions"))
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADSOMs += $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRGPLink] Error while enumerating SOM Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
$SearchPath = "CN=Sites"
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainCon
}
Else
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$SearchPath,$
}
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$ObjSearcher.Filter = "(objectclass=site)"
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","name","gplink","gpoptions"))
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADSOMs += $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRGPLink] Error while enumerating SOM Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectCategory=groupPolicyContainer)"
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADGPOs = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRGPLink] Error while enumerating groupPolicyContainer Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ( ($ADSOMs) -and ($ADGPOs) )
{
Write-Verbose "[*] Total SOMs: $([ADRecon.LDAPClass]::ObjectCount($ADSOMs))"
$SOMObj = [ADRecon.LDAPClass]::SOMParser($ADGPOs, $ADSOMs, $Threads)
Remove-Variable ADSOMs
Remove-Variable ADGPOs
}
}
If ($SOMObj)
{
Return $SOMObj
}
Else
{
Return $null
}
}
# Modified Convert-DNSRecord function from https://github.com/PowerShellMafia/PowerSploit/blob/dev
Function Convert-DNSRecord
{
<#
.SYNOPSIS
Helpers that decodes a binary DNS record blob.
Author: Michael B. Smith, Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: None
.DESCRIPTION
Decodes a binary blob representing an Active Directory DNS entry.
Used by Get-DomainDNSRecord.
Adapted/ported from Michael B. Smith's code at https://raw.githubusercontent.com/mmessano/PowerSh
.PARAMETER DNSRecord
A byte array representing the DNS record.
.OUTPUTS
System.Management.Automation.PSCustomObject
Outputs custom PSObjects with detailed information about the DNS record entry.
.LINK
https://raw.githubusercontent.com/mmessano/PowerShell/master/dns-dump.ps1
#>
[OutputType('System.Management.Automation.PSCustomObject')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
[Byte[]]
$DNSRecord
)
BEGIN {
Function Get-Name
{
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '')]
[CmdletBinding()]
Param(
[Byte[]]
$Raw
)
[Int]$Length = $Raw[0]
[Int]$Segments = $Raw[1]
[Int]$Index = 2
[String]$Name = ''
while ($Segments-- -gt 0)
{
[Int]$SegmentLength = $Raw[$Index++]
while ($SegmentLength-- -gt 0)
{
$Name += [Char]$Raw[$Index++]
}
$Name += "."
}
$Name
}
}
PROCESS
{
# $RDataLen = [BitConverter]::ToUInt16($DNSRecord, 0)
$RDataType = [BitConverter]::ToUInt16($DNSRecord, 2)
$UpdatedAtSerial = [BitConverter]::ToUInt32($DNSRecord, 8)
$TTLRaw = $DNSRecord[12..15]
# reverse for big endian
$Null = [array]::Reverse($TTLRaw)
$TTL = [BitConverter]::ToUInt32($TTLRaw, 0)
$Age = [BitConverter]::ToUInt32($DNSRecord, 20)
If ($Age -ne 0)
{
$TimeStamp = ((Get-Date -Year 1601 -Month 1 -Day 1 -Hour 0 -Minute 0 -Second 0).AddHours
}
Else
{
$TimeStamp = '[static]'
}
$DNSRecordObject = New-Object PSObject
switch ($RDataType)
{
1
{
$IP = "{0}.{1}.{2}.{3}" -f $DNSRecord[24], $DNSRecord[25], $DNSRecord[26], $DNSRecord[2
$Data = $IP
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'A'
}
2
{
$NSName = Get-Name $DNSRecord[24..$DNSRecord.length]
$Data = $NSName
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'NS'
}
5
{
$Alias = Get-Name $DNSRecord[24..$DNSRecord.length]
$Data = $Alias
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'CNAME'
}
6
{
$PrimaryNS = Get-Name $DNSRecord[44..$DNSRecord.length]
$ResponsibleParty = Get-Name $DNSRecord[$(46+$DNSRecord[44])..$DNSRecord.length]
$SerialRaw = $DNSRecord[24..27]
# reverse for big endian
$Null = [array]::Reverse($SerialRaw)
$Serial = [BitConverter]::ToUInt32($SerialRaw, 0)
$RefreshRaw = $DNSRecord[28..31]
$Null = [array]::Reverse($RefreshRaw)
$Refresh = [BitConverter]::ToUInt32($RefreshRaw, 0)
$RetryRaw = $DNSRecord[32..35]
$Null = [array]::Reverse($RetryRaw)
$Retry = [BitConverter]::ToUInt32($RetryRaw, 0)
$ExpiresRaw = $DNSRecord[36..39]
$Null = [array]::Reverse($ExpiresRaw)
$Expires = [BitConverter]::ToUInt32($ExpiresRaw, 0)
$MinTTLRaw = $DNSRecord[40..43]
$Null = [array]::Reverse($MinTTLRaw)
$MinTTL = [BitConverter]::ToUInt32($MinTTLRaw, 0)
$Data = "[" + $Serial + "][" + $PrimaryNS + "][" + $ResponsibleParty + "][" + $Refresh + "][" +
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'SOA'
}
12
{
$Ptr = Get-Name $DNSRecord[24..$DNSRecord.length]
$Data = $Ptr
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'PTR'
}
13
{
[string]$CPUType = ""
[string]$OSType = ""
[int]$SegmentLength = $DNSRecord[24]
$Index = 25
while ($SegmentLength-- -gt 0)
{
$CPUType += [char]$DNSRecord[$Index++]
}
$Index = 24 + $DNSRecord[24] + 1
[int]$SegmentLength = $Index++
while ($SegmentLength-- -gt 0)
{
$OSType += [char]$DNSRecord[$Index++]
}
$Data = "[" + $CPUType + "][" + $OSType + "]"
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'HINFO'
}
15
{
$PriorityRaw = $DNSRecord[24..25]
# reverse for big endian
$Null = [array]::Reverse($PriorityRaw)
$Priority = [BitConverter]::ToUInt16($PriorityRaw, 0)
$MXHost = Get-Name $DNSRecord[26..$DNSRecord.length]
$Data = "[" + $Priority + "][" + $MXHost + "]"
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'MX'
}
16
{
[string]$TXT = ''
[int]$SegmentLength = $DNSRecord[24]
$Index = 25
while ($SegmentLength-- -gt 0)
{
$TXT += [char]$DNSRecord[$Index++]
}
$Data = $TXT
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'TXT'
}
28
{
■■### yeah, this doesn't do all the fancy formatting that can be done for IPv6
$AAAA = ""
for ($i = 24; $i -lt 40; $i+=2)
{
$BlockRaw = $DNSRecord[$i..$($i+1)]
# reverse for big endian
$Null = [array]::Reverse($BlockRaw)
$Block = [BitConverter]::ToUInt16($BlockRaw, 0)
■■■ $AAAA += ($Block).ToString('x4')
■■■ If ($i -ne 38)
{
$AAAA += ':'
}
}
$Data = $AAAA
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'AAAA'
}
33
{
$PriorityRaw = $DNSRecord[24..25]
# reverse for big endian
$Null = [array]::Reverse($PriorityRaw)
$Priority = [BitConverter]::ToUInt16($PriorityRaw, 0)
$WeightRaw = $DNSRecord[26..27]
$Null = [array]::Reverse($WeightRaw)
$Weight = [BitConverter]::ToUInt16($WeightRaw, 0)
$PortRaw = $DNSRecord[28..29]
$Null = [array]::Reverse($PortRaw)
$Port = [BitConverter]::ToUInt16($PortRaw, 0)
$SRVHost = Get-Name $DNSRecord[30..$DNSRecord.length]
$Data = "[" + $Priority + "][" + $Weight + "][" + $Port + "][" + $SRVHost + "]"
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'SRV'
}
default
{
$Data = $([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'UNKNOWN'
}
}
$DNSRecordObject | Add-Member Noteproperty 'UpdatedAtSerial' $UpdatedAtSerial
$DNSRecordObject | Add-Member Noteproperty 'TTL' $TTL
$DNSRecordObject | Add-Member Noteproperty 'Age' $Age
$DNSRecordObject | Add-Member Noteproperty 'TimeStamp' $TimeStamp
$DNSRecordObject | Add-Member Noteproperty 'Data' $Data
Return $DNSRecordObject
}
}
Function Get-ADRDNSZone
{
<#
.SYNOPSIS
Returns all DNS Zones and Records in the current (or specified) domain.
.DESCRIPTION
Returns all DNS Zones and Records in the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER DomainController
[string]
IP Address of the Domain Controller.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.PARAMETER ADROutputDir
[string]
Path for ADRecon output folder.
.PARAMETER OutputType
[array]
Output Type.
.PARAMETER ADRDNSZones
[bool]
.PARAMETER ADRDNSRecords
[bool]
.OUTPUTS
CSV files are created in the folder specified with the information.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[string] $DomainController,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
[Parameter(Mandatory = $true)]
[int] $PageSize = 200,
[Parameter(Mandatory = $true)]
[string] $ADROutputDir,
[Parameter(Mandatory = $true)]
[array] $OutputType,
[Parameter(Mandatory = $false)]
[bool] $ADRDNSZones = $true,
[Parameter(Mandatory = $false)]
[bool] $ADRDNSRecords = $false
)
If ($Method -eq 'ADWS')
{
Try
{
$ADDNSZones = Get-ADObject -LDAPFilter '(objectClass=dnsZone)' -Properties Name,whenC
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating dnsZone Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$DNSZoneArray = @()
If ($ADDNSZones)
{
$DNSZoneArray += $ADDNSZones
Remove-Variable ADDNSZones
}
Try
{
$ADDomain = Get-ADDomain
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error getting Domain Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
Try
{
$ADDNSZones1 = Get-ADObject -LDAPFilter '(objectClass=dnsZone)' -SearchBase "DC=Doma
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating DC=DomainDnsZones,$($ADDom
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($ADDNSZones1)
{
$DNSZoneArray += $ADDNSZones1
Remove-Variable ADDNSZones1
}
Try
{
$ADDNSZones2 = Get-ADObject -LDAPFilter '(objectClass=dnsZone)' -SearchBase "DC=Fores
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating DC=ForestDnsZones,DC=$($ADD
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($ADDNSZones2)
{
$DNSZoneArray += $ADDNSZones2
Remove-Variable ADDNSZones2
}
If ($ADDomain)
{
Remove-Variable ADDomain
}
Write-Verbose "[*] Total DNS Zones: $([ADRecon.ADWSClass]::ObjectCount($DNSZoneArray))"
If ($DNSZoneArray)
{
$ADDNSZonesObj = @()
$ADDNSNodesObj = @()
$DNSZoneArray | ForEach-Object {
# Create the object for each instance.
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name Name -Value $([ADRecon.ADWSCla
Try
{
$DNSNodes = Get-ADObject -SearchBase $($_.DistinguishedName) -LDAPFilter '(objectC
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating $($_.DistinguishedName) dns
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($DNSNodes)
{
$Obj | Add-Member -MemberType NoteProperty -Name RecordCount -Value $($DNSNode
$DNSNodes | ForEach-Object {
$ObjNode = New-Object PSObject
$ObjNode | Add-Member -MemberType NoteProperty -Name ZoneName -Value $Obj.N
$ObjNode | Add-Member -MemberType NoteProperty -Name Name -Value $_.Name
Try
{
$DNSRecord = Convert-DNSRecord $_.dnsrecord[0]
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while converting the DNSRecord"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$ObjNode | Add-Member -MemberType NoteProperty -Name RecordType -Value $DNS
$ObjNode | Add-Member -MemberType NoteProperty -Name Data -Value $DNSRecord
$ObjNode | Add-Member -MemberType NoteProperty -Name TTL -Value $DNSRecord.T
$ObjNode | Add-Member -MemberType NoteProperty -Name Age -Value $DNSRecord.A
$ObjNode | Add-Member -MemberType NoteProperty -Name TimeStamp -Value $DNSR
$ObjNode | Add-Member -MemberType NoteProperty -Name UpdatedAtSerial -Value $D
$ObjNode | Add-Member -MemberType NoteProperty -Name whenCreated -Value $_.w
$ObjNode | Add-Member -MemberType NoteProperty -Name whenChanged -Value $_.w
# TO DO LDAP part
#$ObjNode | Add-Member -MemberType NoteProperty -Name dNSTombstoned -Value
#$ObjNode | Add-Member -MemberType NoteProperty -Name ProtectedFromAccidenta
$ObjNode | Add-Member -MemberType NoteProperty -Name showInAdvancedViewOnly
$ObjNode | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value
$ADDNSNodesObj += $ObjNode
If ($DNSRecord)
{
Remove-Variable DNSRecord
}
}
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name RecordCount -Value $null
}
$Obj | Add-Member -MemberType NoteProperty -Name USNCreated -Value $_.usncreated
$Obj | Add-Member -MemberType NoteProperty -Name USNChanged -Value $_.usnchanged
$Obj | Add-Member -MemberType NoteProperty -Name whenCreated -Value $_.whenCreate
$Obj | Add-Member -MemberType NoteProperty -Name whenChanged -Value $_.whenChan
$Obj | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value $_.Disting
$ADDNSZonesObj += $Obj
}
Write-Verbose "[*] Total DNS Records: $([ADRecon.ADWSClass]::ObjectCount($ADDNSNodes
Remove-Variable DNSZoneArray
}
}
If ($Method -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.PropertiesToLoad.AddRange(("name","whencreated","whenchanged","usncreated",
$ObjSearcher.Filter = "(objectClass=dnsZone)"
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADDNSZones = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating dnsZone Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$ObjSearcher.dispose()
$DNSZoneArray = @()
If ($ADDNSZones)
{
$DNSZoneArray += $ADDNSZones
Remove-Variable ADDNSZones
}
$SearchPath = "DC=DomainDnsZones"
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainCon
}
Else
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($SearchPath
}
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$objSearcherPath.Filter = "(objectClass=dnsZone)"
$objSearcherPath.PageSize = $PageSize
$objSearcherPath.PropertiesToLoad.AddRange(("name","whencreated","whenchanged","usncrea
$objSearcherPath.SearchScope = "Subtree"
Try
{
$ADDNSZones1 = $objSearcherPath.FindAll()
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating $($SearchPath),$($objDomain.dis
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$objSearcherPath.dispose()
If ($ADDNSZones1)
{
$DNSZoneArray += $ADDNSZones1
Remove-Variable ADDNSZones1
}
$SearchPath = "DC=ForestDnsZones"
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Do
Try
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainCon
}
Catch
{
Write-Warning "[Get-ADRForest] Error getting Domain Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
Remove-Variable DomainContext
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainCon
}
Else
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($SearchPath
}
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$objSearcherPath.Filter = "(objectClass=dnsZone)"
$objSearcherPath.PageSize = $PageSize
$objSearcherPath.PropertiesToLoad.AddRange(("name","whencreated","whenchanged","usncrea
$objSearcherPath.SearchScope = "Subtree"
Try
{
$ADDNSZones2 = $objSearcherPath.FindAll()
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating $($SearchPath),DC=$($ADDomai
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$objSearcherPath.dispose()
If ($ADDNSZones2)
{
$DNSZoneArray += $ADDNSZones2
Remove-Variable ADDNSZones2
}
If($ADDomain)
{
Remove-Variable ADDomain
}
Write-Verbose "[*] Total DNS Zones: $([ADRecon.LDAPClass]::ObjectCount($DNSZoneArray))"
If ($DNSZoneArray)
{
$ADDNSZonesObj = @()
$ADDNSNodesObj = @()
$DNSZoneArray | ForEach-Object {
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($Doma
}
Else
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($_.Prop
}
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPat
$objSearcherPath.Filter = "(objectClass=dnsNode)"
$objSearcherPath.PageSize = $PageSize
$objSearcherPath.PropertiesToLoad.AddRange(("distinguishedname","dnsrecord","name","d
Try
{
$DNSNodes = $objSearcherPath.FindAll()
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating $($_.Properties.distinguished
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$objSearcherPath.dispose()
Remove-Variable objSearchPath
# Create the object for each instance.
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name Name -Value $([ADRecon.LDAPClas
If ($DNSNodes)
{
$Obj | Add-Member -MemberType NoteProperty -Name RecordCount -Value $($DNSNode
$DNSNodes | ForEach-Object {
$ObjNode = New-Object PSObject
$ObjNode | Add-Member -MemberType NoteProperty -Name ZoneName -Value $Obj.N
$name = ([string] $($_.Properties.name))
If (-Not $name)
{
$name = ([string] $($_.Properties.dc))
}
$ObjNode | Add-Member -MemberType NoteProperty -Name Name -Value $name
Try
{
$DNSRecord = Convert-DNSRecord $_.Properties.dnsrecord[0]
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while converting the DNSRecord"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$ObjNode | Add-Member -MemberType NoteProperty -Name RecordType -Value $DNS
$ObjNode | Add-Member -MemberType NoteProperty -Name Data -Value $DNSRecord
$ObjNode | Add-Member -MemberType NoteProperty -Name TTL -Value $DNSRecord.T
$ObjNode | Add-Member -MemberType NoteProperty -Name Age -Value $DNSRecord.A
$ObjNode | Add-Member -MemberType NoteProperty -Name TimeStamp -Value $DNSR
$ObjNode | Add-Member -MemberType NoteProperty -Name UpdatedAtSerial -Value $D
$ObjNode | Add-Member -MemberType NoteProperty -Name whenCreated -Value ([Dat
$ObjNode | Add-Member -MemberType NoteProperty -Name whenChanged -Value ([Da
# TO DO
#$ObjNode | Add-Member -MemberType NoteProperty -Name dNSTombstoned -Value
#$ObjNode | Add-Member -MemberType NoteProperty -Name ProtectedFromAccidenta
$ObjNode | Add-Member -MemberType NoteProperty -Name showInAdvancedViewOnly
$ObjNode | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value
$ADDNSNodesObj += $ObjNode
If ($DNSRecord)
{
Remove-Variable DNSRecord
}
}
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name RecordCount -Value $null
}
$Obj | Add-Member -MemberType NoteProperty -Name USNCreated -Value ([string] $($_.Pro
$Obj | Add-Member -MemberType NoteProperty -Name USNChanged -Value ([string] $($_.P
$Obj | Add-Member -MemberType NoteProperty -Name whenCreated -Value ([DateTime] $($
$Obj | Add-Member -MemberType NoteProperty -Name whenChanged -Value ([DateTime] $(
$Obj | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value ([string] $
$ADDNSZonesObj += $Obj
}
Write-Verbose "[*] Total DNS Records: $([ADRecon.LDAPClass]::ObjectCount($ADDNSNodesO
Remove-Variable DNSZoneArray
}
}
If ($ADDNSZonesObj -and $ADRDNSZones)
{
Export-ADR $ADDNSZonesObj $ADROutputDir $OutputType "DNSZones"
Remove-Variable ADDNSZonesObj
}
If ($ADDNSNodesObj -and $ADRDNSRecords)
{
Export-ADR $ADDNSNodesObj $ADROutputDir $OutputType "DNSNodes"
Remove-Variable ADDNSNodesObj
}
}
Function Get-ADRPrinter
{
<#
.SYNOPSIS
Returns all printers in the current (or specified) domain.
.DESCRIPTION
Returns all printers in the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.PARAMETER Threads
[int]
The number of threads to use during processing of objects. Default 10.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Method -eq 'ADWS')
{
Try
{
$ADPrinters = @( Get-ADObject -LDAPFilter '(objectCategory=printQueue)' -Properties driverNa
}
Catch
{
Write-Warning "[Get-ADRPrinter] Error while enumerating printQueue Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADPrinters)
{
Write-Verbose "[*] Total Printers: $([ADRecon.ADWSClass]::ObjectCount($ADPrinters))"
$PrintersObj = [ADRecon.ADWSClass]::PrinterParser($ADPrinters, $Threads)
Remove-Variable ADPrinters
}
}
If ($Method -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectCategory=printQueue)"
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADPrinters = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRPrinter] Error while enumerating printQueue Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADPrinters)
{
$cnt = $([ADRecon.LDAPClass]::ObjectCount($ADPrinters))
If ($cnt -ge 1)
{
Write-Verbose "[*] Total Printers: $cnt"
$PrintersObj = [ADRecon.LDAPClass]::PrinterParser($ADPrinters, $Threads)
}
Remove-Variable ADPrinters
}
}
If ($PrintersObj)
{
Return $PrintersObj
}
Else
{
Return $null
}
}
Function Get-ADRComputer
{
<#
.SYNOPSIS
Returns all computers and/or service principal name (SPN) in the current (or specified) domain.
.DESCRIPTION
Returns all computers and/or service principal name (SPN) in the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER date
[DateTime]
Date when ADRecon was executed.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER DormantTimeSpan
[int]
Timespan for Dormant accounts. Default 90 days.
.PARAMTER PassMaxAge
[int]
Maximum machine account password age. Default 30 days
https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/domain-m
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.PARAMETER Threads
[int]
The number of threads to use during processing of objects. Default 10.
.PARAMETER ADRComputers
[bool]
.PARAMETER ADRComputerSPNs
[bool]
.PARAMETER OnlyEnabled
[bool]
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $true)]
[DateTime] $date,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $DormantTimeSpan = 90,
[Parameter(Mandatory = $true)]
[int] $PassMaxAge = 30,
[Parameter(Mandatory = $true)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false)]
[int] $Threads = 10,
[Parameter(Mandatory = $false)]
[int] $ADRComputers = $true,
[Parameter(Mandatory = $false)]
[int] $ADRComputerSPNs = $false,
[Parameter(Mandatory = $false)]
[int] $OnlyEnabled = $false
)
If ($Method -eq 'ADWS')
{
If (!$ADRComputers)
{
Try
{
If ($OnlyEnabled)
{
$ADComputers = @( Get-ADObject -LDAPFilter "(&(samAccountType=805306369)(service
}
Else
{
$ADComputers = @( Get-ADObject -LDAPFilter "(&(samAccountType=805306369)(service
}
}
Catch
{
Write-Warning "[Get-ADRComputer] Error while enumerating ComputerSPN Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
}
Else
{
Try
{
If ($OnlyEnabled)
{
$ADComputers = @( Get-ADComputer -Filter 'enabled -eq $true' -ResultPageSize $PageS
AccountControl,whenChanged,whenCreated )
}
Else
{
$ADComputers = @( Get-ADComputer -Filter * -ResultPageSize $PageSize -Properties De
nChanged,whenCreated )
}
}
Catch
{
Write-Warning "[Get-ADRComputer] Error while enumerating Computer Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
}
If ($ADComputers)
{
Write-Verbose "[*] Total Computers: $([ADRecon.ADWSClass]::ObjectCount($ADComputers))"
If ($ADRComputers)
{
$ComputerObj = [ADRecon.ADWSClass]::ComputerParser($ADComputers, $date, $Dorman
}
If ($ADRComputerSPNs)
{
$ComputerSPNObj = [ADRecon.ADWSClass]::ComputerSPNParser($ADComputers, $Threa
}
Remove-Variable ADComputers
}
}
If ($Method -eq 'LDAP')
{
If (!$ADRComputers)
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
If ($OnlyEnabled)
{
$ObjSearcher.Filter = "(&(samAccountType=805306369)(servicePrincipalName=*)(!userAcco
}
Else
{
$ObjSearcher.Filter = "(&(samAccountType=805306369)(servicePrincipalName=*))"
}
$ObjSearcher.PropertiesToLoad.AddRange(("name","serviceprincipalname"))
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADComputers = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRComputer] Error while enumerating ComputerSPN Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
}
Else
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
If ($OnlyEnabled)
{
$ObjSearcher.Filter = "(&(samAccountType=805306369)(!userAccountControl:1.2.840.11355
}
Else
{
$ObjSearcher.Filter = "(samAccountType=805306369)"
}
$ObjSearcher.PropertiesToLoad.AddRange(("description","distinguishedname","dnshostname",
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADComputers = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRComputer] Error while enumerating Computer Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
}
If ($ADComputers)
{
Write-Verbose "[*] Total Computers: $([ADRecon.LDAPClass]::ObjectCount($ADComputers))"
If ($ADRComputers)
{
$ComputerObj = [ADRecon.LDAPClass]::ComputerParser($ADComputers, $date, $DormantT
}
If ($ADRComputerSPNs)
{
$ComputerSPNObj = [ADRecon.LDAPClass]::ComputerSPNParser($ADComputers, $Thread
}
Remove-Variable ADComputers
}
}
If ($ComputerObj)
{
Export-ADR -ADRObj $ComputerObj -ADROutputDir $ADROutputDir -OutputType $OutputType -A
Remove-Variable ComputerObj
}
If ($ComputerSPNObj)
{
Export-ADR -ADRObj $ComputerSPNObj -ADROutputDir $ADROutputDir -OutputType $OutputTy
Remove-Variable ComputerSPNObj
}
}
Function Get-ADRLAPSCheck
{
<#
.SYNOPSIS
Checks if LAPS (local administrator) is enabled in the current (or specified) domain.
.DESCRIPTION
Checks if LAPS (local administrator) is enabled in the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomainRootDSE
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER DomainController
[string]
IP Address of the Domain Controller.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.OUTPUTS
Bool.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomainRootDSE,
[Parameter(Mandatory = $false)]
[string] $DomainController,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
)
If ($Method -eq 'ADWS')
{
Try
{
$ADRLAPSCheck = @( Get-ADObject "CN=ms-Mcs-AdmPwd,$((Get-ADRootDSE).schemaNam
}
Catch
{
Write-Verbose "[*] LAPS is not implemented."
Return $false
}
If ($ADRLAPSCheck)
{
Remove-Variable ADRLAPSCheck
Return $true
}
Else
{
Return $false
}
}
If ($Method -eq 'LDAP')
{
Try
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$ADRLAPSCheckDSE = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($D
If (-Not ($ADRLAPSCheckDSE.Path))
{
$ADRLAPSCheck = $false
}
Else
{
$ADRLAPSCheck = $true
$ADRLAPSCheckDSE.dispose()
}
}
Else
{
$ADRLAPSCheck = [ADSI]::Exists("LDAP://CN=ms-Mcs-AdmPwd,$($objDomainRootDSE.sc
}
}
Catch
{
Write-Verbose "[Get-ADRLAPSCheck] Error while checking for existance of LAPS Properties"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($ADRLAPSCheck)
{
Remove-Variable ADRLAPSCheck
Return $true
}
Else
{
Return $false
}
}
}
# based on https://github.com/kfosaaen/Get-LAPSPasswords/blob/master/Get-LAPSPasswords.ps1
Function Get-ADRLAPS
{
<#
.SYNOPSIS
Returns all LAPS (local administrator) stored passwords in the current (or specified) domain.
.DESCRIPTION
Returns all LAPS (local administrator) stored passwords in the current (or specified) domain. Other d
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.PARAMETER Threads
[int]
The number of threads to use during processing of objects. Default 10.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Method -eq 'ADWS')
{
Try
{
$ADComputers = @( Get-ADObject -LDAPFilter "(samAccountType=805306369)" -Properties C
}
Catch
{
Write-Warning "[Get-ADRLAPS] Error while enumerating LAPS Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADComputers)
{
Write-Verbose "[*] Total LAPS Objects: $([ADRecon.ADWSClass]::ObjectCount($ADComputers
$LAPSObj = [ADRecon.ADWSClass]::LAPSParser($ADComputers, $Threads)
Remove-Variable ADComputers
}
}
If ($Method -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(samAccountType=805306369)"
$ObjSearcher.PropertiesToLoad.AddRange(("cn","dnshostname","ms-mcs-admpwd","ms-mcs-adm
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADComputers = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRLAPS] Error while enumerating LAPS Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADComputers)
{
Write-Verbose "[*] Total LAPS Objects: $([ADRecon.LDAPClass]::ObjectCount($ADComputers)
$LAPSObj = [ADRecon.LDAPClass]::LAPSParser($ADComputers, $Threads)
Remove-Variable ADComputers
}
}
If ($LAPSObj)
{
Return $LAPSObj
}
Else
{
Return $null
}
}
Function Get-ADRBitLocker
{
<#
.SYNOPSIS
Returns all BitLocker Recovery Keys stored in the current (or specified) domain.
.DESCRIPTION
Returns all BitLocker Recovery Keys stored in the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER DomainController
[string]
IP Address of the Domain Controller.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[string] $DomainController,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
)
If ($Method -eq 'ADWS')
{
Try
{
$ADBitLockerRecoveryKeys = Get-ADObject -LDAPFilter '(objectClass=msFVE-RecoveryInform
}
Catch
{
Write-Warning "[Get-ADRBitLocker] Error while enumerating msFVE-RecoveryInformation Obje
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADBitLockerRecoveryKeys)
{
$cnt = $([ADRecon.ADWSClass]::ObjectCount($ADBitLockerRecoveryKeys))
If ($cnt -ge 1)
{
Write-Verbose "[*] Total BitLocker Recovery Keys: $cnt"
$BitLockerObj = @()
$ADBitLockerRecoveryKeys | ForEach-Object {
# Create the object for each instance.
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Distinguished Name" -Value $((($
$Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $_.Name
$Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value $_.whenCre
$Obj | Add-Member -MemberType NoteProperty -Name "Recovery Key ID" -Value $([GUID
$Obj | Add-Member -MemberType NoteProperty -Name "Recovery Key" -Value $_.'msFVE
$Obj | Add-Member -MemberType NoteProperty -Name "Volume GUID" -Value $([GUID] $
Try
{
$TempComp = Get-ADComputer -Identity $Obj.'Distinguished Name' -Properties msTPM
}
Catch
{
Write-Warning "[Get-ADRBitLocker] Error while enumerating $($Obj.'Distinguished Nam
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($TempComp)
{
# msTPM-OwnerInformation (Vista/7 or Server 2008/R2)
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-OwnerInformation" -Va
# msTPM-TpmInformationForComputer (Windows 8/10 or Server 2012/R2)
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-TpmInformationForCo
If ($null -ne $TempComp.'msTPM-TpmInformationForComputer')
{
# Grab the TPM Owner Info from the msTPM-InformationObject
$TPMObject = Get-ADObject -Identity $TempComp.'msTPM-TpmInformationForCom
$TPMRecoveryInfo = $TPMObject.'msTPM-OwnerInformation'
}
Else
{
$TPMRecoveryInfo = $null
}
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-OwnerInformation" -Va
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-TpmInformationForCo
$TPMRecoveryInfo = $null
}
$Obj | Add-Member -MemberType NoteProperty -Name "TPM Owner Password" -Value $T
$BitLockerObj += $Obj
}
}
Remove-Variable ADBitLockerRecoveryKeys
}
}
If ($Method -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectClass=msFVE-RecoveryInformation)"
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedName","msfve-recoverypassword","ms
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADBitLockerRecoveryKeys = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRBitLocker] Error while enumerating msFVE-RecoveryInformation Obje
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADBitLockerRecoveryKeys)
{
$cnt = $([ADRecon.LDAPClass]::ObjectCount($ADBitLockerRecoveryKeys))
If ($cnt -ge 1)
{
Write-Verbose "[*] Total BitLocker Recovery Keys: $cnt"
$BitLockerObj = @()
$ADBitLockerRecoveryKeys | ForEach-Object {
# Create the object for each instance.
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Distinguished Name" -Value $((($
$Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value ([string] ($_.Proper
$Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value ([DateTime
$Obj | Add-Member -MemberType NoteProperty -Name "Recovery Key ID" -Value $([GUID
$Obj | Add-Member -MemberType NoteProperty -Name "Recovery Key" -Value ([string] ($_
$Obj | Add-Member -MemberType NoteProperty -Name "Volume GUID" -Value $([GUID] $
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(&(samAccountType=805306369)(distinguishedName=$($Obj.'Disti
$ObjSearcher.PropertiesToLoad.AddRange(("mstpm-ownerinformation","mstpm-tpminform
$ObjSearcher.SearchScope = "Subtree"
Try
{
$TempComp = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRBitLocker] Error while enumerating $($Obj.'Distinguished Nam
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$ObjSearcher.dispose()
If ($TempComp)
{
# msTPM-OwnerInformation (Vista/7 or Server 2008/R2)
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-OwnerInformation" -Va
# msTPM-TpmInformationForComputer (Windows 8/10 or Server 2012/R2)
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-TpmInformationForCo
If ($null -ne $TempComp.Properties.'mstpm-tpminformationforcomputer')
{
# Grab the TPM Owner Info from the msTPM-InformationObject
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$(
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $obj
$objSearcherPath.PropertiesToLoad.AddRange(("mstpm-ownerinformation"))
Try
{
$TPMObject = $objSearcherPath.FindAll()
}
Catch
{
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$objSearcherPath.dispose()
If ($TPMObject)
{
$TPMRecoveryInfo = $([string] $TPMObject.Properties.'mstpm-ownerinformation
}
Else
{
$TPMRecoveryInfo = $null
}
}
Else
{
Try
{
$TPMObject = ([ADSI]"LDAP://$($TempComp.Properties.'mstpm-tpminformation
}
Catch
{
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($TPMObject)
{
$TPMRecoveryInfo = $([string] $TPMObject.Properties.'mstpm-ownerinformation
}
Else
{
$TPMRecoveryInfo = $null
}
}
}
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-OwnerInformation" -Va
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-TpmInformationForCo
$TPMRecoveryInfo = $null
}
$Obj | Add-Member -MemberType NoteProperty -Name "TPM Owner Password" -Value $T
$BitLockerObj += $Obj
}
}
Remove-Variable cnt
Remove-Variable ADBitLockerRecoveryKeys
}
}
If ($BitLockerObj)
{
Return $BitLockerObj
}
Else
{
Return $null
}
}
# Modified ConvertFrom-SID function from https://github.com/PowerShellMafia/PowerSploit/blob/dev/Re
Function ConvertFrom-SID
{
<#
.SYNOPSIS
Converts a security identifier (SID) to a group/user name.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
.DESCRIPTION
Converts a security identifier string (SID) to a group/user name using IADsNameTranslate interface.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER ObjectSid
Specifies one or more SIDs to convert.
.PARAMETER DomainFQDN
Specifies the FQDN of the Domain.
.PARAMETER Credential
Specifies an alternate credential to use for the translation.
.PARAMETER ResolveSIDs
[bool]
Whether to resolve SIDs in the ACLs module. (Default False)
.EXAMPLE
ConvertFrom-SID S-1-5-21-890171859-3433809279-3366196753-1108
TESTLAB\harmj0y
.EXAMPLE
"S-1-5-21-890171859-3433809279-3366196753-1107", "S-1-5-21-890171859-3433809279-3366196
TESTLAB\WINDOWS2$
TESTLAB\harmj0y
BUILTIN\Distributed COM Users
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm', $SecPassword
ConvertFrom-SID S-1-5-21-890171859-3433809279-3366196753-1108 -Credential $Cred
TESTLAB\harmj0y
.INPUTS
[String]
Accepts one or more SID strings on the pipeline.
.OUTPUTS
[String]
The converted DOMAIN\username.
#>
Param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $true)]
[Alias('SID')]
#[ValidatePattern('^S-1-.*')]
[String]
$ObjectSid,
[Parameter(Mandatory = $false)]
[string] $DomainFQDN,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
[Parameter(Mandatory = $false)]
[bool] $ResolveSID = $false
)
BEGIN {
# Name Translator Initialization Types
# https://msdn.microsoft.com/en-us/library/aa772266%28v=vs.85%29.aspx
$ADS_NAME_INITTYPE_DOMAIN = 1 # Initializes a NameTranslate object by setting the domai
#$ADS_NAME_INITTYPE_SERVER = 2 # Initializes a NameTranslate object by setting the serve
$ADS_NAME_INITTYPE_GC = 3 # Initializes a NameTranslate object by locating the global ca
# Name Transator Name Types
# https://msdn.microsoft.com/en-us/library/aa772267%28v=vs.85%29.aspx
#$ADS_NAME_TYPE_1779 = 1 # Name format as specified in RFC 1779. For examp
#$ADS_NAME_TYPE_CANONICAL = 2 # Canonical name format. For example, "Fabrik
$ADS_NAME_TYPE_NT4 = 3 # Account name format used in Windows. For example
#$ADS_NAME_TYPE_DISPLAY = 4 # Display name format. For example, "Jeff Smith".
#$ADS_NAME_TYPE_DOMAIN_SIMPLE = 5 # Simple domain name format. For example,
#$ADS_NAME_TYPE_ENTERPRISE_SIMPLE = 6 # Simple enterprise name format. For exa
#$ADS_NAME_TYPE_GUID = 7 # Global Unique Identifier format. For example, "{95
$ADS_NAME_TYPE_UNKNOWN = 8 # Unknown name type. The system will estimate
#$ADS_NAME_TYPE_USER_PRINCIPAL_NAME = 9 # User principal name format. For exam
#$ADS_NAME_TYPE_CANONICAL_EX = 10 # Extended canonical name format. For exa
#$ADS_NAME_TYPE_SERVICE_PRINCIPAL_NAME = 11 # Service principal name format. For
#$ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME = 12 # A SID string, as defined in the Sec
# https://msdn.microsoft.com/en-us/library/aa772250.aspx
#$ADS_CHASE_REFERRALS_NEVER = (0x00) # The client should never chase the referred-
#$ADS_CHASE_REFERRALS_SUBORDINATE = (0x20) # The client chases only subordinate ref
#$ADS_CHASE_REFERRALS_EXTERNAL = (0x40) # The client chases external referrals. For
$ADS_CHASE_REFERRALS_ALWAYS = (0x60) # Referrals are chased for either the subordin
}
PROCESS {
$TargetSid = $($ObjectSid.TrimStart("O:"))
$TargetSid = $($TargetSid.Trim('*'))
If ($TargetSid -match '^S-1-.*')
{
Try
{
# try to resolve any built-in SIDs first - https://support.microsoft.com/en-us/kb/243330
Switch ($TargetSid) {
'S-1-0' { 'Null Authority' }
'S-1-0-0' { 'Nobody' }
'S-1-1' { 'World Authority' }
'S-1-1-0' { 'Everyone' }
'S-1-2' { 'Local Authority' }
'S-1-2-0' { 'Local' }
'S-1-2-1' { 'Console Logon ' }
'S-1-3' { 'Creator Authority' }
'S-1-3-0' { 'Creator Owner' }
'S-1-3-1' { 'Creator Group' }
'S-1-3-2' { 'Creator Owner Server' }
'S-1-3-3' { 'Creator Group Server' }
'S-1-3-4' { 'Owner Rights' }
'S-1-4' { 'Non-unique Authority' }
'S-1-5' { 'NT Authority' }
'S-1-5-1' { 'Dialup' }
'S-1-5-2' { 'Network' }
'S-1-5-3' { 'Batch' }
'S-1-5-4' { 'Interactive' }
'S-1-5-6' { 'Service' }
'S-1-5-7' { 'Anonymous' }
'S-1-5-8' { 'Proxy' }
'S-1-5-9' { 'Enterprise Domain Controllers' }
'S-1-5-10' { 'Principal Self' }
'S-1-5-11' { 'Authenticated Users' }
'S-1-5-12' { 'Restricted Code' }
'S-1-5-13' { 'Terminal Server Users' }
'S-1-5-14' { 'Remote Interactive Logon' }
'S-1-5-15' { 'This Organization ' }
'S-1-5-17' { 'This Organization ' }
'S-1-5-18' { 'Local System' }
'S-1-5-19' { 'NT Authority' }
'S-1-5-20' { 'NT Authority' }
'S-1-5-80-0' { 'All Services ' }
'S-1-5-32-544' { 'BUILTIN\Administrators' }
'S-1-5-32-545' { 'BUILTIN\Users' }
'S-1-5-32-546' { 'BUILTIN\Guests' }
'S-1-5-32-547' { 'BUILTIN\Power Users' }
'S-1-5-32-548' { 'BUILTIN\Account Operators' }
'S-1-5-32-549' { 'BUILTIN\Server Operators' }
'S-1-5-32-550' { 'BUILTIN\Print Operators' }
'S-1-5-32-551' { 'BUILTIN\Backup Operators' }
'S-1-5-32-552' { 'BUILTIN\Replicators' }
'S-1-5-32-554' { 'BUILTIN\Pre-Windows 2000 Compatible Access' }
'S-1-5-32-555' { 'BUILTIN\Remote Desktop Users' }
'S-1-5-32-556' { 'BUILTIN\Network Configuration Operators' }
'S-1-5-32-557' { 'BUILTIN\Incoming Forest Trust Builders' }
'S-1-5-32-558' { 'BUILTIN\Performance Monitor Users' }
'S-1-5-32-559' { 'BUILTIN\Performance Log Users' }
'S-1-5-32-560' { 'BUILTIN\Windows Authorization Access Group' }
'S-1-5-32-561' { 'BUILTIN\Terminal Server License Servers' }
'S-1-5-32-562' { 'BUILTIN\Distributed COM Users' }
'S-1-5-32-569' { 'BUILTIN\Cryptographic Operators' }
'S-1-5-32-573' { 'BUILTIN\Event Log Readers' }
'S-1-5-32-574' { 'BUILTIN\Certificate Service DCOM Access' }
'S-1-5-32-575' { 'BUILTIN\RDS Remote Access Servers' }
'S-1-5-32-576' { 'BUILTIN\RDS Endpoint Servers' }
'S-1-5-32-577' { 'BUILTIN\RDS Management Servers' }
'S-1-5-32-578' { 'BUILTIN\Hyper-V Administrators' }
'S-1-5-32-579' { 'BUILTIN\Access Control Assistance Operators' }
'S-1-5-32-580' { 'BUILTIN\Remote Management Users' }
Default {
# based on Convert-ADName function from https://github.com/PowerShellMafia/PowerS
If ( ($TargetSid -match '^S-1-.*') -and ($ResolveSID) )
{
If ($Method -eq 'ADWS')
{
Try
{
$ADObject = Get-ADObject -Filter "objectSid -eq '$TargetSid'" -Properties Disting
}
Catch
{
Write-Warning "[ConvertFrom-SID] Error while enumerating Object using SID"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($ADObject)
{
$UserDomain = Get-DNtoFQDN -ADObjectDN $ADObject.DistinguishedName
$ADSOutput = $UserDomain + "\" + $ADObject.sAMAccountName
Remove-Variable UserDomain
}
}
If ($Method -eq 'LDAP')
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$ADObject = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$Do
}
Else
{
$ADObject = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$Do
}
If ($ADObject)
{
If (-Not ([string]::IsNullOrEmpty($ADObject.Properties.samaccountname)) )
{
$UserDomain = Get-DNtoFQDN -ADObjectDN $([string] ($ADObject.Propertie
$ADSOutput = $UserDomain + "\" + $([string] ($ADObject.Properties.samacco
Remove-Variable UserDomain
}
}
}
If ( (-Not $ADSOutput) -or ([string]::IsNullOrEmpty($ADSOutput)) )
{
$ADSOutputType = $ADS_NAME_TYPE_NT4
$Init = $true
$Translate = New-Object -ComObject NameTranslate
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$ADSInitType = $ADS_NAME_INITTYPE_DOMAIN
Try
{
[System.__ComObject].InvokeMember("InitEx","InvokeMethod",$null,$Transla
}
Catch
{
$Init = $false
#Write-Verbose "[ConvertFrom-SID] Error initializing translation for $($TargetS
#Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
Else
{
$ADSInitType = $ADS_NAME_INITTYPE_GC
Try
{
[System.__ComObject].InvokeMember("Init","InvokeMethod",$null,$Translate,
}
Catch
{
$Init = $false
#Write-Verbose "[ConvertFrom-SID] Error initializing translation for $($TargetS
#Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
If ($Init)
{
[System.__ComObject].InvokeMember("ChaseReferral","SetProperty",$null,$Tra
Try
{
[System.__ComObject].InvokeMember("Set","InvokeMethod",$null,$Translate
$ADSOutput = [System.__ComObject].InvokeMember("Get","InvokeMethod",$
}
Catch
{
#Write-Verbose "[ConvertFrom-SID] Error translating $($TargetSid)"
#Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
}
}
If (-Not ([string]::IsNullOrEmpty($ADSOutput)) )
{
Return $ADSOutput
}
Else
{
Return $TargetSid
}
}
}
}
Catch
{
#Write-Output "[ConvertFrom-SID] Error converting SID $($TargetSid)"
#Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
Else
{
Return $TargetSid
}
}
}
# based on https://gallery.technet.microsoft.com/Active-Directory-OU-1d09f989
Function Get-ADRACL
{
<#
.SYNOPSIS
Returns all ACLs for the Domain, OUs, Root Containers, GPO, User, Computer and Group objects in
.DESCRIPTION
Returns all ACLs for the Domain, OUs, Root Containers, GPO, User, Computer and Group objects in
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER DomainController
[string]
IP Address of the Domain Controller.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.PARAMETER ResolveSIDs
[bool]
Whether to resolve SIDs in the ACLs module. (Default False)
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.PARAMETER Threads
[int]
The number of threads to use during processing of objects. Default 10.
.OUTPUTS
PSObject.
.LINK
https://gallery.technet.microsoft.com/Active-Directory-OU-1d09f989
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[string] $DomainController,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
[Parameter(Mandatory = $false)]
[bool] $ResolveSID = $false,
[Parameter(Mandatory = $true)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false)]
[int] $Threads = 10,
[Parameter(Mandatory = $false)]
[string] $DnBase = $($ADDomain.DistinguishedName)
)
If ($Method -eq 'ADWS')
{
If ($Credential -eq [Management.Automation.PSCredential]::Empty)
{
If (Test-Path AD:)
{
Set-Location AD:
}
Else
{
Write-Warning "Default AD drive not found ... Skipping ACL enumeration"
Return $null
}
}
$GUIDs = @{'00000000-0000-0000-0000-000000000000' = 'All'}
Try
{
Write-Verbose "[*] Enumerating schemaIDs"
$schemaIDs = Get-ADObject -SearchBase (Get-ADRootDSE).schemaNamingContext -LDAPFi
}
Catch
{
Write-Warning "[Get-ADRACL] Error while enumerating schemaIDs"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($schemaIDs)
{
$schemaIDs | Where-Object {$_} | ForEach-Object {
# convert the GUID
$GUIDs[(New-Object Guid (,$_.schemaIDGUID)).Guid] = $_.name
}
Remove-Variable schemaIDs
}
Try
{
Write-Verbose "[*] Enumerating Active Directory Rights"
$schemaIDs = Get-ADObject -SearchBase "CN=Extended-Rights,$((Get-ADRootDSE).configur
}
Catch
{
Write-Warning "[Get-ADRACL] Error while enumerating Active Directory Rights"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($schemaIDs)
{
$schemaIDs | Where-Object {$_} | ForEach-Object {
# convert the GUID
$GUIDs[(New-Object Guid (,$_.rightsGUID)).Guid] = $_.name
}
Remove-Variable schemaIDs
}
# Get the DistinguishedNames of Domain, OUs, Root Containers and GroupPolicy objects.
$Objs = @()
Try
{
$ADDomain = Get-ADDomain
}
Catch
{
Write-Warning "[Get-ADRACL] Error getting Domain Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
Try
{
Write-Verbose "[*] Enumerating Domain, OU, GPO, User, Computer and Group Objects"
$Objs += Get-ADObject -SearchBase $DnBase -LDAPFilter '(|(objectClass=domain)(objectCate
}
Catch
{
Write-Warning "[Get-ADRACL] Error while enumerating Domain, OU, GPO, User, Computer and
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($ADDomain)
{
Try
{
Write-Verbose "[*] Enumerating Root Container Objects"
$Objs += Get-ADObject -SearchBase $($ADDomain.DistinguishedName) -SearchScope One
}
Catch
{
Write-Warning "[Get-ADRACL] Error while enumerating Root Container Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
If ($Objs)
{
$ACLObj = @()
Write-Verbose "[*] Total Objects: $([ADRecon.ADWSClass]::ObjectCount($Objs))"
Write-Verbose "[-] DACLs"
$DACLObj = [ADRecon.ADWSClass]::DACLParser($Objs, $GUIDs, $Threads)
#Write-Verbose "[-] SACLs - May need a Privileged Account"
Write-Warning "[*] SACLs - Currently, the module is only supported with LDAP."
#$SACLObj = [ADRecon.ADWSClass]::SACLParser($Objs, $GUIDs, $Threads)
Remove-Variable Objs
Remove-Variable GUIDs
}
}
If ($Method -eq 'LDAP')
{
$GUIDs = @{'00000000-0000-0000-0000-000000000000' = 'All'}
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Do
Try
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainCon
}
Catch
{
Write-Warning "[Get-ADRACL] Error getting Domain Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
Try
{
$ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Fo
$ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
$SchemaPath = $ADForest.Schema.Name
Remove-Variable ADForest
}
Catch
{
Write-Warning "[Get-ADRACL] Error enumerating SchemaPath"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
Else
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$ADForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$SchemaPath = $ADForest.Schema.Name
Remove-Variable ADForest
}
If ($SchemaPath)
{
Write-Verbose "[*] Enumerating schemaIDs"
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainC
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPat
}
Else
{
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher ([ADSI] "LDAP
}
$objSearcherPath.PageSize = $PageSize
$objSearcherPath.filter = "(schemaIDGUID=*)"
Try
{
$SchemaSearcher = $objSearcherPath.FindAll()
}
Catch
{
Write-Warning "[Get-ADRACL] Error enumerating SchemaIDs"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($SchemaSearcher)
{
$SchemaSearcher | Where-Object {$_} | ForEach-Object {
# convert the GUID
$GUIDs[(New-Object Guid (,$_.properties.schemaidguid[0])).Guid] = $_.properties.name[0
}
$SchemaSearcher.dispose()
}
$objSearcherPath.dispose()
Write-Verbose "[*] Enumerating Active Directory Rights"
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainC
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPat
}
Else
{
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher ([ADSI] "LDAP
}
$objSearcherPath.PageSize = $PageSize
$objSearcherPath.filter = "(objectClass=controlAccessRight)"
Try
{
$RightsSearcher = $objSearcherPath.FindAll()
}
Catch
{
Write-Warning "[Get-ADRACL] Error enumerating Active Directory Rights"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($RightsSearcher)
{
$RightsSearcher | Where-Object {$_} | ForEach-Object {
# convert the GUID
$GUIDs[$_.properties.rightsguid[0].toString()] = $_.properties.name[0]
}
$RightsSearcher.dispose()
}
$objSearcherPath.dispose()
}
# Get the Domain, OUs, Root Containers, GPO, User, Computer and Group objects.
$Objs = @()
Write-Verbose "[*] Enumerating Domain, OU, GPO, User, Computer and Group Objects"
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$objSearcherPath.SearchRoot = "LDAP://$DnBase"
$ObjSearcher.Filter = "(|(objectClass=domain)(objectCategory=organizationalunit)(objectCategory
# https://msdn.microsoft.com/en-us/library/system.directoryservices.securitymasks(v=vs.110).aspx
$ObjSearcher.SecurityMasks = [System.DirectoryServices.SecurityMasks]::Dacl -bor [System.Dire
$ObjSearcher.PropertiesToLoad.AddRange(("displayname","distinguishedname","name","ntsecuri
$ObjSearcher.SearchScope = "Subtree"
Try
{
$Objs += $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRACL] Error while enumerating Domain, OU, GPO, User, Computer and
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$ObjSearcher.dispose()
Write-Verbose "[*] Enumerating Root Container Objects"
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectClass=container)"
# https://msdn.microsoft.com/en-us/library/system.directoryservices.securitymasks(v=vs.110).aspx
$ObjSearcher.SecurityMasks = $ObjSearcher.SecurityMasks = [System.DirectoryServices.Securit
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","name","ntsecuritydescriptor","o
$ObjSearcher.SearchScope = "OneLevel"
Try
{
$Objs += $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRACL] Error while enumerating Root Container Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$ObjSearcher.dispose()
If ($Objs)
{
Write-Verbose "[*] Total Objects: $([ADRecon.LDAPClass]::ObjectCount($Objs))"
Write-Verbose "[-] DACLs"
$DACLObj = [ADRecon.LDAPClass]::DACLParser($Objs, $GUIDs, $Threads)
Write-Verbose "[-] SACLs - May need a Privileged Account"
$SACLObj = [ADRecon.LDAPClass]::SACLParser($Objs, $GUIDs, $Threads)
Remove-Variable Objs
Remove-Variable GUIDs
}
}
If ($DACLObj)
{
Export-ADR $DACLObj $ADROutputDir $OutputType "DACLs"
Remove-Variable DACLObj
}
If ($SACLObj)
{
Export-ADR $SACLObj $ADROutputDir $OutputType "SACLs"
Remove-Variable SACLObj
}
}
Function Get-ADRGPOReport
{
<#
.SYNOPSIS
Runs the Get-GPOReport cmdlet if available.
.DESCRIPTION
Runs the Get-GPOReport cmdlet if available and saves in HTML and XML formats.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER UseAltCreds
[bool]
Whether to use provided credentials or not.
.PARAMETER ADROutputDir
[string]
Path for ADRecon output folder.
.OUTPUTS
HTML and XML GPOReports are created in the folder specified.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $true)]
[bool] $UseAltCreds,
[Parameter(Mandatory = $true)]
[string] $ADROutputDir
)
If ($Method -eq 'ADWS')
{
Try
{
# Suppress verbose output on module import
$SaveVerbosePreference = $script:VerbosePreference
$script:VerbosePreference = 'SilentlyContinue'
If ($PSVersionTable.PSEdition -eq "Core")
{
Import-Module GroupPolicy -SkipEditionCheck -WarningAction Stop -ErrorAction Stop | Out-N
}
Else
{
Import-Module GroupPolicy -WarningAction Stop -ErrorAction Stop | Out-Null
}
If ($SaveVerbosePreference)
{
$script:VerbosePreference = $SaveVerbosePreference
Remove-Variable SaveVerbosePreference
}
}
Catch
{
Write-Warning "[Get-ADRGPOReport] Error importing the GroupPolicy Module. Skipping GPOR
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
If ($SaveVerbosePreference)
{
$script:VerbosePreference = $SaveVerbosePreference
Remove-Variable SaveVerbosePreference
}
Return $null
}
Try
{
Write-Verbose "[*] GPOReport XML"
$ADFileName = -join($ADROutputDir,'\','GPO-Report','.xml')
Get-GPOReport -All -ReportType XML -Path $ADFileName
}
Catch
{
If ($UseAltCreds)
{
Write-Warning "[*] Run the tool using RUNAS."
Write-Warning "[*] runas /user:<Domain FQDN>\<Username> /netonly powershell.exe"
Return $null
}
Write-Warning "[Get-ADRGPOReport] Error getting the GPOReport in XML"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
Try
{
Write-Verbose "[*] GPOReport HTML"
$ADFileName = -join($ADROutputDir,'\','GPO-Report','.html')
Get-GPOReport -All -ReportType HTML -Path $ADFileName
}
Catch
{
If ($UseAltCreds)
{
Write-Warning "[*] Run the tool using RUNAS."
Write-Warning "[*] runas /user:<Domain FQDN>\<Username> /netonly powershell.exe"
Return $null
}
Write-Warning "[Get-ADRGPOReport] Error getting the GPOReport in XML"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
If ($Method -eq 'LDAP')
{
Write-Warning "[*] Currently, the module is only supported with ADWS."
}
}
# Modified Invoke-UserImpersonation function from https://github.com/PowerShellMafia/PowerSploit/blo
Function Get-ADRUserImpersonation
{
<#
.SYNOPSIS
Creates a new "runas /netonly" type logon and impersonates the token.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: PSReflect
.DESCRIPTION
This function uses LogonUser() with the LOGON32_LOGON_NEW_CREDENTIALS LogonType
to simulate "runas /netonly". The resulting token is then impersonated with
ImpersonateLoggedOnUser() and the token handle is returned for later usage
with Invoke-RevertToSelf.
.PARAMETER Credential
A [Management.Automation.PSCredential] object with alternate credentials
to impersonate in the current thread space.
.PARAMETER TokenHandle
An IntPtr TokenHandle returned by a previous Invoke-UserImpersonation.
If this is supplied, LogonUser() is skipped and only ImpersonateLoggedOnUser()
is executed.
.PARAMETER Quiet
Suppress any warnings about STA vs MTA.
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword
Invoke-UserImpersonation -Credential $Cred
.OUTPUTS
IntPtr
The TokenHandle result from LogonUser.
#>
[OutputType([IntPtr])]
[CmdletBinding(DefaultParameterSetName = 'Credential')]
Param(
[Parameter(Mandatory = $True, ParameterSetName = 'Credential')]
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential,
[Parameter(Mandatory = $True, ParameterSetName = 'TokenHandle')]
[ValidateNotNull()]
[IntPtr]
$TokenHandle,
[Switch]
$Quiet
)
If (([System.Threading.Thread]::CurrentThread.GetApartmentState() -ne 'STA') -and (-not $PSBound
{
Write-Warning "[Get-ADRUserImpersonation] powershell.exe process is not currently in a single-th
}
If ($PSBoundParameters['TokenHandle'])
{
$LogonTokenHandle = $TokenHandle
}
Else
{
$LogonTokenHandle = [IntPtr]::Zero
$NetworkCredential = $Credential.GetNetworkCredential()
$UserDomain = $NetworkCredential.Domain
If (-Not $UserDomain)
{
Write-Warning "[Get-ADRUserImpersonation] Use credential with Domain FQDN. (<Domain FQ
}
$UserName = $NetworkCredential.UserName
Write-Warning "[Get-ADRUserImpersonation] Executing LogonUser() with user: $($UserDomain)\$
# LOGON32_LOGON_NEW_CREDENTIALS = 9, LOGON32_PROVIDER_WINNT50 = 3
# this is to simulate "runas.exe /netonly" functionality
$Result = $Advapi32::LogonUser($UserName, $UserDomain, $NetworkCredential.Password, 9, 3
$LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error();
If (-not $Result)
{
throw "[Get-ADRUserImpersonation] LogonUser() Error: $(([ComponentModel.Win32Exception]
}
}
# actually impersonate the token from LogonUser()
$Result = $Advapi32::ImpersonateLoggedOnUser($LogonTokenHandle)
If (-not $Result)
{
throw "[Get-ADRUserImpersonation] ImpersonateLoggedOnUser() Error: $(([ComponentModel.Wi
}
Write-Verbose "[Get-ADR-UserImpersonation] Alternate credentials successfully impersonated"
$LogonTokenHandle
}
# Modified Invoke-RevertToSelf function from https://github.com/PowerShellMafia/PowerSploit/blob/dev
Function Get-ADRRevertToSelf
{
<#
.SYNOPSIS
Reverts any token impersonation.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: PSReflect
.DESCRIPTION
This function uses RevertToSelf() to revert any impersonated tokens.
If -TokenHandle is passed (the token handle returned by Invoke-UserImpersonation),
CloseHandle() is used to close the opened handle.
.PARAMETER TokenHandle
An optional IntPtr TokenHandle returned by Invoke-UserImpersonation.
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword
$Token = Invoke-UserImpersonation -Credential $Cred
Invoke-RevertToSelf -TokenHandle $Token
#>
[CmdletBinding()]
Param(
[ValidateNotNull()]
[IntPtr]
$TokenHandle
)
If ($PSBoundParameters['TokenHandle'])
{
Write-Warning "[Get-ADRRevertToSelf] Reverting token impersonation and closing LogonUser() to
$Result = $Kernel32::CloseHandle($TokenHandle)
}
$Result = $Advapi32::RevertToSelf()
$LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error();
If (-not $Result)
{
Write-Error "[Get-ADRRevertToSelf] RevertToSelf() Error: $(([ComponentModel.Win32Exception]
}
Write-Verbose "[Get-ADRRevertToSelf] Token impersonation successfully reverted"
}
# Modified Get-DomainSPNTicket function from https://github.com/PowerShellMafia/PowerSploit/blob/d
Function Get-ADRSPNTicket
{
<#
<#
.SYNOPSIS
Request the kerberos ticket for a specified service principal name (SPN).
Author: machosec, Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Invoke-UserImpersonation, Invoke-RevertToSelf
.DESCRIPTION
This function will either take one SPN strings, and will request a kerberos ticket for the given SPN us
.PARAMETER UserSPN
[string]
Service Principal Name.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $UserSPN
)
Try
{
$Null = [Reflection.Assembly]::LoadWithPartialName('System.IdentityModel')
$Ticket = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -Argument
}
Catch
{
Write-Warning "[Get-ADRSPNTicket] Error requesting ticket for SPN $UserSPN"
Write-Warning "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($Ticket)
{
$TicketByteStream = $Ticket.GetRequest()
}
If ($TicketByteStream)
{
$TicketHexStream = [System.BitConverter]::ToString($TicketByteStream) -replace '-'
# TicketHexStream == GSS-API Frame (see https://tools.ietf.org/html/rfc4121#section-4.1)
# No easy way to parse ASN1, so we'll try some janky regex to parse the embedded KRB_AP_RE
If ($TicketHexStream -match 'a382....3082....A0030201(?<EtypeLen>..)A1.{1,4}.......A282(?<Ciphe
{
$Etype = [Convert]::ToByte( $Matches.EtypeLen, 16 )
$CipherTextLen = [Convert]::ToUInt32($Matches.CipherTextLen, 16)-4
$CipherText = $Matches.DataToEnd.Substring(0,$CipherTextLen*2)
# Make sure the next field matches the beginning of the KRB_AP_REQ.Authenticator object
If ($Matches.DataToEnd.Substring($CipherTextLen*2, 4) -ne 'A482')
{
Write-Warning '[Get-ADRSPNTicket] Error parsing ciphertext for the SPN $($Ticket.ServiceP
$Hash = $null
}
Else
{
$Hash = "$($CipherText.Substring(0,32))`$$($CipherText.Substring(32))"
}
}
Else
{
Write-Warning "[Get-ADRSPNTicket] Unable to parse ticket structure for the SPN $($Ticket.Ser
$Hash = $null
}
}
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "ServicePrincipalName" -Value $Ticket.Serv
$Obj | Add-Member -MemberType NoteProperty -Name "Etype" -Value $Etype
$Obj | Add-Member -MemberType NoteProperty -Name "Hash" -Value $Hash
Return $Obj
}
Function Get-ADRKerberoast
{
<#
.SYNOPSIS
Returns all user service principal name (SPN) hashes in the current (or specified) domain.
.DESCRIPTION
Returns all user service principal name (SPN) hashes in the current (or specified) domain.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
[Parameter(Mandatory = $true)]
[int] $PageSize
)
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$LogonToken = Get-ADRUserImpersonation -Credential $Credential
}
If ($Method -eq 'ADWS')
{
Try
{
$ADUsers = Get-ADObject -LDAPFilter "(&(!objectClass=computer)(servicePrincipalName=*)(!u
}
Catch
{
Write-Warning "[Get-ADRKerberoast] Error while enumerating UserSPN Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADUsers)
{
$UserSPNObj = @()
$ADUsers | ForEach-Object {
ForEach ($UserSPN in $_.servicePrincipalName)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Username" -Value $_.sAMAccoun
$Obj | Add-Member -MemberType NoteProperty -Name "ServicePrincipalName" -Value $U
$HashObj = Get-ADRSPNTicket $UserSPN
If ($HashObj)
{
$UserDomain = $_.DistinguishedName.SubString($_.DistinguishedName.IndexOf('DC='
# JohnTheRipper output format
$JTRHash = "`$krb5tgs`$$($HashObj.ServicePrincipalName):$($HashObj.Hash)"
# hashcat output format
$HashcatHash = "`$krb5tgs`$$($HashObj.Etype)`$*$($_.SamAccountName)`$$UserDom
}
Else
{
$JTRHash = $null
$HashcatHash = $null
}
$Obj | Add-Member -MemberType NoteProperty -Name "John" -Value $JTRHash
$Obj | Add-Member -MemberType NoteProperty -Name "Hashcat" -Value $HashcatHash
$UserSPNObj += $Obj
}
}
Remove-Variable ADUsers
}
}
If ($Method -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(&(!objectClass=computer)(servicePrincipalName=*)(!userAccountControl:1
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","samaccountname","serviceprin
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADUsers = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRKerberoast] Error while enumerating UserSPN Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADUsers)
{
$UserSPNObj = @()
$ADUsers | ForEach-Object {
ForEach ($UserSPN in $_.Properties.serviceprincipalname)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Username" -Value $_.Properties.
$Obj | Add-Member -MemberType NoteProperty -Name "ServicePrincipalName" -Value $U
$HashObj = Get-ADRSPNTicket $UserSPN
If ($HashObj)
{
$UserDomain = $_.Properties.distinguishedname[0].SubString($_.Properties.distinguish
# JohnTheRipper output format
$JTRHash = "`$krb5tgs`$$($HashObj.ServicePrincipalName):$($HashObj.Hash)"
# hashcat output format
$HashcatHash = "`$krb5tgs`$$($HashObj.Etype)`$*$($_.Properties.samaccountname)`$
}
Else
{
$JTRHash = $null
$HashcatHash = $null
}
$Obj | Add-Member -MemberType NoteProperty -Name "John" -Value $JTRHash
$Obj | Add-Member -MemberType NoteProperty -Name "Hashcat" -Value $HashcatHash
$UserSPNObj += $Obj
}
}
Remove-Variable ADUsers
}
}
If ($LogonToken)
{
Get-ADRRevertToSelf -TokenHandle $LogonToken
}
If ($UserSPNObj)
{
Return $UserSPNObj
}
Else
{
Return $null
}
}
# based on https://gallery.technet.microsoft.com/scriptcenter/PowerShell-script-to-find-6fc15ecb
Function Get-ADRDomainAccountsusedforServiceLogon
{
<#
.SYNOPSIS
Returns all accounts used by services on computers in an Active Directory domain.
.DESCRIPTION
Retrieves a list of all computers in the current domain and reads service configuration using Get-Wm
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.PARAMETER Threads
[int]
The number of threads to use during processing of objects. Default 10.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
[Parameter(Mandatory = $true)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
BEGIN {
$readServiceAccounts = [scriptblock] {
# scriptblock to retrieve service list form a remove machine
$hostname = [string] $args[0]
$OperatingSystem = [string] $args[1]
#$Credential = [Management.Automation.PSCredential] $args[2]
$Credential = $args[2]
$timeout = 250
$port = 135
Try
{
$tcpclient = New-Object System.Net.Sockets.TcpClient
$result = $tcpclient.BeginConnect($hostname,$port,$null,$null)
$success = $result.AsyncWaitHandle.WaitOne($timeout,$null)
}
Catch
{
$warning = "$hostname ($OperatingSystem) is unreachable $($_.Exception.Message)"
$success = $false
$tcpclient.Close()
}
If ($success)
{
# PowerShellv2 does not support New-CimSession
If ($PSVersionTable.PSVersion.Major -ne 2)
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$session = New-CimSession -ComputerName $hostname -SessionOption $(New-CimSe
If ($session)
{
$serviceList = @( Get-CimInstance -ClassName Win32_Service -Property Name,Star
}
}
Else
{
$session = New-CimSession -ComputerName $hostname -SessionOption $(New-CimSe
If ($session)
{
$serviceList = @( Get-CimInstance -ClassName Win32_Service -Property Name,Star
}
}
}
Else
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$serviceList = @( Get-WmiObject -Class Win32_Service -ComputerName $hostname -C
}
Else
{
$serviceList = @( Get-WmiObject -Class Win32_Service -ComputerName $hostname -P
}
}
$serviceList
}
Try
{
If ($tcpclient) { $tcpclient.EndConnect($result) | Out-Null }
}
Catch
{
$warning = "$hostname ($OperatingSystem) : $($_.Exception.Message)"
}
$warning
}
Function processCompletedJobs()
{
# reads service list from completed jobs,
# updates $serviceAccount table and removes completed job
$jobs = Get-Job -State Completed
ForEach( $job in $jobs )
{
If ($null -ne $job)
{
$data = Receive-Job $job
Remove-Job $job
}
If ($data)
{
If ( $data.GetType() -eq [Object[]] )
{
$serviceList = $data | Where-Object { if ($_.StartName) { $_ }}
$serviceList | ForEach-Object {
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Account" -Value $_.StartNam
$Obj | Add-Member -MemberType NoteProperty -Name "Service Name" -Value $_.Na
$Obj | Add-Member -MemberType NoteProperty -Name "SystemName" -Value $_.Sys
If ($_.StartName.toUpper().Contains($currentDomain))
{
$Obj | Add-Member -MemberType NoteProperty -Name "Running as Domain User"
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Running as Domain User"
}
$script:serviceAccounts += $Obj
}
}
ElseIf ( $data.GetType() -eq [String] )
{
$script:warnings += $data
Write-Verbose $data
}
}
}
}
}
PROCESS
{
$script:serviceAccounts = @()
[string[]] $warnings = @()
If ($Method -eq 'ADWS')
{
Try
{
$ADDomain = Get-ADDomain
}
Catch
{
Write-Warning "[Get-ADRDomainAccountsusedforServiceLogon] Error getting Domain Conte
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADDomain)
{
$currentDomain = $ADDomain.NetBIOSName.toUpper()
Remove-Variable ADDomain
}
Else
{
$currentDomain = ""
Write-Warning "Current Domain could not be retrieved."
}
Try
{
$ADComputers = Get-ADComputer -Filter { Enabled -eq $true -and OperatingSystem -Like "*
}
Catch
{
Write-Warning "[Get-ADRDomainAccountsusedforServiceLogon] Error while enumerating Win
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADComputers)
{
# start data retrieval job for each server in the list
# use up to $Threads threads
$cnt = $([ADRecon.ADWSClass]::ObjectCount($ADComputers))
Write-Verbose "[*] Total Windows Hosts: $cnt"
$icnt = 0
$ADComputers | ForEach-Object {
$StopWatch = [System.Diagnostics.StopWatch]::StartNew()
If( $_.dnshostname )
■ {
$args = @($_.DNSHostName, $_.OperatingSystem, $Credential)
■■ Start-Job -ScriptBlock $readServiceAccounts -Name "read_$($_.name)" -ArgumentList $a
■■ ++$icnt
■■ If ($StopWatch.Elapsed.TotalMilliseconds -ge 1000)
{
Write-Progress -Activity "Retrieving data from servers" -Status "$("{0:N2}" -f (($icnt/$c
$StopWatch.Reset()
$StopWatch.Start()
■■ }
while ( ( Get-Job -State Running).count -ge $Threads ) { Start-Sleep -Seconds 3 }
■■ processCompletedJobs
■ }
}
# process remaining jobs
Write-Progress -Activity "Retrieving data from servers" -Status "Waiting for background jobs t
Wait-Job -State Running -Timeout 30 | Out-Null
Get-Job -State Running | Stop-Job
processCompletedJobs
Write-Progress -Activity "Retrieving data from servers" -Completed -Status "All Done"
}
}
If ($Method -eq 'LDAP')
{
$currentDomain = ([string]($objDomain.name)).toUpper()
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(&(samAccountType=805306369)(!userAccountControl:1.2.840.113556.1
$ObjSearcher.PropertiesToLoad.AddRange(("name","dnshostname","operatingsystem"))
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADComputers = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRDomainAccountsusedforServiceLogon] Error while enumerating Win
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADComputers)
{
# start data retrieval job for each server in the list
# use up to $Threads threads
$cnt = $([ADRecon.LDAPClass]::ObjectCount($ADComputers))
Write-Verbose "[*] Total Windows Hosts: $cnt"
$icnt = 0
$ADComputers | ForEach-Object {
If( $_.Properties.dnshostname )
■ {
$args = @($_.Properties.dnshostname, $_.Properties.operatingsystem, $Credential)
■■ Start-Job -ScriptBlock $readServiceAccounts -Name "read_$($_.Properties.name)" -Argum
■■ ++$icnt
■■ If ($StopWatch.Elapsed.TotalMilliseconds -ge 1000)
{
■■ Write-Progress -Activity "Retrieving data from servers" -Status "$("{0:N2}" -f (($icnt/$cnt
$StopWatch.Reset()
$StopWatch.Start()
■■ }
■■ while ( ( Get-Job -State Running).count -ge $Threads ) { Start-Sleep -Seconds 3 }
■■ processCompletedJobs
■ }
}
# process remaining jobs
Write-Progress -Activity "Retrieving data from servers" -Status "Waiting for background jobs t
Wait-Job -State Running -Timeout 30 | Out-Null
Get-Job -State Running | Stop-Job
processCompletedJobs
Write-Progress -Activity "Retrieving data from servers" -Completed -Status "All Done"
}
}
If ($script:serviceAccounts)
{
Return $script:serviceAccounts
}
Else
{
Return $null
}
}
}
Function Remove-EmptyADROutputDir
{
<#
.SYNOPSIS
Removes ADRecon output folder if empty.
.DESCRIPTION
Removes ADRecon output folder if empty.
.PARAMETER ADROutputDir
[string]
■Path for ADRecon output folder.
.PARAMETER OutputType
[array]
Output Type.
#>
param(
[Parameter(Mandatory = $true)]
[string] $ADROutputDir,
[Parameter(Mandatory = $true)]
[array] $OutputType
)
Switch ($OutputType)
{
'CSV'
{
$CSVPath = -join($ADROutputDir,'\','CSV-Files')
If (!(Test-Path -Path $CSVPath\*))
{
Write-Verbose "Removed Empty Directory $CSVPath"
Remove-Item $CSVPath
}
}
'XML'
{
$XMLPath = -join($ADROutputDir,'\','XML-Files')
If (!(Test-Path -Path $XMLPath\*))
{
Write-Verbose "Removed Empty Directory $XMLPath"
Remove-Item $XMLPath
}
}
'JSON'
{
$JSONPath = -join($ADROutputDir,'\','JSON-Files')
If (!(Test-Path -Path $JSONPath\*))
{
Write-Verbose "Removed Empty Directory $JSONPath"
Remove-Item $JSONPath
}
}
'HTML'
{
$HTMLPath = -join($ADROutputDir,'\','HTML-Files')
If (!(Test-Path -Path $HTMLPath\*))
{
Write-Verbose "Removed Empty Directory $HTMLPath"
Remove-Item $HTMLPath
}
}
}
If (!(Test-Path -Path $ADROutputDir\*))
{
Remove-Item $ADROutputDir
Write-Verbose "Removed Empty Directory $ADROutputDir"
}
}
Function Get-ADRAbout
{
<#
.SYNOPSIS
Returns information about ADRecon.
.DESCRIPTION
Returns information about ADRecon.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER date
[DateTime]
Date
.PARAMETER ADReconVersion
[string]
ADRecon Version.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.PARAMETER RanonComputer
[string]
Details of the Computer running ADRecon.
.PARAMETER TotalTime
[string]
TotalTime.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Method,
[Parameter(Mandatory = $true)]
[DateTime] $date,
[Parameter(Mandatory = $true)]
[string] $ADReconVersion,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
[Parameter(Mandatory = $true)]
[string] $RanonComputer,
[Parameter(Mandatory = $true)]
[string] $TotalTime
)
$AboutADRecon = @()
$Version = $Method + " Version"
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$Username = $($Credential.UserName)
}
Else
{
$Username = $([Environment]::UserName)
}
$ObjValues = @("Date", $($date), "ADRecon", "https://github.com/adrecon/ADRecon", $Version, $($
For ($i = 0; $i -lt $($ObjValues.Count); $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value $ObjValues[$i]
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ObjValues[$i+1]
$i++
$AboutADRecon += $Obj
}
Return $AboutADRecon
}
Function Invoke-ADRecon
{
<#
.SYNOPSIS
Wrapper function to run ADRecon modules.
.DESCRIPTION
Wrapper function to set variables, check dependencies and run ADRecon modules.
.PARAMETER Method
[string]
Which method to use; ADWS (default), LDAP.
.PARAMETER Collect
[array]
Which modules to run; Tenant, Forest, Domain, Trusts, Sites, Subnets, PasswordPolicy, FineGrained
.PARAMETER DomainController
[string]
IP Address of the Domain Controller.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.PARAMETER OutputDir
[string]
■Path for ADRecon output folder to save the CSV files and the ADRecon-Report.xlsx.
.PARAMETER DormantTimeSpan
[int]
Timespan for Dormant accounts. Default 90 days.
.PARAMETER PassMaxAge
[int]
Maximum machine account password age. Default 30 days
.PARAMETER PageSize
[int]
The PageSize to set for the LDAP searcher object. Default 200.
.PARAMETER Threads
[int]
The number of threads to use during processing of objects. Default 10.
.PARAMETER OnlyEnabled
[bool]
Only collect details for enabled objects.
.PARAMETER UseAltCreds
[bool]
Whether to use provided credentials or not.
.PARAMETER Logo
[string]
Which Logo to use in the excel file? ADRecon (default), CyberCX, Payatu.
.OUTPUTS
STDOUT, CSV, XML, JSON, HTML and/or Excel file is created in the folder specified with the inform
#>
param(
[Parameter(Mandatory = $false)]
[string] $GenExcel,
[Parameter(Mandatory = $false)]
[ValidateSet('ADWS', 'LDAP')]
[string] $Method = 'ADWS',
[Parameter(Mandatory = $false)]
[array] $Collect = 'Default',
[Parameter(Mandatory = $false)]
[string] $DomainController = '',
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::E
[Parameter(Mandatory = $false)]
[array] $OutputType = 'Default',
[Parameter(Mandatory = $false)]
[string] $ADROutputDir,
[Parameter(Mandatory = $false)]
[int] $DormantTimeSpan = 90,
[Parameter(Mandatory = $false)]
[int] $PassMaxAge = 30,
[Parameter(Mandatory = $false)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false)]
[int] $Threads = 10,
[Parameter(Mandatory = $false)]
[bool] $OnlyEnabled = $false,
[Parameter(Mandatory = $false)]
[bool] $UseAltCreds = $false,
[Parameter(Mandatory = $false)]
[ValidateSet('ADRecon', 'CyberCX', 'Payatu')]
[string] $Logo = "ADRecon"
)
If ($PSVersionTable.PSEdition -eq "Core")
{
If ($PSVersionTable.Platform -ne "Win32NT")
{
Write-Warning "[Invoke-ADRecon] Currently not supported ... Exiting"
Return $null
}
}
[string] $ADReconVersion = "v1.27"
Write-Output "[*] ADRecon $ADReconVersion by Prashant Mahajan (@prashant3535)"
If ($GenExcel)
{
If (-Not (Test-Path $GenExcel))
{
Write-Output "[Invoke-ADRecon] Invalid Path ... Exiting"
Return $null
}
Export-ADRExcel -ExcelPath $GenExcel -Logo $Logo
Return $null
}
# Suppress verbose output
$SaveVerbosePreference = $script:VerbosePreference
$script:VerbosePreference = 'SilentlyContinue'
Try
{
If ($PSVersionTable.PSVersion.Major -ne 2)
{
$computer = Get-CimInstance -ClassName Win32_ComputerSystem
$computerdomainrole = ($computer).DomainRole
}
Else
{
$computer = Get-WMIObject win32_computersystem
$computerdomainrole = ($computer).DomainRole
}
}
Catch
{
Write-Output "[Invoke-ADRecon] $($_.Exception.Message)"
}
If ($SaveVerbosePreference)
{
$script:VerbosePreference = $SaveVerbosePreference
Remove-Variable SaveVerbosePreference
}
switch ($computerdomainrole)
{
0
{
[string] $computerrole = "Standalone Workstation"
$Env:ADPS_LoadDefaultDrive = 0
$UseAltCreds = $true
}
1 { [string] $computerrole = "Member Workstation" }
2
{
[string] $computerrole = "Standalone Server"
$UseAltCreds = $true
$Env:ADPS_LoadDefaultDrive = 0
}
3 { [string] $computerrole = "Member Server" }
4 { [string] $computerrole = "Backup Domain Controller" }
5 { [string] $computerrole = "Primary Domain Controller" }
default { Write-Output "Computer Role could not be identified." }
}
$RanonComputer = "$($computer.domain)\$([Environment]::MachineName) - $($computerrole)"
Remove-Variable computer
Remove-Variable computerdomainrole
Remove-Variable computerrole
# If either DomainController or Credentials are provided, treat as non-member
If (($DomainController -ne "") -or ($Credential -ne [Management.Automation.PSCredential]::Empty))
{
# Disable loading of default drive on member
If (($Method -eq 'ADWS') -and (-Not $UseAltCreds))
{
$Env:ADPS_LoadDefaultDrive = 0
}
$UseAltCreds = $true
}
# Import ActiveDirectory module
If ($Method -eq 'ADWS')
{
If (Get-Module -ListAvailable -Name ActiveDirectory)
{
Try
{
# Suppress verbose output on module import
$SaveVerbosePreference = $script:VerbosePreference;
$script:VerbosePreference = 'SilentlyContinue';
Import-Module ActiveDirectory -WarningAction Stop -ErrorAction Stop | Out-Null
If ($SaveVerbosePreference)
{
$script:VerbosePreference = $SaveVerbosePreference
Remove-Variable SaveVerbosePreference
}
}
Catch
{
Write-Warning "[Invoke-ADRecon] Error importing ActiveDirectory Module from RSAT (Remo
$Method = 'LDAP'
If ($SaveVerbosePreference)
{
$script:VerbosePreference = $SaveVerbosePreference
Remove-Variable SaveVerbosePreference
}
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
Else
{
Write-Warning "[Invoke-ADRecon] ActiveDirectory Module from RSAT (Remote Server Administ
$Method = 'LDAP'
}
}
# Compile C# code
# Suppress Debug output
$SaveDebugPreference = $script:DebugPreference
$script:DebugPreference = 'SilentlyContinue'
Try
{
$Advapi32 = Add-Type -MemberDefinition $Advapi32Def -Name "Advapi32" -Namespace ADReco
$Kernel32 = Add-Type -MemberDefinition $Kernel32Def -Name "Kernel32" -Namespace ADRecon
$CLR = ([System.Reflection.Assembly]::GetExecutingAssembly().ImageRuntimeVersion)[1]
If ($Method -eq 'ADWS')
{
If ($PSVersionTable.PSEdition -eq "Core")
{
# Beginning in PowerShell 6, ReferencedAssemblies doesn't include the default .NET assemb
# https://docs.microsoft.com/en-gb/powershell/module/Microsoft.PowerShell.Utility/Add-Type?
<#
TODO: Instead of all assemblies, identify which ones are required.
$refFolder = Split-Path ([PSObject].Assembly.Location)
$refAssemblies = Get-ChildItem -Path $refFolder -Filter "*.dll" | Select-Object -Expand FullNa
Add-Type -TypeDefinition $($ADWSSource + $PingCastleSMBScannerSource) -ReferencedA
Remove-Variable refFolder
Remove-Variable refAssemblies
#>
$refFolder = Join-Path -Path (Split-Path([PSObject].Assembly.Location)) -ChildPath "ref"
Add-Type -TypeDefinition $($ADWSSource + $PingCastleSMBScannerSource) -ReferencedA
(Join-Path -Path $refFolder -ChildPath "System.Collections.dll")
(Join-Path -Path $refFolder -ChildPath "System.Collections.NonGeneric.dll")
(Join-Path -Path $refFolder -ChildPath "System.Threading.dll")
(Join-Path -Path $refFolder -ChildPath "System.Threading.Thread.dll")
(Join-Path -Path $refFolder -ChildPath "System.Diagnostics.TraceSource.dll")
([System.Reflection.Assembly]::LoadWithPartialName("mscorlib")).Location
([System.Reflection.Assembly]::LoadWithPartialName("System.Linq")).Location
([System.Reflection.Assembly]::LoadWithPartialName("System.Private.Xml")).Location
([System.Reflection.Assembly]::LoadWithPartialName("System.Security.AccessControl")).L
([System.Reflection.Assembly]::LoadWithPartialName("System.Net.Sockets")).Location
([System.Reflection.Assembly]::LoadWithPartialName("System.Net.Primitives")).Location
([System.Reflection.Assembly]::LoadWithPartialName("System.Security.Principal.Windows
([System.Reflection.Assembly]::LoadWithPartialName("System.IO.FileSystem.AccessCont
([System.Reflection.Assembly]::LoadWithPartialName("System.Console")).Location
([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.ActiveDirectory.Managem
([System.Reflection.Assembly]::LoadWithPartialName("System.Management.Automation")
([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).Locati
))
Remove-Variable refFolder
}
If ($CLR -eq "4")
{
Add-Type -TypeDefinition $($ADWSSource+$PingCastleSMBScannerSource) -ReferencedA
([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.ActiveDirectory.Managem
([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).Locati
([System.Reflection.Assembly]::LoadWithPartialName("System.XML")).Location
))
}
Else
{
Add-Type -TypeDefinition $($ADWSSource+$PingCastleSMBScannerSource) -ReferencedA
([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.ActiveDirectory.Managem
([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).Locati
([System.Reflection.Assembly]::LoadWithPartialName("System.XML")).Location
)) -Language CSharpVersion3
}
}
If ($Method -eq 'LDAP')
{
If ($PSVersionTable.PSEdition -eq "Core")
{
# Beginning in PowerShell 6, ReferencedAssemblies doesn't include the default .NET assemb
# https://docs.microsoft.com/en-gb/powershell/module/Microsoft.PowerShell.Utility/Add-Type?
<#
# TODO: Instead of all assemblies, identify which ones are required.
$refFolder = Join-Path ( Split-Path ([PSObject].Assembly.Location) ) "ref"
$refAssemblies = Get-ChildItem -Path $refFolder -Filter "*.dll" | Select-Object -Expand FullNa
Add-Type -TypeDefinition $($LDAPSource + $PingCastleSMBScannerSource) -ReferencedA
Remove-Variable refFolder
Remove-Variable refAssemblies
#>
$refFolder = Join-Path -Path (Split-Path([PSObject].Assembly.Location)) -ChildPath "ref"
Add-Type -TypeDefinition $($LDAPSource + $PingCastleSMBScannerSource) -ReferencedA
(Join-Path -Path $refFolder -ChildPath "System.Collections.dll")
(Join-Path -Path $refFolder -ChildPath "System.Collections.NonGeneric.dll")
(Join-Path -Path $refFolder -ChildPath "System.Threading.dll")
(Join-Path -Path $refFolder -ChildPath "System.Threading.Thread.dll")
(Join-Path -Path $refFolder -ChildPath "System.Diagnostics.TraceSource.dll")
([System.Reflection.Assembly]::LoadWithPartialName("System.Linq")).Location
([System.Reflection.Assembly]::LoadWithPartialName("System.Private.Xml")).Location
([System.Reflection.Assembly]::LoadWithPartialName("System.Security.AccessControl")).L
([System.Reflection.Assembly]::LoadWithPartialName("System.Net.Sockets")).Location
([System.Reflection.Assembly]::LoadWithPartialName("System.Net.Primitives")).Location
([System.Reflection.Assembly]::LoadWithPartialName("System.Security.Principal.Windows
([System.Reflection.Assembly]::LoadWithPartialName("System.IO.FileSystem.AccessCont
([System.Reflection.Assembly]::LoadWithPartialName("System.Net.NameResolution")).Loc
([System.Reflection.Assembly]::LoadWithPartialName("System.Console")).Location
([System.Reflection.Assembly]::LoadWithPartialName("System.Management.Automation")
([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).Locati
))
Remove-Variable refFolder
}
Else
{
If ($CLR -eq "4")
{
Add-Type -TypeDefinition $($LDAPSource+$PingCastleSMBScannerSource) -Referenced
([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).Loc
([System.Reflection.Assembly]::LoadWithPartialName("System.XML")).Location
))
}
Else
{
Add-Type -TypeDefinition $($LDAPSource+$PingCastleSMBScannerSource) -Referenced
([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).Loc
([System.Reflection.Assembly]::LoadWithPartialName("System.XML")).Location
)) -Language CSharpVersion3
}
}
}
}
Catch
{
Write-Output "[Invoke-ADRecon] $($_.Exception.Message)"
Return $null
}
If ($SaveDebugPreference)
{
$script:DebugPreference = $SaveDebugPreference
Remove-Variable SaveDebugPreference
}
# Allow running using RUNAS from a non-domain joined machine
# runas /user:<Domain FQDN>\<Username> /netonly powershell.exe
If (($Method -eq 'LDAP') -and ($UseAltCreds) -and ($DomainController -eq "") -and ($Credential -eq
{
Try
{
$objDomain = [ADSI]""
If(!($objDomain.name))
{
Write-Verbose "[Invoke-ADRecon] RUNAS Check, LDAP bind Unsuccessful"
}
$UseAltCreds = $false
$objDomain.Dispose()
}
Catch
{
$UseAltCreds = $true
}
}
If ($UseAltCreds -and (($DomainController -eq "") -or ($Credential -eq [Management.Automation.PSC
{
If (($DomainController -ne "") -and ($Credential -eq [Management.Automation.PSCredential]::Emp
{
Try
{
$Credential = Get-Credential
}
Catch
{
Write-Output "[Invoke-ADRecon] $($_.Exception.Message)"
Return $null
}
}
Else
{
Write-Output "Run Get-Help .\ADRecon.ps1 -Examples for additional information."
Write-Output "[Invoke-ADRecon] Use the -DomainController and -Credential parameter."`n
Return $null
}
}
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$Username = $($Credential.UserName)
}
Else
{
$Username = $([Environment]::UserName)
}
Write-Output "[*] Running on $RanonComputer as $Username"
Remove-Variable Username
Switch ($Collect)
{
'Forest' { $ADRForest = $true }
'Domain' {$ADRDomain = $true }
'Trusts' { $ADRTrust = $true }
'Sites' { $ADRSite = $true }
'Subnets' { $ADRSubnet = $true }
'SchemaHistory' { $ADRSchemaHistory = $true }
'PasswordPolicy' { $ADRPasswordPolicy = $true }
'FineGrainedPasswordPolicy' { $ADRFineGrainedPasswordPolicy = $true }
'DomainControllers' { $ADRDomainControllers = $true }
'Users' { $ADRUsers = $true }
'UserSPNs' { $ADRUserSPNs = $true }
'PasswordAttributes' { $ADRPasswordAttributes = $true }
'Groups' {$ADRGroups = $true }
'GroupChanges' { $ADRGroupChanges = $true }
'GroupMembers' { $ADRGroupMembers = $true }
'OUs' { $ADROUs = $true }
'GPOs' { $ADRGPOs = $true }
'gPLinks' { $ADRgPLinks = $true }
'DNSZones' { $ADRDNSZones = $true }
'DNSRecords' { $ADRDNSRecords = $true }
'Printers' { $ADRPrinters = $true }
'Computers' { $ADRComputers = $true }
'ComputerSPNs' { $ADRComputerSPNs = $true }
'LAPS' { $ADRLAPS = $true }
'BitLocker' { $ADRBitLocker = $true }
'ACLs' { $ADRACLs = $true }
'GPOReport'
{
$ADRGPOReport = $true
$ADRCreate = $true
}
'Kerberoast' { $ADRKerberoast = $true }
'DomainAccountsusedforServiceLogon' { $ADRDomainAccountsusedforServiceLogon = $true }
'Default'
{
$ADRForest = $true
$ADRDomain = $true
$ADRTrust = $true
$ADRSite = $true
$ADRSubnet = $true
$ADRSchemaHistory = $true
$ADRPasswordPolicy = $true
$ADRFineGrainedPasswordPolicy = $true
$ADRDomainControllers = $true
$ADRUsers = $true
$ADRUserSPNs = $true
$ADRPasswordAttributes = $true
$ADRGroups = $true
$ADRGroupMembers = $true
$ADRGroupChanges = $true
$ADROUs = $true
$ADRGPOs = $true
$ADRgPLinks = $true
$ADRDNSZones = $true
$ADRDNSRecords = $true
$ADRPrinters = $true
$ADRComputers = $true
$ADRComputerSPNs = $true
$ADRLAPS = $true
$ADRBitLocker = $true
#$ADRACLs = $true
$ADRGPOReport = $true
#$ADRKerberoast = $true
#$ADRDomainAccountsusedforServiceLogon = $true
If ($OutputType -eq "Default")
{
[array] $OutputType = "CSV","Excel"
}
}
}
Switch ($OutputType)
{
'STDOUT' { $ADRSTDOUT = $true }
'CSV'
{
$ADRCSV = $true
$ADRCreate = $true
}
'XML'
{
$ADRXML = $true
$ADRCreate = $true
}
'JSON'
{
$ADRJSON = $true
$ADRCreate = $true
}
'HTML'
{
$ADRHTML = $true
$ADRCreate = $true
}
'Excel'
{
$ADRExcel = $true
$ADRCreate = $true
}
'All'
{
#$ADRSTDOUT = $true
$ADRCSV = $true
$ADRXML = $true
$ADRJSON = $true
$ADRHTML = $true
$ADRExcel = $true
$ADRCreate = $true
[array] $OutputType = "CSV","XML","JSON","HTML","Excel"
}
'Default'
{
[array] $OutputType = "STDOUT"
$ADRSTDOUT = $true
}
}
If ( ($ADRExcel) -and (-Not $ADRCSV) )
{
$ADRCSV = $true
[array] $OutputType += "CSV"
}
$returndir = Get-Location
$date = Get-Date
# Create Output dir
If ( ($ADROutputDir) -and ($ADRCreate) )
{
If (!(Test-Path $ADROutputDir))
{
New-Item $ADROutputDir -type directory | Out-Null
If (!(Test-Path $ADROutputDir))
{
Write-Output "[Invoke-ADRecon] Error, invalid OutputDir Path ... Exiting"
Return $null
}
}
$ADROutputDir = $((Convert-Path $ADROutputDir).TrimEnd("\"))
Write-Verbose "[*] Output Directory: $ADROutputDir"
}
ElseIf ($ADRCreate)
{
$ADROutputDir = -join($returndir,'\','ADRecon-Report-',$(Get-Date -UFormat %Y%m%d%H%M%
New-Item $ADROutputDir -type directory | Out-Null
If (!(Test-Path $ADROutputDir))
{
Write-Output "[Invoke-ADRecon] Error, could not create output directory"
Return $null
}
$ADROutputDir = $((Convert-Path $ADROutputDir).TrimEnd("\"))
Remove-Variable ADRCreate
}
Else
{
$ADROutputDir = $returndir
}
If ($ADRCSV)
{
$CSVPath = [System.IO.DirectoryInfo] -join($ADROutputDir,'\','CSV-Files')
New-Item $CSVPath -type directory | Out-Null
If (!(Test-Path $CSVPath))
{
Write-Output "[Invoke-ADRecon] Error, could not create output directory"
Return $null
}
Remove-Variable ADRCSV
}
If ($ADRXML)
{
$XMLPath = [System.IO.DirectoryInfo] -join($ADROutputDir,'\','XML-Files')
New-Item $XMLPath -type directory | Out-Null
If (!(Test-Path $XMLPath))
{
Write-Output "[Invoke-ADRecon] Error, could not create output directory"
Return $null
}
Remove-Variable ADRXML
}
If ($ADRJSON)
{
$JSONPath = [System.IO.DirectoryInfo] -join($ADROutputDir,'\','JSON-Files')
New-Item $JSONPath -type directory | Out-Null
If (!(Test-Path $JSONPath))
{
Write-Output "[Invoke-ADRecon] Error, could not create output directory"
Return $null
}
Remove-Variable ADRJSON
}
If ($ADRHTML)
{
$HTMLPath = [System.IO.DirectoryInfo] -join($ADROutputDir,'\','HTML-Files')
New-Item $HTMLPath -type directory | Out-Null
If (!(Test-Path $HTMLPath))
{
Write-Output "[Invoke-ADRecon] Error, could not create output directory"
Return $null
}
Remove-Variable ADRHTML
}
# AD Login
If ($UseAltCreds -and ($Method -eq 'ADWS'))
{
If (!(Test-Path ADR:))
{
Try
{
New-PSDrive -PSProvider ActiveDirectory -Name ADR -Root "" -Server $DomainController -C
}
Catch
{
Write-Output "[Invoke-ADRecon] $($_.Exception.Message)"
If ($ADROutputDir)
{
Remove-EmptyADROutputDir $ADROutputDir $OutputType
}
Return $null
}
}
Else
{
Remove-PSDrive ADR
Try
{
New-PSDrive -PSProvider ActiveDirectory -Name ADR -Root "" -Server $DomainController -C
}
Catch
{
Write-Output "[Invoke-ADRecon] $($_.Exception.Message)"
If ($ADROutputDir)
{
Remove-EmptyADROutputDir $ADROutputDir $OutputType
}
Return $null
}
}
Set-Location ADR:
Write-Debug "ADR PSDrive Created"
#return $null
}
If ($Method -eq 'LDAP')
{
If ($UseAltCreds)
{
Try
{
$objDomain = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainCont
$objDomainRootDSE = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($Do
}
Catch
{
Write-Output "[Invoke-ADRecon] $($_.Exception.Message)"
If ($ADROutputDir)
{
Remove-EmptyADROutputDir $ADROutputDir $OutputType
}
Return $null
}
If(!($objDomain.name))
{
Write-Output "[Invoke-ADRecon] LDAP bind Unsuccessful"
If ($ADROutputDir)
{
Remove-EmptyADROutputDir $ADROutputDir $OutputType
}
Return $null
}
Else
{
Write-Output "[*] LDAP bind Successful"
}
}
Else
{
$objDomain = [ADSI]""
$objDomainRootDSE = ([ADSI] "LDAP://RootDSE")
If(!($objDomain.name))
{
Write-Output "[Invoke-ADRecon] LDAP bind Unsuccessful"
If ($ADROutputDir)
{
Remove-EmptyADROutputDir $ADROutputDir $OutputType
}
Return $null
}
}
Write-Debug "LDAP Bing Successful"
#return $null
}
Write-Output "[*] Commencing - $date"
If ($ADRDomain)
{
Write-Output "[-] Domain"
$ADRObject = Get-ADRDomain -Method $Method -objDomain $objDomain -objDomainRootDSE $
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRDomain
}
If ($ADRForest)
{
Write-Output "[-] Forest"
$ADRObject = Get-ADRForest -Method $Method -objDomain $objDomain -objDomainRootDSE $o
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRForest
}
If ($ADRTrust)
{
Write-Output "[-] Trusts"
$ADRObject = Get-ADRTrust -Method $Method -objDomain $objDomain
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRTrust
}
If ($ADRSite)
{
Write-Output "[-] Sites"
$ADRObject = Get-ADRSite -Method $Method -objDomain $objDomain -objDomainRootDSE $obj
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRSite
}
If ($ADRSubnet)
{
Write-Output "[-] Subnets"
$ADRObject = Get-ADRSubnet -Method $Method -objDomain $objDomain -objDomainRootDSE $
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRSubnet
}
If ($ADRSchemaHistory)
{
Write-Output "[-] SchemaHistory - May take some time"
$ADRObject = Get-ADRSchemaHistory -Method $Method -objDomain $objDomain -objDomainRo
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRSchemaHistory
}
If ($ADRPasswordPolicy)
{
Write-Output "[-] Default Password Policy"
$ADRObject = Get-ADRDefaultPasswordPolicy -Method $Method -objDomain $objDomain
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRPasswordPolicy
}
If ($ADRFineGrainedPasswordPolicy)
{
Write-Output "[-] Fine Grained Password Policy - May need a Privileged Account"
$ADRObject = Get-ADRFineGrainedPasswordPolicy -Method $Method -objDomain $objDomain
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRFineGrainedPasswordPolicy
}
If ($ADRDomainControllers)
{
Write-Output "[-] Domain Controllers"
$ADRObject = Get-ADRDomainController -Method $Method -objDomain $objDomain -Credential $
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRDomainControllers
}
If ($ADRUsers -or $ADRUserSPNs)
{
If (!$ADRUserSPNs)
{
Write-Output "[-] Users - May take some time"
$ADRUserSPNs = $false
}
ElseIf (!$ADRUsers)
{
Write-Output "[-] User SPNs"
$ADRUsers = $false
}
Else
{
Write-Output "[-] Users and SPNs - May take some time"
}
Get-ADRUser -Method $Method -date $date -objDomain $objDomain -DormantTimeSpan $Dorma
Remove-Variable ADRUsers
Remove-Variable ADRUserSPNs
}
If ($ADRPasswordAttributes)
{
Write-Output "[-] PasswordAttributes - Experimental"
$ADRObject = Get-ADRPasswordAttributes -Method $Method -objDomain $objDomain -PageSize
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRPasswordAttributes
}
If ($ADRGroups -or $ADRGroupChanges)
{
If (!$ADRGroupChanges)
{
Write-Output "[-] Groups - May take some time"
$ADRGroupChanges = $false
}
ElseIf (!$ADRGroups)
{
Write-Output "[-] Group Membership Changes - May take some time"
$ADRGroups = $false
}
Else
{
Write-Output "[-] Groups and Membership Changes - May take some time"
}
Get-ADRGroup -Method $Method -date $date -objDomain $objDomain -PageSize $PageSize -Thr
Remove-Variable ADRGroups
Remove-Variable ADRGroupChanges
}
If ($ADRGroupMembers)
{
Write-Output "[-] Group Memberships - May take some time"
$ADRObject = Get-ADRGroupMember -Method $Method -objDomain $objDomain -PageSize $Pa
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRGroupMembers
}
If ($ADROUs)
{
Write-Output "[-] OrganizationalUnits (OUs)"
$ADRObject = Get-ADROU -Method $Method -objDomain $objDomain -PageSize $PageSize -Thr
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADROUs
}
If ($ADRGPOs)
{
Write-Output "[-] GPOs"
$ADRObject = Get-ADRGPO -Method $Method -objDomain $objDomain -PageSize $PageSize -T
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRGPOs
}
If ($ADRgPLinks)
{
Write-Output "[-] gPLinks - Scope of Management (SOM)"
$ADRObject = Get-ADRgPLink -Method $Method -objDomain $objDomain -PageSize $PageSize -
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRgPLinks
}
If ($ADRDNSZones -or $ADRDNSRecords)
{
If (!$ADRDNSRecords)
{
Write-Output "[-] DNS Zones"
$ADRDNSRecords = $false
}
ElseIf (!$ADRDNSZones)
{
Write-Output "[-] DNS Records"
$ADRDNSZones = $false
}
Else
{
Write-Output "[-] DNS Zones and Records"
}
Get-ADRDNSZone -Method $Method -objDomain $objDomain -DomainController $DomainContro
Remove-Variable ADRDNSZones
}
If ($ADRPrinters)
{
Write-Output "[-] Printers"
$ADRObject = Get-ADRPrinter -Method $Method -objDomain $objDomain -PageSize $PageSize -
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRPrinters
}
If ($ADRComputers -or $ADRComputerSPNs)
{
If (-Not $ADRComputerSPNs)
{
Write-Output "[-] Computers - May take some time"
$ADRComputerSPNs = $false
}
ElseIf (-Not $ADRComputers)
{
Write-Output "[-] Computer SPNs"
$ADRComputers = $false
}
Else
{
Write-Output "[-] Computers and SPNs - May take some time"
}
Get-ADRComputer -Method $Method -date $date -objDomain $objDomain -DormantTimeSpan $D
Remove-Variable ADRComputers
Remove-Variable ADRComputerSPNs
}
If ($ADRLAPS)
{
Write-Output "[-] LAPS - Needs Privileged Account to get the passwords"
$ADRLAPSCheck = Get-ADRLAPSCheck -Method $Method -objDomainRootDSE $objDomainRoo
If ($ADRLAPSCheck)
{
$ADRObject = Get-ADRLAPS -Method $Method -objDomain $objDomain -PageSize $PageSize
}
Else
{
Write-Warning "[*] LAPS is not implemented."
}
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRLAPS
}
If ($ADRBitLocker)
{
Write-Output "[-] BitLocker Recovery Keys - Needs Privileged Account"
$ADRObject = Get-ADRBitLocker -Method $Method -objDomain $objDomain -DomainController $
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRBitLocker
}
If ($ADRACLs)
{
Write-Output "[-] ACLs - May take some time"
$ADRObject = Get-ADRACL -Method $Method -objDomain $objDomain -DomainController $Doma
Remove-Variable ADRACLs
}
If ($ADRGPOReport)
{
Write-Output "[-] GPOReport - May take some time"
Get-ADRGPOReport -Method $Method -UseAltCreds $UseAltCreds -ADROutputDir $ADROutputD
Remove-Variable ADRGPOReport
}
If ($ADRKerberoast)
{
Write-Output "[-] Kerberoast"
$ADRObject = Get-ADRKerberoast -Method $Method -objDomain $objDomain -Credential $Crede
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRKerberoast
}
If ($ADRDomainAccountsusedforServiceLogon)
{
Write-Output "[-] Domain Accounts used for Service Logon - Needs Privileged Account"
$ADRObject = Get-ADRDomainAccountsusedforServiceLogon -Method $Method -objDomain $obj
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -
Remove-Variable ADRObject
}
Remove-Variable ADRDomainAccountsusedforServiceLogon
}
$TotalTime = "{0:N2}" -f ((Get-DateDiff -Date1 (Get-Date) -Date2 $date).TotalMinutes)
$AboutADRecon = Get-ADRAbout -Method $Method -date $date -ADReconVersion $ADReconVersi
If ( ($OutputType -Contains "CSV") -or ($OutputType -Contains "XML") -or ($OutputType -Contains "
{
If ($AboutADRecon)
{
Export-ADR -ADRObj $AboutADRecon -ADROutputDir $ADROutputDir -OutputType $OutputTy
}
Write-Output "[*] Total Execution Time (mins): $($TotalTime)"
Write-Output "[*] Output Directory: $ADROutputDir"
$ADRSTDOUT = $false
}
Switch ($OutputType)
{
'STDOUT'
{
If ($ADRSTDOUT)
{
Write-Output "[*] Total Execution Time (mins): $($TotalTime)"
}
}
'HTML'
{
Export-ADR -ADRObj $(New-Object PSObject) -ADROutputDir $ADROutputDir -OutputType $([
}
'EXCEL'
{
Export-ADRExcel -ExcelPath $ADROutputDir -Logo $Logo
}
}
Remove-Variable TotalTime
Remove-Variable AboutADRecon
Set-Location $returndir
Remove-Variable returndir
If (($Method -eq 'ADWS') -and $UseAltCreds)
{
Remove-PSDrive ADR
}
If ($Method -eq 'LDAP')
{
$objDomain.Dispose()
$objDomainRootDSE.Dispose()
}
If ($ADROutputDir)
{
Remove-EmptyADROutputDir $ADROutputDir $OutputType
}
Remove-Variable ADReconVersion
Remove-Variable RanonComputer
}
If ($Log)
{
Start-Transcript -Path "$(Get-Location)\ADRecon-Console-Log.txt"
}
Invoke-ADRecon -GenExcel $GenExcel -Method $Method -Collect $Collect -DomainController $Domai
If ($Log)
{
Stop-Transcript
}