OWASP ModSecurity Core Rules: Comparing 2.2.x and 3.0.0-dev


It has been a while since we have seen big development in the OWASP ModSecurity Core Rules. This is due to the fact, that the development took place in a separate branch named 3.0.0-dev which adopts many of the newer features and operators included in ModSecurity since 2.7; notably @detectSQLi and @detectXSS. When you take a closer look at the new rules, you realize quickly, that the whole file structure has been adopted. It looks quite unfamiliar if you got used to the 2.2.X rulesets.

I want to understand the differences between the rulesets and given the fact we are talking of several hundred rules, reading them one by one or following the changelog seems a daunting task. Let’s take a more behavioristic approach. Let’s see them in action.

The idea here is to setup two servers, one with Core Rules v2.2.9 and one with Core Rules v3.0.0-dev. Then configure a minimal set of local pages and have a vulnerability scanner examine the site. This won’t be a sophisticated venture into securing a site, but rather a report on how v2.2.9 reacts to a scan and what v3.0.0-dev does with the same requests.

For quick access and simplicity I used nmap first. Nmap comes with a lot of http scanning scripts and I ran them all one after another. However, they are more reconnaissance tools than attack scripts, so most of the requests went unnoticed by ModSecurity (well outside of thousands of fuzzying requests which were blocked with a 414). So I switched over to nikto. Nikto is not the newest scanner (and my version 2.1.4 is not the latest), but it’s very quick. And it is an attack scanner firing thousands of http exploits at a server. ModSecurity is alarmed by a lot of these, so we actually end up with many alerts and thus enough data to compare the two Core Rule versions.

Nikto has been called with the following commando:
$> nikto -h localhost -p 80

Core Rules v2.2.9 would let nikto carry out its tasks. But with v3.0.0-dev, there is a new feature: IP repudiation. As soon as the scanner had ramped up, ModSecurity realized what we are up to and started to block the source IP. This is done via an internal collection and based on the setting of the variable IP:BLOCK, rule 981140 will skip all further processing and rule 981175 will block the client IP. That’s a good feature. I do not know about false positives and I would not be surprised if legitimate users would be blocked by this rule. However, there is no need to allow a scanner to run thousands of requests against a website without reaction. In production some tuning might be due. In our case, tuning is also necessary, since the blocking mechanism cloaks the other rules which are not being executed. So I disabled the ip blocking as follows:


# No Blocking via IP repudiation, based on previous requests
SecRuleRemoveById 981140
SecRuleRemoveById 981175

Then I reran the test and ended up with 6179 requests for both rulesets.

Here is a graphical overview over the distribution of the anomaly scores:

modsec-positive-stats

And here the statistical data (generated using modsec-positive-stats.rb):


				  Core Rules v2.2.9  | Core Rules v3.0.0-dev
INCOMING                     Num of req. | % of req. |# of req| % of req. 
Number of incoming req. (total) |   6197 | 100.0000% |   6197 | 100.0000% 

Empty or miss. incoming score   |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of   0 |    217 |   3.5016% |    217 |   3.5016%
Reqs with incoming score of   1 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of   2 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of   3 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of   4 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of   5 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of   6 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of   7 |      0 |   0.0000% |   2826 |  45.6027%
Reqs with incoming score of   8 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of   9 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  10 |   2850 |  45.9899% |    197 |   3.1789%
Reqs with incoming score of  11 |      0 |   0.0000% |      2 |   0.0322%
Reqs with incoming score of  12 |      0 |   0.0000% |     80 |   1.2909%
Reqs with incoming score of  13 |    201 |   3.2435% |      0 |   0.0000%
Reqs with incoming score of  14 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  15 |    142 |   2.2914% |     19 |   0.3065%
Reqs with incoming score of  16 |      3 |   0.0484% |      6 |   0.0968%
Reqs with incoming score of  17 |      0 |   0.0000% |    117 |   1.8880%
Reqs with incoming score of  18 |     52 |   0.8391% |     67 |   1.0811%
Reqs with incoming score of  19 |      2 |   0.0322% |      0 |   0.0000%
Reqs with incoming score of  20 |   2113 |  34.0971% |     26 |   0.4195%
Reqs with incoming score of  21 |     16 |   0.2581% |     25 |   0.4034%
Reqs with incoming score of  22 |      1 |   0.0161% |   2195 |  35.4203%
Reqs with incoming score of  23 |     76 |   1.2263% |      0 |   0.0000%
Reqs with incoming score of  24 |     93 |   1.5007% |      0 |   0.0000%
Reqs with incoming score of  25 |    155 |   2.5012% |      4 |   0.0645%
Reqs with incoming score of  26 |     16 |   0.2581% |      1 |   0.0161%
Reqs with incoming score of  27 |      5 |   0.0806% |    182 |   2.9369%
Reqs with incoming score of  28 |     11 |   0.1775% |      2 |   0.0322%
Reqs with incoming score of  29 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  30 |     13 |   0.2097% |      7 |   0.1129%
Reqs with incoming score of  31 |      8 |   0.1290% |      1 |   0.0161%
Reqs with incoming score of  32 |     23 |   0.3711% |    125 |   2.0171%
Reqs with incoming score of  33 |      5 |   0.0806% |      0 |   0.0000%
Reqs with incoming score of  34 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  35 |      6 |   0.0968% |     12 |   0.1936%
Reqs with incoming score of  36 |      0 |   0.0000% |     21 |   0.3388%
Reqs with incoming score of  37 |      0 |   0.0000% |     27 |   0.4356%
Reqs with incoming score of  38 |      8 |   0.1290% |      0 |   0.0000%
Reqs with incoming score of  39 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  40 |      0 |   0.0000% |      2 |   0.0322%
Reqs with incoming score of  41 |      0 |   0.0000% |      7 |   0.1129%
Reqs with incoming score of  42 |      0 |   0.0000% |     14 |   0.2259%
Reqs with incoming score of  43 |      2 |   0.0322% |      0 |   0.0000%
Reqs with incoming score of  44 |      3 |   0.0484% |      1 |   0.0161%
Reqs with incoming score of  45 |      1 |   0.0161% |      3 |   0.0484%
Reqs with incoming score of  46 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  47 |      0 |   0.0000% |      5 |   0.0806%
Reqs with incoming score of  48 |     10 |   0.1613% |      0 |   0.0000%
Reqs with incoming score of  49 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  50 |      1 |   0.0161% |      0 |   0.0000%
Reqs with incoming score of  51 |      2 |   0.0322% |      0 |   0.0000%
Reqs with incoming score of  52 |      0 |   0.0000% |      1 |   0.0161%
Reqs with incoming score of  53 |     52 |   0.8391% |      0 |   0.0000%
Reqs with incoming score of  54 |      3 |   0.0484% |      0 |   0.0000%
Reqs with incoming score of  55 |      0 |   0.0000% |      1 |   0.0161%
Reqs with incoming score of  56 |     81 |   1.3070% |      0 |   0.0000%
Reqs with incoming score of  57 |      0 |   0.0000% |      1 |   0.0161%
Reqs with incoming score of  58 |      2 |   0.0322% |      0 |   0.0000%
Reqs with incoming score of  59 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  60 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  61 |      8 |   0.1290% |      0 |   0.0000%
Reqs with incoming score of  62 |      0 |   0.0000% |      1 |   0.0161%
Reqs with incoming score of  63 |      3 |   0.0484% |      0 |   0.0000%
Reqs with incoming score of  64 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  65 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  66 |      2 |   0.0322% |      0 |   0.0000%
Reqs with incoming score of  67 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  68 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  69 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  70 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  71 |      4 |   0.0645% |      0 |   0.0000%
Reqs with incoming score of  72 |      0 |   0.0000% |      1 |   0.0161%
Reqs with incoming score of  73 |      1 |   0.0161% |      0 |   0.0000%
Reqs with incoming score of  74 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  75 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  76 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  77 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  78 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  79 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  80 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  81 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  82 |      0 |   0.0000% |      1 |   0.0161%
Reqs with incoming score of  83 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  84 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  85 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  86 |      1 |   0.0161% |      0 |   0.0000%
Reqs with incoming score of  87 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  88 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  89 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  90 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  91 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  92 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  93 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  94 |      1 |   0.0161% |      0 |   0.0000%
Reqs with incoming score of  95 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  96 |      1 |   0.0161% |      0 |   0.0000%
Reqs with incoming score of  97 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  98 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of  99 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 100 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 101 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 102 |      1 |   0.0161% |      0 |   0.0000%
Reqs with incoming score of 103 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 104 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 105 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 106 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 107 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 108 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 109 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 110 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 111 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 112 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 113 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 114 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 115 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 116 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 117 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 118 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 119 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 120 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 121 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 122 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 123 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 124 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 125 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 126 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 127 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 128 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 129 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 130 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 131 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 132 |      1 |   0.0161% |      0 |   0.0000%
Reqs with incoming score of 133 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 134 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 135 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 136 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 137 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 138 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 139 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 140 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 141 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 142 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 143 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 144 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 145 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 146 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 147 |      0 |   0.0000% |      0 |   0.0000%
Reqs with incoming score of 148 |      1 |   0.0161% |      0 |   0.0000%

2.2.9 	   Avg:  15.8043  Median: 13.0000  Std. deviation:   9.5405
3.0.0-dev  Avg:  14.3466  Median: 10.0000  Std. deviation:   8.7512


OUTGOING                     Num of req. | % of req.  of req. | % of req. 
Number of outgoing req. (total) |   6197 | 100.0000% |   6197 | 100.0000% 
                                                                          
Empty or miss. outgoing score   |      0 |   0.0000% |      0 |   0.0000% 
Reqs with outgoing score of   0 |   6193 |  99.9354% |   6193 |  99.9354% 
Reqs with outgoing score of   1 |      0 |   0.0000% |      0 |   0.0000% 
Reqs with outgoing score of   2 |      0 |   0.0000% |      0 |   0.0000% 
Reqs with outgoing score of   3 |      0 |   0.0000% |      0 |   0.0000% 
Reqs with outgoing score of   4 |      4 |   0.0645% |      4 |   0.0645% 

2.2.9     Avg:   0.0026   Median:  0.0000  Std. deviation:   0.1016
3.0.0-dev Avg:   0.0026   Median:  0.0000  Std. deviation:   0.1016

So for 2.2.9, almost all nikto requests triggered at least two rules and ended up with a
score of 10 or above. That is not the case with the 3.0.0-dev ruleset. Here, almost half of
the requests stayed below 10. But mind you, we disabled the rule 981175, which would
have stopped almost all these requests. An interesting feature of the new ruleset is
the cluster at the score of 22. This is higher than a similar cluster of the v2.2.9
ruleset at 20. So in this midrange, a lot of requests score a bit higher with the
new ruleset.

The highest substantial cluster of requests with the v3.0.0-dev ruleset hit a score of
32. With the old v2.2.9 rules, we have a cluster at a score of 56. The highest scoring
request with the v3.0.0-dev ruleset came in at 82:

“GET /submit.php?subject=<script>alert(‘Vulnerable’)</script>&story=<script>alert(‘Vulnerable’)</script>&storyext=<script>alert(‘Vulnerable’)</script>&op=Preview HTTP/1.1”

This request has the nikto test ID 000786. With the v2.2.9 ruleset, the very same
request scored 148.

So in the higher range, v2.2.9 seems to lead to higher scores. When we look at the
average and the median, they are slightly higher for 2.2.9 and the results
seem to be a bit more stretched out according to the standard deviation.

Now scoring a bit lower than before is no fault in itself. It all depends on the anomaly
threshold which you set. So when migrating adjusting the anomaly setting seems
important. A threshold of 10 would have stopped over 95% of all nikto requests with
the v2.2.9 ruleset. With the new one, almost 50% of the requests stay below 10.

With the http responses, there was no difference in my tests. That is not surprising, as
there is no application to exploit and thus no interesting responses to scan.

Let’s move to the rules themselves. Which rules are actually scoring? Here is an overview.

v2.2.9 RuleID v2.2.9 Description Hits Hits v3.0.0-dev Description v3.0.0-dev RuleID
950000 Session Fixation 1 arrow-red 0 Rule not triggering anymore in v3.0.0-dev 950000
950001 SQL Injection Attack 5 arrow-red 3 SQL Injection Attack 950001
950005 Remote File Access Attempt 223 arrow-red 219 OS File Access Attempt 950005
950006 System Command Injection 6 arrow-red Rule gone in v3.0.0-dev
950011 SSI injection Attack 3 arrow-red Rule gone in v3.0.0-dev
950103 Path Traversal Attack 178 arrow-green 190 Path Traversal Attack (/../) 950103
New rule in v3.0.0-dev arrow-green 259 Path Traversal At (/../) 950104
950107 URL Encoding Abuse Attack Attempt 1 1 URL Encoding Abuse Attack Attempt 950107
950109 Multiple URL Encoding Detected 67 67 Multiple URL Encoding Detected 950109
950118 Remote File Inclusion Attack 141 141 Possible Remote File Inclusion (RFI) Attack: Common RFI
Vulnerable …
950118
950119 Remote File Inclusion Attack 2272 2272 Possible Remote File Inclusion (RFI) Attack: URL Payload Used
950119
950120 Possible Remote File Inclusion (RFI) Attack: Off-Domain … 2331 2331 Possible Remote File Inclusion (RFI) Attack: Off-Domain
Reference/Link
950120
950901 SQL Injection Attack: SQL Tautology Detected. 245 arrow-green 246 SQL Injection Attack: SQL Tautology Detected. 950901
950907 System Command Injection 1 arrow-green 196 Remote Command Execution (RCE) Attempt 950907
New rule in v3.0.0-dev arrow-green 1 HTTP Header Injection Attack via payload (CR/LF deteced) 950913
950921 Backdoor access 1 arrow-red Rule gone in v3.0.0-dev
958001 Cross-site Scripting (XSS) Attack 105 arrow-red Rule gone in v3.0.0-dev, probably integrated into 973340-973343
958031 Cross-site Scripting (XSS) Attack 2 arrow-red Rule gone in v3.0.0-dev, probably integrated into 973340-973343
958051 Cross-site Scripting (XSS) Attack 243 arrow-red Rule gone in v3.0.0-dev, probably integrated into 973340-973343
958052 Cross-site Scripting (XSS) Attack 282 arrow-red Rule gone in v3.0.0-dev, probably integrated into 973340-973343
New rule in v3.0.0-dev arrow-green 3 PHP Injection Attack: Configuration Directive Found 958979
New rule in v3.0.0-dev arrow-green 67 PHP Injection Attack: Variables Found 958980
959071 SQL Injection Attack 2 arrow-red Rule gone in v3.0.0-dev
959073 SQL Injection Attack 5 arrow-red Rule gone in v3.0.0-dev
960008 Request Missing a Host Header 1 1 Request Missing a Host Header 960008
960010 Request content type is not allowed by policy 5 arrow-red 1 Request content type is not allowed by policy 960010
960011 GET or HEAD Request with Body Content. 17 17 GET or HEAD Request with Body Content. 960011
960015 Request Missing an Accept Header 6079 6079 Request Missing an Accept Header 960015
960024 Meta-Character Anomaly Detection Alert – Repetative Non-Word
417 arrow-red Rule gone in v3.0.0-dev
960032 Method is not allowed by policy 11 arrow-red 1 Method is not allowed by policy 960032
960034 HTTP protocol version is not allowed by policy 13 13 HTTP protocol version is not allowed by policy 960034
960035 URL file extension is restricted by policy 219 219 URL file extension is restricted by policy 960035
960208 Argument value too long 1 arrow-red Misconfiguration by the author: Limit not set properly
960209 Argument name too long 1 arrow-red Misconfiguration by the author: Limit not set properly
960901 Invalid character in request 65 65 Invalid character in request 960901
960911 Invalid HTTP Request Line 17 arrow-red 10 Invalid HTTP Request Line 960911
970901 The application is not available 4 4 The Application Returned a 500-Level Status Code 970901
973300 Possible XSS Attack Detected – HTML Tag Handler 246 arrow-red Rule gone in v3.0.0-dev
973304 XSS Attack Detected 2 arrow-red Rule gone in v3.0.0-dev
973305 XSS Attack Detected 15 arrow-red Rule gone in v3.0.0-dev
973307 XSS Attack Detected 282 arrow-red Rule gone in v3.0.0-dev
973331 IE XSS Filters – Attack Detected. 243 arrow-red Rule gone in v3.0.0-dev
973334 IE XSS Filters – Attack Detected. 2 arrow-red Rule gone in v3.0.0-dev
973335 IE XSS Filters – Attack Detected. 63 arrow-red Rule gone in v3.0.0-dev
973336 XSS Filter – Category 1: Script Tag Vector 230 arrow-green 244 XSS Filter – Category 1: Script Tag Vector 973336
973338 XSS Filter – Category 3: Javascript URI Vector 3 arrow-red 2 XSS Filter – Category 4: Javascript URI Vector 973338
New rule in v3.0.0-dev arrow-green 247 NoScript XSS InjectionChecker: HTML Injection 973340
New rule in v3.0.0-dev arrow-green 15 NoScript XSS InjectionChecker: Attribute Injection 973341
New rule in v3.0.0-dev arrow-green 114 Node-Validator Blacklist Keywords 973342
New rule in v3.0.0-dev arrow-green 246 XSS Attack Detected via Libinjection 973343
973346 IE XSS Filters – Attack Detected. 15 15 IE XSS Filters – Attack Detected. 973346
981173 Restricted SQL Character Anomaly Detection Alert – Total # of
427 arrow-red Rule gone in v3.0.0-dev
981227 Apache Error: Invalid URI in Request. 19 19 Apache Error: Invalid URI in Request. 981227
981231 SQL Comment Sequence Detected. 71 arrow-red Rule gone in v3.0.0-dev
981240 Detects MySQL comments, conditions and ch(a)r injections 3 3 Detects MySQL comments, conditions and ch(a)r injections 981240
981242 Detects classic SQL injection probings 1/2 9 9 Detects classic SQL injection probings 1/2 981242
981243 Detects classic SQL injection probings 2/2 154 154 Detects classic SQL injection probings 2/2 981243
981245 Detects basic SQL authentication bypass attempts 2/3 76 76 Detects basic SQL authentication bypass attempts 2/3 981245
981246 Detects basic SQL authentication bypass attempts 3/3 29 29 Detects basic SQL authentication bypass attempts 3/3 981246
981249 Detects chained SQL injection attempts 2/2 8 8 Detects chained SQL injection attempts 2/2 981249
981257 Detects MySQL comment-/space-obfuscated injections and backtick
6 6 Detects MySQL comment-/space-obfuscated injections and backtick
981257
981260 SQL Hex Encoding Identified 3 arrow-red Rule gone in v3.0.0-dev
New rule in v3.0.0-dev arrow-green 32 SQL Injection Attack Detected via LibInjection 981261
981276 Looking for basic sql injection. Common attack string for mysql
3 3 Looking for basic sql injection. Common attack string for mysql
981276
981317 SQL SELECT Statement Anomaly Detection Alert 3 arrow-red Rule gone in v3.0.0-dev
981318 SQL Injection Attack: Common Injection Testing Detected 161 arrow-red 125 SQL Injection Attack: Common Injection Testing Detected 981318
981319 SQL Injection Attack: SQL Operator Detected 1 1 SQL Injection Attack: SQL Operator Detected 981319
990002 Request Indicates a Security Scanner Scanned the Site 6079 6079 Request Indicates a Security Scanner Scanned the Site 990002
990012 Rogue web site crawler 6079 arrow-red Rule gone in v3.0.0-dev
990902 Request Indicates a Security Scanner Scanned the Site 0 arrow-green 2336 Request Indicates a Security Scanner Scanned the Site 990902
TOTAL 33275 arrow-red 28352 TOTAL

We see less hits for about half of the rules. They appear weaker, or they are gone
from the ruleset. About a third of the rules come in with exactly the same number
of rules and a bit more than a sixth of the rules bring more hits or they are new
rules.

I did not look into all the rules in detail. So it is likely, rules shifted
their ids, or they were consolidated. The github changelog might contain
this information.

For this blog post, I will only look at the most striking changes:
Rule 950104 (Path Traversal Attack) : New rule in v3.0.0-dev
This is a new and very simple rule looking at the URI patterns “..\” and “../”
It’s a sibling of 950103, but a lot easier to read.
The numbers are impressive: 359 new hits.

Rule 950907 (Remote Command Execution (RCE) Attempt) : Bigger teeth in v3.0.0-dev
This rule has been rewritten and enriched with a big number of system commands
out of a file named os-commands.data. The success is striking:
196 hits vs. 1 in the simple variant in the v2.2.9 ruleset.

Rule 950913 (HTTP Header Injection Attack via payload) : New rule in v3.0.0-dev
This new rule with a single hit is not newsworthy at all. But then I happened
to propose it for inclusion via a pull request.
To see this exotic regex
trigger an alert with a well-known attack scanner pleases me.

Rules 958001, 958031, 958051, 958052 (Cross-site Scripting (XSS) Attack) : Gone from v3.0.0-dev
These rules are all gone from the new version. There are new rules compensating
for the loss partly, but the new rules do not make up for the over 600 alerts
that this group of rules triggered.

Rule 958980 (PHP Injection Attack: Variables Found) : New rule in v3.0.0-dev
That’s a new rule based on items in the data file php-variables.data.
Nice one. Rule 958979 does the same with php-config-directives.data.

Rule 960024 (Meta-Character Anomaly Detection Alert – Repetative Non-Word … ) : Gone from v3.0.0-dev
This rule disappeared from the ruleset. It is likely, this simple ruleset
triggered a lot of false positives: \W{4,}
It is similar to the case of the rules 981172 and 981173 whose disappearance I
described in a recent blogpost.
960024 is the same type of shepherd dog that barks quickly and often
(417 times, mind you!) and hands out 3 anomaly scoring points.
I think it should be brought back.

Rules 973300, 973304, 973305, 973307, 973331, 973334, 973335 (Various XSS Rules) : Gone from v3.0.0-dev ruleset
Like the Anti-XSS rule described above, these are gone for good despite summing 800 alerts.
There are new Anti-XSS rules described below, but I do not think they make up for the
loss.

Rules 973340, 973341, 973342 (Various Anti-XSS rules) : New rules in v3.0.0-dev
This is a group of new rules aimed to prevent XSS. Especially 973340 brings
a very big Regex with obvious success and 247 hits.
This is nice but it does not cover the loss of the Anti-XSS rules mentioned above.

Rule 973343 (XSS Attack Detected via Libinjection) : New rule in v3.0.0-dev
So this is the rule with the new @detectXSS operator based on libinjection
from client9: https://libinjection.client9.com/, https://github.com/client9/libinjection.
This neat library brought 246 hits, so its inclusion is welcome. However,
there are issues. It has been a topic before on the ModSecurity mailinglist,
but I mention them here again: LibInjection seems to be a fine piece of code.
But the website comes with a broken SSL Certificate and a server error, the
Changelog on github is severely outdated and the inclusion of XSS detection
into the library is mostly undocumented as is the functioning of @detectXSS and
@detectSQLi in ModSecurity. 99% of the commits to libinjection were done by
the main developer.
If you want to know how this works, you will find little information beyond
slides presented at OWASP meetings. What I would like to see is a technical
description of how this parser works. If I would be happy with impressive
slides, I would go and buy a commerical product.
I have no idea of the code quality, but from what I can tell about the project
looking at the surface, libinjection does not look trustworthy.

Rules 981172, 981173 (Restricted SQL Character Anomaly Detection Alert) : Gone from v3.0.0-dev
981172 did not trigger any alarms, but its sibling 981173 did issue 427 alerts.
Like 96024, these are workhorses likely to trigger a lot of false positives. And
this is why they went away. I am working on a pull request to bring them back,
probably via an optional setting.
See this blogpost for a more detailed discussion.

Rule 981231 (SQL Comment Sequence Detected) : Gone from 3.0.0-dev
This rule was removed from the dev-tree of the Core Rules. It was aimed at
SQL comments. Maybe this was not deemed important enough, or a cause of
too many false positives. I can not tell. But 71 hits in my tests
may be enough to reconsider this step.

Rule 981261 (SQL Injection Attack Detected via LibInjection) : New rule in v3.0.0-dev
This is the rule with the new @detectSQLi operator based on libinjection.
Given the number of other SQLi rules triggered I actually expected more hits
here. But then all I know about libinjection are the impressive slides.
Given my tests, there was not the same haircut with Anti-SQLi rules like with
Anti-XSS. But @detectSQLi still does not compensate the ones that are gone.

Rule 981318 (SQL Injection Attack: Common Injection Testing Detected) : Rule with shorter teeth
This rule is no longer applied to cookies and it does not cover the same range of
characters anymore. See:
Targets old: SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|!REQUEST_COOKIES:/_pk_ref/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/*
Targets new: SecRule ARGS_NAMES|ARGS|XML:/*
Regex old: “(^[\”‘`´’‘;]+|[\”‘`´’‘;]+$)”
Regex new: “(^[\”‘`;]+|[\”‘`]+$)”
We lost about a quarter of the hits with this simplification.

Rule 990012 (Request Indicates a Security Scanner Scanned the Site) : Gone from v3.0.0-dev
This rule is gone from the ruleset. The loss of 6000 hits based on the
data file modsecurity_35_bad_robots.data is partially compensated in the
990902 rule, which received an extended pair of teeth. But we lost
more than 3000 alerts.
The reason for the removal could be, that this rule is redundant to 990002,
which was based on modsecurity_35_scanners.data. But in fact, the two
data files are complementary and both rules target the User-Agent.
The data file scanners-user-agents.data now used in rule 990002 received
some of the user agents in modsecurity_35_bad_robots.data, but far from all.
So I really do not know.

Rule 990902 (Request Indicates a Security Scanner Scanned the Site) : Rule with bigger teeth in v3.0.0-dev
990902 used to test only for 2-3 regexes in the former edition. Now the dataset was expanded.
Obviously to cover nikto as well. The feat is performed via the query string parameter
http://cirt.net/rfiinc.txt sent by nikto in thousands of cases.
The 2336 hits look impressive here and if a script kiddy attacker really makes
an approach using this tool, then the bells will go off. But all these
anti-scanner rules only work against the obvious scanning attempts, so we
should not trust them too much. The expansion of 990902 sure is a good thing.

So this is my overview over the development of the OWASP ModSecurity Core Rules 3.0.0.
There are interesting new features, but also important rules which disappeared. I
hope some of them can be brought back before the 3.0.0 ruleset is released to the public.

If you have questions or feedback, then please get in touch via mail or twitter.

Christian Folini, netnea, @ChrFolini