Post

Building an Autonomous SOC Lab: From Raw Logs to AI-Driven Threat Intelligence

Building an Autonomous SOC Lab: From Raw Logs to AI-Driven Threat Intelligence

Overview

This document details the design, construction, and operation of a fully functional three-layer Security Operations Center (SOC) lab I built as part of my Final Year Project at KCA University. The platform — which I named the Autonomous SOC AI Platform — runs entirely on a single Windows machine and brings together a real attack environment, a custom-tuned SIEM, and an AI-powered analysis engine.

My role in the project was to build the infrastructure layer: the networks, the SIEM, the detection rules, and the data pipeline that feeds everything into the AI analysis layer built by my teammate Nicole Nyagah. The attack simulation layer was built by my teammate Curtis Okello.

The result is a self-contained, multi-layer security platform with the following components:

  • A VirtualBox-isolated attack environment running DVWA (Damn Vulnerable Web Application)
  • A Wazuh SIEM deployed on WSL2 Ubuntu, receiving real-time logs from a Windows EDR agent
  • Custom detection rules covering 9 MITRE ATT&CK techniques
  • A Tailscale mesh VPN providing zero-trust remote access to the entire lab
  • A dynamic port forwarding system that survives Windows reboots automatically

This documentation covers every command, every design decision, and every mistake made during the build — with the goal that anyone reading it can replicate the lab and understand not just what to run, but why.


Introduction

Most cybersecurity labs I’ve seen online follow the same tired formula: spin up Kali Linux, run nmap, call it a penetration test. I wanted to build something fundamentally different — a lab that doesn’t just simulate a security environment but actually functions like one.

This post documents the complete construction of a three-layer SOC platform I built for my Final Year Project at KCA University. The platform — the Autonomous SOC AI Platform — is a real-time security bridge that sits on top of a Wazuh SIEM, ingests live attack telemetry generated by a custom APT emulator, and feeds everything into an AI-powered analysis engine built by my teammate Nicole Nyagah. My job was to build the infrastructure that made all of this possible.

What I ended up with:

  • A VirtualBox-isolated attack environment running DVWA (Damn Vulnerable Web Application)
  • A Wazuh SIEM deployed on WSL2 Ubuntu, receiving real-time logs from a Windows EDR agent
  • Custom detection rules covering 9 MITRE ATT&CK techniques
  • A Tailscale mesh VPN providing zero-trust remote access to the entire lab
  • A dynamic port forwarding system that survives Windows reboots automatically

By the end of this post, you’ll have everything you need to replicate this lab and understand why each design decision was made — not just what commands to run.


Architecture Overview

Before I touched a single command, I made sure I understood the system I was building. Architecture decisions made early have cascading consequences — this is something I learned the hard way.

Desktop View Architecture Overview

The Three-Layer Design

Layer 1 — Infrastructure (This post) VirtualBox provides the isolated network. The Windows host acts as a gateway. WSL2 hosts the Wazuh SIEM. Port forwarding bridges these otherwise disconnected networks.

Layer 2 — Threat Simulation (Team Member 2) A Python APT emulator generates realistic attack traffic targeting DVWA, simulating APT41, Turla, Lazarus, and Sandworm tradecraft.

Layer 3 — AI Analysis (Team Member 3) A Flask application ingests Wazuh alerts via Server-Sent Events, classifies threats using a Random Forest model, enriches with VirusTotal/GeoIP, and presents everything on a live dashboard.

Why This Architecture?

The fundamental challenge I faced was connecting three isolated network environments:

NetworkSubnetPurpose
VirtualBox Host-Only10.0.60.0/24Isolated VM communication
WSL2 Virtual Network172.27.x.xWazuh SIEM hosting
Tailscale Mesh100.x.x.xRemote team access

These networks cannot see each other by default. The entire infrastructure challenge was making them communicate securely and reliably. Every design decision I made flows from this fundamental problem.


Prerequisites

Host Machine Requirements

RequirementMinimumRecommended
OSWindows 10 ProWindows 11 Pro
RAM16 GB32 GB
CPU4 cores (VT-x/AMD-V enabled)8+ cores
Storage100 GB free200 GB SSD
NetworkAnyEthernet preferred

Security Note: Enable VT-x/AMD-V virtualization in BIOS before proceeding. This is required for VirtualBox to run 64-bit VMs. Access BIOS during boot (usually F2, F10, or DEL key) and enable virtualization technology settings.

Software Requirements

1
2
3
4
5
6
7
8
9
10
11
12
# Download these before starting:
# 1. VirtualBox 7.x
#    https://www.virtualbox.org/wiki/Downloads

# 2. Windows 10/11 ISO (for EDR VM)
#    https://www.microsoft.com/en-us/software-download/windows10

# 3. Tailscale for Windows
#    https://tailscale.com/download/windows

# 4. WSL2 (built into Windows 10/11)
#    Enable via PowerShell - instructions below

Enable WSL2

I opened PowerShell as Administrator and ran:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Enable WSL feature
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

# Enable Virtual Machine Platform
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

# Set WSL2 as default
wsl --set-default-version 2

# Install Ubuntu (this will be our Wazuh host)
wsl --install -d Ubuntu

# Restart your machine after this

After restart, I opened Ubuntu from the Start Menu and completed the initial setup by creating a username and password.

Desktop View WSL2 Ubuntu terminal showing successful installation with Ubuntu version information


Part 1: VirtualBox Configuration

1.1 Installing VirtualBox

I downloaded VirtualBox from the official site and ran the installer, accepting defaults unless I had specific storage preferences.

Security Implication: VirtualBox installs several virtual network adapters on your host. These are safe but will appear in your network connections. Note them — I configured them shortly after.

1.2 Creating the Windows EDR Virtual Machine

This VM is my attack surface — it runs DVWA and hosts the Wazuh agent.

Step 1: Create New VM

  1. Open VirtualBox Manager
  2. Click New (blue icon)
  3. Configure basic settings:
1
2
3
Name:    Windows-EDR
Type:    Microsoft Windows
Version: Windows 10 (64-bit)

Step 2: Allocate Hardware Resources

1
2
3
RAM:    4096 MB (4 GB minimum)
CPU:    2 cores
Video:  128 MB

Why 4 GB RAM? DVWA with Apache and MySQL running simultaneously, plus the Wazuh agent, can consume 2–3 GB. Leaving headroom prevents OOM issues that would generate false log entries and corrupt testing data.

Step 3: Create Virtual Hard Disk

1
2
Size:   60 GB (dynamically allocated)
Format: VDI (VirtualBox Disk Image)

Step 4: Attach Windows ISO

  1. Select your new VM → SettingsStorage
  2. Click the empty CD icon under Controller: IDE
  3. Click the CD icon on the right → Choose a disk file
  4. Select your Windows 10 ISO

1.3 Network Configuration

Network configuration in a multi-layer lab requires careful thought.

The Problem

VirtualBox VMs require two distinct types of connectivity:

  1. Internal communication — for Wazuh agent → manager traffic
  2. Internet access — for updates, downloads, and external integrations

A single adapter cannot cleanly handle both roles in this architecture, so I used a dual-adapter design.


Adapter 1 — NAT (Internet Access)

  1. VM Settings → NetworkAdapter 1
  2. Check Enable Network Adapter
  3. Attached to: NAT Network (or NAT)
  4. (Optional) Select custom NAT network (e.g., 10.0.50.0/24)

Desktop View Adapter 1 (NAT) — provides outbound internet connectivity


Adapter 2 — Host-Only (SOC Communication)

  1. VM Settings → NetworkAdapter 2
  2. Check Enable Network Adapter
  3. Attached to: Host-only Adapter
  4. Name: Select the host-only network (configured below)

Desktop View Adapter 2 (Host-only) — isolated SOC communication network


Security Design Note: The dual-adapter approach separates trust zones:

  • The NAT adapter allows outbound internet access while blocking unsolicited inbound traffic.
  • The host-only adapter enables controlled communication between the VM and the host (Wazuh manager).

This mirrors real-world network segmentation and reduces unintended exposure.


Why Use a NAT Network?

I used a NAT network (e.g., 10.0.50.0/24) instead of default NAT for the following reasons:

  • Controlled outbound access: Allows updates and downloads without exposing the VM externally
  • Predictable addressing: Custom subnet simplifies debugging and documentation
  • Scalability: Multiple VMs can share the same outbound network
  • Segmentation: Keeps internet traffic separate from SOC monitoring traffic

It is optional to use a custom NAT.

Important: The NAT network is not part of the detection pipeline — it exists purely for external connectivity.


1.4 Creating the Host-Only Network

In VirtualBox:

  1. File → Tools → Network Manager
  2. Select Host-only Networks
  3. Click Create
  4. Configure:
1
2
3
IPv4 Address: 10.0.60.1
Subnet Mask:  255.255.255.0
DHCP:         Disabled

Desktop View

Why Disable DHCP? Static addressing ensures consistent communication paths. Dynamic IP changes would break port forwarding and agent connections.


1.5 Configuring Static IP in Windows VM

After Windows was installed:

  1. Open Network Connections
  2. Select the Host-only adapter
  3. Configure IPv4:
1
2
3
4
IP Address:       10.0.60.10
Subnet Mask:      255.255.255.0
Default Gateway:  (leave blank)
DNS:              (leave blank)

Desktop View Static IP configuration


Why no gateway? The host-only network is used exclusively for internal SOC communication. Internet-bound traffic is handled by the NAT adapter, which already has a default gateway.

In multi-homed systems, only one interface should define a default gateway to avoid routing conflicts.


Verification

1
ipconfig /all

Expected output:

  • NAT adapter → 10.0.50.x with gateway
  • Host-only adapter → 10.0.60.10 with no gateway

Desktop View Network configuration

Set Network Profile to Private

1
Get-NetConnectionProfile | Set-NetConnectionProfile -NetworkCategory Private

Why Private? The Windows firewall applies stricter rules on Public networks, blocking inbound connections. Setting the profile to Private allows trusted internal communication (required for Wazuh agent connectivity).


Part 2: Firewall Configuration

2.1 Understanding the Firewall Layers

My lab has three firewall boundaries:

1
2
3
4
5
[Internet] → [Windows Host Firewall] → [VM Network]
                                              ↓
                                    [Windows VM Firewall]
                                              ↓
                                    [WSL2 (no firewall by default)]

Each layer needs specific rules. Missing one layer breaks the entire pipeline.

2.2 Windows VM Firewall Rules

I ran all commands as Administrator in Windows VM PowerShell:

Rule 1: Allow ICMP (Ping)

1
2
3
4
5
6
7
8
New-NetFirewallRule `
  -DisplayName "Allow ICMPv4 - Lab" `
  -Protocol ICMPv4 `
  -IcmpType 8 `
  -Enabled True `
  -Profile Any `
  -Direction Inbound `
  -Action Allow

Purpose: ICMP is disabled by default in Windows. Without this rule, you cannot ping the VM to verify connectivity — your first line of troubleshooting becomes impossible.

Security Implication: Enabling ICMP exposes the host to ping sweeps and ICMP-based reconnaissance. In production, restrict this to specific source IPs (e.g., only from 10.0.60.1).


Rule 2: Allow All Traffic from Internal Network

1
2
3
4
5
6
7
New-NetFirewallRule `
  -DisplayName "Allow Internal Network 10.0.60.0/24" `
  -Direction Inbound `
  -RemoteAddress 10.0.60.0/24 `
  -Action Allow `
  -Profile Any `
  -Enabled True

Purpose: Allows the Windows host (10.0.60.1) and any other VMs on the subnet to communicate with this machine. This includes Wazuh agent enrollment traffic and RDP access.

Security Implication: This rule is broad — it allows all protocols from the entire /24 subnet. In production, restrict to specific ports (3389 for RDP, 1514/1515 for Wazuh). I used the broad rule here for lab simplicity.


Rule 3: Allow Tailscale Network

1
2
3
4
5
6
7
New-NetFirewallRule `
  -DisplayName "Allow Tailscale Network" `
  -Direction Inbound `
  -RemoteAddress 100.0.0.0/8 `
  -Action Allow `
  -Profile Any `
  -Enabled True

Purpose: Tailscale assigns IPs in the 100.64.0.0/10 range. This rule allows team members to connect via Tailscale without their traffic being blocked by the VM firewall.


Rule 4: Enable Remote Desktop

1
2
3
4
5
6
7
8
# Enable RDP in registry
Set-ItemProperty `
  -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' `
  -Name "fDenyTSConnections" `
  -Value 0

# Enable RDP firewall rule group
Enable-NetFirewallRule -DisplayGroup "Remote Desktop"

Purpose: Allows remote management of the Windows VM from the host machine and team members via Tailscale.

Security Implication: RDP on default port 3389 is a significant attack surface. In production: change to a non-standard port, restrict source IPs, enforce Network Level Authentication (NLA), and require MFA.


Verify all rules are in place:

1
2
3
4
Get-NetFirewallRule |
  Where-Object {$_.DisplayName -like "*Allow*" -and $_.Enabled -eq "True"} |
  Select-Object DisplayName, Direction, Action |
  Format-Table -AutoSize

Desktop View Windows VM firewall rules

2.3 Windows Host Firewall Rules (for Port Forwarding)

On the Windows host machine (not the VM), I created rules to allow Wazuh traffic to reach the port forwarding service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Allow Wazuh agent log transmission
New-NetFirewallRule `
  -DisplayName "Wazuh-Agent-1514" `
  -Direction Inbound `
  -LocalPort 1514 `
  -Protocol TCP `
  -Action Allow `
  -Enabled True

# Allow Wazuh agent enrollment
New-NetFirewallRule `
  -DisplayName "Wazuh-Enrollment-1515" `
  -Direction Inbound `
  -LocalPort 1515 `
  -Protocol TCP `
  -Action Allow `
  -Enabled True

# Allow Wazuh API
New-NetFirewallRule `
  -DisplayName "Wazuh-API-55000" `
  -Direction Inbound `
  -LocalPort 55000 `
  -Protocol TCP `
  -Action Allow `
  -Enabled True

# Allow Wazuh Dashboard
New-NetFirewallRule `
  -DisplayName "Wazuh-Dashboard-443" `
  -Direction Inbound `
  -LocalPort 443 `
  -Protocol TCP `
  -Action Allow `
  -Enabled True

Desktop View Windows Defender Firewall with Advanced Security showing the four Wazuh inbound rules

2.4 Firewall Verification

1
2
3
4
5
6
7
8
# On Windows Host - test each critical port
$ports = @(1514, 1515, 55000, 443, 3389)

foreach ($port in $ports) {
    $result = Test-NetConnection -ComputerName 10.0.60.1 -Port $port -WarningAction SilentlyContinue
    $status = if ($result.TcpTestSucceeded) { "✓ OPEN" } else { "✗ CLOSED" }
    Write-Host "Port $port : $status"
}

Desktop View PowerShell output showing port status for all critical ports

Troubleshooting: If a port shows CLOSED despite creating the rule, check the network profile. Rules scoped to “Private” profile won’t apply to “Public” networks. Run Get-NetConnectionProfile and ensure the relevant adapter shows “Private”.


Part 3: Port Forwarding — The Bridge Between Networks

3.1 Understanding the Problem

This is the most technically interesting part of the entire build.

The Challenge:

1
2
3
4
5
6
7
8
Windows VM Agent wants to connect to Wazuh at:
→ 10.0.60.1:1514 (the gateway IP it can reach)

But Wazuh is actually running at:
→ 172.27.62.x:1514 (inside WSL2, which the VM cannot reach)

Solution: Make Windows HOST intercept traffic at 10.0.60.1:1514
and forward it to the actual WSL2 IP.

WSL2 creates a virtual network interface (172.x.x.x subnet) that is separate from the VirtualBox network. There is no direct route between 10.0.60.0/24 and 172.x.x.x. The Windows host is the only machine that can reach both.

3.2 Getting the WSL2 IP

1
2
# On Windows Host
wsl hostname -I

This returned something like 172.27.62.12. I took note of this.

The Critical Problem: WSL2 is assigned a new IP every time Windows restarts. This means your port forwarding rules break on every reboot. I solved this with an automation script in Section 3.4.

3.3 Setting Up Port Forwarding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# Run as Administrator on Windows Host
# Replace 172.27.62.12 with your actual WSL2 IP

$WSL_IP = "172.27.62.12"

# Wazuh agent log transmission
netsh interface portproxy add v4tov4 `
  listenaddress=10.0.60.1 `
  listenport=1514 `
  connectaddress=$WSL_IP `
  connectport=1514

# Wazuh agent enrollment
netsh interface portproxy add v4tov4 `
  listenaddress=10.0.60.1 `
  listenport=1515 `
  connectaddress=$WSL_IP `
  connectport=1515

# Wazuh API
netsh interface portproxy add v4tov4 `
  listenaddress=10.0.60.1 `
  listenport=55000 `
  connectaddress=$WSL_IP `
  connectport=55000

# Wazuh Dashboard
netsh interface portproxy add v4tov4 `
  listenaddress=10.0.60.1 `
  listenport=443 `
  connectaddress=$WSL_IP `
  connectport=443

Verify the rules were created:

1
netsh interface portproxy show all

Desktop View netsh interface portproxy show all output showing all four forwarding rules with listen addresses and connect addresses

3.4 Solving the Reboot Problem — Auto-Update Script

I created a PowerShell script that runs at Windows startup to automatically detect the current WSL2 IP and update port forwarding rules:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Save as: C:\Scripts\Update-WazuhForwarding.ps1

# Get current WSL2 IP dynamically
$WSL_IP = (wsl hostname -I).Trim().Split()[0]

Write-Host "WSL2 IP detected: $WSL_IP" -ForegroundColor Cyan

# Remove existing port forwarding rules
$ports = @(1514, 1515, 55000, 443)

foreach ($port in $ports) {
    netsh interface portproxy delete v4tov4 `
      listenport=$port `
      listenaddress=10.0.60.1 2>$null
}

# Recreate with current WSL2 IP
foreach ($port in $ports) {
    netsh interface portproxy add v4tov4 `
      listenaddress=10.0.60.1 `
      listenport=$port `
      connectaddress=$WSL_IP `
      connectport=$port

    Write-Host "Forwarded 10.0.60.1:$port${WSL_IP}:$port" -ForegroundColor Green
}

Write-Host "`nPort forwarding updated successfully!" -ForegroundColor Green
netsh interface portproxy show all

I then scheduled it to run at Windows startup:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Create scheduled task (run as Administrator)
$action = New-ScheduledTaskAction `
  -Execute "PowerShell.exe" `
  -Argument "-NonInteractive -File C:\Scripts\Update-WazuhForwarding.ps1"

$trigger = New-ScheduledTaskTrigger -AtStartup

$principal = New-ScheduledTaskPrincipal `
  -UserId "SYSTEM" `
  -LogonType ServiceAccount `
  -RunLevel Highest

Register-ScheduledTask `
  -TaskName "Update-Wazuh-Port-Forwarding" `
  -Action $action `
  -Trigger $trigger `
  -Principal $principal `
  -Description "Updates WSL2 port forwarding for Wazuh after reboot"

Why This Matters: Without this script, the entire detection pipeline silently breaks every time Windows restarts. Agents appear connected but stop sending logs. This script is the difference between a lab that works reliably and one that mystifies you with intermittent failures.

3.5 Testing Port Forwarding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# From Windows Host — test each forwarded port
$testPorts = @(
    @{Port=1514; Name="Wazuh Agent"},
    @{Port=1515; Name="Wazuh Enrollment"},
    @{Port=55000; Name="Wazuh API"},
    @{Port=443; Name="Wazuh Dashboard"}
)

foreach ($test in $testPorts) {
    $result = Test-NetConnection `
      -ComputerName 10.0.60.1 `
      -Port $test.Port `
      -WarningAction SilentlyContinue

    $status = if ($result.TcpTestSucceeded) { "✓" } else { "✗" }
    Write-Host "$status $($test.Name) (Port $($test.Port))"
}

Desktop View Port forwarding test


Part 4: Tailscale — Zero-Trust Remote Access

4.1 Why Tailscale Over Traditional VPN

Traditional VPN solutions require:

  • A publicly accessible server
  • Port forwarding on your router
  • Complex PKI certificate management
  • Manual client configuration

Tailscale uses WireGuard under the hood with a coordination server that handles key exchange. The result: encrypted peer-to-peer connections with no exposed ports on your router, no complex configuration, and cross-platform support.

In our lab context, Tailscale solved a specific problem: how do three team members on different networks collaborate on a lab that lives on one person’s machine?

4.2 Installing Tailscale on Windows Host

  1. Download from: https://tailscale.com/download/windows
  2. Run the installer
  3. Click Log in in the system tray icon
  4. Authenticate with your preferred identity provider (Google, Microsoft, or GitHub)

Desktop View Tailscale connections

4.3 Enabling Subnet Routing

This is the key configuration that makes the entire lab accessible to the team without installing Tailscale on every VM:

1
2
3
4
5
6
7
8
# Stop Tailscale
tailscale down

# Restart with subnet routing
tailscale up --advertise-routes=10.0.60.0/24 --accept-routes

# Verify
tailscale status

What this does: My Windows host tells the Tailscale network “I can route traffic to 10.0.60.0/24.” Any Tailscale peer that accepts routes can then reach 10.0.60.10 (the Windows VM) as if they were on the local network.

4.4 Approving the Route in Admin Console

Subnet routes require explicit approval to prevent accidental network exposure:

  1. Go to: https://login.tailscale.com/admin/machines
  2. Find your Windows host machine
  3. Click Edit route settings
  4. Enable checkbox for 10.0.60.0/24
  5. Click Save

Desktop View Tailscale subnet routing

4.5 Team Member Setup

Each team member installed Tailscale and ran:

1
2
3
4
5
# Windows
tailscale up --accept-routes

# Linux/Mac
sudo tailscale up --accept-routes

Verification:

1
2
3
# Team member verifies they can reach the lab
ping 10.0.60.10
Test-NetConnection -ComputerName 10.0.60.10 -Port 3389

Security Note: Tailscale implements a zero-trust model — every device must authenticate before joining the network, and every connection is end-to-end encrypted with WireGuard. This is significantly more secure than leaving RDP ports exposed to the internet.

4.6 Tailscale IP Forwarding (Required for Subnet Routing)

1
2
3
4
5
6
7
# Enable IP forwarding on Windows host
Set-NetIPInterface -Forwarding Enabled

# Verify
Get-NetIPInterface |
  Where-Object {$_.Forwarding -eq "Enabled"} |
  Select-Object InterfaceAlias, Forwarding

Desktop View Tailscale IP forwarding enabled


Part 5: Wazuh — The SIEM Core

5.1 Understanding Wazuh Architecture

Wazuh has three main components:

1
2
3
Wazuh Manager    → Receives and analyzes log data from agents
Wazuh Indexer    → OpenSearch-based storage and indexing engine
Wazuh Dashboard  → Web UI built on OpenSearch Dashboards

All three run on my WSL2 Ubuntu instance. Agents run on monitored endpoints (the Windows VM).

5.2 Installing Wazuh Manager on WSL2

I opened Ubuntu in WSL2 and ran all commands with sudo:

Step 1: System Preparation

1
2
3
4
5
# Update package lists
sudo apt-get update && sudo apt-get upgrade -y

# Install required packages
sudo apt-get install -y curl apt-transport-https gnupg

Step 2: Add Wazuh Repository

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Import Wazuh GPG key
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | \
  gpg --no-default-keyring \
  --keyring gnupg-ring:/usr/share/keyrings/wazuh.gpg \
  --import && \
  chmod 644 /usr/share/keyrings/wazuh.gpg

# Add repository
echo "deb [signed-by=/usr/share/keyrings/wazuh.gpg] \
  https://packages.wazuh.com/4.x/apt/ stable main" | \
  sudo tee -a /etc/apt/sources.list.d/wazuh.list

# Update with new repo
sudo apt-get update

Step 3: Install All-in-One (Manager + Indexer + Dashboard)

1
2
3
4
5
# Download installation assistant
curl -sO https://packages.wazuh.com/4.10/wazuh-install.sh
curl -sO https://packages.wazuh.com/4.10/config.yml

sudo bash ./wazuh-install.sh -a

Important: The installation script outputs credentials at the end. Copy and save these immediately. They include the admin password for the Wazuh dashboard and are shown only once.

Step 4: Verify All Services Are Running

1
2
3
sudo systemctl status wazuh-manager
sudo systemctl status wazuh-indexer
sudo systemctl status wazuh-dashboard

Desktop View systemctl status output for all three Wazuh services showing “active (running)” in green

Step 5: Verify Ports Are Listening

1
sudo ss -tlnp | grep -E "1514|1515|55000|443|9200"

Expected output:

1
2
3
4
5
tcp   LISTEN  0.0.0.0:1514   (wazuh-remoted)
tcp   LISTEN  0.0.0.0:1515   (wazuh-authd)
tcp   LISTEN  0.0.0.0:55000  (python3/Wazuh API)
tcp   LISTEN  0.0.0.0:443    (node/Dashboard)
tcp   LISTEN  *:9200         (java/Indexer)

Desktop View ss -tlnp output showing all Wazuh ports in LISTEN state

5.3 Wazuh Dashboard Setup

Accessing the Dashboard:

I opened my browser and navigated to:

1
https://10.0.60.1

I accepted the self-signed certificate warning (expected in lab environments) and logged in with:

  • Username: admin
  • Password: (from installation output)

Desktop View Wazuh dashboard login page at https://10.0.60.1

Desktop View Wazuh dashboard home page after successful login showing overview panels

5.4 Installing XAMPP and DVWA on Windows VM

DVWA provides the attack surface that generates meaningful security events.

Install XAMPP:

  1. Download XAMPP from: https://www.apachefriends.org/
  2. Install with defaults (ensure Apache and MySQL are selected)
  3. Start XAMPP Control Panel
  4. Click Start next to Apache and MySQL

Install DVWA:

1
2
3
4
5
6
7
# Download DVWA (run in Windows VM PowerShell)
Invoke-WebRequest `
  -Uri "https://github.com/digininja/DVWA/archive/master.zip" `
  -OutFile "C:\dvwa.zip"

Expand-Archive "C:\dvwa.zip" -DestinationPath "C:\xampp\htdocs\"
Rename-Item "C:\xampp\htdocs\DVWA-master" "C:\xampp\htdocs\dvwa"

Configure DVWA database:

  1. Browse to: http://localhost/dvwa/setup.php
  2. Click Create / Reset Database
  3. Log in at http://localhost/dvwa/login.php:
    • Username: admin
    • Password: password
  4. Go to DVWA Security → Set level to Low

Desktop View DVWA running in the Windows VM browser

5.5 Installing Wazuh Agent on Windows VM

On Windows VM — RDP in or use console:

Step 1: Download Agent

1
2
3
4
# Download Windows agent installer
Invoke-WebRequest `
  -Uri "https://packages.wazuh.com/4.x/windows/wazuh-agent-4.10.3-1.msi" `
  -OutFile "C:\wazuh-agent.msi"

Step 2: Install Agent

1
2
3
4
5
# Install with manager IP pointing to gateway
msiexec.exe /i "C:\wazuh-agent.msi" `
  WAZUH_MANAGER="10.0.60.1" `
  WAZUH_AGENT_NAME="Windows-EDR" `
  /qn

Why 10.0.60.1? The agent can’t reach WSL2 directly. I pointed it to the Windows host IP, which has port forwarding rules that redirect the traffic to WSL2 where Wazuh actually listens. This is the entire point of the port forwarding setup.

Step 3: Start the Agent

1
NET START WazuhSvc

Step 4: Verify Agent Status

1
Get-Service WazuhSvc | Select-Object Name, Status, StartType

Desktop View Wazuh service running with Automatic startup type

Step 5: Verify Agent is Connected (on WSL2)

1
2
# On Wazuh Manager (WSL2)
sudo /var/ossec/bin/agent_control -l

Desktop View agent_control -l output showing Windows-EDR agent with “Active” status

5.6 Configuring XAMPP Log Monitoring

The Wazuh agent needs to know where to find Apache logs. By default, it doesn’t monitor XAMPP directories.

I edited the agent configuration file:

1
2
# Open in Notepad as Administrator
notepad "C:\Program Files (x86)\ossec-agent\ossec.conf"

Added inside the <ossec_config> tags:

1
2
3
4
5
6
7
8
9
10
11
<!-- XAMPP Apache Access Logs -->
<localfile>
  <log_format>apache</log_format>
  <location>C:\xampp\apache\logs\access.log</location>
</localfile>

<!-- XAMPP Apache Error Logs -->
<localfile>
  <log_format>apache</log_format>
  <location>C:\xampp\apache\logs\error.log</location>
</localfile>

Restarted the agent:

1
Restart-Service WazuhSvc

Desktop View ossec.conf file open in Notepad showing the localfile blocks for XAMPP logs


Part 6: Custom Detection Rules

6.1 Why Custom Rules?

Wazuh ships with thousands of built-in rules, but they’re general-purpose. For my specific lab — where I’m simulating known APT group techniques against DVWA — I needed rules that understand the context.

Custom rules offer:

  • Precise detection tailored to my attack patterns
  • MITRE ATT&CK technique IDs embedded in alerts
  • Custom severity levels reflecting actual risk
  • Alert groupings that map to APT group identities

6.2 Writing Custom Rules

I opened the custom rules file on WSL2:

1
sudo nano /var/ossec/etc/rules/apt_emulator_rules.xml

The full rule set is available on My Github Repo.

1
2
3
4
5
# Apply by restarting Wazuh Manager
sudo systemctl restart wazuh-manager

# Verify it started successfully
sudo systemctl status wazuh-manager

Common Mistake I Hit: XML special characters in regex patterns cause silent rule failures or prevent Wazuh from starting. Use XML entities:

  • < becomes &lt;
  • > becomes &gt;
  • & becomes &amp;

If Wazuh fails to start after editing rules, check: sudo tail -50 /var/ossec/logs/ossec.log | grep ERROR


Part 7: Building the Wazuh Dashboard

7.1 Accessing the Dashboard

I navigated to https://10.0.60.1 and logged in with my admin credentials.

The dashboard runs on OpenSearch Dashboards and provides Kibana-like visualization capabilities over indexed Wazuh data.

7.2 Dashboard Creation

I created several dashboards using a combination of the official Wazuh documentation and LLM-guided configuration.

The primary reference I used was: https://documentation.wazuh.com/current/user-manual/wazuh-dashboard/creating-custom-dashboards.html

This provided the foundational understanding of:

  • Index patterns (wazuh-alerts-*)
  • Aggregations (Terms, Date Histogram, Metrics)
  • Visualization types (Area, Pie, Data Table, Tag Cloud)

I then used LLM assistance to accelerate:

  • Visualization structuring
  • Field selection
  • Layout design

This hybrid approach enabled rapid development while still maintaining alignment with the underlying data model.


Example Dashboard

Below is one of the visualisation dashboards I created.

APT Dashboard

7.3 Generating Real Alert Data

To generate realistic attack telemetry, I used an automated APT emulation script.

This component was developed by Curtis Okello as part of the Threat Simulation layer.

The script programmatically simulates multiple attack techniques (e.g., SQL Injection, XSS, Path Traversal, brute force) against DVWA, producing consistent and repeatable log data for detection.

APT Emulator Script (GitHub): <URL>


Why Use an Emulator?

  • Ensures consistent attack patterns for testing
  • Enables repeatable experiments
  • Generates high-volume telemetry for SIEM validation
  • Better represents real-world attacker behavior compared to manual testing

How It Fits in the Pipeline

1
2
3
4
5
6
7
8
9
10
11
APT Emulator Script
        ↓
DVWA (Windows VM)
        ↓
Apache Logs
        ↓
Wazuh Agent
        ↓
Wazuh Manager (WSL2)
        ↓
Dashboard Alerts

Part 8: Integration — How It All Works Together

8.1 The Complete Data Pipeline

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[APT Emulator (Python)]
    │
    │ HTTP Attack Traffic
    ▼
[DVWA on Windows VM (10.0.60.10)]
    │
    │ Apache writes to access.log
    ▼
[Wazuh Agent on Windows VM]
    │ Reads access.log every 500ms
    │ Encrypts log data (TLS)
    │ Sends to 10.0.60.1:1514
    ▼
[Windows Host (10.0.60.1)]
    │ netsh port forwarding intercepts
    │ Rewrites destination to WSL2 IP
    ▼
[Wazuh Manager in WSL2 (172.27.62.x:1514)]
    │ Receives raw log
    │ Runs through decoders (apache decoder)
    │ Runs through rules (matches 100001 = SQL Injection)
    │ Creates structured alert JSON
    ▼
[OpenSearch Indexer (:9200)]
    │ Stores alert in wazuh-alerts-* index
    ▼
[Wazuh Dashboard (:443)]
    │ Queries indexed alerts
    │ Renders visualizations
    ▼
[SOC Analyst sees alert on dashboard]
    │
    ▼
[AI Platform (Team Member 3)]
    │ Ingests same alerts via Wazuh API (:55000)
    │ ML model scores threat confidence
    │ Enriches with VirusTotal/GeoIP
    │ Displays on SENTINEL dashboard
    ▼
[Active Response: IP Blocked in Windows Firewall]

Troubleshooting Guide

Issue 1: Agent Not Connecting to Manager

Symptom: agent_control -l shows agent as Disconnected or Never Connected

Diagnosis:

1
2
3
4
5
# Check if manager is receiving anything
sudo tail -50 /var/ossec/logs/ossec.log | grep -i "error\|agent"

# Check agent log on Windows VM
Get-Content "C:\Program Files (x86)\ossec-agent\ossec.log" -Tail 30

Solution Tree:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Is Wazuh Manager running?
  └─ No → sudo systemctl start wazuh-manager

Is port 1514 forwarding correctly?
  └─ Check: netsh interface portproxy show all
  └─ WSL2 IP changed? Run Update-WazuhForwarding.ps1

Can VM reach host?
  └─ ping 10.0.60.1 from Windows VM
  └─ No → Check VirtualBox network adapter settings

Is firewall blocking?
  └─ Temporarily disable VM firewall and retest
  └─ Set-NetFirewallProfile -Profile All -Enabled False
  └─ If fixed → firewall rules issue

Issue 2: Port Forwarding Not Working After Reboot

Symptom: Everything worked yesterday, nothing works today.

Diagnosis:

1
2
3
4
5
# Check current WSL2 IP
wsl hostname -I

# Check what IP port forwarding is pointing to
netsh interface portproxy show all

Solution:

1
2
3
4
5
# Run the update script
.\Update-WazuhForwarding.ps1

# If scheduled task isn't running automatically:
Start-ScheduledTask -TaskName "Update-Wazuh-Port-Forwarding"

Issue 3: Wazuh Manager Fails to Start After Rule Edit

Symptom: systemctl status wazuh-manager shows “failed”

Diagnosis:

1
sudo tail -20 /var/ossec/logs/ossec.log | grep -E "ERROR|CRITICAL"

Common Causes:

  • XML special characters unescaped in regex
  • Missing closing tag in rule XML
  • Duplicate rule IDs

Solution:

1
2
3
4
5
6
7
8
# Validate rule syntax
sudo /var/ossec/bin/wazuh-analysisd -t

# If broken, restore backup
sudo cp /var/ossec/etc/rules/local_rules.xml.bak \
        /var/ossec/etc/rules/local_rules.xml

sudo systemctl start wazuh-manager

Best Practice: Always back up rules before editing: sudo cp /var/ossec/etc/rules/local_rules.xml{,.bak}


Issue 4: Dashboard Shows No Data

Symptom: All visualizations show “No results found”

Diagnosis:

  1. Check time range (set to “Last 24 hours”)
  2. Verify index pattern exists: Management → Index Patterns
  3. Check if agents are sending data
1
2
sudo /var/ossec/bin/agent_control -l
# All agents should show "Active"

Solution:

1
2
3
4
5
# Force agent to send test event
sudo /var/ossec/bin/agent_control -r -u 001

# Watch for data
sudo tail -f /var/ossec/logs/alerts/alerts.json

Issue 5: Wazuh Dashboard Unreachable (HTTPS Error)

Symptom: Browser shows “Connection refused” or “SSL error”

Diagnosis:

1
2
3
4
5
6
7
8
# Check dashboard service
sudo systemctl status wazuh-dashboard

# Check if port 443 is listening
sudo ss -tlnp | grep 443

# Check if port forwarding is set for 443
netsh interface portproxy show all | findstr "443"

Issue 6: Wazuh Agent Enrolls but Immediately Disconnects

Symptom: agent_control -l briefly shows Active, then reverts to Disconnected.

Diagnosis:

1
2
# Check the agent log for connection resets
Get-Content "C:\Program Files (x86)\ossec-agent\ossec.log" -Tail 50 | Select-String "ERROR|reset|timeout"

Common Causes:

  • TLS certificate mismatch between agent version and manager version
  • System clock skew between VM and WSL2 host (Wazuh uses timestamps for certificate validation)
  • Agent installed with wrong manager IP at install time (fix requires reinstall with correct WAZUH_MANAGER value)

Solution:

1
2
3
4
5
# Sync VM clock
w32tm /resync /force

# If clock was the issue, restart the agent
Restart-Service WazuhSvc

Issue 7: Apache Logs Not Appearing in Wazuh

Symptom: Attacks are hitting DVWA, Apache logs exist on disk, but no alerts fire.

Diagnosis:

1
2
3
4
5
# Confirm log file path is correct
Test-Path "C:\xampp\apache\logs\access.log"

# Check if Wazuh agent is reading it
Get-Content "C:\Program Files (x86)\ossec-agent\ossec.conf" | Select-String "xampp"

Common Causes:

  • XAMPP log path in ossec.conf doesn’t match the actual XAMPP install directory
  • XAMPP logging is disabled in Apache’s httpd.conf
  • Log file is 0 bytes because XAMPP hasn’t served any requests yet

Solution:

1
2
# On WSL2 — check what the agent is actually monitoring
sudo /var/ossec/bin/agent_control -i 001

Make sure the path in ossec.conf matches exactly, including capitalisation. Windows paths are case-insensitive at the OS level but Wazuh’s parser can be strict.


Security Best Practices

Network Isolation

  • Never connect VMs to a Bridged adapter unless absolutely necessary
  • Host-only + NAT provides the right balance of isolation and internet access
  • Disable all unnecessary network adapters on VMs

Credential Management

  • Never use default passwords (admin/admin, admin/password) in production
  • Rotate Wazuh API credentials regularly
  • Use Tailscale’s device authorization to prevent unauthorized node joins

Rule Hygiene

  • Document every custom rule with its purpose and MITRE mapping
  • Review rules quarterly — attack patterns evolve
  • Test rules with wazuh-logtest before deploying to production
  • Start with detection-only rules before enabling active response

Log Retention

  • Configure appropriate retention periods in the indexer
  • Ensure logs are stored encrypted at rest
  • Implement log integrity checking (Wazuh supports this natively)

Access Control

  • Restrict Wazuh API access to specific IP addresses
  • Use Tailscale ACLs to limit which peers can reach sensitive ports
  • Enable MFA on your Tailscale account

Challenges Faced & Solutions

Challenge 1: The WSL2 Network Nightmare

The Problem: WSL2 lives in a 172.x.x.x subnet that is completely invisible to VirtualBox VMs on the 10.0.60.0/24 network. Standard port forwarding failed because WSL2’s IP changes on every reboot, making static forwarding rules unreliable. The failure mode was particularly frustrating: the agent appeared to connect during the session it was configured, then silently broke after the next restart.

My Solution: A PowerShell startup script that dynamically detects the current WSL2 IP and rewrites netsh rules. Scheduled as a SYSTEM-level task, it runs before any user logs in, ensuring the pipeline is always operational.

Lesson: In complex multi-network environments, embrace automation early. Manual configuration is a liability, and silent failures are worse than loud ones.


Challenge 2: Asymmetric Firewall Blocking

The Problem: The Windows VM could ping the host (outbound worked), but the host couldn’t ping the VM (inbound blocked). This made diagnosing connectivity issues extremely confusing — tools reported connectivity even when it was broken in the direction that mattered.

My Solution: Windows Firewall was blocking all inbound traffic by default. The network profile was set to “Public” (most restrictive) instead of “Private.” Creating explicit inbound rules for the 10.0.60.0/24 subnet and changing the profile to “Private” resolved both issues.

Lesson: Always test bidirectional connectivity. Outbound working doesn’t mean inbound works.


Challenge 3: Wazuh Rule XML Syntax Errors

The Problem: Regex patterns in rules contained XML special characters (<, >, &) that caused the rule engine to fail silently or crash on startup. The error messages in ossec.log were cryptic and didn’t point directly to the offending line.

My Solution: XML entity encoding. Every < became &lt;, every > became &gt;, every & became &amp;. Additionally, frequency-based rules (triggering after N occurrences) required a parent rule context that wasn’t obvious from documentation.

Lesson: Always validate rules with wazuh-analysisd -t before restarting the service. Keep a working backup. A rule file that silently breaks the manager is harder to debug than a rule that simply doesn’t fire.


Challenge 4: Multi-Adapter Routing Confusion

The Problem: The Windows VM had three network adapters (10.0.50.10, 192.168.100.10, 10.0.60.10) from previous configuration attempts. Windows was routing traffic out the wrong adapter, causing intermittent connectivity.

My Solution: I disabled all unused adapters, set interface metrics to prioritize the correct adapter (lower metric = higher priority), and verified with route print that all traffic destined for 10.0.60.0/24 used the correct interface.

Lesson: Clean up before you configure. Leftover network adapter configurations cause ghost problems that waste hours.


Challenge 5: Wazuh Dashboard Index Pattern Mismatch

The Problem: After installation, the Wazuh dashboard showed no data even though agents were actively sending logs. The indexer was storing data and alerts.json was growing, but visualizations returned zero results.

My Solution: The default index pattern in OpenSearch Dashboards was either missing or pointed to the wrong index name. Navigating to Management → Index Patterns and creating a pattern for wazuh-alerts-4.x-* (matching the actual index name visible under Dev Tools → GET _cat/indices) immediately resolved the issue.

Lesson: Always verify the index pattern matches the actual indices being written to. The SIEM can be fully operational while the dashboard silently queries nothing.


Challenge 6: XAMPP Log Path Inconsistency

The Problem: The Wazuh agent was configured to read Apache logs from C:\xampp\apache\logs\access.log, but attacks against DVWA produced no alerts. The log file existed and was growing, but Wazuh wasn’t picking it up.

My Solution: XAMPP had been installed to a non-default path (D:\xampp\) on this particular machine, while ossec.conf still referenced the default C:\xampp\ path. Updating the path in the agent config and restarting the service immediately started producing alerts.

Lesson: Never assume default install paths. Verify every file path in configuration files against the actual filesystem before spending time debugging the tool itself.


Lessons Learned

1. Documentation Is Part of Engineering

Every configuration change should be documented immediately — not after the lab is “done.” During this build, there were multiple moments where a working configuration existed but couldn’t be reproduced because the exact commands that produced it weren’t recorded. The troubleshooting sections of this post exist because I hit each of those issues without notes and had to rediscover the solutions from scratch. If I had documented as I went, the build time would have been noticeably shorter.

I now treat notes like code: version them, keep them close to the thing they describe, and write them for a future version of myself who remembers nothing.


2. Automate Fragile Dependencies

Anything that changes automatically — like WSL2’s dynamic IP assignment — must be handled by automation, not by manual memory. The startup script that updates port forwarding should have been the first thing I wrote after understanding the network topology, not a late fix applied after experiencing repeated silent failures.

The general principle: if a configuration depends on a value that changes outside your control, write a script that detects and corrects it before the pipeline needs it.


3. Test in Isolation Before Integrating

Each component — VirtualBox networking, port forwarding, Wazuh manager, Wazuh agent, Apache log parsing — should be validated independently before connecting them together. The most time-consuming debugging sessions in this project involved multi-component failures where the actual breaking point was buried under layers of downstream symptoms.

A practical sequence: confirm the agent can reach the manager IP and port before installing the agent. Confirm the manager is receiving logs before writing detection rules. Confirm a rule matches test input before expecting it to match live traffic.


4. Understand Your Tools Deeply

Wazuh’s rule engine has specific behaviors around frequency rules, XML encoding, group inheritance, and decoder chaining that aren’t obvious from surface-level documentation. Time spent reading the actual rule syntax documentation — not just copying example rules — paid off significantly when debugging why certain rules fired inconsistently or not at all.

The same applies to netsh portproxy, which has its own quirks around IPv4-to-IPv4 forwarding and how it interacts with Windows Firewall. Knowing why the command works the way it does made it much easier to diagnose when it stopped working.


5. Pay Attention to Detail in Configuration

Small details caused the majority of the hours lost in this build: a log path with the wrong drive letter, a network profile set to “Public” instead of “Private,” an XML angle bracket that wasn’t escaped. None of these are conceptually difficult problems — they’re attention failures.

I now develop a habit of reading configuration back after writing it, and cross-referencing it against the actual system state (using ipconfig, netsh show, systemctl status, ss -tlnp) to catch most of these before they become debugging sessions. Assume the configuration is wrong until the tool confirms it is right.


6. Silent Failures Are More Dangerous Than Loud Ones

Several failures in this lab presented as “working” on the surface: the agent showed as connected, the dashboard loaded, the rules looked correct — but no alerts were firing. This is the most dangerous failure mode in a security monitoring context because it creates false confidence.

Build verification into your workflow. After each major component is set up, generate a known event and confirm it appears as an alert end-to-end. For this lab, that means running a simple SQL injection request against DVWA and watching for the alert to appear in the dashboard before moving on. If the pipeline is broken, you want to know immediately — not when the AI layer reports no data three days later.


7. Network Topology Affects Everything Downstream

The decision to use WSL2 rather than a dedicated Ubuntu VM for the Wazuh manager was made early to save resources. That single decision created the dynamic IP problem, the port forwarding requirement, the startup script, and several hours of debugging. None of those consequences were visible at the time of the decision.

Before choosing an architecture, think through the network implications of each component’s placement. Where does it live? What IP does it get? Can that IP change? Who needs to reach it? Drawing this out before writing a single command is time well spent.


8. Version Control Your Configuration Files

ossec.conf, the custom rules XML, the port forwarding script, the scheduled task definition — all of these are configuration artifacts that should live in a Git repository. When a rule edit breaks the manager, git diff immediately shows what changed. When the port forwarding script needs to be recreated after a Windows reinstall, it’s a git pull away.

This lab was built without version control on the configuration files initially, which made rolling back broken changes slower than it needed to be.


Possible Improvements & Next Steps

Short-Term Enhancements

1. Wazuh Vulnerability Scanner Enable Wazuh’s built-in vulnerability detection module to add CVE scanning on top of log monitoring.

2. Sysmon Integration Install Sysmon on the Windows VM for deeper telemetry — process creation, network connections, registry changes. This enriches alerts beyond what Apache logs provide.

3. Email/Slack Alerting Configure Wazuh to send high-severity alerts (Level 10+) to a Slack channel or email for immediate notification.

Medium-Term Improvements

4. Additional Attack Surfaces Add more vulnerable applications (Metasploitable, OWASP WebGoat) to expand detection coverage and generate richer training data for the AI model.

5. Network Traffic Analysis Add Zeek or Suricata for network-level detection alongside host-based Wazuh monitoring. Network detection catches attacks that bypass application logging.

Long-Term Architecture

6. Cloud Migration Path The same architecture can be replicated in AWS/Azure:

  • EC2/Azure VM replaces local VMs
  • VPC/VNet replaces host-only network
  • AWS VPN/Azure VPN replaces Tailscale
  • Wazuh Cloud (SaaS) replaces local WSL2 deployment

7. Multi-Site SOC Add agents in multiple geographic locations to simulate a distributed enterprise environment.


Conclusion

Building this lab taught me that security infrastructure isn’t about any single tool — it’s about understanding how components interact across network boundaries, trust domains, and operating system layers.

The deceptively simple statement “agents send logs to the manager” conceals a web of network configuration, firewall rules, port forwarding, and service management that took significant effort to get right reliably. Every production SIEM deployment faces the same complexity at larger scale.

What I built here — a self-contained, multi-layer SOC platform that generates real attack telemetry, detects it with custom rules, and feeds it into an AI analysis engine — is a microcosm of what real enterprise security teams build and operate every day.

The 95%+ detection rate against all simulated APT attacks validated that the infrastructure works. But more importantly, building it gave me a genuine understanding of why it works — and that understanding is what makes a security engineer valuable.

If you replicate this lab, you’ll encounter your own specific challenges. The troubleshooting mindset is more valuable than any specific command: isolate, test, understand, fix.

The full project — including the AI analysis layer built by Nicole Nyagah and the APT emulation framework built by Curtis Okello — represents what’s possible when infrastructure, data science, and security research come together.


References & Resources

This post is licensed under CC BY 4.0 by the author.