mirror of
https://github.com/alibaba/higress.git
synced 2026-03-04 08:30:48 +08:00
1790 lines
58 KiB
Plaintext
1790 lines
58 KiB
Plaintext
# ------------------------------------------------------------------------
|
|
# OWASP ModSecurity Core Rule Set ver.4.0.0-rc1
|
|
# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved.
|
|
# Copyright (c) 2021-2022 Core Rule Set project. 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:DETECTION_PARANOIA_LEVEL "@lt 1" "id:920011,phase:1,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT"
|
|
SecRule TX:DETECTION_PARANOIA_LEVEL "@lt 1" "id:920012,phase:2,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT"
|
|
#
|
|
# -= Paranoia Level 1 (default) =- (apply only when tx.detection_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.
|
|
#
|
|
# Regular expression generated from regex-assembly/920100.ra.
|
|
# To update the regular expression run the following shell script
|
|
# (consult https://coreruleset.org/docs/development/regex_assembly/ for details):
|
|
# crs-toolchain regex update 920100
|
|
#
|
|
# -=[ 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)^(?:get /[^#\?]*(?:\?[^\s\v#]*)?(?:#[^\s\v]*)?|(?:connect (?:(?:[0-9]{1,3}\.){3}[0-9]{1,3}\.?(?::[0-9]+)?|[\--9A-Z_a-z]+:[0-9]+)|options \*|[a-z]{3,10}[\s\v]+(?:[0-9A-Z_a-z]{3,7}?://[\--9A-Z_a-z]*(?::[0-9]+)?)?/[^#\?]*(?:\?[^\s\v#]*)?(?:#[^\s\v]*)?)[\s\v]+[\.-9A-Z_a-z]+)$" \
|
|
"id:920100,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Invalid HTTP Request Line',\
|
|
logdata:'%{request_line}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.warning_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# 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 "name" (FILES) and "filename" (FILES_NAMES) variables.
|
|
# HTML entities may lead to false positives, which is why
|
|
# frequently used ones, such as "ä", are allowed at PL1.
|
|
#
|
|
# -=[ Targets, characters and html entities ]=-
|
|
#
|
|
# 920120 + 920122: PL1 : FILES_NAMES, FILES
|
|
# Disallow ['\";=], except for frequently used HTML entities (see 920120.data).
|
|
#
|
|
# 920121: PL2 : FILES_NAMES, FILES
|
|
# Disallow ['\";=]
|
|
#
|
|
# -=[ References ]=-
|
|
# https://www.owasp.org/index.php/ModSecurity_CRS_RuleID-96000
|
|
# http://www.ietf.org/rfc/rfc2183.txt
|
|
#
|
|
# This rule used to use negative look-behind.
|
|
# See https://github.com/coreruleset/coreruleset/wiki/Technical-Decisions-and-Best-Practices#avoiding-negative-look-behind-in-regular-expressions
|
|
# for an explanation of why it now uses `!@rx` instead to avoid look-around.
|
|
#
|
|
# Regular expression generated from regex-assembly/920120.ra.
|
|
# To update the regular expression run the following shell script
|
|
# (consult https://coreruleset.org/docs/development/regex_assembly/ for details):
|
|
# crs-toolchain regex update 920120
|
|
#
|
|
SecRule FILES|FILES_NAMES "!@rx (?i)^(?:&(?:(?:[acegiln-or-suz]acut|[aeiou]grav|[ain-o]tild)e|[c-elnr-tz]caron|(?:[cgk-lnr-t]cedi|[aeiouy]um)l|[aceg-josuwy]circ|[au]ring|a(?:mp|pos)|nbsp|oslash);|[^\"';=])*$" \
|
|
"id:920120,\
|
|
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:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# Accept only digits in content length
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# This rule uses ModSecurity's rule negation against the regex meaning if the Content-Length header
|
|
# is NOT all digits, then it will match.
|
|
#
|
|
# -=[ References ]=-
|
|
# https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13
|
|
#
|
|
SecRule REQUEST_HEADERS:Content-Length "!@rx ^\d+$" \
|
|
"id:920160,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Content-Length HTTP header is not numeric',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# Do not accept GET or HEAD requests with bodies
|
|
# HTTP standard allows GET requests to have a body but this
|
|
# feature is not used in real life. Attackers could try to force
|
|
# a request body on an unsuspecting web applications.
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# This is a chained rule that first checks the Request Method. If it is a
|
|
# GET or HEAD method, then it checks for the existence of a Content-Length
|
|
# header. If the header exists and its payload is either not a 0 digit or not
|
|
# empty, then it will match.
|
|
#
|
|
# -=[ References ]=-
|
|
# https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3
|
|
#
|
|
SecRule REQUEST_METHOD "@rx ^(?:GET|HEAD)$" \
|
|
"id:920170,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'GET or HEAD Request with Body Content',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
chain"
|
|
SecRule REQUEST_HEADERS:Content-Length "!@rx ^0?$" \
|
|
"t:none,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# This is a sibling of rule 920170
|
|
#
|
|
SecRule REQUEST_METHOD "@rx ^(?:GET|HEAD)$" \
|
|
"id:920171,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'GET or HEAD Request with Transfer-Encoding',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
chain"
|
|
SecRule &REQUEST_HEADERS:Transfer-Encoding "!@eq 0" \
|
|
"t:none,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# Require Content-Length or Transfer-Encoding to be provided with
|
|
# every POST request if the protocol version is not HTTP/2.
|
|
#
|
|
# In case of HTTP/2, see the RFC7540 8.1 p52:
|
|
# HTTP/2 does not use the Transfer-Encoding: chunked anymore, because
|
|
# the underlying transport protocol is already using data frames with
|
|
# known length.
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# This chained rule checks if the protocol is not HTTP/2, then checks
|
|
# request method is POST, if so, it checks that a Content-Length or
|
|
# Transfer-Encoding headers are also present.
|
|
#
|
|
SecRule REQUEST_PROTOCOL "!@within HTTP/2 HTTP/2.0" \
|
|
"id:920180,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'POST without Content-Length or Transfer-Encoding headers',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
chain"
|
|
SecRule REQUEST_METHOD "@streq POST" \
|
|
"chain"
|
|
SecRule &REQUEST_HEADERS:Content-Length "@eq 0" \
|
|
"chain"
|
|
SecRule &REQUEST_HEADERS:Transfer-Encoding "@eq 0" \
|
|
"setvar:'tx.inbound_anomaly_score_pl1=+%{tx.warning_anomaly_score}'"
|
|
|
|
#
|
|
# As per RFC7230 3.3.2: A sender MUST NOT send a Content-Length
|
|
# header field in any message that contains a Transfer-Encoding header
|
|
# field.
|
|
#
|
|
# Related to 920170, 920171 and 920180.
|
|
#
|
|
SecRule &REQUEST_HEADERS:Transfer-Encoding "!@eq 0" \
|
|
"id:920181,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Content-Length and Transfer-Encoding headers present',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
chain"
|
|
SecRule &REQUEST_HEADERS:Content-Length "!@eq 0" \
|
|
"t:none,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.warning_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# Range Header Check
|
|
#
|
|
# RFC7233 2.1 p6:
|
|
# "A byte-range-spec is invalid if the last-byte-pos value is present
|
|
# and less than the first-byte-pos."
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# This rule compares the first and second byte ranges and flags
|
|
# when the first value is greater than the second.
|
|
#
|
|
# -=[ References ]=-
|
|
# https://tools.ietf.org/html/rfc7233
|
|
# https://seclists.org/fulldisclosure/2011/Aug/175
|
|
#
|
|
SecRule REQUEST_HEADERS:Range|REQUEST_HEADERS:Request-Range "@rx (\d+)-(\d+)" \
|
|
"id:920190,\
|
|
phase:1,\
|
|
block,\
|
|
capture,\
|
|
t:none,\
|
|
msg:'Range: Invalid Last Byte Value',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
chain"
|
|
SecRule TX:2 "@lt %{tx.1}" \
|
|
"setvar:'tx.inbound_anomaly_score_pl1=+%{tx.warning_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# Broken/Malicious clients often have duplicate or conflicting headers
|
|
# Automated programs and bots often do not obey the HTTP RFC
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# This rule inspects the Connection header and looks for duplicates of the
|
|
# keep-alive and close options.
|
|
#
|
|
# -=[ References ]=-
|
|
# http://www.bad-behavior.ioerror.us/about/
|
|
# https://tools.ietf.org/html/rfc7233
|
|
#
|
|
SecRule REQUEST_HEADERS:Connection "@rx \b(?:keep-alive|close),\s?(?:keep-alive|close)\b" \
|
|
"id:920210,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Multiple/Conflicting Connection Header Data Found',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.warning_anomaly_score}'"
|
|
|
|
#
|
|
# Check URL encodings
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# There are two different chained rules. We need to separate them as we are inspecting two
|
|
# different variables - REQUEST_URI and REQUEST_BODY. For REQUEST_BODY, we only want to
|
|
# run the @validateUrlEncoding operator if the content-type is application/x-www-form-urlencoding.
|
|
#
|
|
# -=[ References ]=-
|
|
# http://www.ietf.org/rfc/rfc1738.txt
|
|
#
|
|
# -=[ Example payload ]=-
|
|
# http://localhost/?s=a%20b%20c%'/
|
|
# reason: %'/ is not a valid url encoding
|
|
#
|
|
SecRule REQUEST_URI "@rx \x25" \
|
|
"id:920220,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'URL Encoding Abuse Attack Attempt',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/255/153/267/72',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
chain"
|
|
SecRule REQUEST_URI "@validateUrlEncoding" \
|
|
"setvar:'tx.inbound_anomaly_score_pl1=+%{tx.warning_anomaly_score}'"
|
|
|
|
SecRule REQUEST_HEADERS:Content-Type "@rx ^(?i)application/x-www-form-urlencoded" \
|
|
"id:920240,\
|
|
phase:2,\
|
|
block,\
|
|
t:none,\
|
|
msg:'URL Encoding Abuse Attack Attempt',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/255/153/267/72',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
chain"
|
|
SecRule REQUEST_BODY "@rx \x25" \
|
|
"chain"
|
|
SecRule REQUEST_BODY "@validateUrlEncoding" \
|
|
"setvar:'tx.inbound_anomaly_score_pl1=+%{tx.warning_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# Check UTF encoding
|
|
# We only want to apply this check if UTF-8 encoding is actually used by the site, otherwise
|
|
# it will result in false positives.
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# This chained rule first checks to see if the admin has set the TX:CRS_VALIDATE_UTF8_ENCODING
|
|
# variable in the crs-setup.conf file.
|
|
#
|
|
SecRule TX:CRS_VALIDATE_UTF8_ENCODING "@eq 1" \
|
|
"id:920250,\
|
|
phase:2,\
|
|
block,\
|
|
t:none,\
|
|
msg:'UTF8 Encoding Abuse Attack Attempt',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/255/153/267',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
chain"
|
|
SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "@validateUtf8Encoding" \
|
|
"setvar:'tx.inbound_anomaly_score_pl1=+%{tx.warning_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# Disallow use of full-width unicode as decoding evasions may be possible.
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# This rule looks for full-width encoding by looking for %u followed by 2 'f'
|
|
# characters and then 2 hex characters. It is a vulnerability that affected
|
|
# IIS circa 2007.
|
|
# The rule will trigger on %uXXXX formatted chars that are full or half
|
|
# width, as explained above. This %uXXXX format is passed as a raw parameter
|
|
# and is (seemingly only) accepted by IIS (5.0, 6.0, 7.0, and 8.0). Other
|
|
# webservers will only process unicode chars presented as hex UTF-8 bytes.
|
|
#
|
|
# -=[ References ]=-
|
|
# http://www.kb.cert.org/vuls/id/739224
|
|
# https://www.checkpoint.com/defense/advisories/public/2007/cpai-2007-201.html
|
|
# https://github.com/SpiderLabs/owasp-modsecurity-crs/issues/719
|
|
#
|
|
SecRule REQUEST_URI|REQUEST_BODY "@rx \%u[fF]{2}[0-9a-fA-F]{2}" \
|
|
"id:920260,\
|
|
phase:2,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Unicode Full/Half Width Abuse Attack Attempt',\
|
|
logdata:'%{MATCHED_VAR_NAME}=%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-iis',\
|
|
tag:'platform-windows',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/255/153/267/72',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.warning_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# Restrict type of characters sent
|
|
#
|
|
# This is a rule with multiple stricter siblings that grows more
|
|
# restrictive in higher paranoia levels.
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# This rule uses the @validateByteRange operator to restrict the request
|
|
# payloads.
|
|
#
|
|
# -=[ Targets and ASCII Ranges ]=-
|
|
#
|
|
# 920270: PL1 : REQUEST_URI, REQUEST_HEADERS, ARGS and ARGS_NAMES
|
|
# ASCII 1-255 : Full ASCII range without null character
|
|
#
|
|
# 920271: PL2 : REQUEST_URI, REQUEST_HEADERS, ARGS and ARGS_NAMES
|
|
# ASCII 9,10,13,32-126,128-255 : Full visible ASCII range, tab, newline
|
|
#
|
|
# 920272: PL3 : REQUEST_URI, REQUEST_HEADERS, ARGS, ARGS_NAMES and REQUEST_BODY
|
|
# ASCII 32-36,38-126 : Visible lower ASCII range without percent symbol
|
|
#
|
|
# 920273: PL4 : ARGS, ARGS_NAMES and REQUEST_BODY
|
|
# ASCII 38,44-46,48-58,61,65-90,95,97-122
|
|
# A-Z a-z 0-9 = - _ . , : &
|
|
#
|
|
# 920274: PL4 : REQUEST_HEADERS without User-Agent, Referer, Cookie
|
|
# and Structured Header booleans
|
|
# ASCII 32,34,38,42-59,61,65-90,95,97-122
|
|
# A-Z a-z 0-9 = - _ . , : & " * + / SPACE
|
|
#
|
|
# REQUEST_URI and REQUEST_HEADERS User-Agent, Referer and Cookie are very hard
|
|
# to restrict beyond the limits in 920272. Structured Header booleans are
|
|
# validated separately in 920275.
|
|
#
|
|
# 920274 generally has few positives. However, it would detect rare attacks
|
|
# on Accept request headers and friends.
|
|
|
|
SecRule REQUEST_URI|REQUEST_HEADERS|ARGS|ARGS_NAMES "@validateByteRange 1-255" \
|
|
"id:920270,\
|
|
phase:2,\
|
|
block,\
|
|
t:none,t:urlDecodeUni,\
|
|
msg:'Invalid character in request (null character)',\
|
|
logdata:'%{MATCHED_VAR_NAME}=%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# Do not accept requests without common headers.
|
|
# All normal web browsers include Host, User-Agent and Accept headers.
|
|
# Implies either an attacker or a legitimate automation client.
|
|
#
|
|
|
|
#
|
|
# Missing/Empty Host Header
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# These rules will first check to see if a Host header is present.
|
|
# The second check is to see if a Host header exists but is empty.
|
|
#
|
|
SecRule &REQUEST_HEADERS:Host "@eq 0" \
|
|
"id:920280,\
|
|
phase:1,\
|
|
pass,\
|
|
t:none,\
|
|
msg:'Request Missing a Host Header',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'PCI/6.5.10',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.warning_anomaly_score}',\
|
|
skipAfter:END-HOST-CHECK"
|
|
|
|
|
|
SecRule REQUEST_HEADERS:Host "@rx ^$" \
|
|
"id:920290,\
|
|
phase:1,\
|
|
pass,\
|
|
t:none,\
|
|
msg:'Empty Host Header',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.warning_anomaly_score}'"
|
|
|
|
SecMarker "END-HOST-CHECK"
|
|
|
|
|
|
#
|
|
# Empty Accept Header
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# This rule checks if an Accept header exists, but has an empty value.
|
|
# This is only allowed in combination with the OPTIONS method.
|
|
# Additionally, there are some clients sending empty Accept headers.
|
|
# They are covered in another chained rule checking the User-Agent.
|
|
# This technique demands a separate rule to detect an empty
|
|
# Accept header if there is no user agent. This is checked via
|
|
# the separate rule 920311.
|
|
#
|
|
# Exclude some common broken clients sending empty Accept header:
|
|
# "Business/6.6.1.2 CFNetwork/758.5.3 Darwin/15.6.0" (CRS issue #515)
|
|
# "Entreprise/6.5.0.177 CFNetwork/758.4.3 Darwin/15.5.0" (CRS issue #366)
|
|
#
|
|
# -=[ References ]=-
|
|
# https://github.com/SpiderLabs/owasp-modsecurity-crs/issues/366
|
|
#
|
|
|
|
SecRule REQUEST_HEADERS:Accept "@rx ^$" \
|
|
"id:920310,\
|
|
phase:1,\
|
|
pass,\
|
|
t:none,\
|
|
msg:'Request Has an Empty Accept Header',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'NOTICE',\
|
|
chain"
|
|
SecRule REQUEST_METHOD "!@rx ^OPTIONS$" \
|
|
"chain"
|
|
SecRule REQUEST_HEADERS:User-Agent "!@pm AppleWebKit Android Business Enterprise Entreprise" \
|
|
"t:none,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.notice_anomaly_score}'"
|
|
|
|
#
|
|
# This rule is a sibling of rule 920310.
|
|
#
|
|
SecRule REQUEST_HEADERS:Accept "@rx ^$" \
|
|
"id:920311,\
|
|
phase:1,\
|
|
pass,\
|
|
t:none,\
|
|
msg:'Request Has an Empty Accept Header',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'NOTICE',\
|
|
chain"
|
|
SecRule REQUEST_METHOD "!@rx ^OPTIONS$" \
|
|
"chain"
|
|
SecRule &REQUEST_HEADERS:User-Agent "@eq 0" \
|
|
"t:none,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.notice_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# Empty User-Agent Header
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# This rules will check to see if the User-Agent header is empty.
|
|
#
|
|
# Note that there is a second rule, 920320, which will check for
|
|
# the existence of the User-Agent header.
|
|
#
|
|
|
|
SecRule REQUEST_HEADERS:User-Agent "@rx ^$" \
|
|
"id:920330,\
|
|
phase:1,\
|
|
pass,\
|
|
t:none,\
|
|
msg:'Empty User Agent Header',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'NOTICE',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.notice_anomaly_score}'"
|
|
|
|
#
|
|
# Missing Content-Type Header with Request Body
|
|
#
|
|
# -=[ Rule Logic]=-
|
|
# This rule will first check to see if the value of the Content-Length header is
|
|
# non-equal to 0. The chained rule is then checking the existence of the
|
|
# Content-Type header. The RFCs do not state there must be a
|
|
# Content-Type header. However, a request missing a Content-Header is a
|
|
# strong indication of a non-compliant browser.
|
|
#
|
|
# Also, omitting the CT header allows to bypass the Request Body Processor
|
|
# unless you set the optional tx.enforce_bodyproc_urlencoded variable.
|
|
#
|
|
# Note: in default settings, this behavior only provides a NOTICE and will
|
|
# not cause a request to be blocked. However, in paranoia level 2 or
|
|
# higher, we run sibling 920341, which DOES block these requests.
|
|
#
|
|
# -=[ References ]=-
|
|
# http://httpwg.org/specs/rfc7231.html#header.content-type
|
|
|
|
SecRule REQUEST_HEADERS:Content-Length "!@rx ^0$" \
|
|
"id:920340,\
|
|
phase:1,\
|
|
pass,\
|
|
t:none,\
|
|
msg:'Request Containing Content, but Missing Content-Type header',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'NOTICE',\
|
|
chain"
|
|
SecRule &REQUEST_HEADERS:Content-Type "@eq 0" \
|
|
"t:none,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.notice_anomaly_score}'"
|
|
|
|
# Check that the host header is not an IP address
|
|
# This is not an HTTP RFC violation but it is indicative of automated client access.
|
|
# Many web-based worms propagate by scanning IP address blocks.
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# This rule triggers if the Host header contains an IPv4 or IPv6 address, optionally
|
|
# extended with a port number. In the case of IPv6 we covering the address with square
|
|
# brackets and the address without square brackets.
|
|
#
|
|
# The regex consists of three main parts and said optional group:
|
|
#
|
|
# * IPv4 address
|
|
# * IPv6 address with square brackets
|
|
# * IPv6 address without square brackets
|
|
# * optional colon and port number
|
|
#
|
|
# Please note that the regex does not test the validity of the IP addresses.
|
|
# It just tries to detect a potential IP address.
|
|
#
|
|
# -=[ References ]=-
|
|
# https://technet.microsoft.com/en-us/magazine/2005.01.hackerbasher.aspx
|
|
#
|
|
|
|
SecRule REQUEST_HEADERS:Host "@rx (?:^([\d.]+|\[[\da-f:]+\]|[\da-f:]+)(:[\d]+)?$)" \
|
|
"id:920350,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Host header is a numeric IP address',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'PCI/6.5.10',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.warning_anomaly_score}'"
|
|
|
|
|
|
# In most cases, you should expect a certain volume of each a request on your
|
|
# website. For example, a request with 400 arguments, can be suspicious.
|
|
# This file creates limitations on the request.
|
|
#
|
|
# TODO Look at the rules in this file, and define the sizes you'd like to enforce.
|
|
# Note that most of the rules are commented out by default.
|
|
# Uncomment the rules you need
|
|
#
|
|
|
|
|
|
#
|
|
# Maximum number of arguments in request limited
|
|
#
|
|
SecRule &TX:MAX_NUM_ARGS "@eq 1" \
|
|
"id:920380,\
|
|
phase:2,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Too many arguments in request',\
|
|
logdata:'%{MATCHED_VAR_NAME}=%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
chain"
|
|
SecRule &ARGS "@gt %{tx.max_num_args}" \
|
|
"t:none,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
## -- Arguments limits --
|
|
#
|
|
# Limit argument name length
|
|
#
|
|
SecRule &TX:ARG_NAME_LENGTH "@eq 1" \
|
|
"id:920360,\
|
|
phase:2,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Argument name too long',\
|
|
logdata:'%{MATCHED_VAR_NAME}=%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
chain"
|
|
SecRule ARGS_NAMES "@gt %{tx.arg_name_length}" \
|
|
"t:none,t:length,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# Limit argument value length
|
|
#
|
|
# This rule is also triggered by an Apache Struts Remote Code Execution exploit:
|
|
# [ Apache Struts vulnerability CVE-2017-9791 - Exploit tested: https://www.exploit-db.com/exploits/42324 ]
|
|
#
|
|
SecRule &TX:ARG_LENGTH "@eq 1" \
|
|
"id:920370,\
|
|
phase:2,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Argument value too long',\
|
|
logdata:'%{MATCHED_VAR_NAME}=%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
chain"
|
|
SecRule ARGS "@gt %{tx.arg_length}" \
|
|
"t:none,t:length,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# Limit arguments total length
|
|
#
|
|
SecRule &TX:TOTAL_ARG_LENGTH "@eq 1" \
|
|
"id:920390,\
|
|
phase:2,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Total arguments size exceeded',\
|
|
logdata:'%{MATCHED_VAR_NAME}=%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
chain"
|
|
SecRule ARGS_COMBINED_SIZE "@gt %{tx.total_arg_length}" \
|
|
"t:none,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# -- File upload limits --
|
|
#
|
|
# Individual file size is limited
|
|
SecRule &TX:MAX_FILE_SIZE "@eq 1" \
|
|
"id:920400,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Uploaded file size too large',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
chain"
|
|
SecRule REQUEST_HEADERS:Content-Type "@rx ^(?i)multipart/form-data" \
|
|
"chain"
|
|
SecRule REQUEST_HEADERS:Content-Length "@gt %{tx.max_file_size}" \
|
|
"t:none,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# Combined file size is limited
|
|
#
|
|
SecRule &TX:COMBINED_FILE_SIZES "@eq 1" \
|
|
"id:920410,\
|
|
phase:2,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Total uploaded files size too large',\
|
|
logdata:'%{MATCHED_VAR_NAME}=%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
chain"
|
|
SecRule FILES_COMBINED_SIZE "@gt %{tx.combined_file_sizes}" \
|
|
"t:none,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
|
|
#
|
|
# Restrict which content-types we accept.
|
|
#
|
|
|
|
# Restrict Content-Type header to established patterns.
|
|
#
|
|
# This provides generic allow list protection against vulnerabilities like
|
|
# Apache Struts Content-Type arbitrary command execution (CVE-2017-5638).
|
|
#
|
|
# Examples of allowed patterns:
|
|
# - text/plain
|
|
# - text/plain; charset="UTF-8"
|
|
# - multipart/form-data; boundary=----WebKitFormBoundary12345
|
|
# - application/soap+xml; charset=utf-8; action="urn:localhost-hwh#getQuestions"
|
|
# - application/*+json
|
|
|
|
SecRule REQUEST_HEADERS:Content-Type "!@rx ^[\w/.+*-]+(?:\s?;\s?(?:action|boundary|charset|component|start(?:-info)?|type|version)\s?=\s?['\"\w.()+,/:=?<>@#*-]+)*$" \
|
|
"id:920470,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,t:lowercase,\
|
|
msg:'Illegal Content-Type header',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/255/153',\
|
|
tag:'PCI/12.1',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
# In case Content-Type header can be parsed, check the mime-type against
|
|
# the policy defined in the 'allowed_request_content_type' variable.
|
|
# To change your policy, edit crs-setup.conf and activate rule 900220.
|
|
SecRule REQUEST_HEADERS:Content-Type "@rx ^[^;\s]+" \
|
|
"id:920420,\
|
|
phase:1,\
|
|
block,\
|
|
capture,\
|
|
t:none,\
|
|
msg:'Request content type is not allowed by policy',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/255/153',\
|
|
tag:'PCI/12.1',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.content_type=|%{tx.0}|',\
|
|
chain"
|
|
SecRule TX:content_type "!@within %{tx.allowed_request_content_type}" \
|
|
"t:lowercase,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# Restrict charset parameter within the content-type header
|
|
#
|
|
SecRule REQUEST_HEADERS:Content-Type "@rx charset\s*=\s*[\"']?([^;\"'\s]+)" \
|
|
"id:920480,\
|
|
phase:1,\
|
|
block,\
|
|
capture,\
|
|
t:none,\
|
|
msg:'Request content type charset is not allowed by policy',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/255/153',\
|
|
tag:'PCI/12.1',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.content_type_charset=|%{tx.1}|',\
|
|
chain"
|
|
SecRule TX:content_type_charset "!@within %{tx.allowed_request_content_type_charset}" \
|
|
"t:lowercase,\
|
|
ctl:forceRequestBodyVariable=On,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# Restrict charset parameter inside content type header to occur max once.
|
|
#
|
|
SecRule REQUEST_HEADERS:Content-Type "@rx charset.*?charset" \
|
|
"id:920530,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,t:lowercase,\
|
|
msg:'Multiple charsets detected in content type header',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/255/153',\
|
|
tag:'PCI/12.1',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# Restrict protocol versions.
|
|
#
|
|
SecRule REQUEST_PROTOCOL "!@within %{tx.allowed_http_versions}" \
|
|
"id:920430,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'HTTP protocol version is not allowed by policy',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'PCI/6.5.10',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# Restrict file extension
|
|
#
|
|
SecRule REQUEST_BASENAME "@rx \.([^.]+)$" \
|
|
"id:920440,\
|
|
phase:1,\
|
|
block,\
|
|
capture,\
|
|
t:none,\
|
|
msg:'URL file extension is restricted by policy',\
|
|
logdata:'%{TX.0}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'PCI/6.5.10',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.extension=.%{tx.1}/',\
|
|
chain"
|
|
SecRule TX:EXTENSION "@within %{tx.restricted_extensions}" \
|
|
"t:none,t:urlDecodeUni,t:lowercase,\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# Backup or "working" file extension
|
|
# example: index.php~, /index.php~/foo/
|
|
#
|
|
SecRule REQUEST_FILENAME "@rx \.[^.~]+~(?:/.*|)$" \
|
|
"id:920500,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,t:urlDecodeUni,\
|
|
msg:'Attempt to access a backup or working file',\
|
|
logdata:'%{TX.0}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'PCI/6.5.10',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# Restricted HTTP headers
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# The use of certain headers is restricted. They are listed in the variable
|
|
# TX.restricted_headers.
|
|
#
|
|
# The headers are transformed into lowercase before the match. In order to
|
|
# make sure that only complete header names are matching, the names in
|
|
# TX.restricted_headers are wrapped in slashes. This guarantees that the
|
|
# header Range (-> /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_<HEADER_NAME>. The second rule is then executed for all
|
|
# variables of the form TX.header_name_<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:1,\
|
|
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:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'PCI/12.1',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.header_name_%{tx.0}=/%{tx.0}/',\
|
|
chain"
|
|
SecRule TX:/^header_name_/ "@within %{tx.restricted_headers}" \
|
|
"setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
#
|
|
# Rule against CVE-2022-21907
|
|
# This rule blocks Accept-Encoding headers longer than 50 characters.
|
|
# The length of 50 is a heuristic based on the length of values from
|
|
# the RFC (https://datatracker.ietf.org/doc/draft-ietf-httpbis-semantics/)
|
|
# and the respective values assigned by IANA
|
|
# (https://www.iana.org/assignments/http-parameters/http-parameters.xml#content-coding).
|
|
#
|
|
# This rule has a stricter sibling: 920521
|
|
#
|
|
SecRule REQUEST_HEADERS:Accept-Encoding "@gt 50" \
|
|
"id:920520,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,t:lowercase,t:length,\
|
|
msg:'Accept-Encoding header exceeded sensible length',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/255/153',\
|
|
tag:'PCI/12.1',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# Restrict response charsets that we allow.
|
|
# The following rules make sure that the response will be in an ASCII-compatible charset that
|
|
# phase 4 rules can properly understand and block.
|
|
#
|
|
|
|
#
|
|
# Some servers rely on the request Accept header to determine what charset to respond with.
|
|
# This rule restricts these to familiar charsets.
|
|
#
|
|
# Regular expression generated from regex-assembly/920600.ra.
|
|
# To update the regular expression run the following shell script
|
|
# (consult https://coreruleset.org/docs/development/regex_assembly/ for details):
|
|
# crs-toolchain regex update 920600
|
|
#
|
|
SecRule REQUEST_HEADERS:Accept "!@rx ^(?:(?:\*|[^!-\"\(-\),/:-\?\[-\]\{\}]+)/(?:\*|[^!-\"\(-\),/:-\?\[-\]\{\}]+)|\*)(?:[\s\v]*;[\s\v]*(?:charset[\s\v]*=[\s\v]*\"?(?:iso-8859-15?|utf-8|windows-1252)\b\"?|(?:[^\s\v -\"\(-\),/:-\?\[-\]c\{\}]|c(?:[^!-\"\(-\),/:-\?\[-\]h\{\}]|h(?:[^!-\"\(-\),/:-\?\[-\]a\{\}]|a(?:[^!-\"\(-\),/:-\?\[-\]r\{\}]|r(?:[^!-\"\(-\),/:-\?\[-\]s\{\}]|s(?:[^!-\"\(-\),/:-\?\[-\]e\{\}]|e[^!-\"\(-\),/:-\?\[-\]t\{\}]))))))[^!-\"\(-\),/:-\?\[-\]\{\}]*[\s\v]*=[\s\v]*[^!\(-\),/:-\?\[-\]\{\}]+);?)*(?:[\s\v]*,[\s\v]*(?:(?:\*|[^!-\"\(-\),/:-\?\[-\]\{\}]+)/(?:\*|[^!-\"\(-\),/:-\?\[-\]\{\}]+)|\*)(?:[\s\v]*;[\s\v]*(?:charset[\s\v]*=[\s\v]*\"?(?:iso-8859-15?|utf-8|windows-1252)\b\"?|(?:[^\s\v -\"\(-\),/:-\?\[-\]c\{\}]|c(?:[^!-\"\(-\),/:-\?\[-\]h\{\}]|h(?:[^!-\"\(-\),/:-\?\[-\]a\{\}]|a(?:[^!-\"\(-\),/:-\?\[-\]r\{\}]|r(?:[^!-\"\(-\),/:-\?\[-\]s\{\}]|s(?:[^!-\"\(-\),/:-\?\[-\]e\{\}]|e[^!-\"\(-\),/:-\?\[-\]t\{\}]))))))[^!-\"\(-\),/:-\?\[-\]\{\}]*[\s\v]*=[\s\v]*[^!\(-\),/:-\?\[-\]\{\}]+);?)*)*$" \
|
|
"id:920600,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,t:lowercase,\
|
|
msg:'Illegal Accept header: charset parameter',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# Unicode character bypass check for non JSON requests
|
|
# See reported bypass in issue:
|
|
# https://github.com/coreruleset/coreruleset/issues/2512
|
|
#
|
|
SecRule REQBODY_PROCESSOR "!@streq JSON" \
|
|
"id:920540,\
|
|
phase:2,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Possible Unicode character bypass detected',\
|
|
logdata:'%{MATCHED_VAR_NAME}=%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/255/153/267/72',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
chain"
|
|
SecRule REQUEST_URI|REQUEST_HEADERS|ARGS|ARGS_NAMES "@rx (?i)\x5cu[0-9a-f]{4}" \
|
|
"setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# Disallow any raw URL fragments. The '#' character should be omitted or URL-encoded.
|
|
# CRS rules generally do not check REQUEST_URI_RAW, but some servers accept the fragment as part of the URL path/query.
|
|
# This creates false negative evasions.
|
|
#
|
|
SecRule REQUEST_URI_RAW "@contains #" \
|
|
"id:920610,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Raw (unencoded) fragment in request URI',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/1',\
|
|
tag:'OWASP_CRS',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
SecRule TX:DETECTION_PARANOIA_LEVEL "@lt 2" "id:920013,phase:1,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT"
|
|
SecRule TX:DETECTION_PARANOIA_LEVEL "@lt 2" "id:920014,phase:2,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT"
|
|
#
|
|
# -= Paranoia Level 2 =- (apply only when tx.detection_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:1,\
|
|
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',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'paranoia-level/2',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
chain"
|
|
SecRule REQUEST_BASENAME "!@endsWith .pdf" \
|
|
"setvar:'tx.inbound_anomaly_score_pl2=+%{tx.warning_anomaly_score}'"
|
|
|
|
#
|
|
# This is a sibling of rule 920200
|
|
#
|
|
|
|
SecRule REQUEST_BASENAME "@endsWith .pdf" \
|
|
"id:920201,\
|
|
phase:1,\
|
|
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',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'paranoia-level/2',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
chain"
|
|
SecRule REQUEST_HEADERS:Range|REQUEST_HEADERS:Request-Range "@rx ^bytes=(?:(?:\d+)?-(?:\d+)?\s*,?\s*){63}" \
|
|
"setvar:'tx.inbound_anomaly_score_pl2=+%{tx.warning_anomaly_score}'"
|
|
|
|
|
|
SecRule ARGS "@rx %[0-9a-fA-F]{2}" \
|
|
"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',\
|
|
tag:'capec/1000/255/153/267/120',\
|
|
tag:'paranoia-level/2',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
setvar:'tx.inbound_anomaly_score_pl2=+%{tx.warning_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# 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',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'paranoia-level/2',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl2=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
|
|
#
|
|
# 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:1,\
|
|
pass,\
|
|
t:none,\
|
|
msg:'Missing User Agent Header',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'PCI/6.5.10',\
|
|
tag:'paranoia-level/2',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'NOTICE',\
|
|
setvar:'tx.inbound_anomaly_score_pl2=+%{tx.notice_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# 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',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'paranoia-level/2',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl2=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# 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:1,\
|
|
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',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
chain"
|
|
SecRule &REQUEST_HEADERS:Content-Type "@eq 0" \
|
|
"t:none,\
|
|
setvar:'tx.inbound_anomaly_score_pl2=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
SecRule TX:DETECTION_PARANOIA_LEVEL "@lt 3" "id:920015,phase:1,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT"
|
|
SecRule TX:DETECTION_PARANOIA_LEVEL "@lt 3" "id:920016,phase:2,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT"
|
|
#
|
|
# -= Paranoia Level 3 =- (apply only when tx.detection_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
|
|
#
|
|
# This rule is also triggered by the following exploit(s):
|
|
# [ SAP CRM Java vulnerability CVE-2018-2380 - Exploit tested: https://www.exploit-db.com/exploits/44292 ]
|
|
#
|
|
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',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'paranoia-level/3',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl3=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# Missing Accept Header
|
|
#
|
|
# This rule has been moved to PL3
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# This rule generates a notice if the Accept header is missing.
|
|
# RFC 7231 does not enforce the use of the Accept header.
|
|
# It is just typical browser behavior to send and it can indicate a malicious client.
|
|
#
|
|
# Notice: The rule tries to avoid known false positives by ignoring
|
|
# OPTIONS requests, CONNECT requests, and requests coming from known
|
|
# offending User-Agents via two chained rules.
|
|
# As ModSecurity only reports the match of the last matching rule,
|
|
# the alert is misleading.
|
|
#
|
|
SecRule &REQUEST_HEADERS:Accept "@eq 0" \
|
|
"id:920300,\
|
|
phase:1,\
|
|
pass,\
|
|
t:none,\
|
|
msg:'Request Missing an Accept Header',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'PCI/6.5.10',\
|
|
tag:'paranoia-level/3',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'NOTICE',\
|
|
chain"
|
|
SecRule REQUEST_METHOD "!@rx ^(?:OPTIONS|CONNECT)$" \
|
|
"chain"
|
|
SecRule REQUEST_HEADERS:User-Agent "!@pm AppleWebKit Android" \
|
|
"t:none,\
|
|
setvar:'tx.inbound_anomaly_score_pl3=+%{tx.notice_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# PL3: The little known x-up-devcap-post-charset request header can be used to submit
|
|
# a request with a different encoding as an alternative to the charset parameter in
|
|
# the Content-Type header. This can be used to circumvent charset restrictions on
|
|
# the Content-Type header in ASP.NET.
|
|
# Note that this only works in combination with a User-Agent prefix.
|
|
#
|
|
# This rule is based on a blog post by Soroush Dalili at
|
|
# https://soroush.secproject.com/blog/2019/05/x-up-devcap-post-charset-header-in-aspnet-to-bypass-wafs-again/
|
|
#
|
|
SecRule &REQUEST_HEADERS:x-up-devcap-post-charset "@ge 1" \
|
|
"id:920490,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Request header x-up-devcap-post-charset detected in combination with prefix \'UP\' to User-Agent',\
|
|
logdata:'%{MATCHED_VAR_NAME}=%{MATCHED_VAR}',\
|
|
tag:'language-aspnet',\
|
|
tag:'platform-windows',\
|
|
tag:'attack-protocol',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'paranoia-level/3',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
chain"
|
|
SecRule REQUEST_HEADERS:User-Agent "@rx ^(?i)up" \
|
|
"t:none,\
|
|
setvar:'tx.inbound_anomaly_score_pl3=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# Cache-Control Request Header allow list
|
|
#
|
|
# -=[ Rule Logic ]=-
|
|
# This rule aims to strictly allow list the Cache-Control request header
|
|
# values and to blocks all violations. This should be useful to intercept
|
|
# "bad bot" and tools that impersonate a real browser but with wrong request
|
|
# header setup.
|
|
#
|
|
# The regular expression used on this rule tries to match multiple directives
|
|
# in a single value, for example: "max-stale=1, max-age=2". This leads us to
|
|
# use a regular expression that accepts a trailing comma to keep compatibility
|
|
# with all regex engines and not PCRE only. For example: "max-stale=1, max-age=2, "
|
|
#
|
|
# Moreover, this regular expression allows duplicate directives sequence like:
|
|
# "max-stale, max-stale=1, no-cache, no-cache".
|
|
#
|
|
# Standard Cache-Control directives that can be used by the client:
|
|
# - max-age=<seconds>
|
|
# - max-stale[=<seconds>]
|
|
# - min-fresh=<seconds>
|
|
# - no-cache
|
|
# - no-store
|
|
# - no-transform
|
|
# - only-if-cached
|
|
#
|
|
# References:
|
|
# - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
|
|
# - https://regex101.com/r/CZ0Hxu/22
|
|
#
|
|
SecRule &REQUEST_HEADERS:Cache-Control "@gt 0" \
|
|
"id:920510,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,\
|
|
msg:'Invalid Cache-Control request header',\
|
|
logdata:'Invalid Cache-Control value in request found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'header-allowlist',\
|
|
tag:'paranoia-level/3',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/210/272',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
chain"
|
|
SecRule REQUEST_HEADERS:Cache-Control "!@rx ^(?:(?:max-age=[0-9]+|min-fresh=[0-9]+|no-cache|no-store|no-transform|only-if-cached|max-stale(?:=[0-9]+)?)(?:\s*\,\s*|$)){1,7}$" \
|
|
"setvar:'tx.inbound_anomaly_score_pl3=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# This rule checks for valid Accept-Encoding headers
|
|
#
|
|
# This rule has a less strict sibling: 920520
|
|
#
|
|
# Regular expression generated from regex-assembly/920521.ra.
|
|
# To update the regular expression run the following shell script
|
|
# (consult https://coreruleset.org/docs/development/regex_assembly/ for details):
|
|
# crs-toolchain regex update 920521
|
|
#
|
|
SecRule REQUEST_HEADERS:Accept-Encoding "!@rx br|compress|deflate|(?:pack200-)?gzip|identity|\*|^$|aes128gcm|exi|zstd|x-(?:compress|gzip)" \
|
|
"id:920521,\
|
|
phase:1,\
|
|
block,\
|
|
t:none,t:lowercase,\
|
|
msg:'Illegal Accept-Encoding header',\
|
|
logdata:'%{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/3',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/255/153',\
|
|
tag:'PCI/12.1',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl3=+%{tx.critical_anomaly_score}'"
|
|
|
|
SecRule TX:DETECTION_PARANOIA_LEVEL "@lt 4" "id:920017,phase:1,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT"
|
|
SecRule TX:DETECTION_PARANOIA_LEVEL "@lt 4" "id:920018,phase:2,pass,nolog,skipAfter:END-REQUEST-920-PROTOCOL-ENFORCEMENT"
|
|
#
|
|
# -= Paranoia Level 4 =- (apply only when tx.detection_paranoia_level is sufficiently high: 4 or higher)
|
|
#
|
|
|
|
#
|
|
# This is a stricter sibling of rule 920200
|
|
#
|
|
|
|
SecRule REQUEST_BASENAME "@endsWith .pdf" \
|
|
"id:920202,\
|
|
phase:1,\
|
|
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',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'paranoia-level/4',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'WARNING',\
|
|
chain"
|
|
SecRule REQUEST_HEADERS:Range|REQUEST_HEADERS:Request-Range "@rx ^bytes=(?:(?:\d+)?-(?:\d+)?\s*,?\s*){6}" \
|
|
"setvar:'tx.inbound_anomaly_score_pl4=+%{tx.warning_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# This is a stricter sibling of 920270.
|
|
#
|
|
# This rule is also triggered by the following exploit(s):
|
|
# [ SAP CRM Java vulnerability CVE-2018-2380 - Exploit tested: https://www.exploit-db.com/exploits/44292 ]
|
|
#
|
|
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',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'paranoia-level/4',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl4=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# This is a stricter sibling of 920270.
|
|
#
|
|
SecRule REQUEST_HEADERS|!REQUEST_HEADERS:User-Agent|!REQUEST_HEADERS:Referer|!REQUEST_HEADERS:Cookie|!REQUEST_HEADERS:Sec-Fetch-User|!REQUEST_HEADERS:Sec-CH-UA|!REQUEST_HEADERS:Sec-CH-UA-Mobile "@validateByteRange 32,34,38,42-59,61,65-90,95,97-122" \
|
|
"id:920274,\
|
|
phase:1,\
|
|
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',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'paranoia-level/4',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl4=+%{tx.critical_anomaly_score}'"
|
|
|
|
#
|
|
# This is a stricter sibling of 920270.
|
|
# The headers of this rule are Structured Header booleans, for which only `?0`,
|
|
# and `?1` are inconspicuous.
|
|
# Structured Header boolean: https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-19#section-3.3.6
|
|
# Sec-Fetch-User: https://www.w3.org/TR/fetch-metadata/#http-headerdef-sec-fetch-user
|
|
# Sec-CH-UA-Mobile: https://wicg.github.io/ua-client-hints/#sec-ch-ua-mobile
|
|
#
|
|
SecRule REQUEST_HEADERS:Sec-Fetch-User|REQUEST_HEADERS:Sec-CH-UA-Mobile "!@rx ^(?:\?[01])?$" \
|
|
"id:920275,\
|
|
phase:1,\
|
|
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',\
|
|
tag:'capec/1000/210/272',\
|
|
tag:'paranoia-level/4',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.inbound_anomaly_score_pl4=+%{tx.critical_anomaly_score}'"
|
|
|
|
# -=[ 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.
|
|
#
|
|
# We forbid backslashes followed by a list of basic ascii characters - unless
|
|
# the backslash is preceded by another backslash.
|
|
#
|
|
# This rule is also triggered by the following exploit(s):
|
|
# [ SAP CRM Java vulnerability CVE-2018-2380 - Exploit tested: https://www.exploit-db.com/exploits/44292 ]
|
|
#
|
|
SecRule REQUEST_URI|REQUEST_HEADERS|ARGS|ARGS_NAMES "@rx (?:^|[^\x5c])\x5c[cdeghijklmpqwxyz123456789]" \
|
|
"id:920460,\
|
|
phase:2,\
|
|
block,\
|
|
capture,\
|
|
t:none,t:htmlEntityDecode,t:lowercase,\
|
|
msg:'Abnormal character escapes in request',\
|
|
logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\
|
|
tag:'application-multi',\
|
|
tag:'language-multi',\
|
|
tag:'platform-multi',\
|
|
tag:'attack-protocol',\
|
|
tag:'paranoia-level/4',\
|
|
tag:'OWASP_CRS',\
|
|
tag:'capec/1000/153/267',\
|
|
ver:'OWASP_CRS/4.0.0-rc1',\
|
|
severity:'CRITICAL',\
|
|
setvar:'tx.http_violation_score=+%{tx.critical_anomaly_score}',\
|
|
setvar:'tx.inbound_anomaly_score_pl4=+%{tx.critical_anomaly_score}'"
|
|
|
|
|
|
#
|
|
# -= Paranoia Levels Finished =-
|
|
#
|
|
SecMarker "END-REQUEST-920-PROTOCOL-ENFORCEMENT"
|