ModSecurity SQL Injection Challenge: Lessons Learned

This is a post-mortem blog post to discuss the successful Level II evasions found by participants during the recent ModSecurity SQL Injection Challenge.

First of all, I would like to thank all those people that participated in the challenge. All told, we had > 650 participants (based on unique IP addresses) which is a tremendous turn out. This type of community testing has helped to both validate the strengths and expose the weaknesses of the SQL Injection protections of the OWASP ModSecurity Core Rule Set Project. The end result of this challenge is that the SQL Injection rules within the CRS have been massively updated and are now available for immediate download as part of the v2.2.1 release.

Level II Bypasses Analysis: In-Depth

Congratulations goes to the following individuals/teams that achieve Level II status by extracting DB information while evading the inbound CRS SQL Injection rules.

The focus of this blog post is to provide an in-depth discussion of the Level II bypasses that were identifies during the SQLi Challenge.

Let's take a look at each bypass in-depth so that we see how the bypass was achieved and also highlight the changes made to the CRS to make them stronger.

Bypass #1 by Johannes Dahse

Target Application: Acunetix Acuart Site

Example Bypass Request

  • user: acuart@localhost*%2F*bar%0D%0Aselect%23foo%0D%0A1%2C2%2Ccurrent_user

Bypass Analysis

Johannes used a combination of MySQL comments and new line characters to both have a working SQL Injection payload and to evade the current CRS SQLi filters. First of all, let's review the MySQL Reference Guide information about Comments:

8.6. Comment Syntax

MySQL Server supports three comment styles:

  • From a "#" character to the end of the line.

  • From a "-- " sequence to the end of the line. In MySQL, the "-- " (double-dash) comment style requires the second dash to be followed by at least one whitespace or control character (such as a space, tab, newline, and so on). This syntax differs slightly from standard SQL comment syntax, as discussed in Section, "'--' as the Start of a Comment".

  • From a /* sequence to the following */ sequence, as in the C programming language. This syntax enables a comment to extend over multiple lines because the beginning and closing sequences need not be on the same line.

The following example demonstrates all three comment styles:

mysql> SELECT 1+1;     # This comment continues to the end of line
mysql> SELECT 1+1;     -- This comment continues to the end of line
mysql> SELECT 1 /* this is an in-line comment */ + 1;
mysql> SELECT 1+/*this is amultiple-line comment*/1;

The highlighted section is the feature that Johannes used (with %0D%0A as the new line characters). Let's take a look at the first request he used (to extract the DB user). The resulting SQL payload looked something like this:

0 div 1 union#foo*/*bar

However the SQL payload, when executed by the MySQL DB, looked something like this:

0 div 1 union select 1,2,current_user

The ModSecurity CRS has a number of rules that detect SQL injection attacks that utilize the UNION and SELECT keywords. Here is an example rule:

SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "\bunion\b.{1,100}?\bselect\b" \
	"phase:2,rev:'2.2.1',capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,ctl:auditLogParts=+E,block,msg:'SQL Injection Attack',id:'959047',tag:'WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{}-WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.0}"

This ruleset uses a regular expression to identify UNION and SELECT SQL keywords found within 100 characters of each other. The regular expression itself is ok, however the issue related to this bypass has to do with the specific transformation functions used by ModSecurity for this particular rule. The transformation function actions in the rules start with "t:..." and by default they work as a pipeline of normalization functions and the operator in the rule is only executed once at the end after all transformations are completed.

In the rule shown above, the highlighted transformation function is the most relevant - t:replaceComments. The purpose of this transformation function is to combat SQL comment insertion directly into SQL keywords like this - SEL/**/ECT so that it can evade string/regex matches for the keyword while still executing properly by the back-end DB.

The problem with the current replaceComments transformation function operation is how it handles unterminated comments. This is information taken from the Reference Manual for t:replaceComments:

Unterminated comments will also be replaced with a space (ASCII 0x20). However, a standalone termination of a comment (*/) will not be acted upon.

Johannes' payload included an opening/unterminated SQL comment in it which meant that this transformation function actually stripped out the data after the /* string... Here is how it looked in the ModSecurity debug log:

T (1) urlDecodeUni: "0 div 1 union#foo*/*bar\r\nselect#foo\r\n1,2,current_user"
T (1) htmlEntityDecode: "0 div 1 union#foo*/*bar\r\nselect#foo\r\n1,2,current_user"
T (1) lowercase: "0 div 1 union#foo*/*bar\r\nselect#foo\r\n1,2,current_user"
T (1) replaceComments: "0 div 1 union#foo* "
T (0) compressWhitespace: "0 div 1 union#foo* "
Transformation completed in 796 usec.
Executing operator "rx" with param "\\bunion\\b.{1,100}?\\bselect\\b" against ARGS:artist.
Target value: "0 div 1 union#foo* "

As you can see, once the replaceComments transformation function is executed, it removes the end portion of the SQLi payload and therefore when the operator is run at the end of the transformation function chain, it does not match.

Bypass #1 - Lessons Learned

The core problem with this first bypass is that of Impedance Mismatches between how the WAF and back-end DB will handle SQL Comments. The problem is that attacker's may insert SQL Comments almost anywhere within the payloads to evade security filters as it is next to impossible to create regular expressions that can account for this technique.

We chose two different methods of handling SQL Comments in our updated SQL Injection rules.

Detect SQL Comments Themselves

Rather than only trying to remove SQL Comments in order to try and identify the underlying SQL query data, we are now detecting the presence of the SQL Comments themselves. Here is an example rule:

# -=[ Detect SQL Comment Sequences ]=-
"(\/\*\!?|\*\/|\-\-[\s\r\n\v\f]|(?:--[^-]*-)|([^\-&])#.*[\s\r\n\v\f]|;?\\x00)" \
"phase:2,rev:'2.2.2',id:'981231',t:none,t:urlDecodeUni,block,msg:'SQL Comment Sequence Detected.',capture,logdata:'%{tx.0}',tag:'WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',setvar:tx.anomaly_score=+%{tx.warning_anomaly_score},setvar:tx.sql_injection_score=+1,setvar:'tx.msg=%{rule.msg}',setvar:tx.%{}-WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.0}"

Use the multiMatch Action

For rules that are looking for SQL keywords only, you can still use the t:replaceComments transformation function, however you should also use the multiMatch action as well which will execute the operator against the payload after each transformation function that changes data rather than only once at the end of the transformation function chain. Here is an example rule using this action:

	"phase:2,rev:'2.2.2',capture,multiMatch,t:none,t:urlDecodeUni,t:replaceComments,ctl:auditLogParts=+E,block,msg:'Blind SQL Injection Attack',id:'959918',tag:'WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{}-WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.

Bypass #2 by Vladimir Vorontsov

Target Application: Cenzic Crack Me Bank

Example Bypass Request

POST /Kelev/php/accttransaction.php HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:5.0.1) Gecko/20100101 Firefox/5.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Cookie: PHPSESSID=b90ee75723c20bbe4fab8a82f354412c
Content-Type: application/x-www-form-urlencoded
Content-Length: 84


Bypass Analyis

Vladimir used SQL Injection Fragmentation. He was able to split up the SQLi payload so that each individual payload would not trigger any filters however, when placed into the back-end SQL query, they would force boolean logic of true/false. If the query was true, the normal page would appear. If the query was false, however, then an SQL Error message would be returned in the html response body.

The main advantage to using fragmentation is for filter evasions, especially when certain meta-characters such as quotes are filtered by the application. In this case, the SQLi can take advantage of the quotes that are already present within the back-end query.

Vladimir was able to use brute force techniques to enumerate a number of column names in the DB.

Bypass #2 - Lessons Learned

We updated the CRS SQLi rules to add in more generic checks for common methodologies used during attacks.

String Termination/Statement Ending Injection Testing

Attackers will often insert string termination characters into the beginning and/or end of a payload in order to identify any potential SQL error messages leaking through error pages. This techique was also used by Vladimir to end the back-end statement.

To detect these types of injections, we added the following rule to the CRS:

# -=[ String Termination/Statement Ending Injection Testing ]=-
# Identifies common initial SQLi probing requests where attackers insert/append
# quote characters to the existing normal payload to see how the app/db responds.
"(^[\"'`´'';]+|[\"'`´'';]+$)" \
"phase:2,rev:'2.2.2',capture,t:none,t:urlDecodeUni,block,msg:'SQL Injection Attack: Common Injection Testing Detected',id:'981211',logdata:'%{TX.0}',severity:'2',tag:'WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{}-WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.0}"

Detecting SQL Operators

Vladimir's payload also included the "<>" MySQL operator value in it. SQL Tautology utilize these operators to construct their boolean logic.

To detect these types of injections, we added the following rule to the CRS:

# -=[ SQL Operators ]=-
"(?i:(\!\=|\&\&|\|\||>>|<<|>=|<=|<>|<=>|xor|rlike|regexp|isnull)|(?:not\s+between\s+0\s+and)|(?:is\s+null)|(like\s+null)|(?:(?:^|\W)in[+\s]*\([\s\d\"]+[^()]*\))|(?:xor|<>|rlike(?:\s+binary)?)|(?:regexp\s+binary))" \
"phase:2,rev:'2.2.2',capture,t:none,t:urlDecodeUni,block,msg:'SQL Injection Attack: SQL Operator Detected',id:'981212',logdata:'%{TX.0}',severity:'2',tag:'WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.notice_anomaly_score},setvar:tx.anomaly_score=+%{tx.notice_anomaly_score},setvar:tx.%{}-WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.0}"

Bypass #3 by PT Research

Target Application: IBM site

Example Bypass Request

POST /bank/transaction.aspx HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:5.0.1) Gecko/20100101 Firefox/5.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Cookie: ASP.NET_SessionId=c0tx0o455d0b10ylsdr03m55; amSessionId=14408158863; amUserInfo=UserName=YWRtaW4=&Password=JyBvciAnMSc9JzEnOy0t; amUserId=1
Content-Type: application/x-www-form-urlencoded
Content-Length: 53

__VIEWSTATE=%2FwEPDwUKMTYzNDg3OTA4NmRk&after=1 AND (select DCount(last(username)&after=1&after=1) from users where username='ad1min')&before=d

Bypass Analyis

The attack technique used by the PT Research Team was HTTP Parameter Pollution (HPP), which allows an attacker to leverage how an ASP/ASP.Net-based applications treats multiple parameters with the same name - which is to concatenate the payloads into one and separating them with commas.

HPP is a very real attack technique and most security devices do not properly handle it. Trustwave recently released an Advisory for HPP flaws found in a commercial WAF.

Bypass #3 - Lessons Learned

If you are protecting ASP-based web applications, you better have a method of handling HPP attacks. Your security filtering better mimic how the application handles HPP data. The OWASP ModSecurity CRS does have an HPP rules file that will concatenate the payloads of parameters with the same name and then set TX variables. These variables are then inspected along with the other standard ModSecurity variables.

So how did the the attack still work? By an error of omission... We had simply not activated the HPP protection file. This is a practical lessons learned for organizations, you should conduct proper threat modeling for your applications in order to identify any methods of attack that might only impact specific technologies. In this case, we should have evaluated the IBM site and identified that it was a Microsoft-based web application/back-end and thus realized that HPP attacks were possible. Once we activated the HPP rules, these payloads were caught.

Bypass #4 by Ahmad Maulana

Target Application: Cenzic Crack Me Bank

Example Bypass Request

POST /Kelev/php/accttransaction.php HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:5.0.1) Gecko/20100101 Firefox/5.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Cookie: PHPSESSID=bf65e6b31d8446e674acbe332cd3c75f;
Content-Type: application/x-www-form-urlencoded
Content-Length: 57

hUserId=22768&FromDate=1&ToDate=1'UNION/*!0SELECT user,2,3,4,5,6,7,8,9/*!0from/*!0mysql.user/*-&sendbutton1=Get+Statement

Bypass Analyis

Ahmad's attack leveraged 2 attack techniques: 1) Unterminated Comments and, 2) MySQL Comment Extensions for conditional code execution.

Unterminated Comments

As mention prevoiusly, the t:replaceComments transformation function (with use of multiMatch) cased a severe false negative in the rules by hiding data from the final operator check.

MySQL Comment Extensions

The MySQL Comment documentation states to following about Comment Extensions:

MySQL Server supports some variants of C-style comments. These enable you to write code that includes MySQL extensions, but is still portable, by using comments of the following form:

/*! MySQL-specific code */

In this case, MySQL Server parses and executes the code within the comment as it would any other SQL statement, but other SQL servers will ignore the extensions. For example, MySQL Server recognizes theSTRAIGHT_JOIN keyword in the following statement, but other servers will not:

SELECT /*! STRAIGHT_JOIN */ col1 FROM table1,table2 WHERE ...

If you add a version number after the "!" character, the syntax within the comment is executed only if the MySQL version is greater than or equal to the specified version number. The TEMPORARY keyword in the following comment is executed only by servers from MySQL 3.23.02 or higher:


The comment syntax just described applies to how the mysqld server parses SQL statements.

Bypass #4 - Lessons Learned

The changes made to detect these payloads are the same as outlined in Bypass #1.

Bypass #5 by Travis Lee

Target Application: IBM site

Example Bypass Request

GET /bank/transaction.aspx HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:5.0.1) Gecko/20100101 Firefox/5.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Cookie: ASP.NET_SessionId=c0tx0o455d0b10ylsdr03m55; amSessionId=14408158863; amUserInfo=UserName=YWRtaW4=&
Password=JyBvciAnMSc9JzEnLS0=; amUserId=1 union select username,password,3,4 from users
DNT: 1
Connection: keep-alive

Bypass Analyis

In this case, the SQLi payload itself didn't use any advanced obfuscation techniques like the other examples we have highlighted thus far. In this case, the evasion was possible due to the attack vector location - Request Cookie data.

Bypass #5 - Lessons Learned

It's not always what to look for but where to look for it...

This was another case of an error of omission with regards to the ModSecurity rules configuration. Specifically, each rule must specify which specific parts of a web transaction that we want to inspect. Different locations have higher degrees of true positives, false positives and false negatives. In general, request arguments are the #1 attack vector location. Every evasion we have outlined in this blog post pass the malicious data within parameter payloads. While ARGS data probably covers about 95% of the attack vector locations, this doesn't mean that it is the only place where attack may occur. In this case, we should have been inspecting the "amUserId" cookie payload value.

In CRS v2.2.1, we are now also inspecting Cookie Names and Values. Here is an example updated rule:

# -=[ Detect DB Names ]=-
"(?i:(?:m(?:s(?:ysaccessobjects|msysaces|msysobjects|msysqueries|msysrelationships|msysaccessstorage|msysaccessxml|msysmodules|msysmodules2|db)|aster\.\.sysdatabases|ysql\.db)|s(?:ys(?:\.database_name|aux)|chema(?:\W*\(|_name)|qlite(_temp)?_master)|d(?:atabas|b_nam)e\W*\(|information_schema|pg_(catalog|toast)|northwind|tempdb))" \
                "phase:2,rev:'2.2.2',capture,t:none,t:urlDecodeUni,ctl:auditLogParts=+E,block,msg:'SQL Injection Attack: Common DB Names Detected',id:'981210',logdata:'%{TX.0}',severity:'2',tag:'WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{}-WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.0}"

Bypass #6 by Roberto Salgado

Target Application: Acunetix Acuart Site

Example Bypass Request

Bypass Analyis

Roberto's bypass is somewhat similar to Johannes' in that is using the MySQL Comment syntax trick of placing a hash char (#) followed by random text and then a new line char (%0A). Roberto would have achieved Level II status sooner, however after Johannes' submission, we added the multiMatch action which ended up catching Roberto's evasion method.

Even with this technique, the following ModSecurity rule was still catching the "table_name" function at the end.

"(?i:\btable_name\b)" \
	"phase:2,rev:'2.2.2',capture,multiMatch,t:none,t:urlDecodeUni,t:replaceComments,ctl:auditLogParts=+E,block,msg:'Blind SQL Injection Attack',id:'959914',tag:'WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{}-WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.0}"

If you look at the regular expression used, however, you can see that we were using non-word boundaries (\b) on both ends of the text in order to help reduce false positives.

In this particular attack scenario, when the keyword is placed inside of the SQL Comment to execute a conditional command, it bypassed the regular expression.

Bypass #6 - Lessons Learned

In order to catch Roberto's evasion methods, we had to do update our SQL Comment detections to detect the "/*!" syntax.

Bypass #7 by Sqlmap Developers

Target Application: Acunetix Acuart Site

Example Bypass Request,2,database%23sqlmap%0A%28%29

Bypass Analyis

The Sqlmap Developers evaded the CRS signatures by using the MySQL Comment + New Line trick which seperated out the keywords with random text. The Sqlmap team has even added a number of variations of this evasion method to the "tamper scripts" which can be used when running assessments.

Bypass #7 - Lessons Learned

As good as blacklist regular expression may be, they will eventually fail. It is important to keep in mind that there are limits that will be reached when attempting to use a lower level language (REGEX) to describe a higher level language (SQL). There is just no way to be able to capture all of the possible logic in SQL queries using regular expressions.

With that in mind, we opted to use a more generic approach to detecting some of these advanced evasion techniques. Rather than trying to properly mimic SQL queries, we instead added the following rule which simply looks for excessive use of meta-characters within the payloads:

"([\~\!\@\#\$\%\^\&\*\(\)\-\+\=\{\}\[\]\|\:\;\"\'\´\'\'\`\<\>].*){4,}" \
"phase:2,t:none,t:urlDecodeUni,block,id:'981173',rev:'2.2.1',msg:'Restricted SQL Character Anomaly Detection Alert - Total # of special characters exceeded',capture,logdata:'%{tx.1}',setvar:tx.anomaly_score=+%{tx.warning_anomaly_score},setvar:tx.sql_injection_score=+1,setvar:'tx.msg=%{rule.msg}',setvar:tx.%{}-WEB_ATTACK/RESTRICTED_SQLI_CHARS-%{matched_var_name}=%{tx.0}"

Bypass #8 by HackPlayers

Target Application: Acunetix Acuart Site

Example Bypass Request*%2100000table_name*%2F%2C3%20from%20information_schema.tables%20limit%201

Bypass Analyis

HackPlayers used the same MySQL Comment + random text + New Line evasion technique described previously.

Bypass #8 - Lessons Learned

Updated rules now catch this bypass technique.

Bypass #9 by Georgi Geshev

Target Application: Acunetix Acuart Site

Example Bypass Request

Bypass Analyis

Georgi used the vertical tab character (%0B) instead of a space to separate keywords. This is valid SQL syntax and there are a number of different allowed intermediary characters - see Roberto Salgado's SQL Injection Pocket Reference for more information.

Bypass #9 - Lessons Learned

Blacklist filters must be able to account for various allowed intermediary characters as outline in Roberto Salgado's excellent SQL Injection Pocket Reference.

Allowed Intermediary Characters:

  • 09
  • 0A
  • 0B
  • 0C
  • 0D
  • A0

Many of the CRS rules were updated to better handle various space-related characters. The payload is currently caught by the following rule:

"(?i:(?:,.*[)\da-f(\"|'|`|´|'|')](\"|'|`|´|'|')(?:(\"|'|`|´|'|').*(\"|'|`|´|'|')|\Z|[^(\"|'|`|´|'|')]+))|(?:\Wselect.+\W*from)|((?:select|create|rename|truncate|load|alter|delete|update|insert|desc)\s*\(\s*space\s*\())" \
"phase:2,capture,multiMatch,t:none,t:urlDecodeUni,t:replaceComments,block,msg:'Detects MySQL comment-/space-obfuscated injections and backtick termination',id:'981257',tag:'WEB_ATTACK/SQLI',tag:'WEB_ATTACK/ID',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{}-%{rule.msg}',setvar:tx.anomaly_score=+5,setvar:'tx.%{tx.msg}-WEB_ATTACK/SQLI-%{matched_var_name}=%{tx.0}',setvar:'tx.%{tx.msg}-WEB_ATTACK/ID-%{matched_var_name}=%{tx.0}'

Final Analysis

Better... but not perfect

Thanks to the people who participated in the SQLi Challenge, the OWASP ModSecurity Core Rule Set's SQL Injection protections have been significantly improved. With this being said, we have to realize that relying upon negative security filters, no matter how good we think they are, will only serve as a roadblock for determined attackers. Blacklist filters should be used as one layer of defense but not as the only one.

Blacklist filtering is not enough

As evidenced by the successful evasions, using blacklist filtering is not adequate to fully prevent SQL Injection attacks. You must have a multi-pronged approach to address SQL Injection attacks/vulnerabilities. Trustwave SpiderLabs' 360 Application Security Program can help organizations fix all facets of SQL Injection flaws.

One of the Sqlmap Developers, Miroslav Stampar has an outstanding blog post entitled "Rules of (a) thumb for preventing sql injection(s)", where he highlights many different aspects of fixing SQL Injection flaws. He states the following about Blacklist filters:

3) Don't rely on "blacklisting" methods

  • "Blacklists" (e.g. list with common SQL keywords SELECT, UNION, INSERT,...) are easy to circumvent (e.g. with usage of inline comments like SEL/**/ECT) and will trigger false positives in the long run (to be honest, you can do it in the low traffic site, and putting keywords like: SELECT, FROM and WHERE will keep 95% of attackers away, but those 5% will eventually find a way)
  • Experts at WAF/IDS/IPS do this already far better than you'll ever do

Input validation mechanisms should also utilize positive security models which specify the type, format and length of all input values. In addition to input validation, commercial WAFs have transactional learning capabilities to identify anomalies in both input and output. In future blog posts, we will outline some of our research we are conducting in this area.

Hacking Resistance (Time-to-Hack)

Many people wrongly assume that installing a Web Application Firewall will make their sites "Hack Proof." Sadly, this is not reality. The real goal of using a web application firewall should be to gain visibility and to make your web applications more difficult to hack meaning that it should take attackers significantly more time to hack a vulnerable web site with a WAF in front in blocking mode vs. if the WAF was not present at all.

The idea is to substantially increase the "Time-to-Hack" metric associated with compromising a site in order allow for operational security to identify the threat and take appropriate actions.

Think of a WAF as a tool to identify and block the initial probes and to alert incident response personnel. It is up to the IR teams to match wits with an attacker and protect the application as necessary.

With this in mind, we analyzed how long it took for each Level II winner to develop a working evasion for the CRS v2.2.0. We are basing this off of the correlated IP address in the logs that was tied to the final evasion payloads submitted to the ModSecurity team. We also saw that many Level II winners actually tested their payloads using the CRS Demo page so we had to correlate test payloads there as well.

  • Avg. # of Requests to find an evasion: 433
  • Avg. Duration (Time to find an evasion): 72 hrs
  • Shortest # of Requests to find an evasion: 118
  • Shortest Duration (Time to find an evasion): 10 hrs

This data shows that having active monitoring and response capabilities of ongoing web attacks is paramount as it may only a matter of hours before a determined attacker finds a way through your defenses.

I realize that there are a multitude of variables and conditions involved where people can say that these numbers are off (either too high or too low) depending on your defenses and attacker skill level. Keep in mind that this metric was obtained from the ModSecurity WAF using mainly a negative security model ruleset. The point of presenting this data, however, is to have some form of metric available for active web application monitoring and defense discussions related to exploitation timelines.

Trustwave reserves the right to review all comments in the discussion below. Please note that for security and other reasons, we may not approve comments containing links.