This covers the build-out of a functional threat hunting and DFIR lab running on a single repurposed server. The focus is on realistic log volume, proper network segmentation, and having the right tools available for each phase of an
investigation.
Hardware baseline
The lab runs on a Dell PowerEdge R730 (2x Xeon E5-2640v4, 128GB RAM, 4x 1TB SAS in RAID10). A minimum viable setup for this work is 32GB RAM — enough to run 4-5 VMs simultaneously without constant swapping. The storage minimum is approximately
500GB to hold OS images, snapshots, and log data without constant cleanup.
Network topology
Internet
|
[Home Router - 192.168.1.0/24]
|
[Lab Switch - VLAN aware]
|
+-- VLAN 10 (172.16.10.0/24) -- Management
| - Velociraptor server
| - SIEM (Elastic)
| - Zeek sensor
|
+-- VLAN 20 (172.16.20.0/24) -- Attack
| - Kali Linux
|
+-- VLAN 30 (172.16.30.0/24) -- Targets
- Windows 10 (patched)
- Windows 10 (vulnerable - no EDR)
- Windows Server 2019 (DC)
- Ubuntu 22.04 (web target)
VLAN 20 (Attack) can reach VLAN 30 (Targets). VLAN 10 (Management) can reach both. Internet access from VLAN 20 routes through a Zeek tap so all attack tool downloads and C2 callbacks are logged. Targets have no direct internet access.
SIEM stack — Docker Compose
version: "3"
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms4g -Xmx4g
- xpack.security.enabled=true
- ELASTIC_PASSWORD=changeme
volumes:
- esdata:/usr/share/elasticsearch/data
ports:
- "9200:9200"
kibana:
image: docker.elastic.co/kibana/kibana:8.11.0
environment:
- ELASTICSEARCH_URL=http://elasticsearch:9200
- ELASTICSEARCH_USERNAME=kibana_system
- ELASTICSEARCH_PASSWORD=changeme
ports:
- "5601:5601"
depends_on:
- elasticsearch
fleet-server:
image: docker.elastic.co/beats/elastic-agent:8.11.0
environment:
- FLEET_SERVER_ENABLE=true
- FLEET_SERVER_ELASTICSEARCH_HOST=http://elasticsearch:9200
- FLEET_SERVER_SERVICE_TOKEN=<generate_via_kibana>
ports:
- "8220:8220"
volumes:
esdata:
Start with docker compose up -d. Kibana is available at port 5601. First login: create the kibana_system password via the elasticsearch container:
docker exec -it elastic_elasticsearch_1 \ bin/elasticsearch-reset-password -u kibana_system
Sysmon configuration
The SwiftOnSecurity config is a solid starting point but needs additions for a lab:
# Download base config Invoke-WebRequest -Uri https://raw.githubusercontent.com/SwiftOnSecurity/sysmon-config/master/sysmonconfig-export.xml -OutFile sysmon.xml # Install Sysmon with config .\Sysmon64.exe -accepteula -i sysmon.xml # Verify Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" -MaxEvents 5 | Select TimeCreated, Id, Message
Additional rules to add for the lab (add inside the appropriate EventFiltering sections):
<!-- Log all LSASS access regardless of source --> <ProcessAccess onmatch="include"> <TargetImage condition="is">C:\Windows\System32\lsass.exe</TargetImage> </ProcessAccess> <!-- Log all DNS queries --> <DnsQuery onmatch="exclude"> <QueryName condition="end with">.microsoft.com</QueryName> <QueryName condition="end with">.windows.com</QueryName> </DnsQuery>
Elastic Agent deployment to Windows targets
# In Kibana: Fleet > Agent Policies > Create policy "Lab Windows" # Add integrations: Windows (event logs), Sysmon # On Windows target - download the agent from Fleet UI # Then install: .\elastic-agent.exe install --url=https://172.16.10.10:8220 ` --enrollment-token=<token_from_fleet_ui> ` --insecure # lab only - use proper certs in production
After enrollment, events appear in Kibana under Discover within about 30 seconds. The Sysmon integration auto-parses all field mappings — process names, hashes, network connections — without manual index template configuration.
Zeek on the network tap
# Interface ens4 is the span/tap port receiving all lab traffic zeekctl deploy zeekctl status # Verify logs are writing tail -f /opt/zeek/logs/current/conn.log | python3 -m json.tool