# ------------------------------------------------------------------------ # OWASP ModSecurity Core Rule Set ver.3.1.1 # Copyright (c) 2006-2019 Trustwave and contributors. All rights reserved. # # The OWASP ModSecurity Core Rule Set is distributed under # Apache Software License (ASL) version 2 # Please see the enclosed LICENSE file for full details. # ------------------------------------------------------------------------ # # Some protocol violations are common in application layer attacks. # Validating HTTP requests eliminates a large number of application layer attacks. # # The purpose of this rules file is to enforce HTTP RFC requirements that state how # the client is supposed to interact with the server. # https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html # # -= Paranoia Level 0 (empty) =- (apply unconditionally) # SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:920011,phase:1,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT" SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:920012,phase:2,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT" # # -= Paranoia Level 1 (default) =- (apply only when tx.executing_paranoia_level is sufficiently high: 1 or higher) # # # Validate request line against the format specified in the HTTP RFC # # -=[ Rule Logic ]=- # # Uses rule negation against the regex for positive security. The regex specifies the proper # construction of URI request lines such as: # # "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]] # # It also outlines proper construction for CONNECT, OPTIONS and GET requests. # # -=[ References ]=- # https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.2.1 # http://capec.mitre.org/data/definitions/272.html # SecRule REQUEST_LINE "!@rx ^(?i:(?:[a-z]{3,10}\s+(?:\w{3,7}?://[\w\-\./]*(?::\d+)?)?/[^?#]*(?:\?[^#\s]*)?(?:#[\S]*)?|connect (?:\d{1,3}\.){3}\d{1,3}\.?(?::\d+)?|options \*)\s+[\w\./]+|get /[^?#]*(?:\?[^#\s]*)?(?:#[\S]*)?)$" \ "id:920100,\ phase:2,\ block,\ t:none,\ msg:'Invalid HTTP Request Line',\ logdata:'%{request_line}',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'OWASP_CRS/PROTOCOL_VIOLATION/INVALID_REQ',\ tag:'CAPEC-272',\ ver:'OWASP_CRS/3.1.1',\ severity:'WARNING',\ setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl1=+%{tx.notice_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/INVALID_REQ-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" # # Identify multipart/form-data name evasion attempts # # There are possible impedance mismatches between how # ModSecurity interprets multipart file names and how # a destination app server such as PHP might parse the # Content-Disposition data: # # filename-parm := "filename" "=" value # # -=[ Rule Logic ]=- # These rules check for the existence of the ' " ; = meta-characters in # either the file or file name variables. # HTML entities may lead to false positives, why they are allowed on PL1. # Negative look behind assertions allow frequently used entities &_; # # -=[ Targets, characters and html entities ]=- # # 920120: PL1 : FILES_NAMES, FILES # ['\";=] but allowed: # &[aAoOuUyY]uml); &[aAeEiIoOuU]circ; &[eEiIoOuUyY]acute; # &[aAeEiIoOuU]grave; &[cC]cedil; &[aAnNoO]tilde; & ' # # 920121: PL2 : FILES_NAMES, FILES # ['\";=] : ' " ; = meta-characters # # -=[ References ]=- # https://www.owasp.org/index.php/ModSecurity_CRS_RuleID-960000 # http://www.ietf.org/rfc/rfc2183.txt # SecRule FILES_NAMES|FILES "@rx (? /range/) is not matching the restricted header # /content-range/ for example. # # This is a chained rule, where the first rule fills a set of variables of the # form TX.header_name_. The second rule is then executed for all # variables of the form TX.header_name_. # # As a consequence of the construction of the rule, the alert message and the # alert data will not display the original header name Content-Range, but # /content-range/ instead. # # # -=[ References ]=- # https://access.redhat.com/security/vulnerabilities/httpoxy (Header Proxy) # SecRule REQUEST_HEADERS_NAMES "@rx ^.*$" \ "id:920450,\ phase:2,\ block,\ capture,\ t:none,t:lowercase,\ msg:'HTTP header is restricted by policy (%{MATCHED_VAR})',\ logdata:' Restricted header detected: %{MATCHED_VAR}',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'OWASP_CRS/POLICY/HEADER_RESTRICTED',\ tag:'WASCTC/WASC-21',\ tag:'OWASP_TOP_10/A7',\ tag:'PCI/12.1',\ tag:'WASCTC/WASC-15',\ tag:'OWASP_TOP_10/A7',\ tag:'PCI/12.1',\ ver:'OWASP_CRS/3.1.1',\ severity:'CRITICAL',\ setvar:'tx.header_name_%{tx.0}=/%{tx.0}/',\ chain" SecRule TX:/^HEADER_NAME_/ "@within %{tx.restricted_headers}" \ "setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/POLICY/HEADERS_RESTRICTED-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:920013,phase:1,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT" SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:920014,phase:2,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT" # # -= Paranoia Level 2 =- (apply only when tx.executing_paranoia_level is sufficiently high: 2 or higher) # # # -=[ Rule Logic ]=- # # Check the number of range fields in the Range request header. # # An excessive number of Range request headers can be used to DoS a server. # The original CVE proposed an arbitrary upper limit of 5 range fields. # # Several clients are known to request PDF fields with up to 62 range # fields. Therefore the standard rule does not cover PDF files. This is # performed in two separate (stricter) siblings of this rule. # # 920200: PL2: Limit of 5 range header fields for all filenames outside of PDFs # 920201: PL2: Limit of 62 range header fields for PDFs # 920202: PL4: Limit of 5 range header fields for PDFs # # -=[ References ]=- # https://httpd.apache.org/security/CVE-2011-3192.txt SecRule REQUEST_HEADERS:Range|REQUEST_HEADERS:Request-Range "@rx ^bytes=(?:(?:\d+)?\-(?:\d+)?\s*,?\s*){6}" \ "id:920200,\ phase:2,\ block,\ t:none,\ msg:'Range: Too many fields (6 or more)',\ logdata:'%{MATCHED_VAR}',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'OWASP_CRS/PROTOCOL_VIOLATION/INVALID_HREQ',\ tag:'paranoia-level/2',\ ver:'OWASP_CRS/3.1.1',\ severity:'WARNING',\ chain" SecRule REQUEST_BASENAME "!@endsWith .pdf" \ "setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl2=+%{tx.warning_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/INVALID_HREQ-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" # # This is a sibling of rule 920200 # SecRule REQUEST_BASENAME "@endsWith .pdf" \ "id:920201,\ phase:2,\ block,\ t:none,\ msg:'Range: Too many fields for pdf request (63 or more)',\ logdata:'%{MATCHED_VAR}',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'OWASP_CRS/PROTOCOL_VIOLATION/INVALID_HREQ',\ tag:'paranoia-level/2',\ ver:'OWASP_CRS/3.1.1',\ severity:'WARNING',\ chain" SecRule REQUEST_HEADERS:Range|REQUEST_HEADERS:Request-Range "@rx ^bytes=(?:(?:\d+)?\-(?:\d+)?\s*,?\s*){63}" \ "setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl2=+%{tx.warning_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/INVALID_HREQ-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" SecRule ARGS "@rx \%((?!$|\W)|[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})" \ "id:920230,\ phase:2,\ block,\ t:none,\ msg:'Multiple URL Encoding Detected',\ logdata:'%{MATCHED_VAR}',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'OWASP_CRS/PROTOCOL_VIOLATION/EVASION',\ tag:'paranoia-level/2',\ ver:'OWASP_CRS/3.1.1',\ severity:'WARNING',\ setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl2=+%{tx.warning_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/EVASION-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" # # Missing Accept Header # # -=[ Rule Logic ]=- # This rule generates a notice if the Accept header is missing. # SecRule &REQUEST_HEADERS:Accept "@eq 0" \ "id:920300,\ phase:2,\ pass,\ t:none,\ msg:'Request Missing an Accept Header',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'OWASP_CRS/PROTOCOL_VIOLATION/MISSING_HEADER_ACCEPT',\ tag:'WASCTC/WASC-21',\ tag:'OWASP_TOP_10/A7',\ tag:'PCI/6.5.10',\ tag:'paranoia-level/2',\ ver:'OWASP_CRS/3.1.1',\ severity:'NOTICE',\ chain" SecRule REQUEST_METHOD "!@rx ^OPTIONS$" \ "chain" SecRule REQUEST_HEADERS:User-Agent "!@pm AppleWebKit Android" \ "t:none,\ setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl2=+%{tx.notice_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/MISSING_HEADER-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" # # PL2: This is a stricter sibling of 920270. # SecRule REQUEST_URI|REQUEST_HEADERS|ARGS|ARGS_NAMES "@validateByteRange 9,10,13,32-126,128-255" \ "id:920271,\ phase:2,\ block,\ t:none,t:urlDecodeUni,\ msg:'Invalid character in request (non printable characters)',\ logdata:'%{MATCHED_VAR_NAME}=%{MATCHED_VAR}',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'OWASP_CRS/PROTOCOL_VIOLATION/EVASION',\ tag:'paranoia-level/2',\ ver:'OWASP_CRS/3.1.1',\ severity:'CRITICAL',\ setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/EVASION-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" # # Missing User-Agent Header # # -=[ Rule Logic ]=- # This rules will check to see if there is a User-Agent header or not. # SecRule &REQUEST_HEADERS:User-Agent "@eq 0" \ "id:920320,\ phase:2,\ pass,\ t:none,\ msg:'Missing User Agent Header',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'OWASP_CRS/PROTOCOL_VIOLATION/MISSING_HEADER_UA',\ tag:'WASCTC/WASC-21',\ tag:'OWASP_TOP_10/A7',\ tag:'PCI/6.5.10',\ tag:'paranoia-level/2',\ ver:'OWASP_CRS/3.1.1',\ severity:'NOTICE',\ setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl2=+%{tx.notice_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/MISSING_HEADER-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" # # PL2: This is a stricter sibling of 920120. # SecRule FILES_NAMES|FILES "@rx ['\";=]" \ "id:920121,\ phase:2,\ block,\ t:none,t:urlDecodeUni,\ msg:'Attempted multipart/form-data bypass',\ logdata:'%{MATCHED_VAR}',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'OWASP_CRS/PROTOCOL_VIOLATION/INVALID_REQ',\ tag:'CAPEC-272',\ tag:'paranoia-level/2',\ ver:'OWASP_CRS/3.1.1',\ severity:'CRITICAL',\ setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/INVALID_REQ-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" # # PL2: Block on Missing Content-Type Header with Request Body # This is a stricter sibling of rule 920340. # # -=[ References ]=- # http://httpwg.org/specs/rfc7231.html#header.content-type SecRule REQUEST_HEADERS:Content-Length "!@rx ^0$" \ "id:920341,\ phase:2,\ block,\ t:none,\ msg:'Request Containing Content Requires Content-Type header',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'paranoia-level/2',\ ver:'OWASP_CRS/3.1.1',\ severity:'CRITICAL',\ chain" SecRule &REQUEST_HEADERS:Content-Type "@eq 0" \ "t:none,\ setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/MISSING_HEADER-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:920015,phase:1,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT" SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:920016,phase:2,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT" # # -= Paranoia Level 3 =- (apply only when tx.executing_paranoia_level is sufficiently high: 3 or higher) # # # PL 3: This is a stricter sibling of 920270. Ascii range: Printable characters in the low range # SecRule REQUEST_URI|REQUEST_HEADERS|ARGS|ARGS_NAMES|REQUEST_BODY "@validateByteRange 32-36,38-126" \ "id:920272,\ phase:2,\ block,\ t:none,t:urlDecodeUni,\ msg:'Invalid character in request (outside of printable chars below ascii 127)',\ logdata:'%{MATCHED_VAR_NAME}=%{MATCHED_VAR}',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'OWASP_CRS/PROTOCOL_VIOLATION/EVASION',\ tag:'paranoia-level/3',\ ver:'OWASP_CRS/3.1.1',\ severity:'CRITICAL',\ setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl3=+%{tx.critical_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/EVASION-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:920017,phase:1,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT" SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:920018,phase:2,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT" # # -= Paranoia Level 4 =- (apply only when tx.executing_paranoia_level is sufficiently high: 4 or higher) # # # This is a stricter sibling of rule 920200 # SecRule REQUEST_BASENAME "@endsWith .pdf" \ "id:920202,\ phase:2,\ block,\ t:none,\ msg:'Range: Too many fields for pdf request (6 or more)',\ logdata:'%{MATCHED_VAR}',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'OWASP_CRS/PROTOCOL_VIOLATION/INVALID_HREQ',\ tag:'paranoia-level/4',\ ver:'OWASP_CRS/3.1.1',\ severity:'WARNING',\ chain" SecRule REQUEST_HEADERS:Range|REQUEST_HEADERS:Request-Range "@rx ^bytes=(?:(?:\d+)?\-(?:\d+)?\s*,?\s*){6}" \ "setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl4=+%{tx.warning_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/INVALID_HREQ-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" # # This is a stricter sibling of 920270. # SecRule ARGS|ARGS_NAMES|REQUEST_BODY "@validateByteRange 38,44-46,48-58,61,65-90,95,97-122" \ "id:920273,\ phase:2,\ block,\ t:none,t:urlDecodeUni,\ msg:'Invalid character in request (outside of very strict set)',\ logdata:'%{MATCHED_VAR_NAME}=%{MATCHED_VAR}',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'OWASP_CRS/PROTOCOL_VIOLATION/EVASION',\ tag:'paranoia-level/4',\ ver:'OWASP_CRS/3.1.1',\ severity:'CRITICAL',\ setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl4=+%{tx.critical_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/EVASION-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" # # This is a stricter sibling of 920270. # SecRule REQUEST_HEADERS|!REQUEST_HEADERS:User-Agent|!REQUEST_HEADERS:Referer|!REQUEST_HEADERS:Cookie "@validateByteRange 32,34,38,42-59,61,65-90,95,97-122" \ "id:920274,\ phase:2,\ block,\ t:none,t:urlDecodeUni,\ msg:'Invalid character in request headers (outside of very strict set)',\ logdata:'%{MATCHED_VAR_NAME}=%{MATCHED_VAR}',\ tag:'application-multi',\ tag:'language-multi',\ tag:'platform-multi',\ tag:'attack-protocol',\ tag:'OWASP_CRS/PROTOCOL_VIOLATION/EVASION',\ tag:'paranoia-level/4',\ ver:'OWASP_CRS/3.1.1',\ severity:'CRITICAL',\ setvar:'tx.msg=%{rule.msg}',\ setvar:'tx.anomaly_score_pl4=+%{tx.critical_anomaly_score}',\ setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/EVASION-%{MATCHED_VAR_NAME}=%{MATCHED_VAR}'" # -=[ Abnormal Character Escapes ]=- # # [ Rule Logic ] # Consider the following payload: arg=cat+/e\tc/pa\ssw\d # Here, \s and \d were only used to obfuscate the string passwd and a lot of # parsers will silently ignore the non-necessary escapes. The case with \t is # a bit different though, as \t is a natural escape for the TAB character, # so we will avoid this (and \n, \r, etc.). # # This rule aims to detect non-necessary, abnormal escapes. You could say it is # a nice way to forbid the backslash character where it is not needed. # # This is a new rule at paranoia level 4. We expect quite a few false positives # for this rule and we will later evaluate if the rule makes any sense at all. # The rule is redundant with 920273 and 920274 in PL4. But if the rule proofs # to be useful and false positives remain at a reasonable level, then it might # be shifted to PL3 in a future release, where it would be the only rule # covering the backslash escape. # # The rule construct is overly complex due to the fact that matching the # backslash character with \b did not work. \Q\\\E does match the backslash # character though. This is thus the base of the rule. We forbid the backslash # when followed by a list of basic ascii characters - unless the backslash # is preceded by another backslash character, which is being checked via a # negative look-behind construct. If that is the case, the backslash character # is allowed. # SecRule REQUEST_URI|REQUEST_HEADERS|ARGS|ARGS_NAMES "@rx (?