Learn Ethical Hacking (#72) - Hardening Windows and Active Directory
Learn Ethical Hacking (#72) - Hardening Windows and Active Directory

What will I learn
- Windows attack surface reduction -- disabling unnecessary features, protocols, and services;
- Active Directory hardening -- tiered administration, Protected Users, and credential isolation;
- Group Policy hardening -- GPOs that prevent the attacks from episodes 32-33;
- Credential Guard and LAPS -- hardware-backed credential isolation and unique local admin passwords;
- Windows Defender hardening -- ASR rules, controlled folder access, and network protection;
- PowerShell security -- Constrained Language Mode, script block logging, and AMSI;
- Audit policy configuration -- what to log and how to detect attacks in Windows Event Logs;
- Defense: implementing Microsoft's recommended security baselines at scale.
Requirements
- A working modern computer running macOS, Windows or Ubuntu;
- A Windows Server VM with Active Directory;
- Windows 10/11 domain-joined workstation;
- The ambition to learn ethical hacking and security research.
Difficulty
- Intermediate/Advanced
Curriculum (of the Learn Ethical Hacking Series):
- Learn Ethical Hacking (#1) - Why Hackers Win
- Learn Ethical Hacking (#2) - Your Hacking Lab
- Learn Ethical Hacking (#3) - How the Internet Actually Works - For Attackers
- Learn Ethical Hacking (#4) - Reconnaissance - The Art of Not Being Noticed
- Learn Ethical Hacking (#5) - Active Scanning - Mapping the Attack Surface
- Learn Ethical Hacking (#6) - The AI Slop Epidemic - Why AI-Generated Code Is a Security Disaster
- Learn Ethical Hacking (#7) - Passwords - Why Humans Are the Weakest Cipher
- Learn Ethical Hacking (#8) - Social Engineering - Hacking the Human
- Learn Ethical Hacking (#9) - Cryptography for Hackers - What Protects Data (and What Doesn't)
- Learn Ethical Hacking (#10) - The Vulnerability Lifecycle - From Discovery to Patch to Exploit
- Learn Ethical Hacking (#11) - HTTP Deep Dive - Request Smuggling and Header Injection
- Learn Ethical Hacking (#12) - SQL Injection - The Bug That Won't Die
- Learn Ethical Hacking (#13) - SQL Injection Advanced - Extracting Entire Databases
- Learn Ethical Hacking (#14) - Cross-Site Scripting (XSS) - Injecting Code Into Browsers
- Learn Ethical Hacking (#15) - XSS Advanced - Bypassing Filters and CSP
- Learn Ethical Hacking (#16) - Cross-Site Request Forgery - Making Users Attack Themselves
- Learn Ethical Hacking (#17) - Authentication Bypass - Getting In Without a Password
- Learn Ethical Hacking (#18) - Server-Side Request Forgery - Making Servers Betray Themselves
- Learn Ethical Hacking (#19) - Insecure Deserialization - Code Execution via Data
- Learn Ethical Hacking (#20) - File Upload Vulnerabilities - When Users Upload Weapons
- Learn Ethical Hacking (#21) - API Security - The New Attack Surface
- Learn Ethical Hacking (#22) - Business Logic Flaws - When the Code Works But the Logic Doesn't
- Learn Ethical Hacking (#23) - Client-Side Attacks - Beyond XSS
- Learn Ethical Hacking (#24) - Content Management Systems - Hacking WordPress and Friends
- Learn Ethical Hacking (#25) - Web Application Firewalls - Bypassing the Guards
- Learn Ethical Hacking (#26) - The Full Web Pentest - Methodology and Reporting
- Learn Ethical Hacking (#27) - Bug Bounty Hunting - Getting Paid to Hack the Web
- Learn Ethical Hacking (#28) - The AI Web Attack Surface - AI Features as Vulnerabilities
- Learn Ethical Hacking (#29) - Network Sniffing - Seeing Everything on the Wire
- Learn Ethical Hacking (#30) - Wireless Network Attacks - Breaking Wi-Fi
- Learn Ethical Hacking (#31) - Privilege Escalation - Linux
- Learn Ethical Hacking (#32) - Privilege Escalation - Windows
- Learn Ethical Hacking (#33) - Active Directory Attacks - The Crown Jewels
- Learn Ethical Hacking (#34) - Pivoting and Lateral Movement - Spreading Through Networks
- Learn Ethical Hacking (#35) - Cloud Security - AWS Attack and Defense
- Learn Ethical Hacking (#36) - Cloud Security - Azure and GCP
- Learn Ethical Hacking (#37) - Container Security - Docker and Kubernetes Attacks
- Learn Ethical Hacking (#38) - Infrastructure as Code - Securing the Automation
- Learn Ethical Hacking (#39) - Email Security - Phishing Infrastructure and Defense
- Learn Ethical Hacking (#40) - DNS Attacks - Exploiting the Internet's Foundation
- Learn Ethical Hacking (#41) - Exploitation Frameworks - Metasploit and Cobalt Strike
- Learn Ethical Hacking (#42) - Custom Exploit Development - Writing Your Own
- Learn Ethical Hacking (#43) - Exploit Development Advanced - Modern Mitigations and Bypasses
- Learn Ethical Hacking (#44) - Reverse Engineering - Understanding Binaries
- Learn Ethical Hacking (#45) - Supply Chain Attacks - Poisoning the Source
- Learn Ethical Hacking (#46) - The Human Factor - Why Security Training Fails
- Learn Ethical Hacking (#47) - Physical Security and OSINT - The Forgotten Attack Vectors
- Learn Ethical Hacking (#48) - Insider Threats - When the Call Is Coming from Inside the House
- Learn Ethical Hacking (#49) - Deepfakes and AI Deception - The New Social Engineering
- Learn Ethical Hacking (#50) - Red Team Operations - Simulating Real Attacks
- Learn Ethical Hacking (#51) - Incident Response - When Things Go Wrong
- Learn Ethical Hacking (#52) - Threat Intelligence - Knowing Your Enemy
- Learn Ethical Hacking (#53) - Security Architecture - Designing Systems That Resist Attack
- Learn Ethical Hacking (#54) - Compliance and Governance - The Business of Security
- Learn Ethical Hacking (#55) - Privacy and Data Protection - GDPR, CCPA, and Beyond
- Learn Ethical Hacking (#56) - Cryptocurrency Security - Attacking and Defending Digital Assets
- Learn Ethical Hacking (#57) - IoT and Embedded Security - Hacking the Physical World
- Learn Ethical Hacking (#58) - The AI Security Landscape - Attacking and Defending AI Systems
- Learn Ethical Hacking (#59) - Python for Pentesters - Automating Everything
- Learn Ethical Hacking (#60) - Zig for Security Tools - When Speed and Memory Matter
- Learn Ethical Hacking (#61) - Writing Custom Scanners - Beyond Off-the-Shelf
- Learn Ethical Hacking (#62) - C2 Frameworks - Building Command and Control
- Learn Ethical Hacking (#63) - Payload Generation and Evasion - Defeating Antivirus
- Learn Ethical Hacking (#64) - Fuzzing - Finding Bugs at Machine Speed
- Learn Ethical Hacking (#65) - OSINT Automation - Large-Scale Intelligence Gathering
- Learn Ethical Hacking (#66) - Reporting and Documentation - The Professional Difference
- Learn Ethical Hacking (#67) - Continuous Security - DevSecOps and Pipeline Security
- Learn Ethical Hacking (#68) - Wireless and Bluetooth Exploitation Deep Dive
- Learn Ethical Hacking (#69) - Mobile Application Security - Android and iOS
- Learn Ethical Hacking (#70) - Building a Pentesting Practice - Going Professional
- Learn Ethical Hacking (#71) - Hardening Linux - From Default to Fortress
- Learn Ethical Hacking (#72) - Hardening Windows and Active Directory (this post)
Learn Ethical Hacking (#72) - Hardening Windows and Active Directory
Solutions to Episode 71 Exercises
Exercise 1: Linux hardening with Lynis (abbreviated).
Lynis score after hardening: 84/100
Passed: SSH hardening (key-only, no root, custom port), kernel
sysctl parameters, firewall active, AIDE installed, fail2ban
running, SUID binaries audited, mount options on /tmp
Failed: no full disk encryption (VM limitation), no remote
syslog configured, some kernel modules not blacklisted
(usb-storage, firewire), no USB device restrictions
Skipped: full disk encryption (lab VM, not applicable),
remote syslog (single-server lab, no syslog target)
An 84/100 with a fresh Ubuntu Server is a solid result -- remember a default install scores around 50-60 before you touch anything, so this represents a meaningful improvement. The failures are worth examining: full disk encryption is impractical in a VM lab (your hypervisor manages the disk images, encrypting within the guest adds complexity without real benefit in a lab context), and remote syslog requires a second server to receive the logs (which we did not set up in the episode 71 lab). The kernel module blacklisting for usb-storage and firewire is a good recommendation for production servers where USB devices should never be plugged in -- adding blacklist usb-storage and blacklist firewire-core to /etc/modprobe.d/blacklist.conf would address that finding and push the score closer to 90. The remaining gap between 84 and 100 is normal -- Lynis includes checks for configurations that not every server needs (like remote syslog on a single-machine lab), and a perfect 100 is neither practical nor the goal. The goal is conscious, documented decisions about which recommendations to implement and which to skip with justification.
Exercise 2: Privesc attempts after hardening (abbreviated).
SUID abuse: BLOCKED -- non-essential SUID bits removed,
python3 no longer SUID, find no longer SUID
Writable cron: BLOCKED -- cron scripts owned by root, chmod 700
Sudo misconfig: BLOCKED -- no NOPASSWD entries for interactive tools
Kernel exploit: PARTIALLY BLOCKED -- kernel patched, but if
attacker finds 0-day, kernel hardening (ASLR, kptr_restrict)
makes exploitation harder
Docker group: BLOCKED -- no users in docker group
Result: 4 of 5 attack vectors completely blocked.
Kernel exploits mitigated but not eliminated (no defense is perfect).
Four out of five completely blocked, and the fifth (kernel exploits) significantly mitigated. This is exactly the result that demonstrates WHY hardening matters. Every one of these attack vectors worked trivially on a default install in episode 31 -- the SUID python3 binary gave us an instant root shell, writable cron scripts let us schedule a reverse shell as root, and NOPASSWD sudo entries for interactive tools like vim and less were free escalation paths. After hardening, each of those vectors is eliminated. The kernel exploit category is honest about its limitations: no amount of sysctl hardening can prevent a true kernel zero-day, but kptr_restrict = 2 hides kernel pointer addresses (making exploit development harder), ASLR randomizes memory layout (making reliable exploitation harder), and unprivileged_bpf_disabled = 1 removes the most common recent kernel attack surface entirely. A hardened system forces the attacker to bring a zero-day -- and zero-days are expensive, rare, and noisy.
Exercise 3: Ansible hardening playbook (abbreviated).
- name: Harden Ubuntu Server
hosts: all
become: yes
tasks:
- name: SSH key-only auth
lineinfile:
path: /etc/ssh/sshd_config
regexp: "^#?PasswordAuthentication"
line: "PasswordAuthentication no"
notify: restart sshd
- name: Install fail2ban
apt: name=fail2ban state=present
- name: Apply kernel hardening
sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
loop:
- { key: "net.ipv4.ip_forward", value: "0" }
- { key: "kernel.randomize_va_space", value: "2" }
- { key: "kernel.kptr_restrict", value: "2" }
# ... additional sysctl parameters
- name: Configure nftables
copy:
src: nftables.conf
dest: /etc/nftables.conf
notify: restart nftables
- name: Install AIDE
apt: name=aide state=present
- name: Initialize AIDE
command: aideinit
args:
creates: /var/lib/aide/aide.db
The Ansible playbook is the critical bridge between "I hardened one server" and "I harden every server." The lineinfile module for SSH configuration is elegant because it is idempotent -- running the playbook 50 times produces the same result as running it once, with no accidentical duplicate entries or broken config files. The sysctl module with a loop is the right pattern for kernel parameters because each parameter gets applied atomically and verified by Ansible. The creates argument on the AIDE task is important: aideinit should only run once to create the baseline database. Running it again would overwrite the baseline with the current state, which defeats the purpose of integrity monitoring. This playbook connects directly to what we discussed in episode 38 (Infrastructure as Code) and episode 67 (DevSecOps) -- hardening is not a one-time activity, it is a codified state that your automation enforces continuously.
Episode 71 covered hardening Linux -- SSH lockdown, filesystem permissions, kernel sysctl parameters, nftables firewall, AppArmor, AIDE file integrity monitoring, and automated hardening with Lynis and Ansible. That was the first half of the operating system hardening story.
Today we tackle the other half, and it is considerably more complex. Windows hardening is not just about configuring one machine -- it is about securing an entire Active Directory domain where one misconfiguration in one GPO on one server can give an attacker the keys to every machine, every user account, and every piece of data in the organization. Everything we did offensively in episodes 32 (Windows privilege escalation) and 33 (Active Directory attacks) exploited specific AD misconfigurations -- Kerberoasting, DCSync, Golden Tickets, Pass-the-Hash. Today we close those doors from the other side.
The Windows Problem
Windows hardening is more complex than Linux hardening for one fundamental reason: Active Directory. A standalone Windows system has its own attack surface -- services, permissions, the registry, local accounts. But a domain-joined Windows system inherits the attack surface of the entire domain, and the domain is where the real crown jewels live.
Everything from episode 33 (Kerberoasting, DCSync, Golden Tickets, Pass-the-Hash) succeeds because AD defaults are permissive. Microsoft ships Active Directory with configurations that prioritize compatibility over security. They have to -- thousands of enterprise applications rely on legacy protocols, legacy authentication mechanisms, and legacy permissions structures that were designed in the early 2000s when the threat landscape looked nothing like it does today. Your job as the defender is to change those defaults without breaking the 500 applications that depend on them, and that is considerably harder than editing an sshd_config file on a Linux box.
The scale is different too. A Linux server is (usually) one machine with one purpose. An Active Directory domain can span thousands of machines, tens of thousands of user accounts, and hundreds of Group Policy Objects interacting in ways that nobody fully understands -- including the people who configured them. A single misconfigured GPO can undo every other hardening control in the domain, and the worst part is that you might not notice until an attacker shows you during a pentest (or worse, during a real incident).
Tiered Administration
The single most important Active Directory hardening concept -- and the one that blocks the largest number of attacks from episode 33 -- is tiered administration:
Tier 0: Domain Controllers and AD infrastructure
Accounts: Domain Admins, Enterprise Admins, Schema Admins
Rule: NEVER log into anything except Tier 0 systems
Never: use a DA account to check email, browse the web,
or log into a workstation. NEVER.
Tier 1: Member servers (application servers, databases, file servers)
Accounts: Server administrators
Rule: can log into Tier 1 servers, never Tier 0 or Tier 2
Dedicated admin accounts: srvadm-jsmith (not the daily account)
Tier 2: Workstations and end-user devices
Accounts: Helpdesk, workstation administrators
Rule: can log into workstations, never Tier 0 or Tier 1
Daily use accounts: jsmith (no admin privileges)
Why this matters:
Episode 33 attack: find a workstation where a DA is logged in,
steal their token, escalate to Domain Admin.
With tiered administration: DA accounts NEVER touch workstations.
There is no token to steal. The attack chain breaks at the
first step.
The logic is devastatingly simple. In episode 33, the entire Kerberoasting and token theft attack chain started with one assumption: that a Domain Admin account had (at some point) logged into a regular workstation. That assumption is correct in the vast majority of real-world environments because sysadmins use their DA credentials to check something on a user's workstation, to test a group policy application, to troubleshoot a printer driver -- mundane tasks that leave cached credentials in LSASS memory for mimikatz to harvest. Tiered administration eliminates this by making it physically impossible for DA accounts to touch anything below Tier 0. No cached credentials, no tokens to steal, no lateral movement path from workstation to Domain Controller.
# Implement tiering with GPO
# 1. Create OUs: Tier0-Servers, Tier1-Servers, Tier2-Workstations
# 2. GPO on Tier2-Workstations:
# Deny log on locally: Domain Admins, Server Admins
# Deny log on through Remote Desktop: Domain Admins
# 3. GPO on Tier1-Servers:
# Deny log on locally: Domain Admins (use Tier 1 admin accounts)
# 4. GPO on Tier0-Servers:
# Allow log on locally: ONLY Domain Admins
# Deny log on from network: Tier 1 and Tier 2 accounts
The implementation is straightforward GPO work, but the cultural change it requires is enormous. Sysadmins who have spent years using one admin account for everything need to maintain separate credentials for each tier -- and they will resist, because it is less convenient. Having said that, convenience for administrators is exactly the attack surface that episode 33 exploited. The minor inconvenience of switching between a t0-admin account (for DC work) and a t1-srvadm account (for server work) and a regular jsmith account (for email and browsing) is a trivial price compared to the catastrophic cost of a complete domain compromise.
Credential Protection
Once you have tiered administration in place, the next layer is making sure that even IF credentials are somehow exposed, they cannot be easily abused:
# 1. Protected Users Group
# Members cannot: use NTLM auth, DES/RC4 in Kerberos,
# be delegated, have TGTs beyond 4 hours
Add-ADGroupMember -Identity "Protected Users" -Members "admin-t0"
# 2. Credential Guard (Windows 10 Enterprise / Server 2016+)
# Isolates LSASS in a virtualization-based security container
# Prevents mimikatz from reading credentials from memory
# Enable via GPO:
# Computer Config > Admin Templates > System > Device Guard >
# Turn On Virtualization Based Security > Enabled
# Credential Guard: Enabled with UEFI lock
# 3. LAPS (Local Administrator Password Solution)
# Randomizes local admin password on EVERY machine
# Prevents lateral movement via shared local admin credentials
# Install LAPS, extend AD schema, configure GPO:
Install-Module -Name LAPS
Update-LapsADSchema
Set-LapsADConfiguration -DomainController dc01 -PasswordLength 20 \
-PasswordAge 30
# 4. Disable NTLM where possible
# GPO: Network Security: Restrict NTLM
# Start with audit mode, then enforce after reviewing logs
# NTLM is used by: Pass-the-Hash (episode 33)
# Kerberos is resistant to Pass-the-Hash
Each of these four controls directly counters a specific attack technique from episode 33.
The Protected Users group eliminates RC4 encryption in Kerberos tickets -- which is the specific weakness that makes Kerberoasting viable (RC4 tickets can be cracked offline, AES tickets are astronomically harder). It also limits TGT lifetime to 4 hours, which means a stolen Kerberos ticket expires quickly instead of sitting valid for the default 10 hours. And it disables NTLM authentication entirely for group members, which kills Pass-the-Hash for those accounts.
Credential Guard is the direct counter to mimikatz. In episode 33, mimikatz dumped credentials from the LSASS process -- the Local Security Authority Subsystem Service that handles authentication. Credential Guard moves LSASS into a hardware-isolated container using Virtualization-Based Security (VBS), which means even a process running as SYSTEM on the host cannot read the credentials. Mimikatz against a Credential Guard-protected machine simply returns empty results. The "Enabled with UEFI lock" part is critical -- without UEFI lock, an attacker with physical access could disable Credential Guard by modifying boot configuration. With the lock, the setting is baked into firmware and survives even a full OS reinstall.
LAPS (Local Administrator Password Solution) solves one of the most common lateral movement vectors: shared local admin passwords. In quit some organizations, every workstation has the same local administrator password (set during imaging and never changed). Compromise one workstation, extract the local admin hash, and use it to authenticate to every other workstation in the domain -- instant lateral movement across hundreds or thousands of machines. LAPS generates a unique random password for every machine's local admin account and stores it in an AD attribute that only authorized administrators can read. Each password rotates automatically every 30 days. The lateral movement via shared local admin creds attack from episode 34 dies completely.
Disabling NTLM is the nuclear option for Pass-the-Hash. NTLM is the authentication protocol that makes Pass-the-Hash possible -- you capture an NTLM hash and replay it to authenticate without knowing the plaintext password. Kerberos does not have this vulnerability (tickets are encrypted and bound to specific services). The challenge is that NTLM is still required by older applications, printers, and network devices that predate Kerberos integration. The correct approach is to enable NTLM auditing first (Restrict NTLM: Audit in GPO), run it for a few weeks to identify everything that still relies on NTLM, fix or replace those dependencies, and THEN enforce the restriction. Disabling NTLM without the audit phase will break things -- the question is whether you discover the breakage in a controlled way (audit) or an uncontrolled way (production outage at 2 AM).
Group Policy Hardening
Group Policy Objects (GPOs) are the primary mechanism for deploying security configurations across a Windows domain. The hardening controls we configure here map directly to the Attack Surface Reduction (ASR) features that Windows Defender provides:
# Attack Surface Reduction (ASR) rules via GPO
# Block known attack techniques at the OS level
# Block Office applications from creating executable content
# (prevents macro-based malware)
Add-MpPreference -AttackSurfaceReductionRules_Ids `
"3B576869-A4EC-4529-8536-B80A7769E899" `
-AttackSurfaceReductionRules_Actions Enabled
# Block executable content from email and webmail
Add-MpPreference -AttackSurfaceReductionRules_Ids `
"BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550" `
-AttackSurfaceReductionRules_Actions Enabled
# Block credential stealing from LSASS
Add-MpPreference -AttackSurfaceReductionRules_Ids `
"9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2" `
-AttackSurfaceReductionRules_Actions Enabled
# Block process creation from PSExec and WMI
Add-MpPreference -AttackSurfaceReductionRules_Ids `
"d1e49aac-8f56-4280-b9ba-993a6d77406c" `
-AttackSurfaceReductionRules_Actions Enabled
# Controlled Folder Access (ransomware protection)
Set-MpPreference -EnableControlledFolderAccess Enabled
Add-MpPreference -ControlledFolderAccessProtectedFolders "C:\Users"
The ASR rules are defense in depth at the endpoint level. The LSASS protection rule (the one targeting credential stealing) is a software-level complement to Credential Guard -- even if Credential Guard is not available (it requires specific hardware and Enterprise edition), this ASR rule blocks most attempts to access LSASS memory, including mimikatz's default approach. The PSExec/WMI rule targets lateral movement tools directly -- in episode 34, PSExec was one of the primary mechanisms for moving between machines in a domain. Blocking process creation from PSExec and WMI commands does not prevent legitimate WMI queries, but it stops the pattern where an attacker uses WMI or PSExec to spawn a shell on a remote machine.
Controlled Folder Access is Windows Defender's anti-ransomware feature, and it works on a simple principle: only trusted applications (as determined by Microsoft's cloud intelligence) can modify files in protected directories. An unknown executable attempting to encrypt files in C:\Users gets blocked immediately. For organizations that have been hit by ransomware (or seen the devastation it causes -- episode 51 on incident response covered this), Controlled Folder Access is one of the most impactful single controls you can enable. The main friction point is that legitimate but uncommon applications may be blocked initially and need to be whitelisted, which requires a brief audit period after initial deployment.
PowerShell Security
PowerShell is simultaneously the most powerful administrative tool in Windows and the most commonly abused attack vector. Every post-exploitation framework (Cobalt Strike, Empire, PowerSploit -- we covered these in episodes 41 and 62) relies heavily on PowerShell for execution, and locking it down without breaking administrative workflows requires a careful, layered approach:
# 1. Constrained Language Mode
# Limits PowerShell to basic commands -- blocks .NET, COM, Win32 API
# Via GPO: System-wide AppLocker or WDAC policy
# 2. Script Block Logging (see EVERY PowerShell command executed)
# GPO: Admin Templates > Windows Components > Windows PowerShell >
# Turn on PowerShell Script Block Logging > Enabled
# Logs to: Event Log > Microsoft-Windows-PowerShell/Operational
# 3. Module Logging (log which modules are imported)
# GPO: Turn on Module Logging > Enabled > Module Names: *
# 4. Transcription (full transcript of every PS session)
# GPO: Turn on PowerShell Transcription > Enabled
# Include invocation headers > Enabled
# Transcript output directory: \\logserver\ps-transcripts
# 5. Disable PowerShell v2 (bypasses all v5+ security features)
Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2
Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root
# PowerShell v2 is the classic AMSI bypass -- it predates AMSI entirely.
# If v2 is available, attackers just run: powershell -version 2
# Disabling v2 closes this escape hatch.
The layered approach here is important. Constrained Language Mode (CLM) restricts what PowerShell can do -- it blocks direct .NET class access, COM object instantiation, and Win32 API calls, which are the exact mechanisms that attack tools like PowerSploit use to interact with the operating system at a low level. A PowerShell script running in CLM can use built-in cmdlets (Get-Process, Get-Service, etc.) but cannot call [System.Runtime.InteropServices.Marshal]::PtrToStringAuto() or similar .NET methods that attack tools depend on.
Script Block Logging is the detection complement to CLM. Even if an attacker finds a way around CLM (and they will try -- the bypass cat-and-mouse game is constant), Script Block Logging records the full text of every PowerShell command and script block that executes on the system. This includes commands that are dynamically generated, Base64-decoded, or assembled in memory -- the deobfuscation happens BEFORE logging, so you see the actual malicious command, not the encoded wrapper. This is enormously valuable for incident response and threat hunting, because you get a complete record of what the attacker actually ran on the machine ;-)
Disabling PowerShell v2 is one of those controls that seems minor but closes a critical escape hatch. PowerShell version 2 (which shipped with Windows 7 and is still available as an optional feature on Windows 10/11) predates every modern security feature: AMSI (Anti-Malware Scan Interface), CLM, Script Block Logging -- all of it. If v2 is available, an attacker simply runs powershell -version 2 and drops back to a version of PowerShell with zero security controls. It is the equivalent of keeping a fire escape that bypasses every locked door in the building. Removing this optional feature takes one command and eliminates the bypass entirely.
Audit Policy
Without logging, you are defending blind. You can harden every setting, deploy every control, and an attacker can still slip through a gap you did not anticipate -- and if you are not logging the right events, you will never know it happened. Advanced Audit Policy configuration is what transforms a Windows system from a silent black box into a comprehensive security sensor:
# Enable advanced audit policies (replace basic audit)
# GPO: Security Settings > Advanced Audit Policy Configuration
# Account Logon
# Audit Credential Validation: Success, Failure
# Audit Kerberos Authentication Service: Success, Failure
# Audit Kerberos Service Ticket Operations: Success, Failure
# -> Detects: Kerberoasting (ep33), AS-REP roasting
# Logon/Logoff
# Audit Logon: Success, Failure
# Audit Special Logon: Success
# -> Detects: Pass-the-Hash (type 3 NTLM logon from unusual source)
# Object Access
# Audit File System: Success (on sensitive dirs)
# Audit SAM: Success
# -> Detects: SAM database access attempts
# Privilege Use
# Audit Sensitive Privilege Use: Success, Failure
# -> Detects: privilege escalation attempts
# Process Tracking
# Audit Process Creation: Success
# Include command line in process creation events: Enabled
# -> Detects: LOLBins, encoded PowerShell, suspicious processes
# DS Access
# Audit Directory Service Changes: Success
# Audit Directory Service Access: Success
# -> Detects: DCSync (Event 4662 with replication GUIDs)
Let me walk through the detection logic for the episode 33 attacks specifically. Kerberoasting generates Kerberos Service Ticket requests (TGS-REQ) for service accounts -- the audit policy for "Kerberos Service Ticket Operations" logs these as Event 4769. A normal user might request 1-2 service tickets during a login session. An attacker running Kerberoasting requests dozens or hundreds in rapid succession, one for each service account in the domain. That pattern -- many TGS-REQ events from a single source in a short window -- is an unmistakable detection signal. DCSync is even more distinctive: it generates Event 4662 with the specific replication GUIDs (1131f6aa-9c07-11d1-f79f-00c04fc2dcd2 and friends) that indicate directory replication requests. Legitimate DCs replicate with each other; a workstation requesting replication is a Domain Admin-level compromise in progress.
The "Include command line in process creation events" setting deserves special emphasis. Without it, Event 4688 (process creation) tells you that powershell.exe was launched. With it, the event includes the full command line: powershell.exe -enc SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdA... -- and now you can see that someone ran a Base64-encoded command, decode it, and discover they were downloading and executing a reverse shell. The difference between "PowerShell ran" and "PowerShell ran THIS specific malicious command" is the difference between a vague alert and an actionable investigation.
# Install Sysmon for enhanced process and network monitoring
# Download from Microsoft Sysinternals
# Use SwiftOnSecurity's config:
sysmon -accepteula -i sysmonconfig-export.xml
# Sysmon logs:
# Event 1: Process creation (with hashes!)
# Event 3: Network connection
# Event 7: Image loaded (DLL loading)
# Event 8: CreateRemoteThread (process injection!)
# Event 10: ProcessAccess (mimikatz accessing LSASS!)
# Event 11: File creation
# Event 13: Registry modification
Sysmon fills the gaps that native Windows auditing misses. Native Event 4688 tells you a process started. Sysmon Event 1 tells you a process started AND gives you the SHA256 hash of the executable, the command line, the parent process, the user context, and the current directory -- enough information to immediately determine whether the process is legitimate or malicious, without even looking at the binary itself. Sysmon Event 10 (ProcessAccess) is the mimikatz detector: it fires when one process opens a handle to another process's memory, which is exactly what mimikatz does when it reads credentials from LSASS. Sysmon Event 8 (CreateRemoteThread) detects process injection -- a technique we covered in episode 63 (payload evasion) where malicious code creates a thread inside a legitimate process to hide its execution. These events are the raw material for security monitoring and threat hunting, which we will explore in depth when we get to network-level visibility and centralized log analysis.
Service and Protocol Hardening
Every unnecessary protocol and service is an attack surface. Windows ships with quit some legacy protocols enabled by default for backward compatibility, and each one represents a door that attackers know how to open:
# Disable SMBv1 (EternalBlue attack surface)
Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol
Set-SmbServerConfiguration -EnableSMB1Protocol $false
# Disable LLMNR (used for name resolution poisoning, episode 29)
# GPO: Admin Templates > Network > DNS Client >
# Turn off multicast name resolution > Enabled
# Disable NetBIOS over TCP/IP (same poisoning risk)
# Per interface: Network adapter > TCP/IP > Advanced > WINS >
# Disable NetBIOS over TCP/IP
# Disable WPAD (Web Proxy Auto-Discovery -- poisonable)
# GPO: Admin Templates > Network > WPAD >
# Turn off WPAD > Enabled
# Disable WinRM on workstations (reduces lateral movement surface)
# GPO: Computer Config > Admin Templates > Windows Components >
# Windows Remote Management > Disallow WinRM from storing RunAs credentials
Disable-PSRemoting -Force # on individual machines
# Disable RDP unless needed
# GPO: deny Remote Desktop access to non-admin groups
SMBv1 should have been dead years ago, but many organizations still have it enabled because "something might break." That "something" is typically a 15-year-old network scanner, a legacy print server, or an ancient line-of-business application that nobody wants to touch. The EternalBlue exploit (the same one that powered the WannaCry ransomware in 2017) targets SMBv1 specifically. SMBv2 and SMBv3 are not vulnerable to EternalBlue. Disabling SMBv1 eliminates this entire attack class, and any device that genuinely cannot speak SMBv2 (released in 2006 with Windows Vista) probably should not be on your network in 2026.
LLMNR and NetBIOS are the name resolution protocols we exploited in episode 29 using Responder. When a Windows machine cannot resolve a hostname via DNS, it falls back to LLMNR (multicast) and NetBIOS (broadcast) -- and any machine on the local network can respond to those requests with a poisoned answer, redirecting traffic to the attacker. Responder captures NTLM hashes from these poisoned requests, and those hashes can be cracked offline (episode 7) or relayed to other services (episode 33). Disabling both LLMNR and NetBIOS forces all name resolution through DNS, which is centrally managed and much harder to poison. This single change blocks one of the most effective and commonly used network-level attacks in internal penetration tests.
WPAD (Web Proxy Auto-Discovery) has the same poisoning problem. Windows automatically searches for a proxy configuration via WPAD, and if DNS does not have a WPAD entry, the machine falls back to LLMNR and NetBIOS broadcasts -- right into Responder's trap. Even after disabling LLMNR and NetBIOS, WPAD can be poisoned via DNS if the attacker has compromised the DNS server or created a malicious DHCP option 252. The safest approach is to disable WPAD entirely unless your organization actually uses automatic proxy configuration, and most do not.
Microsoft Security Baselines
Manual hardening does not scale, just like we concluded about Linux hardening in episode 71. Microsoft publishes official Security Compliance Toolkit baselines that encode hundreds of Group Policy settings into importable GPO templates:
# Download Microsoft Security Compliance Toolkit
# https://www.microsoft.com/en-us/download/details.aspx?id=55319
# Contains GPO templates for:
# - Windows 11 security baseline
# - Windows Server 2022 security baseline
# - Microsoft Edge security baseline
# - Microsoft 365 Apps security baseline
# Import and apply:
Import-Module .\SecurityBaseline.psm1
Merge-GPOSecurityBaseline -Path .\Win11-Baseline -Domain corp.local
# The baseline configures 400+ settings including:
# - All audit policies from this episode
# - ASR rules
# - Credential Guard
# - LAPS
# - PowerShell security
# - Network protocol restrictions
# - User rights assignments
The security baselines are the Windows equivalent of the Ansible dev-sec hardening role we used for Linux in episode 71 -- a codified, tested, repeatable security configuration that you can apply across your entire fleet. The difference is that Microsoft's baselines come directly from the vendor (so they have been tested for compatibility with their own operating system), and they are updated with every major Windows release to address new attack vectors and features. The baselines cover over 400 individual settings -- far more than anyone would configure manually without missing critical items.
The approach for deploying baselines in a production environment follows the same audit-then-enforce pattern we discussed for NTLM restriction: import the baseline into a test OU, apply it to a representative sample of machines, monitor for application breakage over a 2-week period, create exceptions for legitimate conflicts, and then roll it out to production. The baseline is deliberately aggressive -- it assumes you want maximum security and expects you to create specific exceptions for your environment rather than starting permissive and trying to tighten. This is the correct default. Starting restrictive and loosening is safer than starting loose and trying to tighten, because you discover dependencies early (when you are looking for them) rather than late (when an attacker finds them).
The AI Slop Connection
AI-generated Active Directory configurations are a security nightmare. AI suggests adding service accounts to Domain Admins "because the application needs full access," disabling Credential Guard "to fix compatibility issues," enabling NTLM authentication "because Kerberos is too complicated," running PowerShell in FullLanguage mode on all machines, and configuring GPOs with "Allow All" policies because "the original policy was blocking something."
Each of these AI suggestions undoes a specific hardening control from this episode. The AI fixes the immediate problem (the application works) by creating a much larger problem (the domain is compromised). This is the same pattern we identified in episode 6 (the AI slop epidemic) and have revisited in every hardening episode since: AI optimizes for "it runs" not "it resists attack." Windows and Active Directory security requires understanding the interaction between hundreds of settings across multiple layers -- which protocols are safe to disable, which accounts need which level of access, which GPO settings conflict with each other. An AI that recommends adding a service account to Domain Admins has no concept of tiered administration, no understanding that this single action could enable a complete domain compromise via Kerberoasting, and no awareness that the "compatibility fix" it just suggested undoes three months of hardening work. The fix, as always: use the AI output as a starting draft if you must, but validate every security-relevant setting against the controls in this episode and the Microsoft security baselines before deploying to production.
Exercises
Exercise 1: Implement tiered administration in your AD lab. Create 3 Tier 0 admin accounts, 3 Tier 1 server admin accounts, and 3 Tier 2 helpdesk accounts. Configure GPOs that prevent cross-tier logons. Then attempt to log into a workstation with a DA account -- verify it is denied. Document the GPO configuration and the denied logon attempt with screenshots. Save to ~/lab-notes/ad-tiered-admin.md.
Exercise 2: Enable Credential Guard, LAPS, and Protected Users in your lab. Then attempt the attacks from Episode 33: (a) mimikatz credential dump (should fail with Credential Guard), (b) lateral movement via shared local admin (should fail with LAPS), (c) NTLM relay (should fail for Protected Users members). Document which attacks are now blocked and include the error output from each failed attack. Save to ~/lab-notes/ad-credential-hardening.md.
Exercise 3: Configure Sysmon with SwiftOnSecurity's config and Advanced Audit Policies as described in this episode. Then perform 5 attacks from earlier episodes (Kerberoasting, PsExec lateral movement, PowerShell encoded command execution, LSASS memory access, scheduled task persistence). For each attack, identify the specific Sysmon Event ID and Windows Event ID that detects it. Build a detection cheat sheet mapping attacks to event IDs. Save to ~/lab-notes/windows-detection-cheatsheet.md.
Thanks for your contribution to the STEMsocial community. Feel free to join us on discord to get to know the rest of us!
Please consider delegating to the @stemsocial account (85% of the curation rewards are returned).
Consider setting @stemsocial as a beneficiary of this post's rewards if you would like to support the community and contribute to its mission of promoting science and education on Hive.