Post

Building Argus SOC | Phase 4 Scenario 4 | Web Application Attacks — SQL Injection vs Invisible NIDS

Phase 4 Scenario 4 of the Argus SOC build — SQLmap dumps the DVWA database through four injection techniques. Suricata sees nothing because HTTP_PORTS only covers port 80. After adding 8080 and writing custom rules, the same attack generates 33 alerts.

Building Argus SOC | Phase 4 Scenario 4 | Web Application Attacks — SQL Injection vs Invisible NIDS

📌 Author’s note — This post documents the Argus SOC lab at the time of publication, when the Pi 3B+ served as the MSSP edge sensor and the Pi 5 hosted the vulnerable Docker targets. The architecture was redesigned in Phase 5, which introduced a ThinkCentre M920x running Proxmox with an Active Directory lab as the client infrastructure, and moved the edge-sensor role onto the Pi 5. The detection logic, custom rules, and gap analysis described here remain valid; only the host topology has changed.

Build carried out on real hardware in a controlled home lab. Claude (Anthropic) was used as a reasoning and writing assistant — all deployments, attacks, configurations, and verifications were performed by the author.

Overview

This scenario tests whether the SOC can detect a web application attack against a known vulnerable target. SQLmap automates SQL injection against DVWA on port 8080, extracting database names through four different injection techniques in 16 seconds. The SOC sees nothing — not because the signatures don’t exist, but because Suricata’s HTTP_PORTS variable only includes port 80. Traffic on port 8080 never gets HTTP-layer inspection.

It’s the same class of problem as Scenario 3’s HOME_NET typo: a configuration variable silently scoping rules away from the traffic they’re supposed to match.


Preparing DVWA

DVWA requires an authenticated session with the security level set to Low before SQLmap can reach the injectable parameter. The session cookie was obtained programmatically from Kali:

1
2
3
4
5
6
7
# Get a fresh session cookie
COOKIE=$(curl -s -c - -d "username=admin&password=password&Login=Login" \
  "http://192.168.1.10:8080/login.php" | grep PHPSESSID | awk '{print $NF}')

# Set security to low
curl -s -b "PHPSESSID=$COOKIE" -d "security=low&seclev_submit=Submit" \
  "http://192.168.1.10:8080/security.php"

The Attack

Timestamp: 2026-04-04 15:48 UTC Target: 192.168.1.10:8080 (DVWA on Docker)

1
2
3
sqlmap -u "http://192.168.1.10:8080/vulnerabilities/sqli/?id=1&Submit=Submit" \
  --cookie="PHPSESSID=<redacted-cookie>" \
  --dbs --batch

Duration: ~16 seconds HTTP requests sent: 146

SQLmap identified four injection types on the id GET parameter:

Injection TypePayload Pattern
Boolean-based blindid=1' OR NOT 9661=9661#
Error-based (EXTRACTVALUE)id=1' AND EXTRACTVALUE(1457,CONCAT(...))--
Time-based blind (SLEEP)id=1' AND (SELECT 5407 FROM (SELECT(SLEEP(5)))zhhN)--
UNION query (2 columns)id=1' UNION ALL SELECT CONCAT(...),NULL#

Databases extracted: dvwa and information_schema Back-end identified: MySQL >= 5.1 (MariaDB fork), Apache 2.4.66, PHP 8.5.4

SQLmap output SQLmap output SQLmap — four injection types identified, two databases extracted in 16 seconds


Detection Results: Zero Detection

Suricata: No Alerts

The Suricata tail on Pi 3B+ was completely silent during the entire SQLmap run. 146 HTTP requests carrying SQL injection payloads — UNION ALL SELECT, EXTRACTVALUE, SLEEP(), OR 1=1 — and not a single HTTP-layer signature fired.

Wazuh: No Relevant Alerts

12 alerts during the window — all SSH brute force noise (sshd: Attempt to login using a non-existent user, rule 5710, level 5) on the Hetzner VPS from external internet IPs. Zero alerts from argus-edge-01 related to the web attack.

Claude / Telegram: Never Triggered

No alerts reached n8n. The operator was never notified.

Grafana showing zero detection Grafana SOC dashboard — no indication of an active SQL injection attack

Wazuh showing no SQLi alerts Wazuh event list — only SSH noise from internet bots, nothing about the web attack


Root Cause Analysis

Finding: HTTP_PORTS Set to Port 80 Only

1
2
# suricata.yaml on Pi 3B+
HTTP_PORTS: "80"

DVWA runs on port 8080 (Docker). Suricata’s HTTP inspection rules — including all ET web attack signatures — are scoped to $HTTP_PORTS. With only port 80 listed, Suricata never applied HTTP protocol parsing to traffic on 8080. The SQL injection payloads were visible in the raw packet data, but Suricata wasn’t looking at them as HTTP.

This is the same pattern as the HOME_NET typo in Scenario 3 — a single variable silently breaks an entire category of detection.

Contributing Factors

There are additional layers to this gap. ET web attack rules often scope SQL injection signatures to $EXTERNAL_NET source addresses, which means internal LAN traffic (192.168.1.101 → 192.168.1.10) may not match even with the correct ports. The payloads are also URL-encoded in GET parameters, requiring Suricata’s HTTP decoder to be active on the port before the content matching can work. Without HTTP protocol parsing on 8080, none of this decoding happens.


Remediation

Fix 1 — Expanded HTTP_PORTS (Pi 3B+)

In /etc/suricata/suricata.yaml:

1
2
3
4
5
# Before
HTTP_PORTS: "80"

# After
HTTP_PORTS: "[80,8080,8888,3000]"

Port 8080 covers DVWA. Port 8888 covers MediaMTX on Pi 5. Port 3000 covers Grafana. Any service running HTTP on a non-standard port needs to be listed here or Suricata won’t inspect it.

Fix 2 — Custom Suricata SQL Injection Rules (Pi 3B+)

Added to /etc/suricata/rules/local.rules:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# SQL injection: UNION SELECT in URI
alert http any any -> $HOME_NET $HTTP_PORTS (
  msg:"LOCAL SQL Injection attempt - UNION SELECT in URI";
  flow:established,to_server;
  http.uri;
  content:"UNION"; nocase;
  content:"SELECT"; nocase;
  classtype:web-application-attack;
  sid:9000010; rev:1;
)

# SQL injection: time-based blind (SLEEP)
alert http any any -> $HOME_NET $HTTP_PORTS (
  msg:"LOCAL SQL Injection attempt - time-based blind in URI";
  flow:established,to_server;
  http.uri;
  content:"SLEEP"; nocase;
  classtype:web-application-attack;
  sid:9000011; rev:1;
)

These rules use http.uri — a sticky buffer that operates on the decoded HTTP URI after Suricata’s HTTP parser processes the request. This means URL-encoded payloads like %27%20UNION%20SELECT are decoded before matching. The rules use any as the source address instead of $EXTERNAL_NET, so they catch internal attackers too.

Fix 3 — Wazuh Correlation Rule (Hetzner)

Added to /var/ossec/etc/rules/local_rules.xml:

1
2
3
4
5
6
<rule id="100032" level="12">
  <if_sid>86601</if_sid>
  <match>LOCAL SQL Injection</match>
  <description>Suricata: SQL injection attempt detected from $(data.srcip)</description>
  <mitre><id>T1190</id></mitre>
</rule>

Level 12 ensures the alert passes the n8n pre-filter and reaches Claude for classification.


Re-Test Results: Full Detection

Timestamp: 2026-04-04 19:55 UTC Same target, same technique, fresh SQLmap session.

The SQLmap cache was cleared first to force a full re-scan with all injection probes:

1
2
3
4
5
rm -rf /home/Al3grus/.local/share/sqlmap/output/192.168.1.10

sqlmap -u "http://192.168.1.10:8080/vulnerabilities/sqli/?id=1&Submit=Submit" \
  --cookie="PHPSESSID=$COOKIE;security=low" \
  --dbs --batch --flush-session

Wazuh Alert Escalation

Rule IDDescriptionLevelCount
100032SQL injection attempt detected1233

33 alerts — every UNION SELECT and SLEEP probe matched.

Claude AI Classification

Claude classified the alerts as MEDIUM with confidence 0.90+:

  • T1190 (Exploit Public-Facing Application)
  • Action: investigate
  • Summary: “SQLmap SQL injection attack detected against argus-central (192.168.1.10:8080) from internal IP 192.168.1.101.”

Claude identified SQLmap by name from the payload patterns — the URI structure, injection ordering, and comment markers are distinctive enough for automated tool identification.

Telegram: Alerts Delivered

The operator received Telegram alerts with Claude’s classification identifying the attack tool, target, and MITRE technique.

Wazuh showing 33 SQL injection alerts Wazuh after remediation — 33 SQL injection alerts, all rule 100032 at level 12

Grafana showing 33 critical alerts Grafana — MITRE ATT&CK panel showing “Exploit Public-Facing Application” at 33 hits

SQLmap remediated output SQLmap re-run — same four injection types, same databases extracted

Telegram SQL injection alerts Telegram — Claude classifies as MEDIUM, identifies SQLmap by name


Before / After

MetricBeforeAfter
Suricata alerts033
Wazuh alerts033 (all level 12)
Claude classificationNever reachedMEDIUM, T1190
Telegram alerts0Multiple
SQLmap identified by nameNoYes
Time to operator notificationNever~5 seconds

NIDS vs WAF: What This Scenario Reveals

This scenario highlights an important architectural point. Suricata is a network intrusion detection system — it matches patterns in network traffic. It’s not a web application firewall. Even with the correct ports and custom rules, it can only detect SQL injection patterns it has signatures for. An attacker using obfuscated payloads, encoding tricks, or novel injection techniques would bypass these rules.

In a production environment, web application attacks are better handled at the application layer — mod_security, cloud WAFs, or runtime application self-protection (RASP). Suricata provides a network-level safety net that catches automated tools like SQLmap, but it’s not a substitute for application-layer security.


Progression Across Scenarios

AspectScenario 1Scenario 2Scenario 3Scenario 4
Initial detectionPartialFullZeroZero
Root causeHOME_NET typoN/AHOME_NET + missing sigsHTTP_PORTS + missing sigs
Custom rules002 Suricata + 2 Wazuh2 Suricata + 1 Wazuh
After remediationET SCAN firingN/A12 CRITICAL33 alerts
Claude reachedNo (level 3)Yes — MEDIUMYes — CRITICALYes — MEDIUM
TelegramNoYesYes (11 alerts)Yes

Guest WiFi Isolation — Confirmed

During this scenario, an attempt to run SQLmap from the guest WiFi confirmed that the TP-Link Archer AX55 guest network provides genuine client isolation. The Kali VM could not reach 192.168.1.0/24 from the guest SSID. All attack scenarios were executed from the main WiFi where the attacker shares the same subnet as the targets.


Evidence Collected

Screenshots — Initial Attack (Zero Detection)

ScreenshotFilename
SQLmap output — 4 injection typesphase4_scenario4_sqlmap-output.png
Grafana — zero detectionphase4_scenario4_grafana-zero-detection.png
Wazuh — no SQLi alertsphase4_scenario4_wazuh-no-sqli-alerts.png

Screenshots — After Remediation

ScreenshotFilename
Wazuh — 33 SQL injection alertsphase4_scenario4_remediated-wazuh-sqli-alerts.png
Grafana — 33 critical, T1190phase4_scenario4_remediated-grafana-33-critical.png
SQLmap output (fresh run)phase4_scenario4_remediated-sqlmap-output.png
Telegram — Claude SQLi classificationphase4_scenario4_remediated-telegram-sqli.png

Data Exports

FileDescription
scenario4_wazuh-events-export.csvDownload — zero detection run
scenario4_remediated-wazuh-events-export.csvDownload — successful detection run

What’s Next — Scenario 5

Scenario 5 is the final attack — lateral movement from a compromised Metasploitable container to the Cowrie honeypot on Pi 3B+. Unlike the previous scenarios where the problem was missing detection, Scenario 5 reveals a different gap: the alert fires but Claude classifies it as LOW because it doesn’t recognize the significance of a server IP appearing as an SSH source.


Part of the Argus SOC build series.

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