Network security monitoring always looked intimidating to me — until I actually sat down, set up Snort 3 from scratch on Ubuntu, wrote my own detection rules, and watched it flag every single nmap scan in real time. This post walks through everything: installation, configuration, custom rules, and simulating real attacks to prove it works.

If you have ever wondered how intrusion detection systems actually work under the hood, this is a solid hands-on way to find out.


What We Are Building

A working Snort 3 IDS running on Ubuntu inside a virtual machine, monitored by a Kali Linux attacker VM. Snort will sit on the network interface, watch all traffic, and alert when it detects:

  • ICMP Ping Sweep
  • XMAS Scan
  • FIN Scan
  • NULL Scan
  • SYN Scan
  • TCP Connect Scan
  • UDP Scan

Both VMs are connected via Bridged Adapter in VirtualBox, so they share the same subnet.


Environment Setup

Machine OS Role IP (example)
Defender Ubuntu 22.04 Snort 3 IDS 192.168.1.104
Attacker Kali Linux 2026.1 nmap scan source 192.168.1.106

Both adapters set to Bridged Adapter in VirtualBox settings so they can reach each other.


Step 1 — Update Ubuntu and Install Dependencies

Open a terminal on the Ubuntu VM and run:

sudo apt update
Enter fullscreen mode Exit fullscreen mode

Then install all required build tools and libraries in one command:

sudo apt install -y build-essential \
  libpcap-dev \
  libpcre2-dev \
  libnet1-dev \
  zlib1g-dev \
  luajit \
  hwloc \
  libdumbnet-dev \
  bison \
  flex \
  liblzma-dev \
  openssl \
  libssl-dev \
  pkg-config \
  libhwloc-dev \
  cmake \
  cpputest \
  libsqlite3-dev \
  uuid-dev \
  libcmocka-dev \
  libnetfilter-queue-dev \
  libmnl-dev \
  autotools-dev \
  libluajit-5.1-dev \
  libunwind-dev \
  git \
  wget \
  ethtool
Enter fullscreen mode Exit fullscreen mode


Step 2 — Create a Working Directory

mkdir snort-source-files
cd snort-source-files
Enter fullscreen mode Exit fullscreen mode

Step 3 — Install LibDAQ (Snort's Data Acquisition Library)

LibDAQ is what allows Snort to capture packets from the network interface.

git clone https://github.com/snort3/libdaq.git
cd libdaq
sudo ./bootstrap
sudo ./configure
sudo make
sudo make install
cd ..
Enter fullscreen mode Exit fullscreen mode



Step 4 — Install Tcmalloc (Memory Optimization)

Tcmalloc is a memory allocator from Google that reduces fragmentation and speeds up Snort under load.

wget https://github.com/gperftools/gperftools/releases/download/gperftools-2.10/gperftools-2.10.tar.gz
tar xzf gperftools-2.10.tar.gz
cd gperftools-2.10
sudo ./configure
sudo make
sudo make install
cd ..
Enter fullscreen mode Exit fullscreen mode


Step 5 — Install Snort 3

Now the main event — clone and build Snort 3 from source:

git clone https://github.com/snort3/snort3.git
cd snort3
sudo ./configure_cmake.sh --prefix=/usr/local --enable-tcmalloc
cd build
sudo make
sudo make install
Enter fullscreen mode Exit fullscreen mode

The sudo make step takes several minutes — this is normal. You will see progress climb from 0% to 100%.

After install, create a symlink and verify the version:

sudo ldconfig
sudo ln -s /usr/local/bin/snort /usr/sbin/snort
snort -V
Enter fullscreen mode Exit fullscreen mode

Expected output:

-*> Snort++ <*-
   Version 3.12.2.0
   By Martin Roesch & The Snort Team
Enter fullscreen mode Exit fullscreen mode


Step 6 — Configure the Network Interface

Find your interface name (mine was enp0s3):

ip a
Enter fullscreen mode Exit fullscreen mode

Set it to promiscuous mode so Snort can capture all traffic:

sudo ip link set dev enp0s3 promisc on
sudo ethtool -K enp0s3 gro off lro off
Enter fullscreen mode Exit fullscreen mode

Create a systemd service to persist this across reboots:

sudo nano /etc/systemd/system/snort3-nic.service
Enter fullscreen mode Exit fullscreen mode

Paste this content:

[Unit]
Description=Set Snort3 NIC in promiscuous mode

[Service]
Type=oneshot
ExecStart=/sbin/ip link set dev enp0s3 promisc on
ExecStart=/sbin/ethtool -K enp0s3 gro off lro off

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

Enable and start it:

sudo systemctl daemon-reload
sudo systemctl enable --now snort3-nic.service
sudo systemctl status snort3-nic.service
Enter fullscreen mode Exit fullscreen mode


Step 7 — Create the Rules Directory and Configure snort.lua

sudo mkdir -p /usr/local/etc/rules/local-rules
cd /usr/local/etc/snort
sudo nano snort.lua
Enter fullscreen mode Exit fullscreen mode

Make the following changes in snort.lua:

-- Set your network
HOME_NET = '192.168.0.0/24'
EXTERNAL_NET = '!$HOME_NET'

include 'snort_defaults.lua'
Enter fullscreen mode Exit fullscreen mode

Scroll to the detection section and add the IPS block:

ips =
{
    include = '/usr/local/etc/rules/local-rules/local.rules',

    variables =
    {
        nets =
        {
            HOME_NET = HOME_NET,
            EXTERNAL_NET = EXTERNAL_NET
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Save and exit with Ctrl+X → Y → Enter.


Step 8 — Write the Detection Rules

sudo nano /usr/local/etc/rules/local-rules/local.rules
Enter fullscreen mode Exit fullscreen mode

Add these seven rules:

alert icmp any any -> $HOME_NET any (msg:"NMAP Ping Sweep Scan"; dsize:0; sid:1000001; rev:1;)

alert tcp any any -> $HOME_NET 22 (msg:"NMAP XMAS Scan"; flags:FPU; sid:1000002; rev:1;)

alert tcp any any -> $HOME_NET 22 (msg:"NMAP FIN Scan"; flags:F; sid:1000003; rev:1;)

alert tcp any any -> $HOME_NET 22 (msg:"NMAP NULL Scan"; flags:0; sid:1000004; rev:1;)

alert tcp any any -> $HOME_NET 22 (msg:"NMAP SYN Scan"; flags:S; sid:1000005; rev:1;)

alert tcp any any -> $HOME_NET 22 (msg:"NMAP TCP Connect Scan"; sid:1000006; rev:1;)

alert udp any any -> $HOME_NET any (msg:"NMAP UDP Scan"; sid:1000007; rev:1;)
Enter fullscreen mode Exit fullscreen mode


Step 9 — Validate the Configuration

Before running Snort live, test that the config file loads without errors:

snort -c /usr/local/etc/snort/snort.lua -T
Enter fullscreen mode Exit fullscreen mode

You should see at the end:

Snort successfully validated the configuration (with 0 warnings).
o")~   Snort exiting
Enter fullscreen mode Exit fullscreen mode


Step 10 — Start Snort in Alert Mode

sudo snort -c /usr/local/etc/snort/snort.lua -i enp0s3 -A alert_fast
Enter fullscreen mode Exit fullscreen mode

Snort is now running and watching enp0s3 for any traffic that matches the rules.


Step 11 — Simulate Attacks from Kali Linux

On the Kali VM, run each scan against the Ubuntu IP:

1. Ping Sweep

nmap -sn 192.168.1.104
Enter fullscreen mode Exit fullscreen mode

2. XMAS Scan

sudo nmap -sX 192.168.1.104
Enter fullscreen mode Exit fullscreen mode

3. FIN Scan

sudo nmap -sF 192.168.1.104
Enter fullscreen mode Exit fullscreen mode

4. NULL Scan

sudo nmap -sN 192.168.1.104
Enter fullscreen mode Exit fullscreen mode

5. SYN Scan

sudo nmap -sS 192.168.1.104
Enter fullscreen mode Exit fullscreen mode

6. TCP Connect Scan

nmap -sT 192.168.1.104
Enter fullscreen mode Exit fullscreen mode

7. UDP Scan

sudo nmap -sU 192.168.1.104
Enter fullscreen mode Exit fullscreen mode


How to Verify

Watch the Snort terminal on Ubuntu while each nmap command runs on Kali. You will see live alert lines like this:

05/14-18:13:07.004392 [**] [1:1000001:1] "NMAP Ping Sweep" [**] [Priority: 0] {ICMP} 192.168.1.106 -> 192.168.1.104
05/14-18:15:21.301976 [**] [1:1000002:1] "NMAP XMAS Scan" [**] [Priority: 0] {TCP} 192.168.1.106:34107 -> 192.168.1.104:22
05/14-18:16:03.900911 [**] [1:1000003:1] "NMAP FIN Scan" [**] [Priority: 0] {TCP} 192.168.1.106:41928 -> 192.168.1.104:22
05/14-18:16:53.770290 [**] [1:1000004:1] "NMAP NULL Scan" [**] [Priority: 0] {TCP} 192.168.1.106:52411 -> 192.168.1.104:22
05/14-18:17:26.976133 [**] [1:1000005:1] "NMAP SYN Scan" [**] [Priority: 0] {TCP} 192.168.1.106:40209 -> 192.168.1.104:22
05/14-18:15:21.301976 [**] [1:1000006:1] "NMAP TCP Connect Scan" [**] [Priority: 0] {TCP} 192.168.1.106:34107 -> 192.168.1.104:22
05/14-18:12:48.402112 [**] [1:1000007:1] "NMAP UDP Scan" [**] [Priority: 0] {UDP} 192.168.1.1:1900 -> 239.255.255.250:1900
Enter fullscreen mode Exit fullscreen mode

Each attack type triggers its corresponding rule — that means the IDS is working exactly as expected.


What I Learned

Working through this end to end taught me several things that documentation alone does not convey:

Snort rules are just pattern matchers. At their core, each rule is a protocol + direction + match condition. Once you understand that structure, writing new rules feels natural.

Promiscuous mode matters. Without promisc on, Snort only sees traffic addressed directly to the host. Flipping that one flag is what makes it a proper network monitor.

Building from source builds understanding. Watching each dependency compile, seeing LibDAQ slot into place, and then seeing Snort start up — it makes the whole stack tangible rather than abstract.

nmap flag combinations map to Snort flags directly. The -sX XMAS scan sets FIN, PSH, and URG flags — which maps to flags:FPU in the rule. That connection between the attacker's tooling and the defender's signature clicked during this lab.

Alert mode is just the beginning. Snort can also run in inline IPS mode and actively drop packets. This setup is the foundation for that.


Common Mistakes

Mistake What Goes Wrong Fix
Using Snort 2 instead of Snort 3 Config file format is completely different Always clone from github.com/snort3/snort3
Wrong HOME_NET subnet Rules never fire because traffic is not matched Check your actual subnet with ip a and set it exactly
Not setting promiscuous mode Snort misses traffic not destined for the host sudo ip link set dev enp0s3 promisc on
Interface name mismatch in command Snort starts but captures nothing Confirm with ip a — may be ens33, eth0, or enp0s3
Rules path typo in snort.lua Snort validates but fires no alerts Double-check the exact path including subdirectories
Skipping sudo ldconfig after install Snort binary cannot find shared libraries Always run sudo ldconfig before testing
flags:0 for NULL scan May vary between Snort versions Test with snort -c snort.lua -T and verify rule loads

Conclusion

Snort 3 is not the simplest tool to set up from scratch, but going through the full build process is worth it. You end up with a real understanding of how each piece fits together — LibDAQ capturing packets, the rules engine pattern matching, and the alert output confirming what the attacker is doing.

The combination of Snort writing alerts in one window while nmap runs in another makes the whole thing feel live and real. Each scan lights up the terminal on the defender side, which is a genuinely satisfying moment.

If you set this up yourself and run into issues, drop a comment below — happy to help debug.