Difference between revisions of "IDPS System with Snort / Suricata"

From Embedded Lab Vienna for IoT & Security
Jump to navigation Jump to search
(Created page with "== Summary == This documentation descries the setup, configuration and testing of an Intrusion Detecetion Prevention System (IDPS) used in a unviersity project. The system was built in VMware and uses open-source tools such as Snort, Suricata, iptables and NFQUEUE. The main goal was to detect and block malicious traffic in a controlled lab environment. The project follows the same structure for each attack: * No IDS / IPS: attack runs without protection * IDS mode: a...")
 
 
(24 intermediate revisions by the same user not shown)
Line 1: Line 1:
== Summary ==
== Summary ==


This documentation descries the setup, configuration and testing of an Intrusion Detecetion Prevention System (IDPS) used in a unviersity project.
This documentation describes the setup, configuration and testing of an Intrusion Detecetion Prevention System (IDPS) used in a unviersity project.


The system was built in VMware and uses open-source tools such as Snort, Suricata, iptables and NFQUEUE. The main goal was to detect and block malicious traffic in a controlled lab environment.
The system was built in VMware and uses open-source tools such as Snort, Suricata, iptables and NFQUEUE. The main goal was to detect and block malicious traffic in a controlled lab environment.
Line 47: Line 47:
== Network Setup ==
== Network Setup ==


The lab lab uses VMWare as its virtualization platform, for easy of use and more customization options. It also allows snapshots and each member to work independently on the VMs. The setup uses two isolated internal networks: one for the victim and one for the attacker.
The lab lab uses VMWare as its virtualization platform, for easy of use and more customization options. It also allows snapshots and each member to work independently on the VMs. The setup uses two isolated internal networks: one for the victim and one for the attacker. The virtual networks were created in the VMware Virtual Network Editor, whereby the internal networks were mapped to host-only networks and the external one for internet access was a NAT network, sharing the IP of the host machine.


== Topology ==


=== Topology ===
Note: In the real topology, vmbr0 corresponds to ens33, vmbr1 to ens37 and vmbr2 to ens38.
[[File:Topology.jpeg|800px|border]]


=== Network Plan ===
=== Network Plan ===
Line 92: Line 96:
|}
|}


The IDPS machine acts as the gateway between both networks. This forces attacker-to-victim traffic through the IDPS, which allows packet inspection, attack detection and blocking. :contentReference[oaicite:2]{index=2}
The IDPS machine acts as the gateway between both networks. This forces attacker to victim traffic through the IDPS, which allows packet inspection, attack detection and blocking.
 
'''Screenshot placement:''' Add a topology screenshot here showing Attacker → IDPS → Victim.


== IP Forwarding ==
== IP Forwarding ==


IP forwarding must be enabled on the IDPS machine:
IP forwarding must be enabled on the IDPS machine, which essentially enables an internet connection, since it forwards packets to external networks:


<pre>
<pre>
Line 104: Line 106:
</pre>
</pre>


To make it permanent:
To make it permanent, open the <strong>/etc/sysctl.conf</strong> file and add or uncomment the line below:
 
<pre>
<pre>
sudo nano /etc/sysctl.conf
net.ipv4.ip_forward=1
</pre>
</pre>


Add or uncomment:
To check if it is enabled, do:
 
<pre>
<pre>
net.ipv4.ip_forward=1
cat /proc/sys/net/ipv4/ip_forward
</pre>
</pre>


Line 123: Line 123:


<pre>
<pre>
Traffic passes normally
→ Traffic passes normally
IDS observes traffic
→ IDS observes traffic
Alert is generated
→ Alert is generated
Attack still reaches victim
→ Attack still reaches victim
</pre>
</pre>


Line 134: Line 134:


<pre>
<pre>
Packet arrives
→ Packet arrives
iptables sends it to NFQUEUE
→ iptables sends it to NFQUEUE
Snort / Suricata inspects it
→ Snort / Suricata inspects it
Packet is accepted or dropped
→ Packet is accepted or dropped
</pre>
</pre>


This is why drop rules only work when the tool is actually inline through NFQUEUE.
This is why drop rules only work when the tool is actually inline through NFQUEUE.
== Snort Setup ==
Snort was used mainly for the network-based attacks, such as SSH brute force, port scanning and ICMP ping flood.
Snort works with rules. A rule describes what traffic should be detected or blocked. In IDS mode, Snort uses `alert` rules. In IPS mode, the rule action can be changed to `drop`.
The main Snort configuration file is:
<pre>
/etc/snort/snort.conf
</pre>
Important network variables were configured inside this file:
<pre>
ipvar HOME_NET 192.168.10.0/24
ipvar EXTERNAL_NET 192.168.20.0/24
</pre>
In this project:
* `HOME_NET` = victim network
* `EXTERNAL_NET` = attacker network
Local custom rules were added in:
<pre>
/etc/snort/rules/local.rules
</pre>
The local rules file must be included in `snort.conf`:
<pre>
include $RULE_PATH/local.rules
</pre>
Before running Snort, the configuration was tested with:
<pre>
sudo snort -T -c /etc/snort/snort.conf
</pre>
Snort alerts were saved under:
<pre>
/var/log/snort/alert
</pre>
== Suricata Setup ==
Suricata was used for the application-layer attacks, mainly SQL injection and HTTP flood.
Suricata can inspect normal packet data, but it can also understand protocols such as HTTP. This makes it useful for detecting attacks inside URLs, HTTP methods and web requests.
The main Suricata configuration file is:
<pre>
/etc/suricata/suricata.yaml
</pre>
Custom rules were added in:
<pre>
/etc/suricata/rules/local.rules
</pre>
The local rules file must be enabled in `suricata.yaml` under the rule files section:
<pre>
rule-files:
  - local.rules
</pre>
Before starting Suricata, the configuration was tested with:
<pre>
sudo suricata -T -c /etc/suricata/suricata.yaml
</pre>
Suricata logs were checked mainly in:
<pre>
/var/log/suricata/suricata.log
/var/log/suricata/fast.log
</pre>
For IDS mode, Suricata was run on the attacker-side interface:
<pre>
sudo suricata -c /etc/suricata/suricata.yaml -i ens38 -l /var/log/suricata
</pre>
For IPS mode, Suricata was run inline with NFQUEUE:
<pre>
sudo suricata -c /etc/suricata/suricata.yaml -q 0 -l /var/log/suricata
</pre>
== What is NFQUEUE and Why Is It Needed? ==
NFQUEUE is used when Snort or Suricata should work as an IPS.
Normally, traffic is forwarded by the Linux kernel without waiting for Snort or Suricata. In that case, the tools can detect attacks, but they cannot stop the packets.
NFQUEUE changes this behavior.
<pre>
Packet arrives at IDPS
→ iptables sends packet to NFQUEUE
→ Snort / Suricata inspects it
→ packet is accepted or dropped
</pre>
This means the packet waits for a decision before it reaches the victim.
Without NFQUEUE:
* `alert` rules can generate logs
* `drop` rules do not really block forwarded traffic
* the attack still reaches the victim
With NFQUEUE:
* traffic is inspected inline
* `drop` rules can actually block packets
* the IDPS works as a prevention system


== Attack 1: SSH Brute Force ==
== Attack 1: SSH Brute Force ==
Line 146: Line 273:
=== Description ===
=== Description ===


The SSH brute force attack was used to test login attack detection.
The SSH brute force attack was used to test login attack detection and prevention.


A weak test user was created on the victim. The attacker used Hydra to try passwords against SSH. The project progress presentation describes this as a weak user setup on the victim with Hydra used from the attacker. :contentReference[oaicite:3]{index=3}
A weak test user was created on the victim machine. The attacker then used Hydra to try many passwords against SSH.
 
This represents a common real-world attack where an attacker tries to guess login credentials.


=== Attack Command ===
=== Attack Command ===
Line 158: Line 287:
=== No IDPS ===
=== No IDPS ===


Without IDPS:
Without IDPS protection:


* Attack runs normally
* The attack runs normally
* No real-time IDPS alert
* Hydra can keep trying passwords
* Logs only exist on the victim
* No real-time alert is generated by the IDPS
* Manual log checking is needed
* The only evidence is in the victim logs


Victim log:
Victim log:
Line 170: Line 299:
/var/log/auth.log
/var/log/auth.log
</pre>
</pre>
[[File:Ssh auth log.png|1100px]]


=== Snort IDS ===
=== Snort IDS ===


Snort was used to detect SSH connection attempts.
In IDS mode, Snort was used to detect SSH connection attempts.


Example rule:
Example rule:
Line 179: Line 310:
<pre>
<pre>
alert tcp any any -> 192.168.10.10 22
alert tcp any any -> 192.168.10.10 22
(msg:"SSH Attempt"; flags:S; sid:1000004;)
(msg:"SSH Brute Force Attempt"; flags:S; sid:1000004;)
</pre>
</pre>
Rule explanation:
* `alert` means Snort generates an alert, but does not block
* `tcp` means the rule checks TCP traffic
* `any any` means any source IP and any source port
* `192.168.10.10 22` means the victim SSH service
* `flags:S` matches TCP SYN packets, which are used when starting a TCP connection
* `msg` is the alert text shown in the logs
* `sid` is the unique rule ID


Result:
Result:


* Snort detects SSH attempts
* SSH attempts are detected
* Alerts are generated
* Alerts are logged
* Attack still continues
* The brute force attack can still continue
 
[[File:Ssh ids.png|1100px]]


=== Snort IPS ===
=== Snort IPS ===


In IPS mode, Snort blocks repeated SSH attempts.
In IPS mode, the rule was changed to `drop` and traffic was sent through NFQUEUE.


NFQUEUE example:
NFQUEUE rules:


<pre>
<pre>
iptables -I FORWARD -p tcp --dport 22 -j NFQUEUE
iptables -I FORWARD -p tcp --dport 22 -j NFQUEUE --queue-num 0
iptables -I FORWARD -p tcp --sport 22 -j NFQUEUE
iptables -I FORWARD -p tcp --sport 22 -j NFQUEUE --queue-num 0
</pre>
</pre>


Line 203: Line 346:
<pre>
<pre>
drop tcp any any -> 192.168.10.10 22
drop tcp any any -> 192.168.10.10 22
(msg:"SSH Brute Force BLOCKED";
(msg:"IPS BLOCKED: SSH Scan Attempt";
threshold:type both, track by_src, count 5, seconds 60;
threshold:type both, track by_src, count 5, seconds 60;
sid:1000002;)
sid:1000002;)
</pre>
</pre>
Rule explanation:
* `drop` means Snort blocks matching packets in inline mode
* destination port `22` targets SSH
* `threshold` avoids blocking every single SSH packet immediately
* `track by_src` counts attempts from each source IP
* `count 5, seconds 60` triggers after 5 attempts in 60 seconds


Result:
Result:


* Brute force is detected
* Repeated SSH attempts are detected
* Repeated attempts are blocked
* Snort drops matching packets
* Alerts are logged
* The brute force attack is interrupted


'''Screenshot placement:''' Add one screenshot of Hydra running and one Snort alert screenshot.
[[File:Ssh ips.png|1100px]]


== Attack 2: TCP SYN Port Scanning ==
== Attack 2: TCP SYN Port Scanning ==
Line 220: Line 371:
=== Description ===
=== Description ===


Port scanning was used to simulate reconnaissance. An attacker normally scans ports before choosing the next attack method. The port scanning presentation described this as a TCP SYN port scan using Nmap, detected and prevented with Snort. :contentReference[oaicite:4]{index=4}
Port scanning was used to simulate reconnaissance.
 
Before launching a specific attack, an attacker often scans the target to discover open ports and services.
 
In this project, the victim had several services open to make the scan result more realistic.


=== Victim Services ===
=== Victim Services ===
The victim had multiple open services to make the scan realistic:


{| class="wikitable"
{| class="wikitable"
Line 245: Line 398:
| Demo TCP service
| Demo TCP service
|}
|}
These services made the Nmap result more meaningful and helped show how IPS can hide or block selected scan traffic. :contentReference[oaicite:5]{index=5}


=== Attack Command ===
=== Attack Command ===
Line 253: Line 404:
sudo nmap -sS 192.168.10.10
sudo nmap -sS 192.168.10.10
</pre>
</pre>
The `-sS` option performs a TCP SYN scan. This type of scan sends SYN packets to check which ports respond.


=== No IDPS ===
=== No IDPS ===


Result:
Without protection:


* Nmap discovers open ports
* Nmap discovers open ports
* Services are visible
* Services are visible to the attacker
* No blocking is applied
* No alerts are generated
* No packets are blocked
 
This gives the attacker useful information for future attacks.
 
[[File:Nmap default scan.png|800px]]


=== Snort IDS ===
=== Snort IDS ===
Snort was first used in IDS mode.


<pre>
<pre>
Line 270: Line 430:
sid:1000005; rev:1;)
sid:1000005; rev:1;)
</pre>
</pre>
Rule explanation:
* `alert` means detect only
* `tcp` checks TCP traffic
* source network is the attacker network
* destination is the victim
* `flags:S` matches SYN packets
* `threshold` triggers only after multiple packets
* `track by_src` counts packets per attacker IP
* `count 10, seconds 5` means 10 SYN packets within 5 seconds
* `msg` is the alert message


Result:
Result:


* Multiple SYN packets are detected
* Port scan is detected
* Alert is logged under /var/log/snort/alert
* Alert is saved under `/var/log/snort/alert`
* Scan still reaches victim
* Scan still reaches the victim
 
[[File:Nmap ids.png|1000px]]


=== Snort IPS ===
=== Snort IPS ===
For IPS mode, the rule action was changed to `drop`.


<pre>
<pre>
Line 286: Line 462:
</pre>
</pre>


NFQUEUE:
Rule explanation:
 
* `drop` blocks matching packets in inline mode
* the rule still watches for SYN packets
* the threshold avoids blocking normal single connections
* when the scan rate becomes suspicious, Snort starts dropping packets
 
NFQUEUE setup:


<pre>
<pre>
Line 300: Line 483:
Result:
Result:


* Scan is detected
* Scan traffic is detected
* Matching packets are dropped
* Matching packets are dropped
* Nmap receives less useful information
* Nmap receives fewer useful results


'''Screenshot placement:''' Add one Nmap result without IPS and one Snort IPS console/log screenshot.
[[File:Nmap ips.png|1000px]]


== Email Notification System ==
== Email Notification System ==
Line 310: Line 493:
Email notifications were added so the administrator does not need to watch the Snort terminal all the time.
Email notifications were added so the administrator does not need to watch the Snort terminal all the time.


The email presentation describes the flow as Snort live output being read by a wrapper script, then sent to the administrator by email. :contentReference[oaicite:6]{index=6}
=== Setup ===
 
Postfix was installed on the IDPS machine and configured to send mail through Gmail SMTP.


=== Setup ===
Setup summary:


* Installed Postfix and mail tools
* Installed Postfix and mail tools
* Configured Gmail SMTP
* Configured Gmail SMTP relay
* Created a Gmail App Password
* Created a Gmail App Password
* Used the app password for authentication
* Used the App Password for authentication


=== Flow ===
A normal Gmail password was not used because Gmail blocks simple password authentication from scripts and Linux mail tools.
 
=== Notification Flow ===


<pre>
<pre>
Snort live output
Snort live output
→ wrapper script reads alert messages
→ wrapper script reads alert/drop messages
→ email is sent to administrator
→ mail command sends summary
→ Postfix forwards mail through Gmail SMTP
→ administrator receives email
</pre>
 
The wrapper script runs Snort in IPS mode and reads the live console output line by line. It shows the output in the terminal, saves it into a temporary buffer, and sends the collected output by email every 30 seconds. A lock file is used to prevent the script from sending too many emails at the same time. This way, the administrator receives a short email summary of Snort alerts instead of manually watching the terminal.
 
<pre>
EMAIL_TO="censored@gmail.com"
HOSTNAME=$(hostname)
BUFFER="/tmp/snort-live-output-buffer.txt"
LOCK="/tmp/snort-live-output-lock"
SEND_INTERVAL=30
 
SNORT_CMD="snort -Q --daq nfq --daq-var queue=0 -c /etc/snort/snort.conf -A console"
 
> "$BUFFER"
rm -f "$LOCK"
 
sudo stdbuf -oL -eL $SNORT_CMD 2>&1 | while IFS= read -r line; do
    # Show Snort output live in terminal
    echo "$line"
 
    # Save every Snort output line to buffer
    echo "$line" >> "$BUFFER"
 
    # Start delayed email sender if not already running
    if [ ! -f "$LOCK" ]; then
        touch "$LOCK"
 
        (
            sleep "$SEND_INTERVAL"
 
            if [ -s "$BUFFER" ]; then
                {
                    echo "Snort live output from $HOSTNAME"
                    echo
                    echo "Time: $(date)"
                    echo
                    echo "Last $SEND_INTERVAL seconds of Snort output:"
                    echo
                    cat "$BUFFER"
                } | mail -s "Snort Live Output from $HOSTNAME" "$EMAIL_TO"
 
                > "$BUFFER"
            fi
 
            rm -f "$LOCK"
        ) &
    fi
done
</pre>
</pre>


Result:
Result:


* Alerts can be sent by email
* Admin receives alerts by email
* Admin gets faster visibility
* Monitoring is easier
* Useful for monitoring during attacks
* The IDPS behaves more like a real monitoring system


'''Screenshot placement:''' Add one screenshot of a received alert email.
[[File:Email notifications.png|500px]]


== Attack 3: Spoofed ICMP Ping Flood ==
== Attack 3: Spoofed ICMP Ping Flood ==
Line 339: Line 576:
=== Description ===
=== Description ===


The ping flood attack sends a large number of ICMP Echo Requests to the victim. In this case, the attacker also randomizes the source IP addresses.
The spoofed ping flood attack sends a high number of ICMP Echo Request packets to the victim.


This makes source-based detection less reliable because the traffic does not look like it comes from only one source. The final ping flood presentation used hping3 with spoofed source IPs and compared No IDPS, Snort IDS and Snort IPS. :contentReference[oaicite:7]{index=7}
The attacker also randomizes the source IP address. This makes detection harder because the attack does not appear to come from one fixed IP.


=== Attack Command ===
=== Attack Command ===
Line 348: Line 585:
hping3 -1 --flood -V --rand-source 192.168.10.10
hping3 -1 --flood -V --rand-source 192.168.10.10
</pre>
</pre>
Command explanation:
* `-1` uses ICMP mode
* `--flood` sends packets as fast as possible
* `-V` enables verbose output
* `--rand-source` randomizes the source IP
* `192.168.10.10` is the victim


=== Monitoring ===
=== Monitoring ===
Line 362: Line 607:
ping 192.168.10.10
ping 192.168.10.10
</pre>
</pre>
The `tcpdump` output shows the incoming ICMP packets. The gateway ping shows whether the victim becomes less responsive.


=== No IDPS ===
=== No IDPS ===
Line 370: Line 617:
* Source IPs are random/spoofed
* Source IPs are random/spoofed
* Gateway ping becomes less stable
* Gateway ping becomes less stable
* No alerts
* No alerts are generated
* No blocking
* No packets are blocked
[[File:Tcpdump pingflood.png|700px]]


'''Screenshot placement:''' Add tcpdump screenshot showing random source IPs.
[[File:Unreliable ping.png|700px]]


=== Snort IDS ===
=== Snort IDS ===


Because the attacker spoofs source IPs, the rule tracks by destination.
Because the source IP is spoofed, the rule tracks by destination instead of source.


<pre>
<pre>
Line 385: Line 633:
sid:1000501; rev:1;)
sid:1000501; rev:1;)
</pre>
</pre>
Rule explanation:
* `alert` means detection only
* `icmp` checks ICMP traffic
* `any any` is used because the source IP is spoofed
* destination is the victim
* `itype:8` matches ICMP Echo Requests
* `track by_dst` counts traffic going to the victim
* `count 100, seconds 1` triggers after 100 requests in 1 second


Result:
Result:
Line 390: Line 648:
* Flood is detected
* Flood is detected
* Alert is logged
* Alert is logged
* Flood still reaches victim
* Flood still reaches the victim
[[File:Ping flood ids.png|1200px]]


=== Snort IPS ===
=== Snort IPS ===
The same idea was used in IPS mode, but with `drop`.


<pre>
<pre>
Line 401: Line 662:
</pre>
</pre>


NFQUEUE:
Rule explanation:
 
* `drop` blocks the matching ICMP packets
* source is still `any` because it is spoofed
* detection is based on high traffic rate to the victim
* once the threshold is reached, packets are dropped
 
NFQUEUE setup:


<pre>
<pre>
Line 417: Line 685:


* Flood is detected
* Flood is detected
* Matching ICMP packets are dropped
* ICMP packets are dropped
* Less traffic reaches the victim
* Less traffic reaches the victim
* Gateway ping becomes more stable
* Gateway ping becomes more stable
 
[[File:Ping flood ips.png|1200px]]
'''Screenshot placement:''' Add Snort IPS log/drop screenshot and one improved gateway ping screenshot.


== Attack 4: SQL Injection ==
== Attack 4: SQL Injection ==
Line 427: Line 694:
=== Description ===
=== Description ===


For this attack, a simple vulnerable PHP product lookup website was created on the victim. It was hosted on Apache and used SQLite as the database.
SQL injection was used to test an application-layer attack.


The SQL injection presentation describes the goal as simulating an application-layer attack against a vulnerable page, with detection and blocking done using Suricata. :contentReference[oaicite:8]{index=8}
For this scenario, a simple vulnerable PHP product lookup website was created on the victim. It was hosted on Apache and used SQLite as the database.
 
The attacker entered the malicious input directly in Firefox.


=== Website Setup ===
=== Website Setup ===


The victim hosted:
The vulnerable page was:


<pre>
<pre>
/sqli-demo.php?id=1
/sqli-demo.php?id=1
<?php
$db = new SQLite3('/var/www/html/products.db');
$id = $_GET['id'] ?? '1';
$sql = "SELECT id, name, price FROM products WHERE id = $id";
$result = @$db->query($sql);
?>
<!DOCTYPE html>
<html>
<head>
    <title>Product Lookup</title>
</head>
<body>
    <h1>Product Lookup Demo</h1>
    <hr>
    <?php
    if ($result) {
        while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
            echo "<p>";
            echo "<strong>ID:</strong> " . htmlspecialchars($row['id']) . "<br>";
            echo "<strong>Name:</strong> " . htmlspecialchars($row['name']) . "<br>";
            echo "<strong>Price:</strong> " . htmlspecialchars($row['price']);
            echo "</p><hr>";
        }
    } else {
        echo "<p>Query failed.</p>";
    }
    ?>
</body>
</html>
</pre>
</pre>


The database contained a simple products table:
The database contained a simple product table:


{| class="wikitable"
{| class="wikitable"
Line 465: Line 769:
=== Vulnerable PHP Query ===
=== Vulnerable PHP Query ===


The PHP file reads the id value from the URL:
The PHP file reads the ID value from the URL:


<pre>
<pre>
Line 471: Line 775:
</pre>
</pre>


Then it inserts the value directly into the SQL query:
Then it directly inserts the value into the SQL query:


<pre>
<pre>
Line 477: Line 781:
</pre>
</pre>


This is vulnerable because user input is used directly as part of the SQL command.
This is vulnerable because the application trusts user input. The input becomes part of the SQL command.


=== Original Query ===
=== Original Query ===
Line 495: Line 799:
Result:
Result:


* Only product ID 1 is returned
* only product ID 1 is returned


=== Malicious Query ===
=== Malicious Query ===
Line 511: Line 815:
</pre>
</pre>


Since 1=1 is always true, every row in the products table is returned.
Since `1=1` is always true, all rows in the product table are returned.
 
=== Why This Query Is Vulnerable ===


=== Why This Is Vulnerable ===
The input is not filtered or parameterized.


The application does not filter or parameterize the input.
Filtered input means checking the value before using it. For example, a product ID should only contain numbers.


Filtered input means checking the input before using it. For example, an ID should only be a number.
Parameterized input means the SQL structure is fixed, and user input is passed separately as data. This prevents the input from becoming SQL code.


Parameterized input means the SQL query structure is fixed, and user input is passed separately as data. This prevents user input from becoming SQL code.
In this demo, the input was directly added into the SQL string, so the attacker could change the query logic.


=== No IDPS ===
=== No IDPS ===


The attacker entered the SQL injection directly in Firefox:
The attacker used Firefox:


<pre>
<pre>
Line 531: Line 837:
Result:
Result:


* Request reached the PHP page
* Request reaches the PHP page
* Query was manipulated
* SQL query is manipulated
* All products were returned
* All products are returned
* No alert
* No alert is generated
* No blocking
* No traffic is blocked


'''Screenshot placement:''' Add screenshot of the browser showing all products returned.
[[File:Website injected.png|800px|border]]


=== Suricata IDS ===
=== Suricata IDS ===
Suricata was used to inspect the HTTP URI.


<pre>
<pre>
Line 549: Line 857:
sid:2001001; rev:1;)
sid:2001001; rev:1;)
</pre>
</pre>
Rule explanation:
* `alert` means detect and log only
* `http` tells Suricata to inspect HTTP traffic
* source is the attacker network
* destination is the victim web server
* `flow:established,to_server` checks established requests going to the server
* `http.uri` tells Suricata to inspect the URL
* the `pcre` searches for the vulnerable page and the `OR 1=1` pattern
* `sid` is the rule ID


Result:
Result:


* Injection is detected
* SQL injection is detected
* Alert is logged
* Alert is logged
* Injection still succeeds
* Injection still succeeds


Logs were checked in:
Logs:


<pre>
<pre>
/var/log/suricata/suricata.log
/var/log/suricata/suricata.log
</pre>
</pre>
[[File:Sql ids.png|1300px]]


=== Suricata IPS ===
=== Suricata IPS ===
The IDS rule was changed from `alert` to `drop`.


<pre>
<pre>
Line 572: Line 895:
sid:2001001; rev:2;)
sid:2001001; rev:2;)
</pre>
</pre>
Rule explanation:
* `drop` blocks the malicious HTTP request
* the same URI pattern is used
* when the SQL injection is detected, the packet is dropped before the app processes it


=== NFQUEUE for HTTP ===
=== NFQUEUE for HTTP ===
Line 588: Line 917:
</pre>
</pre>


Result:
=== Why Both Directions Were Needed ===
 
Suricata needs enough TCP and HTTP flow information to inspect the request correctly.


* SQL injection pattern is detected
The SQL injection rule uses:
* Malicious request is dropped
* Web app does not return all products


=== Why Both Directions Were Needed ===
<pre>
flow:established,to_server;
http.uri;
</pre>


Suricata needs to see enough of the full HTTP flow to inspect the request correctly.
This means Suricata must understand that the request belongs to an established HTTP flow and that it is going to the server.


At first, only attacker-to-victim traffic was queued. The rule did not match correctly.
At first, only attacker-to-victim traffic was queued. The rule did not match correctly.


After queueing both directions, Suricata could properly inspect the HTTP URI and block the request.
After queueing both directions, Suricata could understand the full HTTP flow and block the malicious request.


'''Screenshot placement:''' Add one Suricata log screenshot and one screenshot showing the blocked request.
Result:
 
* SQL injection pattern is detected
* Request is dropped
* Web app does not return all products
 
[[File:Website ips.png|800px|border]]


== Attack 5: HTTP Flood ==
== Attack 5: HTTP Flood ==
Line 608: Line 946:
=== Description ===
=== Description ===


The HTTP flood targets the victim Apache web server by sending many HTTP GET requests. This is a Layer 7 DoS-style attack.
The HTTP flood targeted the Apache web server by sending a large number of HTTP GET requests.


The final presentation describes the goal as generating an HTTP flood using wrk, showing the impact on Apache access logs, and detecting/blocking it with Suricata. :contentReference[oaicite:9]{index=9}
This is a Layer 7 DoS-style attack. It does not exploit SQL logic. Instead, it tries to overload the web server by forcing it to handle many requests.


=== Attack Command ===
=== Attack Command ===
Line 618: Line 956:
</pre>
</pre>


Meaning:
Command explanation:


* 12 threads
* `wrk` is an HTTP benchmarking tool used to generate a high load, especially on multi core CPUs. More about wrk can be found in the [[#References|References]] section
* 400 connections
* `-t12` uses 12 threads
* 30 seconds duration
* `-c400` opens 400 connections
* `-d30s` runs the test for 30 seconds
* target is the Apache server on the victim


=== Monitoring ===
=== Monitoring ===
Line 631: Line 971:
sudo tail -f /var/log/apache2/access.log
sudo tail -f /var/log/apache2/access.log
</pre>
</pre>
[[File:Apache log no attack.png|800px]]
This shows how many requests are reaching Apache.


=== No IDPS ===
=== No IDPS ===
Line 636: Line 980:
Result:
Result:


* Apache receives thousands of GET requests per second
* Apache receives many GET requests
* Access log fills quickly with 200 OK entries
* Access log fills quickly with 200 OK entries
* No alert
* No alerts are generated
* No blocking
* No blocking is applied


'''Screenshot placement:''' Add Apache log screenshot under flood.
[[File:Apache log with attack.png|800px]]


=== Suricata IDS ===
=== Suricata IDS ===
Line 652: Line 996:
sid:9000001; rev:1;)
sid:9000001; rev:1;)
</pre>
</pre>
Rule explanation:
* `alert` means Suricata only logs the attack
* `http` inspects HTTP traffic
* destination is the victim web server
* `flow:to_server,established` checks established requests to Apache
* `threshold` detects a high request rate
* `track by_src` counts requests from each source IP
* `count 100, seconds 5` triggers when the source sends 100 requests within 5 seconds


Result:
Result:


* HTTP flood is detected
* HTTP flood is detected
* Alert is logged
* Alert is generated
* Apache still receives the requests
* Apache still receives the requests
[[File:http flood ids.png|800px]]


=== Suricata IPS ===
=== Suricata IPS ===
Line 668: Line 1,024:
sid:9000001; rev:2;)
sid:9000001; rev:2;)
</pre>
</pre>
Rule explanation:
* `drop` blocks traffic after the threshold is reached
* the rule does not block normal browsing immediately
* it reacts when the request rate becomes suspicious


NFQUEUE:
NFQUEUE:
Line 687: Line 1,049:
* Excessive HTTP requests are dropped
* Excessive HTTP requests are dropped
* Apache receives fewer successful requests
* Apache receives fewer successful requests
* The web server stays more stable


'''Screenshot placement:''' Add Suricata IPS drops screenshot.
[[File:http flood ips.png|800px]]


== Results Summary ==
== Results Summary ==
Line 726: Line 1,089:
== Main Lessons Learned ==
== Main Lessons Learned ==


* IDS mode gives visibility, but does not block traffic.
* IDS mode gives visibility, but does not stop the attack.
* IPS mode is needed to actively stop attacks.
* IPS mode is needed when traffic should be blocked.
* NFQUEUE is required when Snort or Suricata should drop packets inline.
* NFQUEUE is required for inline blocking.
* Source-based detection is not always reliable when source IPs are spoofed.
* Snort worked well for network-based attacks.
* Suricata worked well for HTTP and application-layer attacks.
* For spoofed traffic, tracking by destination can be better than tracking by source.
* For HTTP inspection, Suricata needs enough flow visibility.
* For HTTP inspection, Suricata needs enough flow visibility.
* Email alerts are useful for administrator monitoring.
* Email notifications make alerts easier to monitor.
 
== Conclusion ==
 
This project showed how an IDPS can detect and block multiple attack types in a controlled VMware lab.
 
The attacks covered both network-layer and application-layer scenarios:
 
* SSH brute force
* Port scanning
* Spoofed ICMP ping flood
* SQL injection
* HTTP flood


The final result showed that IDS mode is useful for detection and logging, while IPS mode provides active protection by dropping malicious traffic before it reaches the victim.


== References ==
== References ==


* https://wikipedia.org
* https://www.snort.org/documents
* https://google.com
* https://docs.suricata.io/en/suricata-8.0.5/
* https://www.netfilter.org/
* https://labex.io/tutorials/hydra-brute-force-ssh-in-hydra-549926
* https://nmap.org/book/synscan.html
* https://ubuntu.com/server/docs/how-to/mail-services/install-postfix/
* https://owasp.org/www-community/attacks/SQL_Injection
* https://clouddocs.f5.com/training/community/firewall/html/class2/ddos2.html
* https://github.com/wg/wrk


[[Category:Documentation]]
[[Category:Documentation]]

Latest revision as of 20:56, 8 June 2026

Summary

This documentation describes the setup, configuration and testing of an Intrusion Detecetion Prevention System (IDPS) used in a unviersity project.

The system was built in VMware and uses open-source tools such as Snort, Suricata, iptables and NFQUEUE. The main goal was to detect and block malicious traffic in a controlled lab environment.

The project follows the same structure for each attack:

  • No IDS / IPS: attack runs without protection
  • IDS mode: attack is detected and logged
  • IPS mode: attack is detected and blocked

The main attacks tested were:

  • SSH brute force
  • TCP SYN port scanning
  • Spoofed ICMP ping flood
  • SQL injection
  • HTTP flood

Requirements

Virtual Machines

The lab uses three main virtual machines:

  • Attacker VM (Kali Linux)
  • IDPS VM (Ubuntu Server)
  • Victim VM (Ubuntu Server)

Tools

  • VMware Workstation Pro
  • Snort
  • Suricata
  • iptables
  • NFQUEUE
  • Apache2
  • PHP
  • SQLite
  • Nmap
  • hping3
  • wrk
  • Postfix
  • tcpdump

Network Setup

The lab lab uses VMWare as its virtualization platform, for easy of use and more customization options. It also allows snapshots and each member to work independently on the VMs. The setup uses two isolated internal networks: one for the victim and one for the attacker. The virtual networks were created in the VMware Virtual Network Editor, whereby the internal networks were mapped to host-only networks and the external one for internet access was a NAT network, sharing the IP of the host machine.


Topology

Note: In the real topology, vmbr0 corresponds to ens33, vmbr1 to ens37 and vmbr2 to ens38.

Topology.jpeg

Network Plan

Network Subnet Purpose
VictimNet 192.168.10.0/24 Victim side
AttackerNet 192.168.20.0/24 Attacker side

IP Plan

Machine IP Address Role
Victim 192.168.10.10 Target machine
Attacker 192.168.20.10 Attack machine
IDPS victim side 192.168.10.254 Gateway for VictimNet
IDPS attacker side 192.168.20.254 Gateway for AttackerNet

The IDPS machine acts as the gateway between both networks. This forces attacker to victim traffic through the IDPS, which allows packet inspection, attack detection and blocking.

IP Forwarding

IP forwarding must be enabled on the IDPS machine, which essentially enables an internet connection, since it forwards packets to external networks:

sudo sysctl -w net.ipv4.ip_forward=1

To make it permanent, open the /etc/sysctl.conf file and add or uncomment the line below:

net.ipv4.ip_forward=1

To check if it is enabled, do:

cat /proc/sys/net/ipv4/ip_forward

IDS and IPS Concept

IDS Mode

IDS mode only detects and logs suspicious traffic.

→ Traffic passes normally
→ IDS observes traffic
→ Alert is generated
→ Attack still reaches victim

IPS Mode

IPS mode runs inline. This means the packet must pass through the security tool before it reaches the victim.

→ Packet arrives
→ iptables sends it to NFQUEUE
→ Snort / Suricata inspects it
→ Packet is accepted or dropped

This is why drop rules only work when the tool is actually inline through NFQUEUE.

Snort Setup

Snort was used mainly for the network-based attacks, such as SSH brute force, port scanning and ICMP ping flood.

Snort works with rules. A rule describes what traffic should be detected or blocked. In IDS mode, Snort uses `alert` rules. In IPS mode, the rule action can be changed to `drop`.

The main Snort configuration file is:

/etc/snort/snort.conf

Important network variables were configured inside this file:

ipvar HOME_NET 192.168.10.0/24
ipvar EXTERNAL_NET 192.168.20.0/24

In this project:

  • `HOME_NET` = victim network
  • `EXTERNAL_NET` = attacker network

Local custom rules were added in:

/etc/snort/rules/local.rules

The local rules file must be included in `snort.conf`:

include $RULE_PATH/local.rules

Before running Snort, the configuration was tested with:

sudo snort -T -c /etc/snort/snort.conf

Snort alerts were saved under:

/var/log/snort/alert

Suricata Setup

Suricata was used for the application-layer attacks, mainly SQL injection and HTTP flood.

Suricata can inspect normal packet data, but it can also understand protocols such as HTTP. This makes it useful for detecting attacks inside URLs, HTTP methods and web requests.

The main Suricata configuration file is:

/etc/suricata/suricata.yaml

Custom rules were added in:

/etc/suricata/rules/local.rules

The local rules file must be enabled in `suricata.yaml` under the rule files section:

rule-files:
  - local.rules

Before starting Suricata, the configuration was tested with:

sudo suricata -T -c /etc/suricata/suricata.yaml

Suricata logs were checked mainly in:

/var/log/suricata/suricata.log
/var/log/suricata/fast.log

For IDS mode, Suricata was run on the attacker-side interface:

sudo suricata -c /etc/suricata/suricata.yaml -i ens38 -l /var/log/suricata

For IPS mode, Suricata was run inline with NFQUEUE:

sudo suricata -c /etc/suricata/suricata.yaml -q 0 -l /var/log/suricata

What is NFQUEUE and Why Is It Needed?

NFQUEUE is used when Snort or Suricata should work as an IPS.

Normally, traffic is forwarded by the Linux kernel without waiting for Snort or Suricata. In that case, the tools can detect attacks, but they cannot stop the packets.

NFQUEUE changes this behavior.

Packet arrives at IDPS
→ iptables sends packet to NFQUEUE
→ Snort / Suricata inspects it
→ packet is accepted or dropped

This means the packet waits for a decision before it reaches the victim.

Without NFQUEUE:

  • `alert` rules can generate logs
  • `drop` rules do not really block forwarded traffic
  • the attack still reaches the victim

With NFQUEUE:

  • traffic is inspected inline
  • `drop` rules can actually block packets
  • the IDPS works as a prevention system

Attack 1: SSH Brute Force

Description

The SSH brute force attack was used to test login attack detection and prevention.

A weak test user was created on the victim machine. The attacker then used Hydra to try many passwords against SSH.

This represents a common real-world attack where an attacker tries to guess login credentials.

Attack Command

hydra -l testuser -P customlist.txt ssh://192.168.10.10

No IDPS

Without IDPS protection:

  • The attack runs normally
  • Hydra can keep trying passwords
  • No real-time alert is generated by the IDPS
  • The only evidence is in the victim logs

Victim log:

/var/log/auth.log

Ssh auth log.png

Snort IDS

In IDS mode, Snort was used to detect SSH connection attempts.

Example rule:

alert tcp any any -> 192.168.10.10 22
(msg:"SSH Brute Force Attempt"; flags:S; sid:1000004;)

Rule explanation:

  • `alert` means Snort generates an alert, but does not block
  • `tcp` means the rule checks TCP traffic
  • `any any` means any source IP and any source port
  • `192.168.10.10 22` means the victim SSH service
  • `flags:S` matches TCP SYN packets, which are used when starting a TCP connection
  • `msg` is the alert text shown in the logs
  • `sid` is the unique rule ID

Result:

  • SSH attempts are detected
  • Alerts are logged
  • The brute force attack can still continue

Ssh ids.png

Snort IPS

In IPS mode, the rule was changed to `drop` and traffic was sent through NFQUEUE.

NFQUEUE rules:

iptables -I FORWARD -p tcp --dport 22 -j NFQUEUE --queue-num 0
iptables -I FORWARD -p tcp --sport 22 -j NFQUEUE --queue-num 0

Blocking rule:

drop tcp any any -> 192.168.10.10 22
(msg:"IPS BLOCKED: SSH Scan Attempt";
threshold:type both, track by_src, count 5, seconds 60;
sid:1000002;)

Rule explanation:

  • `drop` means Snort blocks matching packets in inline mode
  • destination port `22` targets SSH
  • `threshold` avoids blocking every single SSH packet immediately
  • `track by_src` counts attempts from each source IP
  • `count 5, seconds 60` triggers after 5 attempts in 60 seconds

Result:

  • Repeated SSH attempts are detected
  • Snort drops matching packets
  • The brute force attack is interrupted

Ssh ips.png

Attack 2: TCP SYN Port Scanning

Description

Port scanning was used to simulate reconnaissance.

Before launching a specific attack, an attacker often scans the target to discover open ports and services.

In this project, the victim had several services open to make the scan result more realistic.

Victim Services

Port Service
21 FTP
22 SSH
80 HTTP / Apache
8000 Demo HTTP service
9000 Demo TCP service

Attack Command

sudo nmap -sS 192.168.10.10

The `-sS` option performs a TCP SYN scan. This type of scan sends SYN packets to check which ports respond.

No IDPS

Without protection:

  • Nmap discovers open ports
  • Services are visible to the attacker
  • No alerts are generated
  • No packets are blocked

This gives the attacker useful information for future attacks.

Nmap default scan.png

Snort IDS

Snort was first used in IDS mode.

alert tcp 192.168.20.0/24 any -> 192.168.10.10 any
(msg:"NMAP SYN Port Scan Detected"; flags:S;
threshold:type both, track by_src, count 10, seconds 5;
sid:1000005; rev:1;)

Rule explanation:

  • `alert` means detect only
  • `tcp` checks TCP traffic
  • source network is the attacker network
  • destination is the victim
  • `flags:S` matches SYN packets
  • `threshold` triggers only after multiple packets
  • `track by_src` counts packets per attacker IP
  • `count 10, seconds 5` means 10 SYN packets within 5 seconds
  • `msg` is the alert message

Result:

  • Port scan is detected
  • Alert is saved under `/var/log/snort/alert`
  • Scan still reaches the victim

Nmap ids.png

Snort IPS

For IPS mode, the rule action was changed to `drop`.

drop tcp 192.168.20.0/24 any -> 192.168.10.10 any
(msg:"PORT SCAN BLOCKED"; flags:S;
threshold:type both, track by_src, count 20, seconds 5;
sid:1000011; rev:1;)

Rule explanation:

  • `drop` blocks matching packets in inline mode
  • the rule still watches for SYN packets
  • the threshold avoids blocking normal single connections
  • when the scan rate becomes suspicious, Snort starts dropping packets

NFQUEUE setup:

iptables -I FORWARD 1 -s 192.168.20.0/24 -d 192.168.10.0/24 -p tcp -j NFQUEUE --queue-num 0

Run Snort inline:

snort -Q --daq nfq --daq-var queue=0 -c /etc/snort/snort.conf -A console

Result:

  • Scan traffic is detected
  • Matching packets are dropped
  • Nmap receives fewer useful results

Nmap ips.png

Email Notification System

Email notifications were added so the administrator does not need to watch the Snort terminal all the time.

Setup

Postfix was installed on the IDPS machine and configured to send mail through Gmail SMTP.

Setup summary:

  • Installed Postfix and mail tools
  • Configured Gmail SMTP relay
  • Created a Gmail App Password
  • Used the App Password for authentication

A normal Gmail password was not used because Gmail blocks simple password authentication from scripts and Linux mail tools.

Notification Flow

Snort live output
→ wrapper script reads alert/drop messages
→ mail command sends summary
→ Postfix forwards mail through Gmail SMTP
→ administrator receives email

The wrapper script runs Snort in IPS mode and reads the live console output line by line. It shows the output in the terminal, saves it into a temporary buffer, and sends the collected output by email every 30 seconds. A lock file is used to prevent the script from sending too many emails at the same time. This way, the administrator receives a short email summary of Snort alerts instead of manually watching the terminal.

EMAIL_TO="censored@gmail.com" 
HOSTNAME=$(hostname)
BUFFER="/tmp/snort-live-output-buffer.txt"
LOCK="/tmp/snort-live-output-lock"
SEND_INTERVAL=30

SNORT_CMD="snort -Q --daq nfq --daq-var queue=0 -c /etc/snort/snort.conf -A console"

> "$BUFFER"
rm -f "$LOCK"

sudo stdbuf -oL -eL $SNORT_CMD 2>&1 | while IFS= read -r line; do
    # Show Snort output live in terminal
    echo "$line"

    # Save every Snort output line to buffer
    echo "$line" >> "$BUFFER"

    # Start delayed email sender if not already running
    if [ ! -f "$LOCK" ]; then
        touch "$LOCK"

        (
            sleep "$SEND_INTERVAL"

            if [ -s "$BUFFER" ]; then
                {
                    echo "Snort live output from $HOSTNAME"
                    echo
                    echo "Time: $(date)"
                    echo
                    echo "Last $SEND_INTERVAL seconds of Snort output:"
                    echo
                    cat "$BUFFER"
                } | mail -s "Snort Live Output from $HOSTNAME" "$EMAIL_TO"

                > "$BUFFER"
            fi

            rm -f "$LOCK"
        ) &
    fi
done

Result:

  • Admin receives alerts by email
  • Monitoring is easier
  • The IDPS behaves more like a real monitoring system

Email notifications.png

Attack 3: Spoofed ICMP Ping Flood

Description

The spoofed ping flood attack sends a high number of ICMP Echo Request packets to the victim.

The attacker also randomizes the source IP address. This makes detection harder because the attack does not appear to come from one fixed IP.

Attack Command

hping3 -1 --flood -V --rand-source 192.168.10.10

Command explanation:

  • `-1` uses ICMP mode
  • `--flood` sends packets as fast as possible
  • `-V` enables verbose output
  • `--rand-source` randomizes the source IP
  • `192.168.10.10` is the victim

Monitoring

On the victim:

sudo tcpdump -ni any 'icmp and dst host 192.168.10.10'

From the gateway:

ping 192.168.10.10

The `tcpdump` output shows the incoming ICMP packets. The gateway ping shows whether the victim becomes less responsive.

No IDPS

Result:

  • Victim receives many ICMP packets
  • Source IPs are random/spoofed
  • Gateway ping becomes less stable
  • No alerts are generated
  • No packets are blocked

Tcpdump pingflood.png

Unreliable ping.png

Snort IDS

Because the source IP is spoofed, the rule tracks by destination instead of source.

alert icmp any any -> 192.168.10.10 any
(msg:"SPOOFED ICMP PING FLOOD DETECTED";
itype:8; detection_filter:track by_dst, count 100, seconds 1;
sid:1000501; rev:1;)

Rule explanation:

  • `alert` means detection only
  • `icmp` checks ICMP traffic
  • `any any` is used because the source IP is spoofed
  • destination is the victim
  • `itype:8` matches ICMP Echo Requests
  • `track by_dst` counts traffic going to the victim
  • `count 100, seconds 1` triggers after 100 requests in 1 second

Result:

  • Flood is detected
  • Alert is logged
  • Flood still reaches the victim

Ping flood ids.png

Snort IPS

The same idea was used in IPS mode, but with `drop`.

drop icmp any any -> 192.168.10.10 any
(msg:"SPOOFED ICMP PING FLOOD BLOCKED";
itype:8; detection_filter:track by_dst, count 100, seconds 1;
sid:1000502; rev:1;)

Rule explanation:

  • `drop` blocks the matching ICMP packets
  • source is still `any` because it is spoofed
  • detection is based on high traffic rate to the victim
  • once the threshold is reached, packets are dropped

NFQUEUE setup:

sudo iptables -F FORWARD
sudo iptables -I FORWARD 1 -i ens38 -o ens37 -p icmp -j NFQUEUE --queue-num 0

Run Snort inline:

sudo snort -Q --daq nfq --daq-var queue=0 -c /etc/snort/snort.conf -A console

Result:

  • Flood is detected
  • ICMP packets are dropped
  • Less traffic reaches the victim
  • Gateway ping becomes more stable

Ping flood ips.png

Attack 4: SQL Injection

Description

SQL injection was used to test an application-layer attack.

For this scenario, a simple vulnerable PHP product lookup website was created on the victim. It was hosted on Apache and used SQLite as the database.

The attacker entered the malicious input directly in Firefox.

Website Setup

The vulnerable page was:

/sqli-demo.php?id=1

<?php
$db = new SQLite3('/var/www/html/products.db');

$id = $_GET['id'] ?? '1';

$sql = "SELECT id, name, price FROM products WHERE id = $id";

$result = @$db->query($sql);
?>

<!DOCTYPE html>
<html>
<head>
    <title>Product Lookup</title>
</head>
<body>
    <h1>Product Lookup Demo</h1>
    <hr>

    <?php
    if ($result) {
        while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
            echo "<p>";
            echo "<strong>ID:</strong> " . htmlspecialchars($row['id']) . "<br>";
            echo "<strong>Name:</strong> " . htmlspecialchars($row['name']) . "<br>";
            echo "<strong>Price:</strong> " . htmlspecialchars($row['price']);
            echo "</p><hr>";
        }
    } else {
        echo "<p>Query failed.</p>";
    }
    ?>
</body>
</html>

The database contained a simple product table:

ID Product Price
1 Laptop 999 EUR
2 Phone 699 EUR
3 Tablet 399 EUR
4 Headphones 149 EUR

Vulnerable PHP Query

The PHP file reads the ID value from the URL:

$id = $_GET['id'] ?? '1';

Then it directly inserts the value into the SQL query:

$sql = "SELECT id, name, price FROM products WHERE id = $id";

This is vulnerable because the application trusts user input. The input becomes part of the SQL command.

Original Query

Normal request:

/sqli-demo.php?id=1

SQL query:

SELECT id, name, price FROM products WHERE id = 1;

Result:

  • only product ID 1 is returned

Malicious Query

Malicious request:

/sqli-demo.php?id=1 OR 1=1

SQL query:

SELECT id, name, price FROM products WHERE id = 1 OR 1=1;

Since `1=1` is always true, all rows in the product table are returned.

Why This Query Is Vulnerable

The input is not filtered or parameterized.

Filtered input means checking the value before using it. For example, a product ID should only contain numbers.

Parameterized input means the SQL structure is fixed, and user input is passed separately as data. This prevents the input from becoming SQL code.

In this demo, the input was directly added into the SQL string, so the attacker could change the query logic.

No IDPS

The attacker used Firefox:

http://192.168.10.10/sqli-demo.php?id=1 OR 1=1

Result:

  • Request reaches the PHP page
  • SQL query is manipulated
  • All products are returned
  • No alert is generated
  • No traffic is blocked

Website injected.png

Suricata IDS

Suricata was used to inspect the HTTP URI.

alert http 192.168.20.0/24 any -> 192.168.10.10 80
(msg:"SQL Injection Attempt Detected - Boolean OR Pattern";
flow:established,to_server;
http.uri;
pcre:"/sqli-demo\.php\?id=[^&]*\bOR\b[^&]*1=1/Ui";
sid:2001001; rev:1;)

Rule explanation:

  • `alert` means detect and log only
  • `http` tells Suricata to inspect HTTP traffic
  • source is the attacker network
  • destination is the victim web server
  • `flow:established,to_server` checks established requests going to the server
  • `http.uri` tells Suricata to inspect the URL
  • the `pcre` searches for the vulnerable page and the `OR 1=1` pattern
  • `sid` is the rule ID

Result:

  • SQL injection is detected
  • Alert is logged
  • Injection still succeeds

Logs:

/var/log/suricata/suricata.log

Sql ids.png

Suricata IPS

The IDS rule was changed from `alert` to `drop`.

drop http 192.168.20.0/24 any -> 192.168.10.10 80
(msg:"SQL Injection Attempt BLOCKED - Boolean OR Pattern";
flow:established,to_server;
http.uri;
pcre:"/sqli-demo\.php\?id=[^&]*\bOR\b[^&]*1=1/Ui";
sid:2001001; rev:2;)

Rule explanation:

  • `drop` blocks the malicious HTTP request
  • the same URI pattern is used
  • when the SQL injection is detected, the packet is dropped before the app processes it

NFQUEUE for HTTP

For Suricata IPS, HTTP traffic had to be queued in both directions:

sudo iptables -I FORWARD 1 -i ens38 -o ens37 -p tcp --dport 80 -j NFQUEUE --queue-num 0
sudo iptables -I FORWARD 2 -i ens37 -o ens38 -p tcp --sport 80 -j NFQUEUE --queue-num 0

Run Suricata inline:

sudo suricata -c /etc/suricata/suricata.yaml -q 0 -l /var/log/suricata

Why Both Directions Were Needed

Suricata needs enough TCP and HTTP flow information to inspect the request correctly.

The SQL injection rule uses:

flow:established,to_server;
http.uri;

This means Suricata must understand that the request belongs to an established HTTP flow and that it is going to the server.

At first, only attacker-to-victim traffic was queued. The rule did not match correctly.

After queueing both directions, Suricata could understand the full HTTP flow and block the malicious request.

Result:

  • SQL injection pattern is detected
  • Request is dropped
  • Web app does not return all products

Website ips.png

Attack 5: HTTP Flood

Description

The HTTP flood targeted the Apache web server by sending a large number of HTTP GET requests.

This is a Layer 7 DoS-style attack. It does not exploit SQL logic. Instead, it tries to overload the web server by forcing it to handle many requests.

Attack Command

wrk -t12 -c400 -d30s http://192.168.10.10/

Command explanation:

  • `wrk` is an HTTP benchmarking tool used to generate a high load, especially on multi core CPUs. More about wrk can be found in the References section
  • `-t12` uses 12 threads
  • `-c400` opens 400 connections
  • `-d30s` runs the test for 30 seconds
  • target is the Apache server on the victim

Monitoring

On the victim:

sudo tail -f /var/log/apache2/access.log

Apache log no attack.png

This shows how many requests are reaching Apache.

No IDPS

Result:

  • Apache receives many GET requests
  • Access log fills quickly with 200 OK entries
  • No alerts are generated
  • No blocking is applied

Apache log with attack.png

Suricata IDS

alert http any any -> 192.168.10.10 80
(msg:"HTTP Flood Detected - High Request Rate";
flow:to_server,established;
threshold:type threshold, track by_src, count 100, seconds 5;
sid:9000001; rev:1;)

Rule explanation:

  • `alert` means Suricata only logs the attack
  • `http` inspects HTTP traffic
  • destination is the victim web server
  • `flow:to_server,established` checks established requests to Apache
  • `threshold` detects a high request rate
  • `track by_src` counts requests from each source IP
  • `count 100, seconds 5` triggers when the source sends 100 requests within 5 seconds

Result:

  • HTTP flood is detected
  • Alert is generated
  • Apache still receives the requests

Http flood ids.png

Suricata IPS

drop http any any -> 192.168.10.10 80
(msg:"HTTP Flood Blocked - High Request Rate";
flow:to_server,established;
threshold:type threshold, track by_src, count 100, seconds 5;
sid:9000001; rev:2;)

Rule explanation:

  • `drop` blocks traffic after the threshold is reached
  • the rule does not block normal browsing immediately
  • it reacts when the request rate becomes suspicious

NFQUEUE:

sudo iptables -I FORWARD 1 -i ens38 -o ens37 -p tcp --dport 80 -j NFQUEUE --queue-num 0
sudo iptables -I FORWARD 2 -i ens37 -o ens38 -p tcp --sport 80 -j NFQUEUE --queue-num 0

Run Suricata inline:

sudo suricata -c /etc/suricata/suricata.yaml -q 0 -l /var/log/suricata

Result:

  • High request rate is detected
  • Excessive HTTP requests are dropped
  • Apache receives fewer successful requests
  • The web server stays more stable

Http flood ips.png

Results Summary

Attack Tool IDS Result IPS Result
SSH Brute Force Snort Login attempts detected Repeated attempts blocked
Port Scanning Snort SYN scan detected Scan packets dropped
Ping Flood Snort ICMP flood detected Flood packets dropped
SQL Injection Suricata Malicious URI detected HTTP request dropped
HTTP Flood Suricata High request rate detected Excessive requests dropped

Main Lessons Learned

  • IDS mode gives visibility, but does not stop the attack.
  • IPS mode is needed when traffic should be blocked.
  • NFQUEUE is required for inline blocking.
  • Snort worked well for network-based attacks.
  • Suricata worked well for HTTP and application-layer attacks.
  • For spoofed traffic, tracking by destination can be better than tracking by source.
  • For HTTP inspection, Suricata needs enough flow visibility.
  • Email notifications make alerts easier to monitor.


References