This is Part 6 of the AD DFIR Lab series. We configure all the auditing before running the attacks — if we don’t do it now, the attacks won’t leave a trace.

Why audit before attacking

A DFIR lab without auditing is like investigating a crime without security cameras. The attacks happen, but when you come to investigate there’s nothing left. That’s why Phase 8 comes before Phase 9 (attacks): instrument first, attack second, and then the artifacts are there to analyze.

What we’ll capture:

Windows:

  • Sysmon with olafhartong’s sysmon-modular (2704 lines of rules)
  • Windows Advanced Audit Policy via auditpol (not GPO)
  • PowerShell logging: Script Block (4104), Module (4103), Transcription
  • Command line in 4688
  • Operational logs: WinRM, WMI-Activity, TaskScheduler

Linux:

  • auditd with 50 DFIR rules (exec, auth, sudo, ssh, sssd, systemd, cron, priv esc)
  • persistent journald in /var/log/journal/

Windows: Sysmon with sysmon-modular

The Sysmon that ships with GOAD is from 2021 (v13) and uses the SwiftOnSecurity config. We use the latest Sysmon (v15+) with olafhartong’s sysmon-modular, which has better coverage for modern MITRE ATT&CK techniques.

# On the Proxmox host
cd /root/lab/audit
wget https://download.sysinternals.com/files/Sysmon.zip
wget https://raw.githubusercontent.com/olafhartong/sysmon-modular/master/sysmonconfig.xml
# 2704 lines of rules with MITRE ATT&CK mapping

And the Ansible playbook installs Sysmon on all 6 Windows VMs:

- name: Copy Sysmon.zip to Windows VM
  ansible.windows.win_copy:
    src: /root/lab/audit/Sysmon.zip
    dest: 'C:\sysmon\Sysmon.zip'

- name: Unzip Sysmon
  community.windows.win_unzip:
    src: 'C:\sysmon\Sysmon.zip'
    dest: 'C:\sysmon'

- name: Copy sysmon-modular config
  ansible.windows.win_copy:
    src: /root/lab/audit/sysmonconfig.xml
    dest: 'C:\sysmon\sysmonconfig.xml'

- name: Install Sysmon (first time)
  ansible.windows.win_command: 'C:\sysmon\Sysmon64.exe -accepteula -i C:\sysmon\sysmonconfig.xml'
  when: sysmon_svc.exists is not defined or not sysmon_svc.exists

- name: Update Sysmon config (if already installed)
  ansible.windows.win_command: 'C:\sysmon\Sysmon64.exe -c C:\sysmon\sysmonconfig.xml'
  when: sysmon_svc.exists is defined and sysmon_svc.exists

The logic is well thought out: on a fresh install use -i; if already installed use -c to update the config without reinstalling.

Key Sysmon events captured by the modular config

ID Event Why
1 ProcessCreate With command line — the foundation of all analysis
3 NetworkConnect Outbound connections
7 ImageLoaded Loaded DLLs (DLL sideloading detection)
8 CreateRemoteThread Process injection
10 ProcessAccess Opening handles to other processes (mimikatz → lsass.exe)
11 FileCreate File creation in suspicious paths
13 RegistryValueSet Registry modification (persistence)
22 DnsQuery DNS resolution — gold for C2 detection

After the attacks, we’ll have thousands of these events in Microsoft-Windows-Sysmon/Operational.evtx.

Windows Advanced Audit Policy

Sysmon is great but doesn’t catch everything. Security.evtx is where Kerberos events (4768, 4769), logons (4624, 4625), and DCSync (4662) live — DCSync being the attack where someone impersonates a domain controller to pull password hashes over the replication protocol. Windows groups these events into audit subcategories (finer-grained than the top-level categories), and each one has to be switched on explicitly with auditpol:

- name: Enable Account Logon auditing (4768-4776 Kerberos)
  ansible.windows.win_shell: |
    auditpol /set /subcategory:"Credential Validation" /success:enable /failure:enable
    auditpol /set /subcategory:"Kerberos Authentication Service" /success:enable /failure:enable
    auditpol /set /subcategory:"Kerberos Service Ticket Operations" /success:enable /failure:enable

- name: Enable DS Access (4662 — DCSync detection)
  ansible.windows.win_shell: |
    auditpol /set /subcategory:"Directory Service Access" /success:enable /failure:enable
    auditpol /set /subcategory:"Directory Service Changes" /success:enable /failure:enable
    auditpol /set /subcategory:"Directory Service Replication" /success:enable /failure:enable

Enabled categories (Success + Failure):

Category Key events
Account Logon 4768 (TGT request), 4769 (TGS request), 4771, 4776
Logon/Logoff 4624 (success), 4625 (failure), 4634, 4647, 4648, 4672 (special)
Object Access 5140/5145 (file share), 4656 (handle), 4663 (access)
DS Access 4662 (DCSync detection)
Detailed Tracking 4688 (process creation), 4689 (termination)
Privilege Use 4673, 4674
Account Management 4720-4738 (user changes), 4726 (delete)
Policy Change 4719, 4907

Command line in 4688

By default Event 4688 only records the executable name, not the arguments. For DFIR this is useless — you need to see what parameters the attacker used. Enabled via a registry key:

- name: Enable command line in 4688
  ansible.windows.win_regedit:
    path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit
    name: ProcessCreationIncludeCmdLine_Enabled
    data: 1
    type: dword

Without this you’d see powershell.exe but not powershell.exe -enc SQBFAHgA.... With this, you capture the full payload.

PowerShell logging

Attackers use PowerShell for everything. Without PowerShell logging you lose half the artifacts of a modern incident. Three levels:

# Script Block Logging — Event 4104 (the most important)
- ansible.windows.win_regedit:
    path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging
    name: EnableScriptBlockLogging
    data: 1
    type: dword

# Module Logging — Event 4103 (all modules)
- ansible.windows.win_regedit:
    path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging
    name: EnableModuleLogging
    data: 1
    type: dword

- ansible.windows.win_regedit:
    path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging\ModuleNames
    name: '*'
    data: '*'
    type: string

# Transcription — text files with everything typed
- ansible.windows.win_regedit:
    path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription
    name: EnableTranscripting
    data: 1
    type: dword

- ansible.windows.win_regedit:
    path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription
    name: OutputDirectory
    data: 'C:\PSTranscripts'
    type: string

With this:

  • Event 4104: any script block PowerShell executes (even Base64-obfuscated, it’s decoded before being logged)
  • Event 4103: every individual command from every module
  • Transcription: text files in C:\PSTranscripts with the entire session

Log sizing (important for limited disk)

The Hetzner server has 2×512 GB NVMe in RAID1, with ~310 GB free in the ZFS pool after creating all VMs. Logs can grow fast if we don’t cap them.

Conservative calculation (per-VM and total):

Log Per VM Total 6 Windows
Security 512 MB 3 GB
Sysmon 512 MB 3 GB
PowerShell Operational 256 MB 1.5 GB
WinRM Operational 128 MB 768 MB
WMI-Activity 128 MB 768 MB
TaskScheduler 128 MB 768 MB
Windows subtotal ~1.6 GB ~10 GB
Linux auditd - 600 MB
journald - 500 MB
TOTAL   ~11 GB

With 310 GB free in ZFS, the safety ratio is ~28x. Comfortable.

# Sizes applied with wevtutil
- ansible.windows.win_shell: wevtutil sl Security /ms:536870912
- ansible.windows.win_shell: wevtutil sl Microsoft-Windows-Sysmon/Operational /ms:536870912
- ansible.windows.win_shell: wevtutil sl Microsoft-Windows-PowerShell/Operational /ms:268435456

Gotcha: the initial sizes I set (1GB each) were excessive for a lab on a shared server. The first iteration could have reached ~24 GB of potential logs. Checking disk ratios before launching the attacks made me realize this and I reduced the values. Always calculate the worst case before enabling full auditing in a disk-limited environment.

Linux: auditd with DFIR rules

For Ubuntu (LNX01), we use auditd with DFIR-focused rules, not compliance:

## ======= AUTHENTICATION =======
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k identity
-w /etc/sudoers.d/ -p wa -k identity

## ======= PROCESS EXECUTION =======
-a always,exit -F arch=b64 -S execve -k exec
-a always,exit -F arch=b32 -S execve -k exec

## ======= SUDO (uid != euid = escalated) =======
-a always,exit -F arch=b64 -S execve -C uid!=euid -F euid=0 -k sudo_exec

## ======= SSH =======
-w /etc/ssh/sshd_config -p wa -k sshd
-w /root/.ssh/ -p wa -k root_ssh

## ======= PRIVILEGE ESCALATION =======
-w /bin/su -p x -k priv_esc
-w /usr/bin/sudo -p x -k priv_esc
-w /usr/bin/pkexec -p x -k priv_esc

## ======= KERNEL MODULES =======
-a always,exit -F arch=b64 -S init_module -S delete_module -k modules

## ======= SSSD / KERBEROS =======
-w /etc/sssd/ -p wa -k sssd
-w /etc/krb5.conf -p wa -k kerberos
-w /etc/krb5.keytab -p wa -k kerberos

## ======= CRON / SYSTEMD =======
-w /etc/cron.d/ -p wa -k cron
-w /etc/crontab -p wa -k cron
-w /var/spool/cron/ -p wa -k cron
-w /etc/systemd/ -p wa -k systemd

50 rules total, with keys for quick filtering:

# Search privileged executions
sudo ausearch -k sudo_exec --start recent

# Search /etc/passwd modifications
sudo ausearch -k identity

# Search SSSD authentication attempts
sudo ausearch -k sssd

auditd size

Configured for 600MB total (200MB × 3 files) with automatic rotation:

max_log_file = 200
num_logs = 3
max_log_file_action = ROTATE

And persistent journald (survives reboots), capped at 500 MB:

[Journal]
SystemMaxUse=500M
MaxRetentionSec=30day
Storage=persistent

Post-deployment verification

# Sysmon running on Windows
ansible -i goad/inventory dc01 -m win_shell -a '(Get-Service sysmon64).Status'
# Running

# Sysmon events already captured
ansible -i goad/inventory dc01 -m win_shell -a '(Get-WinEvent -ListLog Microsoft-Windows-Sysmon/Operational).RecordCount'
# 1660

# Audit policy active
ansible -i goad/inventory dc01 -m win_shell -a 'auditpol /get /category:* | Select-String "Success and Failure"'
# System Integrity         Success and Failure
# Logon                    Success and Failure
# Process Creation         Success and Failure
# ...

# auditd on Linux
ssh ubuntu@192.168.10.32 'sudo auditctl -l | wc -l'
# 50

All 7 domain machines are instrumented. When we launch the attacks in Phase 9, each technique will leave traces in the EVTX/logs that we can analyze later with tools like masstin.


Next: Part 7 — Fire and Blood: Attack Scenarios and Forensic Analysis (coming soon)

Previous: Part 5 — The Smallfolk: Users, Groups and Vulnerabilities