As the Identity and Authentication source of most Enterprises, Active Directory is the backbone of local and federated authentication. Coupled with the prevalence of Cloud computing, organizations are depending more-and-more on federated authentication and expanding their Active Directory into the Cloud.

While most organizations upgrade their Active Directory through Domain Controller OS upgrades, AD by design is meant to be as backwards compatible as possible, and therefore leaves many legacy protocols and configuration settings enabled. Many Domains originated long ago, and have been upgraded throughout the years, but the legacy protocols and settings enabled from the onset were never disabled. Attackers have developed automated tools to exploit these weaknesses, that while legitimate, are insecure none the less.

The firewall and network perimeter can no longer be considered the security boundary. You must assume that an internal user will inadvertently click on something and become compromised. The workstation is now the security boundary, and the older the origins of the AD network, the easier it becomes for an attacker to perform reconnaissance, compromise network traffic, sniff passwords, and move throughout your network.

With each new OS release from Microsoft come a series of improvements, many aimed at the stability and recoverability of Active Directory, but many are only enabled once extra steps are taken to configure and implement them. The audit outlined below will report what’s currently enabled, what should be disabled, ensures AD health, and provides recommendations for further securing AD based on your unique environment.

“An ounce of prevention is better than having to rebuild your entire Domain from backup.”

– Benjamin Franklin

Requirements

RSAT Tools

PowerSploit (Recon and Exfiltration)

Group Policy

Run Powershell GP-Link report Shows OU tree and applied GP

Backup all GPOs Backup-GPO -All -Path c:\GPOBackup

Check CentralStore for ADMX storage Test-Path \\<DC_Name>\SYSVOL\<YOURDOMAIN>\Policies\PolicyDefinitions

Are there ADM GPOs or have all been converted to ADMX dir/W *.adm /S on DC SYSVOL

Check SYSVOL Contents consistent across DCs? Only on Server 2012R2 or 2016 DFL Open GPMC > Domain Name > Status tab: Detect Now FRS or DFSR in use? Run dfsrmig /getmigrationstate on a Domain Controller State ‘Eliminated’ means DFSR is in use Check DFSR health This DFS and SYSVOL Monitor script will count GPO objects. Run ./Get-DomainDFSHealth.ps1 to load function into memory Run Get-DomainDFSHealth function to generate the report

Is DFSR configured for automated recovery? Default as of 2008R2 is to disable AutoRecover - this is to prevent data loss from unsynchronized upstream server http://wiki.ledhed.net/index.php?title=DFS_AutoRecovery Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\DFSR\Parameters\"" | select StopReplicationOnAutoRecovery Run this on a DC. “0” or no key means disabled.

Audit Group Policy creators and permissions The owner has the right to change GP. If a delegated Admin created the policy and it was moved, they retain permissions Get-GPO -All | Select DisplayName,Owner PowerView provides a quick way to scan all the permissions for all domain GPOs: Get-NetGPO | %{Get-ObjectAcl -ResolveGUIDs -Name $_.Name} |select ObjectDN, IdentityReference, ActiveDirectoryRights > GP_Permissions.txt

Use PolicyAnalyzer from the Security Compliance Toolkit to compare GP to Microsoft recommendations More details here

Check for orphaned GPOs Get-GPOMappings

Check existing password Policy Get-ADDefaultDomainPasswordPolicy

Check User Rights Assignment in Default Domain Controller Policy Easiest way is to use GPMC to show policy settings, highlight and copy, paste into notepad, and open in Excel

Check GPO local group assignment Get-NetGPOGroup gets all GPOs in a Domain that set “Restricted Groups” on on target machines

Check GPOs for stored passwords Get-GPPPassword Get-GPPAutologon Most of the time, the following XML files will contain credentials: groups.xml, scheduledtasks.xml, & Services.xml. The credentials are AES-256 bit encrypted but Microsoft has published the AES encryption key on MSDN which can be used to decrypt the password. Since authenticated users (any domain user or users in a trusted domain) have read access to SYSVOL, anyone in the domain can search the SYSVOL share for XML files containing “cpassword” which is the value that contains the AES encrypted password.



Domain Controllers

Get a list of all DCs, verify all are Global Catalog servers Get-ADDomainController -Filter * | Select Domain,Name,IPv4Address,IsGlobalCatalog,Site,OperatingSystem

Disable any unused network adapters - DCs should only have a single adapter netsh interface ipv4 show interfaces

Check for correct DNS client configuration Point to partner as primary and self as secondary netsh interface ipv4 show dnsservers

Check for existence of Backups Full image backup Date of last restoration test? In a secure location? Using third-party software? System State backup - easily accessible during recovery wbadmin get versions (and repadmin /showbackup )

Eventlog entries PS script to identify most worrisome IDs PS Script to show all record counts Get-WinEvent -ListLog * -EA silentlycontinue | where {$_.RecordCount} | sort RecordCount -Descending PS script to list last 5 errors in all logs Get-WinEvent -ListLog * -EA silentlycontinue | Where-Object {$_.recordcount -AND $_.lastwritetime -ge [datetime]::today} | ForEach-Object {Get-WinEvent -LogName $_.logname -MaxEvents 5 | Where-Object {$_.LevelDisplayName -eq "Error"} } | ft -AutoSize PS script to grab all errors and warnings and sort them by count Get-WinEvent -ListLog * -EA silentlycontinue | Where-Object { $_.recordcount } | ForEach-Object { Get-WinEvent -FilterHashTable @{LogName=$_.logname; StartTime=(get-date).AddDays(-5) } –MaxEvents 1000 | where-object {$_.LevelDisplayName -like 'Error' -OR $_.LevelDisplayName -like 'Warning'} }

Check for installed patches get-hotfix 3011780 #Server 2012 and earlier And all other patches

Check running agents Autoruns and Process Explorer

Check services Get-Service | where {$_.Status -eq "Running"}

Check Service Accounts Get-WmiObject win32_service | where {($_.startname -ne "LocalSystem") -and ($_.startname -ne "NT AUTHORITY\NetworkService") -and ($_.startname -ne "NT AUTHORITY\NETWORK SERVICE") -and ($_.startname -ne "NT AUTHORITY\LocalService") } | FT name, startname, startmode

Check installed Roles and Features Get-WindowsFeature | Where {$_.installed -eq "True"}

Check for additional software running on the DC. Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | select DisplayName,Publisher,InstallDate | Sort DisplayName

Do you know the DSRM password?

Check for correct IPv6 settings Don’t just uncheck the protocol on the adapter, set registry to “Prefer IPv4 over IPv6” Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\ Key DisabledComponents should read 32 for “Prefer IPv4 over IPv6” or 255 to “Disable IPv6”

Check time configuration Have DCs synchronize their time from PDC, and have PDC synchronize via NTP Configure the PDC to automatically synchronize time via GPO or just set manually Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters\ | select Type, NtpServer Servers should report NT5DS (use Domain to find PDC Emulator), and PDC should report NTP w32tm /query /status and w32tm /query /configuration for more info AD Summary report also has time settings

Check Replication and DC Health (from elevated prompt) dcdiag /v /c /d /e /s:DC_Name dcdiag /e /test:DNS /DNSAll repadmin /queue repadmin /replsummary repadmin /showrepl Get-ADReplicationFailure -Target contoso.com -Scope Domain

Harden config Disable services Disable Print Spooler Disable remote registry CISecurity benchmark recommendations NIST recommendations



Active Directory

Run Powershell AD Summary Report Identify FSMO holders Identify DFL and FFL Is the AD Recycle Bin enabled? Are there Trusts in place Check site links and replication in a multi-site environment

Take an AD Explorer snapshot Snapshot comparison can be done in the future

Check tombstone lifetime (if blank value is 60) (Get-ADObject -Identity “CN=Directory Service,CN=Windows NT,CN=Services,$((Get-ADRootDSE).configurationNamingContext)” -Properties tombstoneLifetime).tombstoneLifetime

Further check on trusts and the relationship mapping Invoke-MapDomainTrust recursively maps all reachable domain and forests trusts

Audit service accounts and remove unused ones Find accounts that have never logged on Get-ADUser -Filter {(lastlogontimestamp -notlike "*") -and (enabled -eq $true)} | Select Name,DistinguishedName Audit accounts with non-expiring passwords

Get-ADUser -filter * -properties Name, PasswordNeverExpires | where { $_.passwordNeverExpires -eq "true" } | where {$_.enabled -eq "true"} | fl Name,SamAccountName,PasswordNeverExpires Set long (20+ char) passwords on used service accounts - prevents Kerbroasting Consider using Group Managed Service Accounts Find existing Managed Service Accounts Get-ADServiceAccount -Filter * -Properties *

Find accounts with AdminCount set to 1 Get-ADUser -filter {AdminCount -eq 1} -Properties * | select Name,DistinguishedName,LastLogonDate,Enabled,PasswordNeverExpires,MemberOf

Search AD for interesting accounts and groups Get-ADUser -Filter {name -like "*adm*"} -Properties name | Select-Object Name Get-ADGroup -Filter {name -like "*adm*"} -Properties name | Select-Object Name

Audit Group membership Schema Admins should be empty, Enterprise Admins has Administrator Get-NetGroup "Enterprise Admins" | Get-NetGroupMember | Select GroupName,MemberName Get-NetGroup "Schema Admins" | Get-NetGroupMember | Select GroupName,MemberName Get-NetGroup "Domain Admins" | Get-NetGroupMember | Select GroupName,MemberName Get-NetGroup "Administrators" | Get-NetGroupMember | Select GroupName,MemberName Get-NetGroup "Backup Operators" | Get-NetGroupMember | Select GroupName,MemberName Get-NetGroup "Account Operators" | Get-NetGroupMember | Select GroupName,MemberName Get-NetGroup "DNS Admins" | Get-NetGroupMember | Select GroupName,MemberName Get-NetGroup "Print Operators" | Get-NetGroupMember | Select GroupName,MemberName Get-NetGroup "Server Operators" | Get-NetGroupMember | Select GroupName,MemberName Get-NetGroup "Group Policy Creator Owners" | Get-NetGroupMember | Select GroupName,MemberName Get-NetGroup "Protected Users" | Get-NetGroupMember | Select GroupName,MemberName

Eventlog Ideally implement some form of automated log parsing and alerting Monitor Group Membership, Account creation Requires the correct audit settings

AD Account cleanup Remove accounts that are no longer being used (LastLogonTime) Find accounts where password is older than the password policy deadline. This script will show pwd expiration date for all users. Get-ADUser -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and PasswordLastSet -gt 0 } ` -Properties "Name", "msDS-UserPasswordExpiryTimeComputed" | sort msDS-UserPasswordExpiryTimeComputed | Select-Object -Property "Name", ` @{Name = "PasswordExpiry"; Expression = {[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed").tolongdatestring() }}

Run ADACLScanner to view CSV of AD ACL permissions and create baseline for later use Are there any delegated admins? Compare to results of previous run or download template of default permissions Compare to default “Each NC root combined” template to see permission differences for AdminSDHolder and every AD partition Also run ACLight, it will focus on finding privileged access

Consider running zBang which combines ACLight with a Skeleton Key scan, SID History scan, Risky SPN scan, and Mystique scan

Find accounts with DS-Replication

Find accounts with the SIDHistory attribute set Get-ADUser -Filter * -Property sIDHistory | Where sIDHistory | Select-Object name, sIDHistory -ExpandProperty sidHistory | Format-Table name, sIDHistory –AutoSize Powershell module for working with SID History Copy to PS Modules Dir, Import-Module sidhistory, Export-SIDMapping Since the user’s SID changes when the new account is created, the old SID needs to map to the new one. When a user in Domain A is migrated to Domain B, a new user account is created in DomainB and DomainA user’s SID is added to DomainB’s user account’s SID History attribute. This ensures that DomainB user can still access resources in DomainA. If forged however it can also grant permissions in DomainA.

Check for computers with Unconstrained Delegation Use Search-KerbDelegatedAccounts.ps1 from Github Get-ADcomputer -Filter {(TrustedForDelegation -eq $True) -AND (PrimaryGroupID -eq 515)} -Properties TrustedForDelegation,TrustedToAuthForDelegation,servicePrincipalName,Description

Consider adding Admin accounts to Protected Users. Place user accounts in the group - but until thoroughly tested do not add all Administrative accounts so you don’t lock out all accounts. All DCs must be 2008 or later This group blocks its members from using Kerberos delegation, blocks NTLM, forces AES, disables cached logon, and more. If not, at least check the box “Account is sensitive and cannot be delegated”. This ensures that an account’s credentials cannot be forwarded to other computers or services on the network by a trusted application.



DNS

dnscmd /enumzones Check for proper replication partition placement of each zone Domain zone in Domain Partition _msdcs.Domain.com in Forest Partition

Check Forwarders and Root Hints

Check for proper registration of NS and SRV records and cleanup old stale records Get-DnsServerResourceRecord -RRType SRV -ZoneName "contoso.com" | sort Hostname | ft -AutoSize | Out-File -filepath C:\DNS_Records.txt -NoClobber -Width 200 Get-DnsServerResourceRecord -RRType SRV -ZoneName "_msdcs.contoso.com" | sort Hostname | ft -AutoSize | Out-File -filepath C:\DNS_Records.txt -Append -Width 200 Get-DnsServerResourceRecord -RRType NS -ZoneName "contoso.com" | sort Hostname | ft -AutoSize | Out-File -filepath C:\DNS_Records.txt -Append -Width 200 Get-DnsServerResourceRecord -RRType NS -ZoneName "_msdcs.contoso.com" | sort Hostname | ft -AutoSize | Out-File -filepath C:\DNS_Records.txt -Append -Width 200

Check if scavenging enabled on each zone Only configure on a single server dnscmd <DCName> /info

Check if Secure updates are enabled to other Non-AD DNS servers

External DNS Check dnsstuff.com

NetBIOS and WINS setup Either disable NetBIOS or have a WINS server to prevent broadcasts Who is Browse Master? Start Computer Browser service on a server on each subnet otherwise a workstation will win election Disable NetBIOS through DHCP (workstations) or with a script (servers)



Cloud

Check Azure subscription level. You need a Premium subscription to access Azure AD logs

Centralize Cloud logs along with on-prem logs (Azure Graph API)

Check what agents are running to sync AD with the Cloud provider

Use MFA on all Cloud admin accounts

Consider adopting Azure AD Privileged Identity Management

Run the Azure AD Cloud Assessment Powershell Script

Run the Active Directory Health Check in Azure Monitor

Enable Azure AD Connect password hash sync

Enable O365 logging

Misc Checks

Look into disabling legacy protocols: LLMNR, WDigest, WPAD, LM & NTLM auth

Are there other servers that need secured: Certificate servers, Azure AD connect server, ADFS servers, password vaults Is Azure AD Connect up-to-date?

Check for additional running AD sync agents (Proofpoint, Mimecast, Duo, Google, etc.)

Deploy LAPS or make local admin passwords unique and not based on a pattern

Set separate auditing policies in Default Domain and Default Domain Controller Group Policies

Upgrade to Powershell v5 or newer. Uninstall version 2 so it can never be invoked.

Lock down PowerShell to only talk to RFC1918 ranges with Host based firewall rules blocking outbound access

What is the Powershell execution policy set to? Get-ExecutionPolicy Enable Powershell Script Block Logging Computer Configuration > Policies > Administrative Templates > Windows Components > Windows PowerShell

SPN scanning setspn -X Script to list all Domain SPN List of common SPNs

Reset your KRBTGT password using scripts available from Microsoft Prevents Golden Ticket attack ONLY use Microsoft’s scripts for this, or you could break your domain for 10+ hours Announcing the script availability Download the script Password has to be changed twice to ensure there is no password history maintained Microsoft states that resetting the KRBTGT account password is only supported in a Windows Server 2008 Domain Functional Level (DFL) or higher. When the DFL is raised from 2003 to 2008 (or higher), the KRBTGT account password is changed automatically

Disable NetBIOS (insecure legacy protocol) has to be done for each network adapter or through DHCP settings

Blocking Net Session Enumeration with NetCease Authenticated Users are granted NetSessionEnumeration privileges by default, allowing any user to see who has an established session on another machine in the Domain, allowing targeting. This tool removes this permission from Authenticated Users. If you run this, capture BloodHound info before and after

Hyper-V - enable shielded VMs for DCs

Disable SMBv1, enable SMBv2 and v3 Can use NMAP SMB scripts to discover machine info over the network SMBv2 was introduced with Server 2008, SMBv1 still needed for XP and 2003 Disable manually or with Group Policy Use Wireshark to listen for any SMB traffic tcp.port eq 445 or tcp.port eq 139 don’t go by column in capture window, must look inside packet for version details You can audit SMBv1 in Windows 10, Windows Server 2016, and Windows Server 2012 R2 via an update. That way you can configure your Windows Servers to see if disabling SMB1 would break someone: Set-SmbServerConfiguration –AuditSmb1Access $true Then just examine the SMBServer\Audit event log on the systems Need to set keys for both client and server

Applocker - Out of the box application whitelisting. Can start in audit mode. Austrailian Cyber Defense Hardening Win10 doc has a good starting rule base.

If you have remote logging and alerting enabled consider running Sysmon on remote endpoints to capture more activity

Centralize all AD, ADFS, and Azure logs to one location

Bitlocker

Have AD Admin, Server Admin, and Workstation Admin groups

Separate Admin accounts from user accounts (Admins get 2 accounts) Does it need to be a Domain Admin all the time? Maybe just have it as a member of a Server Admin group Be mindful of where you login - you can restrict where an account can logon Invoke-UserHunter (PowerSploit) enumerates sessions and logged in users and matches the result with a list of targeted users See what other workstations Domain Admins have been logging into. Uses NetSessionEnumeration in the background Separate physical workstation is best but virtual can work also (PAW) If virtual login to workstation as user, run a local VM for user applications, RDP from host to servers as AD admin If physical they are not admins on those workstations no running agents, manual patching restrictive firewall policy - only admin traffic

The workstation is the security boundary, not the firewall Implement Device Guard and Credential Guard Needs compatible hardware Device Guard is a group of key features designed to harden a computer system against malware. Its focus is preventing malicious code from running by ensuring only known good code can run. Can be difficult to implement, only for mission critical servers Credential Guard is a specific feature that is not part of Device Guard that aims to isolate and harden key system and user secrets against compromise, helping to minimize the impact and breadth of a Pass the Hash style attack. Isolates LSASS. Check if these are running by launching MSINFO32.EXE and looking under System Summary

Implement AD Health Check emails