Project Zero: Network Lab (Part 2)

case study

Network Defenses

last edited 2026-07-29

#IP Address Changes

This table summarizes the static and dynamic IP assignments for every VM, including their default gateways and whether they sit on the host‑only LAN or DMZ. It clearly shows which services are on 192.168.15.0/24 and which are exposed in the 10.0.5.0/24 DMZ behind the pfSense firewall.

Hostname [project-zero- … ]IP AddressDefault GatewayNetwork Adapter
-dc (corp.project-zero-dc.com)192.168.15.25192.168.15.2Host-only LAN
-corp-svr10.0.5.1010.0.5.2Host-only DMZ
-sec-box192.168.15.50192.168.15.2Host-only LAN
-sec-work192.168.15.53 or (dynamic)192.168.15.2Host-only LAN
-win-client192.168.15.51 or (dynamic)192.168.15.2Host-only LAN
-linux-client192.168.15.52 or (dynamic)192.168.15.2Host-only LAN
kalidynamic192.168.15.2Host-only LAN
corp-fw192.168.15.2 (LAN)Router & FirewallHost-only WAN, DMZ, LAN
corp-svr-web-svr10.0.5.10:80Web ServerHost-only LAN
corp-svr-dns-svr10.0.5.10:53DNS ServerHost-only LAN
corp-svr-ftp-svr10.0.5.10:21File ServerHost-only LAN

1.Domain controller IP change

Configuring a static IP on the domain controller in the 192.168.15.0/24 range and pointing its default gateway at 192.168.15.2. The successful ping to 192.168.15.2 confirms the DC is now routed through the corp-fw firewall on the LAN segment.

Updating the domain controller to use the new LAN IP and pfSense gateway.
Updating the domain controller to use the new LAN IP and pfSense gateway.
Verifying connectivity from the domain controller to the pfSense LAN gateway at 192.168.15.2 using ping.
Verifying connectivity from the domain controller to the pfSense LAN gateway at 192.168.15.2 using ping.

2.pfSense DHCP pool and leases

pfSense is configured to hand out a small dynamic range (192.168.15.5–192.168.15.10) on the LAN, and the leases page shows project-zero-sec-work, linux-client, and project-zero-win-client all receiving addresses from that pool. This verifies that my client and security agent machines are using pfSense as their DHCP server and are correctly attached to the protected LAN.

Configuring the pfSense LAN DHCP pool to allocate addresses from 192.168.15.5 to 192.168.15.10.
Configuring the pfSense LAN DHCP pool to allocate addresses from 192.168.15.5 to 192.168.15.10.
pfSense DHCP leases showing  project-zero-sec-work ,  linux-client , and  project-zero-win-client  receiving IPs from the LAN address pool.
pfSense DHCP leases showing project-zero-sec-work, linux-client, and project-zero-win-client receiving IPs from the LAN address pool.

##Assign Static IP Addresses On [project-zero-sec-box] & [project-zero-corp-svr]

I assigned static IP addresses to my project-zero-sec-box on the LAN and project-zero-corp-svr1 in the DMZ, pointed both at the pfSense gateway, and verified they can reach the internet and upstream DNS.

1.project-zero-sec-box on LAN

project-zero-sec-box set to a static address of 192.168.15.50/24 with 192.168.15.2 as the default gateway in the network settings. This pins the sec-box to a fixed IP on the protected LAN so it can reliably act as a security monitoring node behind corp-fw.

Configuring  project-zero-sec-box  with a static LAN IP and pfSense gateway.
Configuring project-zero-sec-box with a static LAN IP and pfSense gateway.
ip a  output confirms the interface on  enp0s3  is up with that IP, proving the sec-box is correctly attached to the protected LAN.
ip a output confirms the interface on enp0s3 is up with that IP, proving the sec-box is correctly attached to the protected LAN.

2.Setting project-zero-corp-svr1 to a static DMZ IP with the pfSense DMZ gateway

project-zero-corp-svr1 configured with a static address of 10.0.5.10/24 and 10.0.5.2 as its default gateway in the network settings. This pins the web/server VM to a fixed IP in the DMZ so pfSense can consistently route and filter traffic to it.

Setting  project-zero-corp-svr1  to a static DMZ IP with the pfSense DMZ gateway.
Setting project-zero-corp-svr1 to a static DMZ IP with the pfSense DMZ gateway.
Verifying the DMZ IP configuration and connectivity for  project-zero-corp-svr1 .
Verifying the DMZ IP configuration and connectivity for project-zero-corp-svr1.

#Wazuh Manager & UFW Rules on [project-zero-corp-svr]

Enabling UFW on project-zero-corp-svr and adding rules to allow traffic on ports 1514 and 1515 over both UDP and TCP. These ports are required for Wazuh agent communication and enrollment, so opening them ensures agents can register with the manager and send events without being blocked by the local firewall.

Enabling UFW and opening Wazuh manager ports on  project-zero-corp-svr .
Enabling UFW and opening Wazuh manager ports on project-zero-corp-svr.

# Add Wazuh Agents to[project-zero-sec-work] & [project-zero-corp-svr]

1.Wazuh dashboard agent deployment wizard

Navigating to the Wazuh dashboard’s Agents/Endpoint section and selecting a Linux agent deployment option. Here I set the server address, agent name, and group so the dashboard can generate a tailored install command for my project-zero-sec-work host.

Server Address: 192.168.15.50

Assign an agent name: project-zero-sec-work

Groups: linux

Using the Wazuh dashboard to generate a Linux agent deployment command.
Using the Wazuh dashboard to generate a Linux agent deployment command.
Screenshot from project documentation
Screenshot from project documentation

2.Running the Wazuh agent installer on [project-zero-sec-work]

Here, I run the curl command from the wizard to download wazuh-agent-4.9.2-1.x86_64.rpm and install it on project-zero-sec-work. The terminal output confirms the package is verified and installed, placing the agent binaries on my sec-box.

Wazuh dashboard providing the curl-based Linux agent installation command.
Wazuh dashboard providing the curl-based Linux agent installation command.
Downloading and installing the Wazuh agent RPM on  project-zero-sec-work .
Downloading and installing the Wazuh agent RPM on project-zero-sec-work.

3.Enabling and starting the Wazuh agent service

Running systemctl daemon-reload, systemctl enable wazuh-agent, and systemctl start wazuh-agent to register and start the service. The final systemctl status wazuh-agent output confirms the agent is loaded and actively running on the host.

plain text
sudo systemctl daemon-reload
sudo systemctl enable wazuh-agent
sudo systemctl start wazuh-agent
Enabling and starting the Wazuh agent service on  project-zero-sec-work .
Enabling and starting the Wazuh agent service on project-zero-sec-work.

4.Repeat the same steps for [project-zero-corp-svr]

The Wazuh dashboard lists both my sec-box and DMZ server agents as connected and reporting telemetry. This verifies that the agents successfully registered with the manager and are now contributing logs and alerts to my security monitoring stack.

Wazuh dashboard confirming agents on  project-zero-sec-work  and  project-zero-corp-svr  are active.
Wazuh dashboard confirming agents on project-zero-sec-work and project-zero-corp-svr are active.

#Update Wazuh Agents On Workstations [project-zero-linux-client] . [project-zero-win-client] .[project-zero-sec-work] .[project-zero-dc]

I updated the ossec.conf client configuration on my Linux workstation, domain controller, and Windows client so all Wazuh agents now report to the project-zero-sec-box manager IP. After restarting each agent service, I confirmed in the Wazuh dashboard that the agents registered correctly and show the expected IPs and statuses.

1.Linux client ossec.conf

Editing /var/ossec/etc/ossec.conf on project-zero-linux-client to set the <address> field to 192.168.15.50 and keep port 1514/tcp. This change ensures the Linux workstation’s Wazuh agent forwards events directly to the sec-box manager on the LAN.

Pointing the Linux client Wazuh agent at the  project-zero-sec-box  manager IP.
Pointing the Linux client Wazuh agent at the project-zero-sec-box manager IP.

2.Wazuh dashboard showing updated Linux client entry

The Wazuh Agents table lists the Linux client with its updated IP and group, confirming it is enrolled under the correct manager and configuration profile. This verifies that the ossec.conf change on the Linux workstation was applied successfully.

Wazuh dashboard reflecting the Linux client agent reporting to the new manager.
Wazuh dashboard reflecting the Linux client agent reporting to the new manager.

3.Windows DC ossec.conf (file location)

Windows domain controller’s Wazuh agent configuration file opened from the OSSEC agent install directory. I’m preparing to update the <address> element so the DC’s agent reports to the same sec-box manager as the Linux hosts.

Editing the Windows DC Wazuh agent config in  C:\Program Files (x86)\ossec-agent\ossec.conf .
Editing the Windows DC Wazuh agent config in C:\Program Files (x86)\ossec-agent\ossec.conf.
Configuring the Windows DC Wazuh agent to use 192.168.15.50 as its manager.
Configuring the Windows DC Wazuh agent to use 192.168.15.50 as its manager.

4.Restarting the Wazuh service on the DC

Here I run NET STOP WAZUH followed by NET START WAZUH from an elevated command prompt, and both commands complete successfully. Restarting the service reloads the updated ossec.conf so the DC agent reconnects to the new manager IP without requiring a reboot.

Restarting the Windows Wazuh service to apply the updated  ossec.conf .
Restarting the Windows Wazuh service to apply the updated ossec.conf.
Wazuh dashboard reflecting the DC agent reporting to the new manager.
Wazuh dashboard reflecting the DC agent reporting to the new manager.

5.Windows client ossec.conf

I updated the Wazuh agent’s ossec.conf so it points to the project-zero-sec-box manager at 192.168.15.50 and verified in the Wazuh dashboard that the project-zero-win-client agent shows as active with the correct IP.

Configuring the  project-zero-win-client  Wazuh agent to use 192.168.15.50 as its manager.
Configuring the project-zero-win-client Wazuh agent to use 192.168.15.50 as its manager.
Wazuh dashboard confirming the  project-zero-win-client  agent is enrolled and active.
Wazuh dashboard confirming the project-zero-win-client agent is enrolled and active.

#Add [project-zero-kali]to CORP-DMZ

Enabled DHCP on the DMZ interface and successfully placed your Kali box into the CORP‑DMZ with an address from that pool.

1.pfSense DMZ DHCP configuration

The DMZ tab of the pfSense DHCP server with DHCP enabled on the DMZ interface and an address pool range of 10.0.5.3–10.0.5.4 inside the 10.0.5.0/24 subnet. This configuration allows DMZ hosts like project-zero-kali to automatically receive an IP address when they join the CORP‑DMZ network.

Enabling the pfSense DHCP server on the DMZ and defining a small address pool for CORP‑DMZ clients.
Enabling the pfSense DHCP server on the DMZ and defining a small address pool for CORP‑DMZ clients.

2.Kali client IP assignment in CORP‑DMZ

project-zero-kali  receiving a 10.0.5.x address from the DMZ DHCP pool.
project-zero-kali receiving a 10.0.5.x address from the DMZ DHCP pool.

#Install pfSuricata on [project-zero-corp-fw]

I installed the Suricata IDS/IPS package on my project-zero-corp-fw pfSense firewall via the Package Manager and then tuned the system’s advanced networking settings by disabling hardware checksum offload, reducing NIC offload issues and ensuring Suricata can reliably inspect traffic.

1. Navigating to pfSense Package Manager

System → Package Manager in the pfSense web GUI. From here I can search for and install the Suricata IDS/IPS package on project-zero-corp-fw

Opening the pfSense Package Manager to install Suricata.
Opening the pfSense Package Manager to install Suricata.

2.Installing the Suricata packag

Searched for “suricata” under Available Packages and click Install on the Suricata entry, which pulls in version 7.x as a pfSense package. This step adds the Suricata engine and its management UI under the Services menu.

Selecting the Suricata IDS/IPS package for installation in pfSense.
Selecting the Suricata IDS/IPS package for installation in pfSense.
Confirming Suricata is installed on  project-zero-corp-fw .
Confirming Suricata is installed on project-zero-corp-fw.

3. Navigating to Advanced networking settings

Accessing pfSense System → A dvanced to adjust network interface settings.
Accessing pfSense System → Advanced to adjust network interface settings.

4. Hardware checksum offload settings

Disabling problematic offloads helps ensure Suricata receives accurate packet data and avoids offload-related packet loss on pfSense.

Reviewing hardware checksum and offload options for Suricata compatibility.
Reviewing hardware checksum and offload options for Suricata compatibility.
pfSense prompting for a reboot after updating hardware checksum settings.
pfSense prompting for a reboot after updating hardware checksum settings.

I enabled Suricata on the pfSense LAN interface, switched it to inline IPS mode with “Block Offenders” enabled, and tuned the engine’s performance settings so it can actively drop malicious traffic while efficiently inspecting packets on project-zero-corp-fw

Accessing the Suricata configuration page from the pfSense Services menu.
Accessing the Suricata configuration page from the pfSense Services menu.
Adding a new Suricata instance to protect the LAN interface.
Adding a new Suricata instance to protect the LAN interface.

5.Enabling Suricata on LAN and sending logs to the system

Suricata enabled on the LAN (em1) interface, with “Send alerts to system log” checked so pfSense receives Suricata alerts alongside firewall logs. This ensures LAN traffic is inspected by Suricata and that detections are centrally visible in pfSense logging.

Enabling Suricata on LAN and forwarding alerts to the pfSense system log.
Enabling Suricata on LAN and forwarding alerts to the pfSense system log.

Suricata interface overview showing the LAN instance configured.
Suricata interface overview showing the LAN instance configured.

6.Enabling Block Offenders and IPS Mode

Here, under Alert and Block Settings, I enable “Block Offenders” and select “Inline Mode” for IPS mode, which allows Suricata to drop packets from hosts that trigger rules instead of only generating alerts. This turns Suricata on the LAN into an active IPS capable of blocking malicious traffic in real time.

Configuring Suricata to block offenders and run in inline IPS mode.
Configuring Suricata to block offenders and run in inline IPS mode.

7.Performance and detection engine tuning

Tuning Suricata run mode and pattern-matching algorithms for better IPS performance.
Tuning Suricata run mode and pattern-matching algorithms for better IPS performance.

##Download Open-Source Detection Rules

Enabled the ETOpen Emerging Threats ruleset and the Snort GPLv2 Community rules in Suricata’s Global Settings so my pfSense IDS/IPS can detect a wide range of threats using free, regularly updated open‑source signatures.

1.Install ETOpen Emerging Threats and Snort GPLv2 Community rules

These options subscribe my pfSense Suricata instance to two free, community‑maintained rule feeds, giving it broad coverage of common malware, exploits, and network attacks without requiring paid subscriptions.

Enabling ETOpen Emerging Threats and Snort GPLv2 Community rules in Suricata Global Settings.
Enabling ETOpen Emerging Threats and Snort GPLv2 Community rules in Suricata Global Settings.

##Custom Rule Blocking

Added a custom Suricata rule to drop ICMP requests on the LAN, triggered it by pinging the DMZ server from my Linux client, and verified that pfSense records “ICMP Request Blocked” alerts for those blocked packets.

1.Custom ICMP drop rule in pfSuricata

The Suricata LAN Rules tab with a custom rule drop icmp any any -> any any (msg:"ICMP Request Blocked"; sid:2; rev:1;) added under custom.rules. Defining this rule instructs Suricata to immediately drop ICMP echo traffic on the LAN and tag it with the “ICMP Request Blocked” message.

Creating a custom Suricata rule to drop ICMP requests on the LAN.
Creating a custom Suricata rule to drop ICMP requests on the LAN.

2.Linux client ping test Navigate to [project-zero-linux-client]. Attempt to ping the [project-zero-corp-svr] host.

Testing the custom ICMP drop rule by pingi ng  project-zero-corp-svr  from the Linux client.
Testing the custom ICMP drop rule by pinging project-zero-corp-svr from the Linux client.

3.Suricata alert log showing ICMP blocked

The Suricata alerts view on the LAN instance shows multiple entries with action “DROP” and description “ICMP Request Blocked” for ICMP traffic from the Linux client. These alerts confirm that the custom rule is firing and that Suricata is successfully blocking the ICMP pings in inline IPS mode.

pfSense Suricata alert log showing dropped ICMP traffic labeled “ICMP Request Blocked.”
pfSense Suricata alert log showing dropped ICMP traffic labeled “ICMP Request Blocked.”

# Layer 2 Security Monitoring (Prevent ARP Cache Poisoning)

-Virtual Machine: [project-zero-win-client]

-Virtual Machine: [project-zero-attacker]

-Virtual Machine: [project-zero-corp-fw]

In this layer 2 monitoring step, I installed and enabled Arpwatch on pfSense to watch ARP activity on my LAN and DMZ interfaces. I then launched an ARP poisoning attack from project-zero-kali using Ettercap against project-zero-corp-svr, and verified that Arpwatch recorded the gateway’s IP/MAC pairing—providing a baseline that will later be used to spot ARP table manipulation.

Arpwatch is a package that can be installed on pfSense, a network monitoring tool that keeps track of Address Resolution Protocol (ARP) traffic, logging pairings of IP addresses and their corresponding MAC addresses, along with timestamps.

1. Installing Arpwatch via Package Manager

Navigate to "System" ➔ "Package Manager". Look up "arpwatch" ➔ Install

Searching for and installing the Arpwatch package in the pfSense Package Manager.
Searching for and installing the Arpwatch package in the pfSense Package Manager.
Confirming Arpwatch is installed on the pfSense firewall.
Confirming Arpwatch is installed on the pfSense firewall.

2.Enabling Arpwatch on LAN and DMZ

Configuring Arpwatch to listen for ARP packets on LAN and DMZ interfaces.
Configuring Arpwatch to listen for ARP packets on LAN and DMZ interfaces.

3.Kali DMZ interface IP and MAC

The ip a output on the Kali VM shows eth0 configured in the DMZ with IP 10.0.5.3/24 and its MAC address. This confirms that Kali is correctly attached to CORP‑DMZ and ready to act as the attacker in an ARP poisoning scenario against the DMZ server.

Verifying  project-zero-kali  network interface details before launching an ARP attack.
Verifying project-zero-kali network interface details before launching an ARP attack.

4.Running Ettercap ARP poisoning from Kali

The Ettercap command sudo ettercap -T -M arp:remote /10.0.5.10// /10.0.5.2// running on Kali, which positions the attacker between the DMZ server (10.0.5.10) and the gateway (10.0.5.2). Ettercap reports both victims and begins marking them as ARP poisoning targets, simulating a man‑in‑the‑middle attack.

Launching an Ettercap ARP poisoning attack from  project-zero-kali  against the DMZ gateway and server.
Launching an Ettercap ARP poisoning attack from project-zero-kali against the DMZ gateway and server.

5.Arpwatch database entry for DMZ gateway

Shows the Arpwatch Database tab listing interface OPT1 with IP 10.0.5.2 and MAC 08:00:27:63:b0:05, along with a timestamp. This baseline entry lets me detect any later changes or flip‑flop events that would indicate ARP spoofing against the DMZ gateway.

Arpwatch database showing the DMZ gateway’s IP/MAC pairing recorded on OPT1.
Arpwatch database showing the DMZ gateway’s IP/MAC pairing recorded on OPT1.

6.ARP table on project-zero-corp-svr

The arp -a output on project-zero-corp-svr shows the gateway 10.0.5.2 mapped to MAC 08:00:27:f6:91:d9 on enp0s3. Comparing this with Arpwatch’s records helps confirm whether the ARP poisoning attempt has altered the server’s view of the gateway, supporting ARP cache poisoning detection.

Checking the DMZ server’s ARP table to verify the gateway’s IP/MAC mapping.
Checking the DMZ server’s ARP table to verify the gateway’s IP/MAC mapping.

#Layer 3-4 Security Monitoring (IP DoS)

I simulate a TCP DoS attack against my web-svr using hping3, then use a custom pfSuricata rule with thresholding to detect and drop the flood once it exceeds a defined packet rate.

1.pfSuricata installation and custom DoS rule logic

After installing the pfSense Suricata package, I add a custom rule that tracks how many TCP packets a single source sends to 10.0.5.10 in a one‑second window, dropping traffic when the count goes over 100. This turns Suricata into a simple rate‑limiting control that can identify an hping3‑style flood and block it once the threshold is crossed

Installing pfSuricata and defining a threshold‑based TCP DoS rule.
Installing pfSuricata and defining a threshold‑based TCP DoS rule.

drop ip any any -> 10.0.5.10 any (msg:"DROP: IP Packets Going to corp-svr"; sid:1000003; rev:1;)

Here is the most restrictive and unpractical of rules. For any request, drop all packets to 10.0.5.10. This would be a good rule for testing purposes only. We still want our employees to navigate to the Project Zero portal.

drop ip any any -> 10.0.5.10 any (msg:"DROP: DoS - Too many packets from attacker"; threshold:type both, track by_src, count 100, seconds 1; sid:1000006; rev:1;)

Here is the most efficient rule. We perform pattern matching by tracking how many IP packets are sent over a 1 second interval. If the count exceeds 100, then the network traffic is blocked.

2.Running the hping3 flood from project-zero-attacker

On project-zero-attacker, I run sudo hping3 -c 250000 -d 120 -S -p 80 -V 10.0.5.10 to bombard the web server with SYN packets on port 80. The hping3 statistics show that all 250,000 packets are transmitted but only a tiny fraction are received, indicating heavy packet loss once Suricata starts dropping the flood.

Launching a TCP flood against  project-zero-corp-svr  with hping3 from the attacker VM.
Launching a TCP flood against project-zero-corp-svr with hping3 from the attacker VM.

3. Suricata alerts showing DoS packets dropped

In the Suricata Alerts view, multiple entries appear with action “DROP” and description “DoS – Too many packets from attacker,” all for TCP traffic from 192.168.15.10 to 10.0.5.10:80. These alerts confirm that the threshold‑based rule is firing and that pfSuricata is actively blocking the hping3 flood aimed at the web server.suricata+2

Suricata alert log showing “DROP: DoS – Too many packets from attacker” events.
Suricata alert log showing “DROP: DoS – Too many packets from attacker” events.

#Layer 7 (FTP Attack)

In this final layer 7 prevention step, I use a custom Suricata rule on pfSense to restrict FTP access to my DMZ FTP container so that only project-zero-linux-client is allowed to connect, while any other host is blocked and logged as an unauthorized attempt.

1.Layer 7 FTP rule concept

I add a custom rule drop tcp ![192.168.15.5] any -> 10.0.5.10 21 (msg:"DROP: Unauthorized FTP access attempt"; sid:1000007; rev:1;), which uses the ! operator to match any source IP except 192.168.15.5. This turns Suricata into a simple application aware firewall control. Every FTP connection to 10.0.5.10:21 is dropped unless it originates from my trusted Linux client.

2.Starting the FTP server in the DMZ

On project-zero-corp-svr, I run sudo docker start ftp-svr to bring up the FTP container exposed on port 21 of 10.0.5.10. This gives Suricata a real service endpoint to protect, enforcing my “single trusted client” rule on live FTP traffic.

Launching the  ftp-svr  container on  project-zero-corp-svr  in the DMZ.
Launching the ftp-svr container on project-zero-corp-svr in the DMZ.

3.Unauthorized FTP attempt blocked and logged

From the Kali attacker VM, an ftp 10.0.5.10 attempt fails with “Connection refused,” and the Suricata alert view records TCP drops from 192.168.15.8 to 10.0.5.10:21 with the description “DROP: Unauthorized FTP access attempt.” Together, these outputs confirm that the layer 7 rule is matching non‑trusted FTP clients and actively blocking them at the firewall.

FTP connection from the Kali attacker to 10.0.5.10 is refused, indicating the Suricata FTP access control rule is successfully blocking unauthorized clients.
FTP connection from the Kali attacker to 10.0.5.10 is refused, indicating the Suricata FTP access control rule is successfully blocking unauthorized clients.
Attacker FTP connection refused while Suricata logs “DROP: Unauthorized FTP access attempt.
Attacker FTP connection refused while Suricata logs “DROP: Unauthorized FTP access attempt.

#Ingest pfSuricata Logs Into Wazuh on [project-zero-corp-fw]

-Virtual Machine [project-zero-sec-work] is turned on and configured.

-Virtual Machine [project-zero-corp-fw] is turned on.

In this step, I install the syslog-ng package on project-zero-corp-fw, configure it to listen on the LAN interface over UDP port 1514, and then enable pfSense remote logging so firewall and system events are sent to syslog-ng. This creates a central log feed that Wazuh can later consume.

pfSense Logs

1.Installing syslog-ng via Package Manager

Adding the syslog-ng package on pfSense to act as a local syslog server.
Adding the syslog-ng package on pfSense to act as a local syslog server.

2.Opening the syslog-ng service

Accessing the syslog-ng configuration page from the pfSense Services menu.
Accessing the syslog-ng configuration page from the pfSense Services menu.

3.Configuring syslog-ng to listen on LAN UDP 1514

The syslog-ng General settings where I enable the service, select the LAN interface, choose UDP as the default protocol, and set the default port to 1514 while including the standard SCL config library. With these options saved, syslog-ng is ready to receive log traffic from pfSense on LAN:1514.

Enabling syslog-ng on the LAN interface using UDP port 1514 for incoming logs.
Enabling syslog-ng on the LAN interface using UDP port 1514 for incoming logs.

4.Navigating to pfSense System Logs settings

Opening pfSense System Logs to configure remote logging toward syslog-ng.
Opening pfSense System Logs to configure remote logging toward syslog-ng.

5.Enabling remote logging to the syslog-ng listener

I enable remote logging, set the remote log server to 192.168.15.50:1514, and select log categories like system and firewall events to be forwarded. This ensures pfSense streams its key security logs into syslog-ng on project-zero-sec-work, where Wazuh can ingest and correlate them with host and Suricata alerts.

Configuring pfSense to send firewall and system events to the local syslog-ng server.
Configuring pfSense to send firewall and system events to the local syslog-ng server.

6.Configuring pfSense firewall rules

To allow pfSense to forward logs to the Wazuh server, I first verified that the required firewall rules were present on both the LAN and DMZ interfaces. These rules permit UDP traffic on port 5514 from the firewall to the security server while maintaining the existing segmentation between network zones. Without these rules, the syslog messages generated by pfSense would never reach the Wazuh manager for processing.

Firewall rules permitting pfSense to forward syslog traffic to the Wazuh server over UDP port 5514.
Firewall rules permitting pfSense to forward syslog traffic to the Wazuh server over UDP port 5514.
Firewall rules permitting pfSense to forward syslog traffic to the Wazuh server over UDP port 5514.
Firewall rules permitting pfSense to forward syslog traffic to the Wazuh server over UDP port 5514.

7.Configuring Wazuh to receive remote syslog

On the sec-box, I updated the ossec.conf configuration file to enable remote syslog ingestion from pfSense. The new <remote> block instructs Wazuh to listen on UDP port 5514, accept log messages only from the trusted pfSense firewall (192.168.15.2), and bind the listener to the security server's interface. Restricting log sources helps prevent unauthorized systems from injecting events into the SIEM.

Completed Wazuh syslog configuration allowing secure ingestion of pfSense logs.
Completed Wazuh syslog configuration allowing secure ingestion of pfSense logs.

8.Confirming pfSense logs are reaching Wazuh

After restarting the Wazuh manager, I monitored the archives.log file using tail -f to verify that log messages were being received successfully. The live output confirms that DHCP and system events generated by pfSense are arriving on the Wazuh server in real time, validating that the remote syslog configuration is functioning correctly before moving on to detection and analysis.

Live monitoring confirms pfSense events are successfully arriving at the Wazuh manager.
Live monitoring confirms pfSense events are successfully arriving at the Wazuh manager.

9.Viewing pfSense authentication logs in Wazuh

I intentionally generated a failed login attempt on the pfSense web interface. Wazuh successfully ingested the authentication event and displayed it within the SIEM, proving that firewall logs are being collected, parsed, and indexed for security monitoring. This provides centralized visibility into administrative activity and failed authentication attempts occurring on the firewall.

Log back in with a bad password.
Log back in with a bad password.
Failed pfSense authentication event successfully ingested and displayed within Wazuh.
Failed pfSense authentication event successfully ingested and displayed within Wazuh.

#Ingest Security Logs Into Wazuh On [project-zero-sec-work]

To integrate Suricata with Wazuh, I configured the Wazuh agent to monitor Suricata's generated log files. By adding custom <localfile> entries to ossec.conf, Wazuh continuously ingests both the structured eve.json logs and the fast.log alert file. After restarting the Wazuh agent, Suricata detection events are automatically forwarded into Wazuh, providing centralized visibility for network intrusion alerts.

-Virtual Machine [project-zero-sec-work] is turned on and configured.

-Virtual Machine [project-zero-corp-fw] is turned on.

1.Configuring Wazuh to monitor Suricata logs

On project-zero-sec-work, I updated the ossec.conf configuration file by adding two <localfile> blocks. These instruct the Wazuh agent to continuously monitor Suricata's eve.json file for structured JSON events and fast.log for traditional syslog-style alerts. Collecting both log formats provides Wazuh with detailed intrusion data while maintaining compatibility with standard Suricata alert logs.

Adding Suricata log sources to the Wazuh agent configuration for continuous log ingestion.
Adding Suricata log sources to the Wazuh agent configuration for continuous log ingestion.

2.Verifying Suricata alerts in Wazuh

After restarting the Wazuh agent, I generated network traffic to verify the integration. The screenshot shows a Suricata detection successfully ingested into Wazuh, including the alert signature, severity, source and destination information, and packet metadata. This confirms that Suricata events are being collected, parsed, and indexed correctly.

Suricata alert successfully ingested and displayed in the Wazuh dashboard.
Suricata alert successfully ingested and displayed in the Wazuh dashboard.

#Group Policy Object Organization on [project-zero-dc]

-Virtual Machine: [project-zero-win-client]

-Virtual Machine: [project-zero-dc]

-Virtual Machine: [project-zero-corp-fw]

To strengthen the Active Directory environment, I organized domain objects into a dedicated Organizational Unit (OU) and applied Group Policy Objects (GPOs) to enforce centralized security controls. Rather than configuring individual workstations manually, Group Policy allows security settings to be managed from the Domain Controller and automatically applied to all systems within the targeted OU. This approach improves consistency, simplifies administration, and reduces the likelihood of configuration drift across the enterprise.

Before implementing Active Directory Group Policies, I removed the DHCP Server role from the Domain Controller. In this environment, pfSense serves as the network's dedicated DHCP server, making the Windows DHCP service unnecessary. Removing the role eliminates the possibility of conflicting IP address assignments and ensures the Domain Controller is focused solely on identity and directory services.

1.Opening the Remove Roles and Features Wizard

Opening the Server Manager wizard to remove the DHCP Server role from the Domain Controller.
Opening the Server Manager wizard to remove the DHCP Server role from the Domain Controller.

2.Removing the DHCP Server role

Within the wizard, I unchecked the DHCP Server role to remove it from the Domain Controller. This ensures pfSense remains the only DHCP server on the network, preventing IP address conflicts while allowing the Domain Controller to focus on Active Directory services.

Removing the DHCP Server role to allow pfSense to act as the network's sole DHCP server.
Removing the DHCP Server role to allow pfSense to act as the network's sole DHCP server.

##Create Workstations OU

To simplify the management of domain-joined systems, I created a dedicated Organizational Unit (OU) for workstations within Active Directory. Organizing computers and users into a dedicated OU allows Group Policy Objects (GPOs) to be applied selectively, providing centralized administration while keeping domain resources logically structured. This approach makes future policy deployment, system hardening, and administrative tasks significantly easier.

1.Opening Active Directory Users and Computers

Opening the Active Directory Users and Computers management console.
Opening the Active Directory Users and Computers management console.

2.Creating a new Organizational Unit

Creating a new Organizational Unit within the Active Directory domain.
Creating a new Organizational Unit within the Active Directory domain.

3.Naming the Organizational Unit

I created a new Organizational Unit named ProjectZero Workstations. This OU will serve as the central location for workstation computer accounts and users that will later receive targeted Group Policy configurations.

Creating the ProjectZero Workstations Organizational Unit.
Creating the ProjectZero Workstations Organizational Unit.

4.Moving workstation computer accounts

After creating the OU, I moved the domain-joined workstation computer accounts into ProjectZero Workstations. Placing these systems inside the OU ensures that any GPOs linked to it will automatically apply to the appropriate workstations.

Moving workstation computer accounts into the ProjectZero Workstations OU.
Moving workstation computer accounts into the ProjectZero Workstations OU.

5.Moving user accounts

I then moved the user accounts into the same Organizational Unit. Organizing both users and workstations within a dedicated OU allows security policies to be managed centrally and applied consistently throughout the environment.

Moving domain user accounts into the ProjectZero Workstations OU.
Moving domain user accounts into the ProjectZero Workstations OU.

6.Verifying the Organizational Unit

Completed Organizational Unit containing the target workstations and user accounts for future GPO deployment.
Completed Organizational Unit containing the target workstations and user accounts for future GPO deployment.

##[project-zero-win-client] Settings Change

1.Configuring the workstation to use the Domain Controller for DNS

On project-zero-win-client, I updated the network adapter settings to use the Domain Controller (192.168.15.25) as the preferred DNS server. Active Directory relies on DNS for locating domain services, so directing DNS queries to the Domain Controller is required before the workstation can successfully join the domain.

Configuring the workstation to use the Domain Controller as its primary DNS server.
Configuring the workstation to use the Domain Controller as its primary DNS server.

2.Creating the Transcripts directory

I created a Transcripts folder on the workstation's C: drive. This directory will later be used by Group Policy to store PowerShell transcription logs, providing a centralized location for recording PowerShell activity for auditing and security monitoring.

Creating the Transcripts directory for future PowerShell transcription logging.
Creating the Transcripts directory for future PowerShell transcription logging.

##Testing GPO

To verify that Group Policy was functioning correctly, I created a test Group Policy Object (GPO) and linked it to the ProjectZero Workstations Organizational Unit. The policy enforces an account lockout configuration, demonstrating how security settings can be centrally managed and automatically applied to all users and computers within the targeted OU. This validation confirms that the Active Directory structure is correctly configured for future security hardening policies. 1. Opening Group Policy Management

Opening the Group Policy Management console.
Opening the Group Policy Management console.

1.Creating a new Group Policy Object

Within the ProjectZero Workstations OU, I selected Create a GPO in this domain, and Link it here. Linking the policy directly to the OU ensures it will automatically apply to all objects contained within it.

Creating and linking a new Group Policy Object to the ProjectZero Workstations OU.
Creating and linking a new Group Policy Object to the ProjectZero Workstations OU.
Creating the PZ: Account Lockout Policy Group Policy Object.
Creating the PZ: Account Lockout Policy Group Policy Object.
Created the PZ: Account Lockout Policy Group Policy Object.
Created the PZ: Account Lockout Policy Group Policy Object.

2.Configuring the Account Lockout Policy

Using the Group Policy Management Editor, I navigated to the Account Lockout Policy settings and configured the desired security controls. These settings determine how Windows responds to repeated failed logon attempts, helping protect domain accounts against password guessing and brute-force attacks.

Configuring account lockout settings within the Group Policy Management Editor.
Configuring account lockout settings within the Group Policy Management Editor.
Configured account lockout settings applied within the test Group Policy.
Configured account lockout settings applied within the test Group Policy.

3.Verifying the GPO is enforced

Finally, I verified that the PZ: Account Lockout Policy was successfully linked to the ProjectZero Workstations OU with Enforced enabled. This confirms the policy will take precedence and be applied consistently to all users and computers within the Organizational Unit.

Verifying the Account Lockout Policy is linked and enforced on the ProjectZero Workstations OU.
Verifying the Account Lockout Policy is linked and enforced on the ProjectZero Workstations OU.

##GPO: User Access Control (UAC)

Within the Security Options policy settings, I configured the UAC policy to require administrator credentials for elevation requests and enabled the secure desktop prompt. Running administrators in Admin Approval Mode ensures privileged actions require explicit approval before execution, providing an additional layer of protection against unauthorized administrative changes.

plain text
User Account Control: Behavior of the elevation prompt for standard users &#10132; Prompt for credentials on the secure desktop

User Account Control: Switch to the secure desktop when prompting for elevation &#10132; Enabled

To improve visibility into PowerShell activity, I configured Group Policy to enable Module Logging, Script Block Logging, and PowerShell Transcription on all managed workstations. These logging features provide detailed records of PowerShell execution, allowing administrators to audit activity, investigate security incidents, and detect malicious or unauthorized scripts through centralized log collection.

1.Opening the PowerShell logging policies

Within the Group Policy Management Editor, I navigated to the Windows PowerShell administrative template, where the available PowerShell logging policies can be centrally configured for all domain-joined workstations.

Navigating to the Windows PowerShell Group Policy settings.
Navigating to the Windows PowerShell Group Policy settings.

2.Enabling PowerShell Module Logging

I enabled Module Logging and configured it to monitor all PowerShell modules by specifying the wildcard (*). This ensures that execution details are captured regardless of which module is used, providing comprehensive visibility into PowerShell activity.

Enabling PowerShell Module Logging for all PowerShell modules.
Enabling PowerShell Module Logging for all PowerShell modules.
Enabling PowerShell Script Block Logging through Group Policy.
Enabling PowerShell Script Block Logging through Group Policy.

3.Configuring PowerShell Transcription

Finally, I enabled PowerShell Transcription and configured the output directory as C:\Transcripts. Every interactive PowerShell session is automatically recorded and saved to this location, creating an audit trail that can be reviewed during incident response or security investigations.

Configuring PowerShell Transcription to store session logs in  C:\Transcripts .
Configuring PowerShell Transcription to store session logs in C:\Transcripts.

##GPO: Limit RDP Access

To reduce the attack surface of remote administration, I created a Group Policy that restricts Remote Desktop Protocol (RDP) access to authorized users and trusted systems only. Rather than allowing unrestricted RDP connections, the policy uses Windows Defender Firewall to limit who can establish remote sessions, helping protect workstations against unauthorized access and lateral movement.

1.Allow only specific IPs via Windows Firewall (on the client):

Create/Edit a GPO ➔ Name it: px:Limit RDP

GPO Location: Computer Configuration > Policies > Windows Settings > Security Settings > Windows Defender Firewall with Advanced Security

Navigate to:

plain text
Inbound Rules > New Rule > Type: Predefined > Remote Desktop

Click Next, leave all checked.

Choose "Allow the connection if it is secure".

Click Next. Check "Authorized users" Add.. johnd@corp.project-zero-dc.com

Click Next.

Check "Authorized users" Add..  PROJECT-ZERO-WIN-CLIENT.

Finish.

To reduce the risk of malware execution, I implemented a Software Restriction Policy (SRP) through Group Policy. The policy blocks executable files from running within the Windows %TEMP% directory, a common location used by malware and phishing payloads. Deploying this restriction through Active Directory ensures the policy is applied consistently across all managed workstations.

Create/Edit a GPO ➔ Name it: px:Limit %TEMP%

GPO Location: Computer Configuration > Policies > Windows Settings > Security Settings > Software Restriction Policies

Navigate to:

plain text
Right-click Software Restriction Policies > New Software Restriction Policies

CLick into "Security Levels" Folder.

    Double-click Disallowed, "Set as Default"

    Right-click Additional Rules "New Path Rule".

Path Rules to Add: %TEMP%\*.exe Disallowed

With the security policies deployed, I validated that the configured Group Policy Objects were successfully applied to the workstation. Verifying policy enforcement confirms that centralized security controls are functioning as expected before relying on them for endpoint protection.

Successful Group Policy enforcement confirms that the workstation is receiving domain security policies.
Successful Group Policy enforcement confirms that the workstation is receiving domain security policies.

2.Validating Group Policy application

After refreshing Group Policy on the workstation, Windows prompted the user to change their password in accordance with the configured domain policy. This confirms that the Group Policy Objects created throughout the project were successfully applied to the workstation, demonstrating that centralized policy management is functioning correctly.

The workstation prompts the domain user to change their password, confirming the Account Policy GPO was successfully applied and enforced through Active Directory.
The workstation prompts the domain user to change their password, confirming the Account Policy GPO was successfully applied and enforced through Active Directory.