Skip to content

About

ModSecurity + OWASP CRS WAF lab: tuning, false positives, sqlmap testing

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

WAF Lab: ModSecurity + OWASP Core Rule Set

A hands-on lab for learning how a Web Application Firewall (WAF) works in practice. It covers deploying ModSecurity with the OWASP Core Rule Set (CRS) on Apache, then testing it against real attacks on an intentionally vulnerable application (DVWA).

Disclaimer: This project is for educational purposes only. DVWA contains real vulnerabilities. Never expose it to the internet, and only run attack tools against systems you own or are authorized to test.


Table of Contents


Objectives

  • Deploy ModSecurity and the OWASP CRS on Apache
  • Understand the difference between DetectionOnly and On engine modes
  • Read WAF logs and understand Anomaly Scoring
  • Write custom ModSecurity rules
  • Configure CRS Paranoia Levels
  • Identify and resolve false positives with narrowly scoped exclusions
  • Validate protection using sqlmap and compare results with and without blocking

Lab Environment

Component Details
Operating system Ubuntu (virtual machine)
Web server Apache 2.4 (listening on port 8081)
WAF engine ModSecurity v2 (libapache2-mod-security2)
Rule set OWASP Core Rule Set (cloned from GitHub)
Target application DVWA (security level: Low)
Attack tool sqlmap

Repository Structure

WAF-lab/
├── README.md
├── config/
│   ├── 000-default.conf          # Apache VirtualHost (port 8081)
│   ├── ports.conf                # Apache listening port
│   ├── security2.conf            # Loads ModSecurity, CRS, and exclusions
│   ├── modsecurity-engine.txt    # ModSecurity engine mode (SecRuleEngine)
│   └── crs-setup-active.conf     # Active CRS settings (Paranoia Level)
└── rules/
    ├── custom.conf               # Custom rule (id:10001)
    └── exclusions-after-crs.conf # False-positive exclusions

Setup

1. Install Apache and ModSecurity

sudo apt update
sudo apt install apache2 libapache2-mod-security2 -y
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf

2. Download the OWASP Core Rule Set

cd /etc/modsecurity
sudo git clone https://github.com/coreruleset/coreruleset.git
cd coreruleset
sudo cp crs-setup.conf.example crs-setup.conf

3. Load the rules

Edit /etc/apache2/mods-enabled/security2.conf (see config/security2.conf):

<IfModule security2_module>
    SecDataDir /var/cache/modsecurity
    IncludeOptional /etc/modsecurity/*.conf
    Include /etc/modsecurity/coreruleset/crs-setup.conf
    Include /etc/modsecurity/coreruleset/rules/*.conf
    Include /etc/modsecurity/exclusions-after-crs.conf
    # IncludeOptional /usr/share/modsecurity-crs/*.load   # disabled to avoid loading CRS twice
</IfModule>

Exclusions must be loaded after the CRS rules, otherwise they have no effect.

4. Change the Apache port

Nginx was already bound to port 80 on the VM, so Apache runs on 8081:

sudo sed -i 's/Listen 80/Listen 8081/' /etc/apache2/ports.conf
sudo sed -i 's/<VirtualHost \*:80>/<VirtualHost *:8081>/' /etc/apache2/sites-available/000-default.conf

5. Validate and start

sudo apachectl configtest
sudo systemctl restart apache2

6. Install DVWA (target application)

sudo apt install -y php libapache2-mod-php php-mysqli php-gd mariadb-server git
cd /var/www/html
sudo git clone https://github.com/digininja/DVWA.git dvwa
sudo cp dvwa/config/config.inc.php.dist dvwa/config/config.inc.php
sudo mysql -e "CREATE DATABASE dvwa; CREATE USER 'dvwa'@'localhost' IDENTIFIED BY 'p@ssw0rd'; GRANT ALL ON dvwa.* TO 'dvwa'@'localhost'; FLUSH PRIVILEGES;"
sudo chown -R www-data:www-data /var/www/html/dvwa
sudo systemctl restart apache2

Then open http://<VM-IP>:8081/dvwa/setup.php, create the database, and set DVWA Security to Low.


Experiments and Results

Experiment 1: Baseline attacks

curl -i "http://<VM-IP>:8081/?q=<script>alert(1)</script>"   # XSS
curl -i "http://<VM-IP>:8081/?id=1' OR '1'='1"               # SQL injection
curl -i "http://<VM-IP>:8081/?file=../../etc/passwd"         # Path traversal

Result: all three requests return 403 Forbidden.

Experiment 2: On vs DetectionOnly

Switch the engine mode:

# Log only, do not block
sudo sed -i 's/SecRuleEngine On/SecRuleEngine DetectionOnly/' /etc/modsecurity/modsecurity.conf
sudo systemctl restart apache2

# Log and block
sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf
sudo systemctl restart apache2

Manual SQL injection (1' OR '1'='1) on the DVWA SQL Injection page:

Engine mode Outcome
DetectionOnly Attack succeeds and all usernames are returned
On 403 Forbidden

Experiment 3: sqlmap against the WAF

sqlmap -u "http://<VM-IP>:8081/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit" \
  --cookie="PHPSESSID=<SESSION>; security=low" --random-agent --batch
Engine = On Engine = DetectionOnly
sqlmap verdict not injectable is vulnerable
Blocked requests 144 returned 403 none
Findings none time-based blind and UNION query injection
Information leaked none DBMS type, OS, web server version

Important note: without --random-agent, sqlmap was blocked on the very first request because of its User-Agent (scanner-detection rules, 913xxx). That does not prove SQL injection protection. Using a random User-Agent ensures the blocking comes from the SQL injection rules (942xxx).

Conclusion: the WAF does not fix the vulnerability, it prevents it from being reached (virtual patching).


Tuning: Paranoia Level and False Positives

Paranoia Level

Paranoia Level was raised to 2 in crs-setup.conf (see config/crs-setup-active.conf):

SecAction \
    "id:900000,\
    phase:1,\
    pass,\
    t:none,\
    nolog,\
    tag:'OWASP_CRS',\
    setvar:tx.blocking_paranoia_level=2"

Higher levels enable more detection rules, which increases both protection and the likelihood of false positives.

Investigating a false positive

This legitimate request was blocked:

curl -i "http://<VM-IP>:8081/dvwa/?comment=please+select+one+from+the+list+and+union+of+items"

Log analysis:

Rule ID Description Score
920350 Host header is a numeric IP address 3
942200 MySQL comment/space-obfuscated injection detection 5
942260 Basic SQL authentication bypass attempt 5
949110 Inbound Anomaly Score exceeded (total 13 vs. threshold 5) block

Resolution

A narrowly scoped exclusion in rules/exclusions-after-crs.conf:

SecRuleUpdateTargetById 942200 "!ARGS:comment"
SecRuleUpdateTargetById 942260 "!ARGS:comment"

This excludes one parameter from two rules instead of disabling them site-wide.

Verification

Test Result
Legitimate text in comment Allowed (302 redirect to login)
SQL injection on id 403 Forbidden
SQL injection inside comment 403 Forbidden (other rules still inspect it)

Prefer SecRuleUpdateTargetById over SecRuleRemoveById, which disables a rule everywhere.


Custom Rule

rules/custom.conf:

SecRule ARGS:test "@contains hacker" "id:10001,phase:2,deny,status:403,msg:'My first rule'"
Part Meaning
ARGS:test Inspect the request parameter named test
@contains hacker Match if the value contains the string hacker
id:10001 Unique rule ID (custom rules use IDs above 10000)
phase:2 Evaluate after request arguments and body are parsed
deny,status:403 Block the request with HTTP 403
msg Message written to the logs
curl -i "http://<VM-IP>:8081/?test=hacker"   # 403 Forbidden
curl -i "http://<VM-IP>:8081/?test=hello"    # 200 OK

Useful Commands

# Check the current engine mode
grep SecRuleEngine /etc/modsecurity/modsecurity.conf

# Validate Apache configuration
sudo apachectl configtest

# See which rules fired, ranked by frequency
sudo grep -oE 'id "[0-9]+"' /var/log/apache2/error.log | sort | uniq -c | sort -rn | head

# Full audit log
sudo tail -n 50 /var/log/apache2/modsec_audit.log

CRS rule ID reference

Range Category
913xxx Scanner and tool detection
920xxx Protocol enforcement
930xxx Local file inclusion / path traversal
941xxx Cross-site scripting (XSS)
942xxx SQL injection
949110 Inbound anomaly score evaluation (blocking decision)
980170 Anomaly score summary

Key Takeaways

  • DetectionOnly observes attacks, On blocks them. The vulnerability itself remains in both cases.
  • CRS uses anomaly scoring: individual rules add points, and the request is blocked when the total exceeds the threshold.
  • Tuning is the real work of operating a WAF: reducing false positives without weakening protection.
  • Exclusions should be as narrow as possible (one parameter, specific rules).
  • A WAF is an additional defensive layer. It does not replace fixing vulnerabilities in the application.

References


License

Provided for educational use. ModSecurity, OWASP CRS, DVWA, and sqlmap are independent projects with their own licenses.

About

ModSecurity + OWASP CRS WAF lab: tuning, false positives, sqlmap testing

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors