From a1e7e8cd51d6b1329cdbf5f6c4541099caabcbbe Mon Sep 17 00:00:00 2001 From: Felipe Zipitria Date: Tue, 5 Apr 2022 09:47:12 -0300 Subject: [PATCH] feat: added modsecurity secrule language ModSecurity Rules language is a DSL for creating security policies. https://github.com/SpiderLabs/ModSecurity/wiki/ModSecurity-Rules-Language-Porting-Specification. Released under the Apache2 License. Signed-off-by: Felipe Zipitria --- .gitmodules | 3 + grammars.yml | 2 + lib/linguist/languages.yml | 11 + ...EQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf | 211 +++ .../REQUEST-901-INITIALIZATION.conf | 435 +++++ ...QUEST-903.9001-DRUPAL-EXCLUSION-RULES.conf | 423 +++++ ...ST-903.9002-WORDPRESS-EXCLUSION-RULES.conf | 833 ++++++++ ...ST-903.9003-NEXTCLOUD-EXCLUSION-RULES.conf | 560 ++++++ ...EST-903.9004-DOKUWIKI-EXCLUSION-RULES.conf | 273 +++ ...QUEST-903.9005-CPANEL-EXCLUSION-RULES.conf | 64 + ...UEST-903.9006-XENFORO-EXCLUSION-RULES.conf | 646 +++++++ ...EQUEST-903.9007-PHPBB-EXCLUSION-RULES.conf | 273 +++ ...T-903.9008-PHPMYADMIN-EXCLUSION-RULES.conf | 416 ++++ .../REQUEST-905-COMMON-EXCEPTIONS.conf | 55 + .../REQUEST-910-IP-REPUTATION.conf | 302 +++ .../REQUEST-911-METHOD-ENFORCEMENT.conf | 76 + .../REQUEST-912-DOS-PROTECTION.conf | 327 ++++ .../REQUEST-913-SCANNER-DETECTION.conf | 204 ++ .../REQUEST-920-PROTOCOL-ENFORCEMENT.conf | 1688 +++++++++++++++++ .../REQUEST-921-PROTOCOL-ATTACK.conf | 365 ++++ .../REQUEST-930-APPLICATION-ATTACK-LFI.conf | 177 ++ .../REQUEST-931-APPLICATION-ATTACK-RFI.conf | 153 ++ .../REQUEST-932-APPLICATION-ATTACK-RCE.conf | 982 ++++++++++ .../REQUEST-933-APPLICATION-ATTACK-PHP.conf | 731 +++++++ ...EQUEST-934-APPLICATION-ATTACK-GENERIC.conf | 243 +++ .../REQUEST-941-APPLICATION-ATTACK-XSS.conf | 963 ++++++++++ .../REQUEST-942-APPLICATION-ATTACK-SQLI.conf | 1584 ++++++++++++++++ ...3-APPLICATION-ATTACK-SESSION-FIXATION.conf | 133 ++ .../REQUEST-944-APPLICATION-ATTACK-JAVA.conf | 294 +++ .../REQUEST-949-BLOCKING-EVALUATION.conf | 205 ++ .../RESPONSE-950-DATA-LEAKAGES.conf | 140 ++ .../RESPONSE-951-DATA-LEAKAGES-SQL.conf | 477 +++++ .../RESPONSE-952-DATA-LEAKAGES-JAVA.conf | 104 + .../RESPONSE-953-DATA-LEAKAGES-PHP.conf | 131 ++ .../RESPONSE-954-DATA-LEAKAGES-IIS.conf | 152 ++ .../RESPONSE-955-WEB-SHELLS.conf | 600 ++++++ .../RESPONSE-959-BLOCKING-EVALUATION.conf | 191 ++ .../RESPONSE-980-CORRELATION.conf | 164 ++ vendor/README.md | 1 + vendor/grammars/vscode-language-modsecurity | 1 + 40 files changed, 14593 insertions(+) create mode 100644 samples/ModSecurity SecRule/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-901-INITIALIZATION.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-903.9001-DRUPAL-EXCLUSION-RULES.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-903.9002-WORDPRESS-EXCLUSION-RULES.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-903.9003-NEXTCLOUD-EXCLUSION-RULES.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-903.9004-DOKUWIKI-EXCLUSION-RULES.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-903.9005-CPANEL-EXCLUSION-RULES.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-903.9006-XENFORO-EXCLUSION-RULES.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-903.9007-PHPBB-EXCLUSION-RULES.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-903.9008-PHPMYADMIN-EXCLUSION-RULES.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-905-COMMON-EXCEPTIONS.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-910-IP-REPUTATION.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-911-METHOD-ENFORCEMENT.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-912-DOS-PROTECTION.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-913-SCANNER-DETECTION.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-920-PROTOCOL-ENFORCEMENT.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-921-PROTOCOL-ATTACK.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-930-APPLICATION-ATTACK-LFI.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-931-APPLICATION-ATTACK-RFI.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-932-APPLICATION-ATTACK-RCE.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-933-APPLICATION-ATTACK-PHP.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-934-APPLICATION-ATTACK-GENERIC.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-941-APPLICATION-ATTACK-XSS.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-942-APPLICATION-ATTACK-SQLI.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-944-APPLICATION-ATTACK-JAVA.conf create mode 100644 samples/ModSecurity SecRule/REQUEST-949-BLOCKING-EVALUATION.conf create mode 100644 samples/ModSecurity SecRule/RESPONSE-950-DATA-LEAKAGES.conf create mode 100644 samples/ModSecurity SecRule/RESPONSE-951-DATA-LEAKAGES-SQL.conf create mode 100644 samples/ModSecurity SecRule/RESPONSE-952-DATA-LEAKAGES-JAVA.conf create mode 100644 samples/ModSecurity SecRule/RESPONSE-953-DATA-LEAKAGES-PHP.conf create mode 100644 samples/ModSecurity SecRule/RESPONSE-954-DATA-LEAKAGES-IIS.conf create mode 100644 samples/ModSecurity SecRule/RESPONSE-955-WEB-SHELLS.conf create mode 100644 samples/ModSecurity SecRule/RESPONSE-959-BLOCKING-EVALUATION.conf create mode 100644 samples/ModSecurity SecRule/RESPONSE-980-CORRELATION.conf create mode 160000 vendor/grammars/vscode-language-modsecurity diff --git a/.gitmodules b/.gitmodules index 9d006811cd..a0a30ca517 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1143,6 +1143,9 @@ [submodule "vendor/grammars/vscode-jest"] path = vendor/grammars/vscode-jest url = https://github.com/jest-community/vscode-jest +[submodule "vendor/grammars/vscode-language-modsecurity"] + path = vendor/grammars/vscode-language-modsecurity + url = https://github.com/irvinlim/vscode-language-modsecurity.git [submodule "vendor/grammars/vscode-lean"] path = vendor/grammars/vscode-lean url = https://github.com/leanprover/vscode-lean diff --git a/grammars.yml b/grammars.yml index bd8ad9915c..e85c597683 100644 --- a/grammars.yml +++ b/grammars.yml @@ -1007,6 +1007,8 @@ vendor/grammars/vscode-janet: - source.janet vendor/grammars/vscode-jest: - source.jest.snap +vendor/grammars/vscode-language-modsecurity: +- source.modsecurity vendor/grammars/vscode-lean: - markdown.lean.codeblock - source.lean diff --git a/lib/linguist/languages.yml b/lib/linguist/languages.yml index f1bdb8b72e..4fbfc293b5 100644 --- a/lib/linguist/languages.yml +++ b/lib/linguist/languages.yml @@ -3830,6 +3830,17 @@ Mirah: codemirror_mode: ruby codemirror_mime_type: text/x-ruby language_id: 232 +ModSecurity SecRule: + type: data + color: "#d12127" + aliases: + - modsecurity configuration file + - secrule configuration file + extensions: + - ".conf" + tm_scope: source.modsecurity + ace_mode: text + language_id: 163529485 Modelica: type: programming color: "#de1d31" diff --git a/samples/ModSecurity SecRule/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf b/samples/ModSecurity SecRule/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf new file mode 100644 index 0000000000..899450f88b --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf @@ -0,0 +1,211 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# +# The purpose of this file is to hold LOCAL exceptions for your site. The +# types of rules that would go into this file are one where you want to +# short-circuit inspection and allow certain transactions to pass through +# inspection or if you want to alter rules that are applied. +# +# This file is named REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example for a +# very specific reason. Files affixed with the .example extension are designed +# to contain user created/modified data. The '.example'. extension should be +# renamed to end in .conf. The advantage of this is that when OWASP CRS is +# updated, the updates will not overwrite a user generated configuration file. +# +# As a result of this design paradigm users are encouraged NOT to directly +# modify rules. Instead they should use this +# REQUEST-900-EXCLUSION-RULES-BEFORE-CRS and the +# RESPONSE-999-EXCLUSION-RULES-AFTER-CRS file to modify OWASP rules using +# methods similar to the examples specified below. +# +# REQUEST-900-EXCLUSION-RULES-BEFORE-CRS and +# RESPONSE-999-EXCLUSION-RULES-AFTER-CRS serve different purposes. ModSecurity +# effectively maintains two different context: startup, and per transaction. +# As a rule, directives are processed within the startup context. While they +# can affect the per transaction context they generally remain fixed during the +# execution of ModSecurity. +# +# As a result if one wanted to disable a rule at bootup the SecRuleRemoveById +# directive or one of its siblings would have to be placed AFTER the rule is +# listed, otherwise it will not have knowledge of the rules existence (since +# these rules are read in at the same time). This means that when using +# directives that effect SecRules, these exceptions should be placed AFTER all +# the existing rules. This is why RESPONSE-999-EXCLUSION-RULES-AFTER-CRS is +# designed such that it loads LAST. +# +# Conversely, ModSecurity supports several actions that can change the state of +# the underlying configuration during the per transaction context, this is when +# rules are being processed. Generally, these are accomplished by using the +# 'ctl' action. As these are part of a rule, they will be evaluated in the +# order rules are applied (by physical location, considering phases). As a +# result of this ordering a 'ctl' action should be placed with consideration to +# when it will be executed. This is particularly relevant for the 'ctl' options +# that involve modifying ID's (such as ruleRemoveById). In these cases it is +# important that such rules are placed BEFORE the rule ID they will affect. +# Unlike the setup context, by the time we process rules in the per-transaction +# context, we are already aware of all the rule ID's. It is by this logic that +# we include rules such as this BEFORE all the remaining rules. As a result +# REQUEST-900-EXCLUSION-RULES-BEFORE-CRS is designed to load FIRST. +# +# As a general rule: +# ctl:ruleEngine -> place in REQUEST-900-EXCLUSION-RULES-BEFORE-CRS +# ctl:ruleRemoveById -> place in REQUEST-900-EXCLUSION-RULES-BEFORE-CRS +# ctl:ruleRemoveByMsg -> place in REQUEST-900-EXCLUSION-RULES-BEFORE-CRS +# ctl:ruleRemoveByTag -> place in REQUEST-900-EXCLUSION-RULES-BEFORE-CRS +# ctl:ruleRemoveTargetById -> place in REQUEST-900-EXCLUSION-RULES-BEFORE-CRS +# ctl:ruleRemoveTargetByMsg -> place in REQUEST-900-EXCLUSION-RULES-BEFORE-CRS +# ctl:ruleRemoveTargetByTag -> place in REQUEST-900-EXCLUSION-RULES-BEFORE-CRS +# +# SecRuleRemoveById -> place in RESPONSE-999-EXCLUSION-RULES-AFTER-CRS +# SecRuleRemoveByMsg -> place in RESPONSE-999-EXCLUSION-RULES-AFTER-CRS +# SecRuleRemoveByTag -> place in RESPONSE-999-EXCLUSION-RULES-AFTER-CRS +# SecRuleUpdateActionById -> place in RESPONSE-999-EXCLUSION-RULES-AFTER-CRS +# SecRuleUpdateTargetById -> place in RESPONSE-999-EXCLUSION-RULES-AFTER-CRS +# SecRuleUpdateTargetByMsg -> place in RESPONSE-999-EXCLUSION-RULES-AFTER-CRS +# SecRuleUpdateTargetByTag -> place in RESPONSE-999-EXCLUSION-RULES-AFTER-CRS +# +# +# What follows are a group of examples that show you how to perform rule +# exclusions. +# +# +# Example Exclusion Rule: Disable inspection for an authorized client +# +# This ruleset allows you to control how ModSecurity will handle traffic +# originating from Authorized Vulnerability Scanning (AVS) sources. See +# related blog post - +# http://blog.spiderlabs.com/2010/12/advanced-topic-of-the-week-handling-authorized-scanning-traffic.html +# +# Allow List ASV network block (no blocking or logging of AVS traffic) Update +# IP network block as appropriate for your AVS traffic +# +# ModSec Rule Exclusion: Disable Rule Engine for known ASV IP +# SecRule REMOTE_ADDR "@ipMatch 192.168.1.100" \ +# "id:1000,\ +# phase:1,\ +# pass,\ +# nolog,\ +# ctl:ruleEngine=Off" +# +# +# Example Exclusion Rule: Removing a specific ARGS parameter from inspection +# for an individual rule +# +# This rule shows how to conditionally exclude the "password" +# parameter for rule 942100 when the REQUEST_URI is /index.php +# ModSecurity Rule Exclusion: 942100 SQL Injection Detected via libinjection +# +# SecRule REQUEST_URI "@beginsWith /index.php" \ +# "id:1001,\ +# phase:1,\ +# pass,\ +# nolog,\ +# ctl:ruleRemoveTargetById=942100;ARGS:password" +# +# +# Example Exclusion Rule: Removing a specific ARGS parameter from inspection +# for only certain attacks +# +# Attack rules within the CRS are tagged, with tags such as 'attack-lfi', +# 'attack-sqli', 'attack-xss', 'attack-injection-php', et cetera. +# +# ModSecurity Rule Exclusion: Disable inspection of ARGS:pwd +# for all rules tagged attack-sqli +# SecRule REQUEST_FILENAME "@endsWith /wp-login.php" \ +# "id:1002,\ +# phase:2,\ +# pass,\ +# nolog,\ +# ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:pwd" +# + +# Example Exclusion Rule: Removing a specific ARGS parameter from inspection +# for all CRS rules +# +# This rule illustrates that we can use tagging very effectively to allow list a +# common false positive across an entire ModSecurity instance. This can be done +# because every rule in OWASP_CRS is tagged with OWASP_CRS. This will NOT +# affect custom rules. +# +# ModSecurity Rule Exclusion: Disable inspection of ARGS:pwd +# for all CRS rules +# SecRule REQUEST_FILENAME "@endsWith /wp-login.php" \ +# "id:1003,\ +# phase:2,\ +# pass,\ +# nolog,\ +# ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pwd" + +# +# Example Exclusion Rule: Removing a range of rules +# +# This rule illustrates that we can remove a rule range via a ctl action. +# This uses the fact, that rules are grouped by topic in rule files covering +# a certain id range. +# +# ModSecurity Rule Exclusion: Disable all SQLi and XSS rules +# SecRule REQUEST_FILENAME "@beginsWith /admin" \ +# "id:1004,\ +# phase:2,\ +# pass,\ +# nolog,\ +# ctl:ruleRemoveById=941000-942999" +# +# +# The application specific rule exclusion files +# REQUEST-903.9001-DRUPAL-EXCLUSION-RULES.conf +# REQUEST-903.9002-WORDPRESS-EXCLUSION-RULES.conf +# bring additional examples which can be useful then tuning a service. + + +# +# Example Rule: Allow monitoring tools and scripts +# +# Uncomment this rule to allow all requests from trusted IPs and User-Agent. +# This can be useful for monitoring tools like Monit, Nagios, or other agents. +# For example, if you're using AWS Load Balancer, you may need to trust all +# requests from "10.0.0.0/8" subnet that come with the user-agent +# "ELB-HealthChecker/2.0". By doing this, all requests that match these +# conditions will not be matched against the following rules: +# +# - id: 911100 (allowed methods) +# - tag: attack-dos (DoS protection) +# - id: 913100,913110,913120,913101,913102 (scan detection) +# - id: 920280 (missing/empty host header) +# - id: 920350 (IP address in host header) +# - tag: attack-disclosure (all RESPONSE-*-DATA-LEAKAGES rules) +# +# SecRule REMOTE_ADDR "@ipMatch 10.0.0.0/8" \ +# "id:1005,\ +# phase:1,\ +# pass,\ +# nolog,\ +# chain" +# SecRule REQUEST_METHOD "@pm GET HEAD" "chain" +# SecRule REQUEST_HEADERS:User-Agent "@pm ELB-HealthChecker" \ +# "ctl:ruleRemoveById=911100,\ +# ctl:ruleRemoveById=913100,\ +# ctl:ruleRemoveById=913110,\ +# ctl:ruleRemoveById=913120,\ +# ctl:ruleRemoveById=913101,\ +# ctl:ruleRemoveById=913102,\ +# ctl:ruleRemoveById=920280,\ +# ctl:ruleRemoveById=920350,\ +# ctl:ruleRemoveByTag=attack-disclosure,\ +# ctl:ruleRemoveByTag=attack-dos" + +SecRule REQUEST_URI "/anything" \ + "id:2009,\ + log,\ + auditlog,\ + phase:1,\ + pass,\ + ctl:ruleRemoveTargetById=941100-941950;ARGS:testwaf" diff --git a/samples/ModSecurity SecRule/REQUEST-901-INITIALIZATION.conf b/samples/ModSecurity SecRule/REQUEST-901-INITIALIZATION.conf new file mode 100644 index 0000000000..aa8873bece --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-901-INITIALIZATION.conf @@ -0,0 +1,435 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# +# This file REQUEST-901-INITIALIZATION.conf initializes the Core Rules +# and performs preparatory actions. It also fixes errors and omissions +# of variable definitions in the file crs-setup.conf. +# The crs-setup.conf can and should be edited by the user, this file +# is part of the CRS installation and should not be altered. +# + + +# +# -=[ Rules Version ]=- +# +# Rule version data is added to the "Producer" line of Section H of the Audit log: +# +# - Producer: ModSecurity for Apache/2.9.1 (http://www.modsecurity.org/); OWASP_CRS/3.1.0. +# +# Ref: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#wiki-SecComponentSignature +# +SecComponentSignature "OWASP_CRS/3.4.0-dev" + +# +# -=[ Default setup values ]=- +# +# The CRS checks the tx.crs_setup_version variable to ensure that the setup +# file is included at the correct time. This detects situations where +# necessary settings are not defined, for instance if the file +# inclusion order is incorrect, or if the user has forgotten to +# include the crs-setup.conf file. +# +# If you are upgrading from an earlier version of the CRS and you are +# getting this error, please make a new copy of the setup template +# crs-setup.conf.example to crs-setup.conf, and re-apply your policy +# changes. There have been many changes in settings syntax from CRS2 +# to CRS3, so an old setup file may cause unwanted behavior. +# +# If you are not planning to use the crs-setup.conf template, you must +# manually set the tx.crs_setup_version variable before including +# the CRS rules/* files. +# +# The variable is a numerical representation of the CRS version number. +# E.g., v3.0.0 is represented as 300. +# + +SecRule &TX:crs_setup_version "@eq 0" \ + "id:901001,\ + phase:1,\ + deny,\ + status:500,\ + log,\ + auditlog,\ + msg:'ModSecurity Core Rule Set is deployed without configuration! Please copy the crs-setup.conf.example template to crs-setup.conf, and include the crs-setup.conf file in your webserver configuration before including the CRS rules. See the INSTALL file in the CRS directory for detailed instructions',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL'" + + +# +# -=[ Default setup values ]=- +# +# Some constructs or individual rules will fail if certain parameters +# are not set in the crs-setup.conf file. The following rules will catch +# these cases and assign sane default values. +# + +# Default Inbound Anomaly Threshold Level (rule 900110 in crs-setup.conf) +SecRule &TX:inbound_anomaly_score_threshold "@eq 0" \ + "id:901100,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.inbound_anomaly_score_threshold=5'" + +# Default Outbound Anomaly Threshold Level (rule 900110 in crs-setup.conf) +SecRule &TX:outbound_anomaly_score_threshold "@eq 0" \ + "id:901110,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.outbound_anomaly_score_threshold=4'" + +# Default Early Blocking (rule 900120 in crs-setup.conf) +SecRule &TX:early_blocking "@eq 0" \ + "id:901115,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.early_blocking=0'" + +# Default Paranoia Level (rule 900000 in crs-setup.conf) +SecRule &TX:paranoia_level "@eq 0" \ + "id:901120,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.paranoia_level=1'" + +# Default Executing Paranoia Level (rule 900000 in crs-setup.conf) +SecRule &TX:executing_paranoia_level "@eq 0" \ + "id:901125,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.executing_paranoia_level=%{TX.PARANOIA_LEVEL}'" + +# Default Sampling Percentage (rule 900400 in crs-setup.conf) +SecRule &TX:sampling_percentage "@eq 0" \ + "id:901130,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.sampling_percentage=100'" + +# Default Anomaly Scores (rule 900100 in crs-setup.conf) +SecRule &TX:critical_anomaly_score "@eq 0" \ + "id:901140,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.critical_anomaly_score=5'" + +SecRule &TX:error_anomaly_score "@eq 0" \ + "id:901141,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.error_anomaly_score=4'" + +SecRule &TX:warning_anomaly_score "@eq 0" \ + "id:901142,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.warning_anomaly_score=3'" + +SecRule &TX:notice_anomaly_score "@eq 0" \ + "id:901143,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.notice_anomaly_score=2'" + +# Default do_reput_block +SecRule &TX:do_reput_block "@eq 0" \ + "id:901150,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.do_reput_block=0'" + +# Default block duration +SecRule &TX:reput_block_duration "@eq 0" \ + "id:901152,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.reput_block_duration=300'" + +# Default HTTP policy: allowed_methods (rule 900200) +SecRule &TX:allowed_methods "@eq 0" \ + "id:901160,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_methods=GET HEAD POST OPTIONS'" + +# Default HTTP policy: allowed_request_content_type (rule 900220) +SecRule &TX:allowed_request_content_type "@eq 0" \ + "id:901162,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_request_content_type=|application/x-www-form-urlencoded| |multipart/form-data| |multipart/related| |text/xml| |application/xml| |application/soap+xml| |application/x-amf| |application/json| |application/cloudevents+json| |application/cloudevents-batch+json| |application/octet-stream| |application/csp-report| |application/xss-auditor-report| |text/plain|'" + +# Default HTTP policy: allowed_request_content_type_charset (rule 900270) +SecRule &TX:allowed_request_content_type_charset "@eq 0" \ + "id:901168,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_request_content_type_charset=|utf-8| |iso-8859-1| |iso-8859-15| |windows-1252|'" + +# Default HTTP policy: allowed_http_versions (rule 900230) +SecRule &TX:allowed_http_versions "@eq 0" \ + "id:901163,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_http_versions=HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0'" + +# Default HTTP policy: restricted_extensions (rule 900240) +SecRule &TX:restricted_extensions "@eq 0" \ + "id:901164,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.restricted_extensions=.asa/ .asax/ .ascx/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .rdb/ .resources/ .resx/ .sql/ .swp/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/'" + +# Default HTTP policy: restricted_headers (rule 900250) +SecRule &TX:restricted_headers "@eq 0" \ + "id:901165,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.restricted_headers=/proxy/ /lock-token/ /content-range/ /if/ /user-agentt/'" + +# Default HTTP policy: static_extensions (rule 900260) +SecRule &TX:static_extensions "@eq 0" \ + "id:901166,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.static_extensions=/.jpg/ /.jpeg/ /.png/ /.gif/ /.js/ /.css/ /.ico/ /.svg/ /.webp/'" + +# Default enforcing of body processor URLENCODED +SecRule &TX:enforce_bodyproc_urlencoded "@eq 0" \ + "id:901167,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.enforce_bodyproc_urlencoded=0'" + +# +# -=[ Initialize internal variables ]=- +# + +# Initialize anomaly scoring variables. +# All _score variables start at 0, and are incremented by the various rules +# upon detection of a possible attack. +# sql_error_match is used for shortcutting rules for performance reasons. + +SecAction \ + "id:901200,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.anomaly_score=0',\ + setvar:'tx.anomaly_score_pl1=0',\ + setvar:'tx.anomaly_score_pl2=0',\ + setvar:'tx.anomaly_score_pl3=0',\ + setvar:'tx.anomaly_score_pl4=0',\ + setvar:'tx.sql_injection_score=0',\ + setvar:'tx.xss_score=0',\ + setvar:'tx.rfi_score=0',\ + setvar:'tx.lfi_score=0',\ + setvar:'tx.rce_score=0',\ + setvar:'tx.php_injection_score=0',\ + setvar:'tx.http_violation_score=0',\ + setvar:'tx.session_fixation_score=0',\ + setvar:'tx.inbound_anomaly_score=0',\ + setvar:'tx.outbound_anomaly_score=0',\ + setvar:'tx.outbound_anomaly_score_pl1=0',\ + setvar:'tx.outbound_anomaly_score_pl2=0',\ + setvar:'tx.outbound_anomaly_score_pl3=0',\ + setvar:'tx.outbound_anomaly_score_pl4=0',\ + setvar:'tx.sql_error_match=0'" + + +# +# -=[ Initialize collections ]=- +# +# Create both Global and IP collections for rules to use. +# There are some CRS rules that assume that these two collections +# have already been initiated. +# + +SecRule REQUEST_HEADERS:User-Agent "@rx ^.*$" \ + "id:901318,\ + phase:1,\ + pass,\ + t:none,t:sha1,t:hexEncode,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.ua_hash=%{MATCHED_VAR}'" + +SecAction \ + "id:901321,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + initcol:global=global,\ + initcol:ip=%{remote_addr}_%{tx.ua_hash},\ + setvar:'tx.real_ip=%{remote_addr}'" + +# +# -=[ Initialize Correct Body Processing ]=- +# +# Force request body variable and optionally request body processor +# + +# Force body variable +SecRule REQBODY_PROCESSOR "!@rx (?:URLENCODED|MULTIPART|XML|JSON)" \ + "id:901340,\ + phase:1,\ + pass,\ + nolog,\ + noauditlog,\ + msg:'Enabling body inspection',\ + tag:'paranoia-level/1',\ + ctl:forceRequestBodyVariable=On,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Force body processor URLENCODED +SecRule TX:enforce_bodyproc_urlencoded "@eq 1" \ + "id:901350,\ + phase:1,\ + pass,\ + t:none,t:urlDecodeUni,\ + nolog,\ + noauditlog,\ + msg:'Enabling forced body inspection for ASCII content',\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQBODY_PROCESSOR "!@rx (?:URLENCODED|MULTIPART|XML|JSON)" \ + "ctl:requestBodyProcessor=URLENCODED" + + +# +# -=[ Easing In / Sampling Percentage ]=- +# +# This is used to send only a limited percentage of requests into the Core +# Rule Set. The selection is based on TX.sampling_percentage and a pseudo +# random number calculated below. +# +# Use this to ease into a new Core Rules installation with an existing +# productive service. +# +# See +# https://www.netnea.com/cms/2016/04/26/easing-in-conditional-modsecurity-rule-execution-based-on-pseudo-random-numbers/ +# + +# +# Generate the pseudo random number +# +# ATTENTION: This is no cryptographically secure random number. It's just +# a cheap way to get some random number suitable for sampling. +# +# We take the entropy contained in the UNIQUE_ID. We hash that variable and +# take the first integer numbers out of it. Theoretically, it is possible +# but highly improbable that there are no integers in a hexEncoded sha1 hash. +# In the very rare event that two integers are not matched (due to only being +# a-f in all, or all but one positions) 901450 will not be triggered. +# Leading zeros are not removed from the two-digit random number, and are +# handled gracefullly by 901450 + +SecRule TX:sampling_percentage "@eq 100" \ + "id:901400,\ + phase:1,\ + pass,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-SAMPLING" + +SecRule UNIQUE_ID "@rx ^[a-f]*([0-9])[a-f]*([0-9])" \ + "id:901410,\ + phase:1,\ + pass,\ + capture,\ + t:sha1,t:hexEncode,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'TX.sampling_rnd100=%{TX.1}%{TX.2}'" + +# +# Sampling decision +# +# If a request is allowed to pass without being checked by the CRS, there is no +# entry in the audit log (for performance reasons), but an error log entry is +# being written. If you want to disable the error log entry, then issue the +# following directive somewhere after the inclusion of the CRS +# (E.g., RESPONSE-999-EXCEPTIONS.conf). +# +# SecRuleUpdateActionById 901450 "nolog" +# + + +SecRule TX:sampling_rnd100 "!@lt %{tx.sampling_percentage}" \ + "id:901450,\ + phase:1,\ + pass,\ + log,\ + noauditlog,\ + msg:'Sampling: Disable the rule engine based on sampling_percentage %{TX.sampling_percentage} and random number %{TX.sampling_rnd100}',\ + ctl:ruleRemoveByTag=OWASP_CRS,\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecMarker "END-SAMPLING" + + +# +# Configuration Plausibility Checks +# + +# Make sure executing paranoia level is not lower than paranoia level +SecRule TX:executing_paranoia_level "@lt %{tx.paranoia_level}" \ + "id:901500,\ + phase:1,\ + deny,\ + status:500,\ + t:none,\ + log,\ + msg:'Executing paranoia level configured is lower than the paranoia level itself. This is illegal. Blocking request. Aborting',\ + ver:'OWASP_CRS/3.4.0-dev'" diff --git a/samples/ModSecurity SecRule/REQUEST-903.9001-DRUPAL-EXCLUSION-RULES.conf b/samples/ModSecurity SecRule/REQUEST-903.9001-DRUPAL-EXCLUSION-RULES.conf new file mode 100644 index 0000000000..970aedea05 --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-903.9001-DRUPAL-EXCLUSION-RULES.conf @@ -0,0 +1,423 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# These exclusions remedy false positives in a default Drupal install. +# The exclusions are only active if crs_exclusions_drupal=1 is set. +# See rule 900130 in crs-setup.conf.example for instructions. + +# +# [ POLICY ] +# +# Drupal is a complex application that is hard to secure with the CRS. This set +# of exclusion rules aims to sanitise the CRS in a way that allows a default +# Drupal setup to be installed and configured without much hassle as far as +# ModSecurity and the CRS are concerned. +# +# The exclusion rules are fairly straight forward in the sense that they +# disable CRS on a set of well-known parameter fields that are often the source +# of false positives / false alarms of the CRS. This includes namely the +# session cookie, the password fields and article/node bodies. +# +# This is based on two assumptions: - You have a basic trust in your +# authenticated users who are allowed to edit nodes. - Drupal allows html +# content in nodes and it protects your users from attacks via these fields. +# +# If you think these assumptions are wrong or if you would prefer a more +# careful/secure approach, you can disable the exclusion rules handling of said +# node body false positives. Do this by placing the following directive in +# RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf. +# +# SecRuleRemoveById 9001200-9001299 +# +# This will mean the CRS remain intact for the editing of node bodies. +# +# The exclusion rules in this file work without the need to define a Drupal +# installation path prefix. Instead they look at the URI from the end - or +# they use regular expressions when targeting dynamic URL. This is all not +# totally foolproof. In some cases, an advanced attacker might be able to +# doctor a request in a way that one of these exclusion rules is triggered +# and the request will bypass all further inspection despite not being a +# Drupal request at all. These exclusion rules could thus be leveraged to +# disable the CRS completely. This is why these rules are off by default. +# +# The CRS rules covered by this ruleset are the rules with Paranoia Level 1 and +# 2. If you chose to run Paranoia Level 3 or 4, you will be facing additional +# false positives which you need to handle yourself. +# +# This set of exclusion rules does not cover any additional Drupal modules +# outside of core. +# +# The exclusion rules are based on Drupal 8.1.10. +# +# And finally: This set of exclusion rules is in an experimental state. If you +# encounter false positives with the basic Drupal functionality and they are +# not covered by this rule file, then please report them. The aim is to be able +# to install and run Drupal core in a seamless manner protected by +# ModSecurity / CRS up to the paranoia level 2. + + +SecRule &TX:crs_exclusions_drupal|TX:crs_exclusions_drupal "@eq 0" \ + "id:9001000,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-DRUPAL-RULE-EXCLUSIONS" + +SecRule &TX:crs_exclusions_drupal|TX:crs_exclusions_drupal "@eq 0" \ + "id:9001001,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-DRUPAL-RULE-EXCLUSIONS" + + +# [ Table of Contents ] +# +# 9001100 Session Cookie +# 9001110 Password +# 9001120 FREE for use +# 9001130 FREE for use +# 9001140 Content and Descriptions +# 9001150 FREE for use +# 9001160 Form Token +# 9001170 Text Formats and Editors +# 9001180 WYSIWYG/CKEditor Assets and Upload +# 9001190 FREE for use +# 9001200 Content and Descriptions +# +# The rule id range from 9001200 to 9001999 is reserved for future +# use (Drupal plugins / modules). + + +# [ Session Cookie ] +# +# Giving the session cookie a dynamic name is most unfortunate +# from a ModSecurity perspective. The rule language does not allow +# us to disable rules in a granular way for individual cookies with +# dynamic names. So we need to disable rule causing false positives +# for all cookies and their names. +# +# Rule Exclusion Session Cookie: 942450 SQL Hex Encoding Identified +# +SecAction "id:9001100,\ + phase:2,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetById=942450;REQUEST_COOKIES_NAMES,\ + ctl:ruleRemoveTargetById=942450;REQUEST_COOKIES,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ Password ] +# +# Disable the CRS completely for all occurrences of passwords. +# +SecRule REQUEST_FILENAME "@endsWith /core/install.php" \ + "id:9001110,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:account[pass][pass1],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:account[pass][pass2],\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@endsWith /user/login" \ + "id:9001112,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass,\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@endsWith /admin/people/create" \ + "id:9001114,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass[pass1],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass[pass2],\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@rx /user/[0-9]+/edit$" \ + "id:9001116,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:current_pass,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass[pass1],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass[pass2],\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ Admin Settings (general) ] +# +# Disable known false positives for various fields used on admin pages. +# +# Rule Exclusion: 920271 Invalid character in request on multiple fields/paths +# Rule Exclusion: 942430 Restricted SQL Character Anomaly Detection (args) +# Disabled completely for admin/config pages +# For the people/accounts page, we disable the CRS completely for a number of +# freeform text fields. +# +SecRule REQUEST_FILENAME "@contains /admin/config/" \ + "id:9001122,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveById=942430,\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@endsWith /admin/config/people/accounts" \ + "id:9001124,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveById=920271,\ + ctl:ruleRemoveById=942440,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:user_mail_cancel_confirm_body,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:user_mail_password_reset_body,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:user_mail_register_admin_created_body,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:user_mail_register_no_approval_required_body,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:user_mail_register_pending_approval_body,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:user_mail_status_activated_body,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:user_mail_status_blocked_body,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:user_mail_status_canceled_body,\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@endsWith /admin/config/development/configuration/single/import" \ + "id:9001126,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveById=920271,\ + ctl:ruleRemoveById=942440,\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@endsWith /admin/config/development/maintenance" \ + "id:9001128,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveById=942440,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# +# [ Content and Descriptions ] +# +# Disable known false positives for field "ids[]". +# +# Rule Exclusion: 942130 SQL Injection Attack: SQL Tautology Detected +# +SecRule REQUEST_FILENAME "@endsWith /contextual/render" \ + "id:9001140,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetById=942130;ARGS:ids[],\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ Form Token / Build ID ] +# +# Rule Exclusion for form_build_id: 942440 SQL Comment Sequence Detected on ... +# Rule Exclusion for form_token: 942450 SQL Hex Encoding +# Rule Exclusion for form_build_id: 942450 SQL Hex Encoding +# +# This is applied site-wide. +# +SecAction "id:9001160,\ + phase:2,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetById=942440;ARGS:form_build_id,\ + ctl:ruleRemoveTargetById=942450;ARGS:form_token,\ + ctl:ruleRemoveTargetById=942450;ARGS:form_build_id,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ Text Formats and Editors ] +# +# Disable the CRS completely for two fields triggering many, many rules +# +# Rule Exclusion for two fields: 942440 SQL Comment Sequence Detected +# +SecRule REQUEST_FILENAME "@endsWith /admin/config/content/formats/manage/full_html" \ + "id:9001170,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:editor[settings][toolbar][button_groups],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:filters[filter_html][settings][allowed_html],\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ WYSIWYG/CKEditor Assets and Upload ] +# +# Disable the unnecessary requestBodyAccess and for binary uploads +# bigger than an arbitrary limit of 31486341 bytes. +# +# Extensive checks make sure these uploads are really legitimate. +# +# Rule 9001180 was commented out in 2021 in order to fight CVE-2021-35368. +# +#SecRule REQUEST_METHOD "@streq POST" \ +# "id:9001180,\ +# phase:1,\ +# pass,\ +# t:none,\ +# nolog,\ +# noauditlog,\ +# ver:'OWASP_CRS/3.4.0-dev',\ +# chain" +# SecRule REQUEST_FILENAME "@rx /admin/content/assets/add/[a-z]+$" \ +# "chain" +# SecRule REQUEST_COOKIES:/S?SESS[a-f0-9]+/ "@rx ^[a-zA-Z0-9_-]+" \ +# "ctl:requestBodyAccess=Off" + +# Rule 9001182 was commented out in 2021 in order to fight CVE-2021-35368. +# +#SecRule REQUEST_METHOD "@streq POST" \ +# "id:9001182,\ +# phase:1,\ +# pass,\ +# t:none,\ +# nolog,\ +# noauditlog,\ +# ver:'OWASP_CRS/3.4.0-dev',\ +# chain" +# SecRule REQUEST_FILENAME "@rx /admin/content/assets/manage/[0-9]+$" \ +# "chain" +# SecRule ARGS_GET:destination "@streq admin/content/assets" \ +# "chain" +# SecRule REQUEST_HEADERS:Content-Length "@gt 31486341" \ +# "chain" +# SecRule REQUEST_COOKIES:/S?SESS[a-f0-9]+/ "@rx ^[a-zA-Z0-9_-]+" \ +# "ctl:requestBodyAccess=Off" + +# Rule 9001184 was commented out in 2021 in order to fight CVE-2021-35368. +# +#SecRule REQUEST_METHOD "@streq POST" \ +# "id:9001184,\ +# phase:1,\ +# pass,\ +# t:none,\ +# nolog,\ +# noauditlog,\ +# ver:'OWASP_CRS/3.4.0-dev',\ +# chain" +# SecRule REQUEST_FILENAME "@rx /file/ajax/field_asset_[a-z0-9_]+/[ua]nd/0/form-[a-z0-9A-Z_-]+$" \ +# "chain" +# SecRule REQUEST_HEADERS:Content-Length "@gt 31486341" \ +# "chain" +# SecRule REQUEST_HEADERS:Content-Type "@rx ^(?i)multipart/form-data" \ +# "chain" +# SecRule REQUEST_COOKIES:/S?SESS[a-f0-9]+/ "@rx ^[a-zA-Z0-9_-]+" \ +# "ctl:requestBodyAccess=Off" + + +# +# [ Content and Descriptions ] +# +# Disable the CRS completely for node bodies and other free text fields. +# Other rules are disabled individually. +# +# Rule Exclusion for ARGS:uid[0][target_id]: 942410 SQL Injection Attack +# Rule Exclusion for ARGS:destination: 932110 RCE: Windows Command Inj. +# +SecRule REQUEST_FILENAME "@endsWith /node/add/article" \ + "id:9001200,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:body[0][value],\ + ctl:ruleRemoveTargetById=942410;ARGS:uid[0][target_id],\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@endsWith /node/add/page" \ + "id:9001202,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:body[0][value],\ + ctl:ruleRemoveTargetById=942410;ARGS:uid[0][target_id],\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@rx /node/[0-9]+/edit$" \ + "id:9001204,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:body[0][value],\ + ctl:ruleRemoveTargetById=942410;ARGS:uid[0][target_id],\ + ctl:ruleRemoveTargetById=932110;ARGS:destination,\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@endsWith /block/add" \ + "id:9001206,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:body[0][value],\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@endsWith /admin/structure/block/block-content/manage/basic" \ + "id:9001208,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:description,\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@rx /editor/filter_xss/(?:full|basic)_html$" \ + "id:9001210,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:value,\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@rx /user/[0-9]+/contact$" \ + "id:9001212,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:message[0][value],\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@endsWith /admin/config/development/maintenance" \ + "id:9001214,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:maintenance_mode_message,\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecRule REQUEST_FILENAME "@endsWith /admin/config/services/rss-publishing" \ + "id:9001216,\ + phase:1,\ + pass,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:feed_description,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +SecMarker "END-DRUPAL-RULE-EXCLUSIONS" diff --git a/samples/ModSecurity SecRule/REQUEST-903.9002-WORDPRESS-EXCLUSION-RULES.conf b/samples/ModSecurity SecRule/REQUEST-903.9002-WORDPRESS-EXCLUSION-RULES.conf new file mode 100644 index 0000000000..2e4fb44760 --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-903.9002-WORDPRESS-EXCLUSION-RULES.conf @@ -0,0 +1,833 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# These exclusions remedy false positives in a default WordPress install. +# The exclusions are only active if crs_exclusions_wordpress=1 is set. +# See rule 900130 in crs-setup.conf.example for instructions. +# +# Note that the WordPress comment field itself is currently NOT excluded +# from checking. The reason is that malicious content is regularly being +# posted to WordPress comment forms, and there have been various cases +# of XSS and even RCE vulnerabilities exploited by WordPress comments. + +SecRule &TX:crs_exclusions_wordpress|TX:crs_exclusions_wordpress "@eq 0" \ + "id:9002000,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-WORDPRESS" + +SecRule &TX:crs_exclusions_wordpress|TX:crs_exclusions_wordpress "@eq 0" \ + "id:9002001,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-WORDPRESS" + + +# +# -=[ WordPress Front-End ]=- +# + + +# +# [ Login form ] +# + +# User login password +SecRule REQUEST_FILENAME "@endsWith /wp-login.php" \ + "id:9002100,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pwd,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Reset password +SecRule REQUEST_FILENAME "@endsWith /wp-login.php" \ + "id:9002120,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@streq resetpass" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass1,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass1-text,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass2" + + +# +# [ Comments ] +# + +# Post comment +SecRule REQUEST_FILENAME "@endsWith /wp-comments-post.php" \ + "id:9002130,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=931130;ARGS:url,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ Gutenberg Editor ] +# Used when a user (auto)saves a post/page with Gutenberg. +# + +# Gutenberg +SecRule REQUEST_FILENAME "@rx /wp-json/wp/v[0-9]+/(?:posts|pages)" \ + "id:9002140,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:content,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:json.content,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Gutenberg via rest_route for sites without pretty permalinks +SecRule REQUEST_FILENAME "@endsWith /index.php" \ + "id:9002141,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule &ARGS_GET:rest_route "@eq 1" \ + "t:none,\ + nolog,\ + chain" + SecRule ARGS_GET:rest_route "@rx ^/wp/v[0-9]+/(?:posts|pages|widget-types)" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:content,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:json.content,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:instance.raw.text" + +# Gutenberg upload image/media +SecRule REQUEST_FILENAME "@rx /wp-json/wp/v[0-9]+/media" \ + "id:9002142,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveById=200002,\ + ctl:ruleRemoveById=200003,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Gutenberg upload image/media via rest_route for sites without pretty permalinks +SecRule REQUEST_FILENAME "@endsWith /index.php" \ + "id:9002143,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule &ARGS_GET:rest_route "@eq 1" \ + "t:none,\ + nolog,\ + chain" + SecRule ARGS_GET:rest_route "@rx ^/wp/v[0-9]+/media" \ + "t:none,\ + ctl:ruleRemoveById=200002,\ + ctl:ruleRemoveById=200003" + +# +# [ Live preview ] +# Used when an administrator customizes the site and previews the result +# as a normal user. +# + +# Theme select +# Example: wp_customize=on&theme=twentyfifteen&customized= +# {"old_sidebars_widgets_data":{"wp_inactive_widgets":[], +# "sidebar-1":["search-2","recent-posts-2","recent-comments-2", +# "archives-2","categories-2","meta-2"]}}&nonce=XXX& +# customize_messenger_channel=preview-0 +SecRule ARGS:wp_customize "@streq on" \ + "id:9002150,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule &ARGS:action "@eq 0" \ + "t:none,\ + ctl:ruleRemoveTargetById=942200;ARGS:customized,\ + ctl:ruleRemoveTargetById=942260;ARGS:customized,\ + ctl:ruleRemoveTargetById=942300;ARGS:customized,\ + ctl:ruleRemoveTargetById=942330;ARGS:customized,\ + ctl:ruleRemoveTargetById=942340;ARGS:customized,\ + ctl:ruleRemoveTargetById=942370;ARGS:customized,\ + ctl:ruleRemoveTargetById=942430;ARGS:customized,\ + ctl:ruleRemoveTargetById=942431;ARGS:customized,\ + ctl:ruleRemoveTargetById=942460;ARGS:customized" + +# Appearance -> Widgets -> Live Preview +SecRule ARGS:wp_customize "@streq on" \ + "id:9002160,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@rx ^(?:|customize_save|update-widget)$" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=942200;ARGS:customized,\ + ctl:ruleRemoveTargetById=942260;ARGS:customized,\ + ctl:ruleRemoveTargetById=942300;ARGS:customized,\ + ctl:ruleRemoveTargetById=942330;ARGS:customized,\ + ctl:ruleRemoveTargetById=942340;ARGS:customized,\ + ctl:ruleRemoveTargetById=942370;ARGS:customized,\ + ctl:ruleRemoveTargetById=942430;ARGS:customized,\ + ctl:ruleRemoveTargetById=942431;ARGS:customized,\ + ctl:ruleRemoveTargetById=942460;ARGS:customized,\ + ctl:ruleRemoveTargetById=920230;ARGS:partials,\ + ctl:ruleRemoveTargetById=941320;ARGS:partials,\ + ctl:ruleRemoveTargetById=942180;ARGS:partials,\ + ctl:ruleRemoveTargetById=942200;ARGS:partials,\ + ctl:ruleRemoveTargetById=942260;ARGS:partials,\ + ctl:ruleRemoveTargetById=942330;ARGS:partials,\ + ctl:ruleRemoveTargetById=942340;ARGS:partials,\ + ctl:ruleRemoveTargetById=942370;ARGS:partials,\ + ctl:ruleRemoveTargetById=942430;ARGS:partials,\ + ctl:ruleRemoveTargetById=942431;ARGS:partials,\ + ctl:ruleRemoveTargetById=942460;ARGS:partials" + + + +# Self calls to wp-cron.php?doing_wp_cron=[timestamp] +# These requests may be missing Accept, Content-Length headers. +# This rule must run in phase:1. +SecRule REQUEST_FILENAME "@endsWith /wp-cron.php" \ + "id:9002200,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveById=920180,\ + ctl:ruleRemoveById=920300,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ Cookies ] + +# WP Session Manager +# Cookie: _wp_session=[hex]||[timestamp]||[timestamp] +# detected SQLi using libinjection with fingerprint 'n&1' +SecRule REQUEST_COOKIES:_wp_session "@rx ^[0-9a-f]+\|\|\d+\|\|\d+$" \ + "id:9002300,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule &REQUEST_COOKIES:_wp_session "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=942100;REQUEST_COOKIES:_wp_session" + + +# +# -=[ WordPress Administration Back-End (wp-admin) ]=- +# + +# Skip this section for performance unless /wp-admin/ is in filename + +SecRule REQUEST_FILENAME "!@contains /wp-admin/" \ + "id:9002400,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-WORDPRESS-ADMIN" + +SecRule REQUEST_FILENAME "!@contains /wp-admin/" \ + "id:9002401,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-WORDPRESS-ADMIN" + + +# +# [ Installation ] +# + +# WordPress installation: exclude database password +SecRule REQUEST_FILENAME "@endsWith /wp-admin/setup-config.php" \ + "id:9002410,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:step "@streq 2" \ + "t:none,\ + chain" + SecRule &ARGS:step "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pwd" + +# WordPress installation: exclude admin password +SecRule REQUEST_FILENAME "@endsWith /wp-admin/install.php" \ + "id:9002420,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:step "@streq 2" \ + "t:none,\ + chain" + SecRule &ARGS:step "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:admin_password,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:admin_password2,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass1-text" + + +# +# [ User management ] +# + +# Edit logged-in user +SecRule REQUEST_FILENAME "@endsWith /wp-admin/profile.php" \ + "id:9002520,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@streq update" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=931130;ARGS:url,\ + ctl:ruleRemoveTargetById=931130;ARGS:facebook,\ + ctl:ruleRemoveTargetById=931130;ARGS:instagram,\ + ctl:ruleRemoveTargetById=931130;ARGS:linkedin,\ + ctl:ruleRemoveTargetById=931130;ARGS:myspace,\ + ctl:ruleRemoveTargetById=931130;ARGS:pinterest,\ + ctl:ruleRemoveTargetById=931130;ARGS:soundcloud,\ + ctl:ruleRemoveTargetById=931130;ARGS:tumblr,\ + ctl:ruleRemoveTargetById=931130;ARGS:youtube,\ + ctl:ruleRemoveTargetById=931130;ARGS:wikipedia,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass1,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass1-text,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass2" + +# Edit user +SecRule REQUEST_FILENAME "@endsWith /wp-admin/user-edit.php" \ + "id:9002530,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@streq update" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=931130;ARGS:url,\ + ctl:ruleRemoveTargetById=931130;ARGS:url,\ + ctl:ruleRemoveTargetById=931130;ARGS:facebook,\ + ctl:ruleRemoveTargetById=931130;ARGS:instagram,\ + ctl:ruleRemoveTargetById=931130;ARGS:linkedin,\ + ctl:ruleRemoveTargetById=931130;ARGS:myspace,\ + ctl:ruleRemoveTargetById=931130;ARGS:pinterest,\ + ctl:ruleRemoveTargetById=931130;ARGS:soundcloud,\ + ctl:ruleRemoveTargetById=931130;ARGS:tumblr,\ + ctl:ruleRemoveTargetById=931130;ARGS:youtube,\ + ctl:ruleRemoveTargetById=931130;ARGS:wikipedia,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass1,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass1-text,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass2" + +# Create user +SecRule REQUEST_FILENAME "@endsWith /wp-admin/user-new.php" \ + "id:9002540,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@streq createuser" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=931130;ARGS:url,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass1,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass1-text,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass2" + + +# +# [ General exclusions ] +# + +# _wp_http_referer and wp_http_referer are passed on a lot of wp-admin pages +SecAction \ + "id:9002600,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=920230;ARGS:_wp_http_referer,\ + ctl:ruleRemoveTargetById=931130;ARGS:_wp_http_referer,\ + ctl:ruleRemoveTargetById=932150;ARGS:_wp_http_referer,\ + ctl:ruleRemoveTargetById=932200;ARGS:_wp_http_referer,\ + ctl:ruleRemoveTargetById=941100;ARGS:_wp_http_referer,\ + ctl:ruleRemoveTargetById=942130;ARGS:_wp_http_referer,\ + ctl:ruleRemoveTargetById=942200;ARGS:_wp_http_referer,\ + ctl:ruleRemoveTargetById=942230;ARGS:_wp_http_referer,\ + ctl:ruleRemoveTargetById=942260;ARGS:_wp_http_referer,\ + ctl:ruleRemoveTargetById=942431;ARGS:_wp_http_referer,\ + ctl:ruleRemoveTargetById=942440;ARGS:_wp_http_referer,\ + ctl:ruleRemoveTargetById=920230;ARGS:wp_http_referer,\ + ctl:ruleRemoveTargetById=931130;ARGS:wp_http_referer,\ + ctl:ruleRemoveTargetById=932150;ARGS:wp_http_referer,\ + ctl:ruleRemoveTargetById=932200;ARGS:wp_http_referer,\ + ctl:ruleRemoveTargetById=941100;ARGS:wp_http_referer,\ + ctl:ruleRemoveTargetById=942130;ARGS:wp_http_referer,\ + ctl:ruleRemoveTargetById=942200;ARGS:wp_http_referer,\ + ctl:ruleRemoveTargetById=942230;ARGS:wp_http_referer,\ + ctl:ruleRemoveTargetById=942260;ARGS:wp_http_referer,\ + ctl:ruleRemoveTargetById=942431;ARGS:wp_http_referer,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# +# [ Content editing ] +# + +# Edit posts and pages +# /wp-admin/post.php, /wp-admin/post.php?t=[timestamp] +# - Themes do not properly escape post_title in HTML, so beware of XSS +# and be conservative in excluding this parameter. +# - Parameter _wp_http_referer can appear multiple times. +SecRule REQUEST_FILENAME "@endsWith /wp-admin/post.php" \ + "id:9002700,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@rx ^(?:edit|editpost)$" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:post_title,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:content,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:excerpt,\ + ctl:ruleRemoveById=920272,\ + ctl:ruleRemoveById=921180" + +# Autosave posts and pages +# ARGS_NAMES:data[wp-check-locked-posts][] can appear multiple times +SecRule REQUEST_FILENAME "@endsWith /wp-admin/admin-ajax.php" \ + "id:9002710,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@streq heartbeat" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:data[wp_autosave][post_title],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:data[wp_autosave][content],\ + ctl:ruleRemoveTargetById=941270;ARGS:data[wp_autosave][post_title],\ + ctl:ruleRemoveTargetById=942431;ARGS_NAMES:data[wp-refresh-post-lock][post_id],\ + ctl:ruleRemoveTargetById=942431;ARGS_NAMES:data[wp-refresh-post-lock][lock],\ + ctl:ruleRemoveTargetById=942431;ARGS_NAMES:data[wp-check-locked-posts][],\ + ctl:ruleRemoveById=921180,\ + ctl:ruleRemoveById=920272" + +# Edit menus +SecRule REQUEST_FILENAME "@endsWith /wp-admin/nav-menus.php" \ + "id:9002720,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@rx ^(?:update|edit)$" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=932200;ARGS,\ + ctl:ruleRemoveTargetById=932150;ARGS,\ + ctl:ruleRemoveTargetById=942460;ARGS:menu-name,\ + ctl:ruleRemoveTargetById=932200;ARGS:nav-menu-data,\ + ctl:ruleRemoveTargetById=941330;ARGS:nav-menu-data,\ + ctl:ruleRemoveTargetById=941340;ARGS:nav-menu-data,\ + ctl:ruleRemoveTargetById=942200;ARGS:nav-menu-data,\ + ctl:ruleRemoveTargetById=942260;ARGS:nav-menu-data,\ + ctl:ruleRemoveTargetById=942330;ARGS:nav-menu-data,\ + ctl:ruleRemoveTargetById=942340;ARGS:nav-menu-data,\ + ctl:ruleRemoveTargetById=942430;ARGS:nav-menu-data,\ + ctl:ruleRemoveTargetById=942431;ARGS:nav-menu-data,\ + ctl:ruleRemoveTargetById=942460;ARGS:nav-menu-data" + +# Edit text widgets (can contain custom HTML) +SecRule REQUEST_FILENAME "@endsWith /wp-admin/admin-ajax.php" \ + "id:9002730,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@rx ^(?:save-widget|update-widget)$" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[0][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[1][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[2][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[3][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[4][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[5][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[6][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[7][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[8][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[9][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[10][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[11][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[12][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[13][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[14][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[15][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[16][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[17][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[18][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[19][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[20][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[21][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[22][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[23][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[24][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[25][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[26][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[27][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[28][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[29][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[30][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[31][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[32][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[33][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[34][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[35][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[36][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[37][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[38][text],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:widget-text[39][text]" + +# Reorder widgets +SecRule REQUEST_FILENAME "@endsWith /wp-admin/admin-ajax.php" \ + "id:9002740,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@streq widgets-order" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=942430;ARGS:sidebars[sidebar-1],\ + ctl:ruleRemoveTargetById=942431;ARGS:sidebars[sidebar-1],\ + ctl:ruleRemoveTargetById=942430;ARGS:sidebars[sidebar-2],\ + ctl:ruleRemoveTargetById=942431;ARGS:sidebars[sidebar-2],\ + ctl:ruleRemoveTargetById=942430;ARGS:sidebars[sidebar-3],\ + ctl:ruleRemoveTargetById=942431;ARGS:sidebars[sidebar-3],\ + ctl:ruleRemoveTargetById=942430;ARGS:sidebars[sidebar-4],\ + ctl:ruleRemoveTargetById=942431;ARGS:sidebars[sidebar-4],\ + ctl:ruleRemoveTargetById=942430;ARGS:sidebars[sidebar-5],\ + ctl:ruleRemoveTargetById=942431;ARGS:sidebars[sidebar-5],\ + ctl:ruleRemoveTargetById=942430;ARGS:sidebars[sidebar-6],\ + ctl:ruleRemoveTargetById=942431;ARGS:sidebars[sidebar-6],\ + ctl:ruleRemoveTargetById=942430;ARGS:sidebars[sidebar-7],\ + ctl:ruleRemoveTargetById=942431;ARGS:sidebars[sidebar-7]" + +# Create permalink sample for new post +SecRule REQUEST_FILENAME "@endsWith /wp-admin/admin-ajax.php" \ + "id:9002750,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@streq sample-permalink" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:new_title,\ + ctl:ruleRemoveTargetById=941270;ARGS:new_title" + +# Add external link to menu +SecRule REQUEST_FILENAME "@endsWith /wp-admin/admin-ajax.php" \ + "id:9002760,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@streq add-menu-item" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=931130;ARGS:menu-item[-1][menu-item-url]" + +# Editor: Add Media, Insert Media, Insert into page +SecRule REQUEST_FILENAME "@endsWith /wp-admin/admin-ajax.php" \ + "id:9002770,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@streq send-attachment-to-editor" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:html" + + +# +# [ Options and Settings ] +# + +# Change site URL +SecRule REQUEST_FILENAME "@endsWith /wp-admin/options.php" \ + "id:9002800,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:option_page "@streq general" \ + "t:none,\ + chain" + SecRule &ARGS:option_page "@eq 1" \ + "t:none,\ + chain" + SecRule ARGS:action "@streq update" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=931130;ARGS:home,\ + ctl:ruleRemoveTargetById=931130;ARGS:siteurl" + +# Permalink settings +# permalink_structure=/index.php/%year%/%monthnum%/%day%/%postname%/ +SecRule REQUEST_FILENAME "@endsWith /wp-admin/options-permalink.php" \ + "id:9002810,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=920230;ARGS:selection,\ + ctl:ruleRemoveTargetById=920272;ARGS:selection,\ + ctl:ruleRemoveTargetById=942431;ARGS:selection,\ + ctl:ruleRemoveTargetById=920230;ARGS:permalink_structure,\ + ctl:ruleRemoveTargetById=920272;ARGS:permalink_structure,\ + ctl:ruleRemoveTargetById=942431;ARGS:permalink_structure,\ + ctl:ruleRemoveTargetById=920272;REQUEST_BODY,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Comments deny list and moderation list +SecRule REQUEST_FILENAME "@endsWith /wp-admin/options.php" \ + "id:9002820,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:option_page "@streq discussion" \ + "t:none,\ + chain" + SecRule &ARGS:option_page "@eq 1" \ + "t:none,\ + chain" + SecRule ARGS:action "@streq update" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:blacklist_keys,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:moderation_keys" + +# Posts/pages overview search +SecRule REQUEST_FILENAME "@endsWith /wp-admin/edit.php" \ + "id:9002830,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:s,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Wordpress Site Health +# The wordpress site health page makes use of embedded SQL/PHP +# which triggers PHP/SQL leak rules. +SecRule REQUEST_FILENAME "@rx /wp-admin/site-health.php" \ + "id:9002840,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveById=951220,\ + ctl:ruleRemoveById=953110,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# +# [ Helpers ] +# + +# /wp-admin/load-scripts.php?c=0&load%5B%5D=hoverIntent,common, +# admin-bar,wp-ajax-response,jquery-color,wp-lists,quicktags, +# jquery-query,admin-comments,svg-painter,heartbeat,&load%5B%5D= +# wp-auth-check,wp-a11y,wplink,jquery-ui-core,jquery-ui-widget, +# jquery-ui-position,jquery-ui-menu,jquery-ui-autocomplete&ver=4.6.1 +# +# /wp-admin/load-styles.php?c=0&dir=ltr&load%5B%5D=dashicons, +# admin-bar,buttons,media-views,common,forms,admin-menu,dashboard, +# list-tables,edit,revisions,media,themes,about,nav-menu&load%5B%5D= +# s,widgets,site-icon,l10n,wp-auth-check&ver=4.6.1 +# +# /wp-admin/load-scripts.php?c=0&load%5B%5D=hoverIntent,common, +# admin-bar,jquery-ui-widget,jquery-ui-position,wp-pointer, +# wp-ajax-response,jquery-color,wp-lists,quicktags, +# jqu&load%5B%5D=ery-query,admin-comments,jquery-ui-core, +# jquery-ui-mouse,jquery-ui-sortable,postbox,dashboard,underscore, +# customize-base,customize&load%5B%5D=-loader,thickbox,plugin-install, +# wp-util,wp-a11y,updates,shortcode,media-upload,svg-painter, +# jquery-ui-accordion&ver=3f9999390861a0133beda3ee8acf152e +SecRule REQUEST_FILENAME "@rx /wp-admin/load-(?:scripts|styles)\.php$" \ + "id:9002900,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveById=921180,\ + ctl:ruleRemoveTargetById=920273;ARGS_NAMES:load[],\ + ctl:ruleRemoveTargetById=942432;ARGS_NAMES:load[],\ + ctl:ruleRemoveTargetById=942360;ARGS:load[],\ + ctl:ruleRemoveTargetById=942430;ARGS:load[],\ + ctl:ruleRemoveTargetById=942431;ARGS:load[],\ + ctl:ruleRemoveTargetById=942432;ARGS:load[],\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# Site health output can trigger database error rule. +SecRule REQUEST_FILENAME "@endsWith /wp-admin/site-health.php" \ + "id:9002910,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveById=951220,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ Plugins management ] +# + +# Updating or deleting a plugin +SecRule REQUEST_FILENAME "@endsWith /wp-admin/admin-ajax.php" \ + "id:9002950,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@rx ^(?:update-plugin|delete-plugin)$" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=932120;ARGS:plugin,\ + ctl:ruleRemoveTargetById=932120;ARGS:slug" + +SecMarker "END-WORDPRESS-ADMIN" + + +# +# [ Plugins ] +# + +# Plugin slug: aioseo +# Plugin URL: https://wordpress.org/plugins/all-in-one-seo-pack/ +# FP: ARGS_NAMES:json.wizard.additionalInformation.social.profiles.sameUsername.included.array_2 +# This hits on '.profile', a protected file name. +SecRule REQUEST_FILENAME "@endsWith /wp-json/aioseo/v1/wizard" \ + "id:9002960,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=930120;ARGS_NAMES,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +SecMarker "END-WORDPRESS" diff --git a/samples/ModSecurity SecRule/REQUEST-903.9003-NEXTCLOUD-EXCLUSION-RULES.conf b/samples/ModSecurity SecRule/REQUEST-903.9003-NEXTCLOUD-EXCLUSION-RULES.conf new file mode 100644 index 0000000000..06e880f0d4 --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-903.9003-NEXTCLOUD-EXCLUSION-RULES.conf @@ -0,0 +1,560 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. (not) All rights reserved. +# Copyright (c) 2021 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. +# +# ------------------------------------------------------------------------ + +# These exclusions remedy false positives in a default Nextcloud install. +# They will likely work with OwnCloud too, but you may have to modify them. +# The exclusions are only active if crs_exclusions_nextcloud=1 is set. +# See rule 900130 in crs-setup.conf.example for instructions. +# +# To relax upload restrictions for only the php files that need it, +# you put something like this in crs-setup.conf: +# +# SecRule REQUEST_FILENAME "@rx /(?:remote.php|index.php)/" \ +# "id:9003600,\ +# phase:1,\ +# t:none,\ +# nolog,\ +# pass,\ +# setvar:'tx.restricted_extensions=.bak/ .config/ .conf/'" +# +# Large uploads can be modified with SecRequestBodyLimit. Or they +# can be more controlled by using the following: +# +# SecRule REQUEST_URI "@endsWith /index.php/apps/files/ajax/upload.php" \ +# "id:9003610,\ +# phase:1,\ +# t:none,\ +# nolog,\ +# ctl:requestBodyLimit=1073741824" +# +# Please note, that `ctl:requestBodyLimit` action does not work +# in libmodsecurity3! +# +# --------------------- + + +SecRule &TX:crs_exclusions_nextcloud|TX:crs_exclusions_nextcloud "@eq 0" \ + "id:9003000,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-NEXTCLOUD" + +SecRule &TX:crs_exclusions_nextcloud|TX:crs_exclusions_nextcloud "@eq 0" \ + "id:9003001,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-NEXTCLOUD" + + +# +# [ File Manager ] +# + +# The web interface uploads files, and interacts with the user. +SecRule REQUEST_FILENAME "@contains /remote.php/webdav" \ + "id:9003100,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveByTag=attack-injection-php,\ + ctl:ruleRemoveById=941000-942999,\ + ctl:ruleRemoveById=951000-951999,\ + ctl:ruleRemoveById=953100-953130,\ + ctl:ruleRemoveById=920420,\ + ctl:ruleRemoveById=920440,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Skip PUT parsing for invalid encoding / protocol violations in binary files. +SecRule REQUEST_METHOD "@streq PUT" \ + "id:9003105,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQUEST_FILENAME "@rx /remote\.php/(?:webdav|dav)" \ + "t:none,\ + ctl:ruleRemoveById=920000-920999,\ + ctl:ruleRemoveById=932000-932999,\ + ctl:ruleRemoveById=921150,\ + ctl:ruleRemoveById=930110,\ + ctl:ruleRemoveById=930120" + +# Fix for Nextcloud Desktop Client / Allow sending source code +SecRule REQUEST_METHOD "@pm PROPFIND PUT" \ + "id:9003106,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQUEST_FILENAME "@rx /remote\.php/(?:webdav|dav/files)" \ + "t:none,\ + ctl:ruleRemoveById=921110" + +# Allow the data type 'text/vcard' +SecRule REQUEST_FILENAME "@contains /remote.php/dav/files/" \ + "id:9003110,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_request_content_type=%{tx.allowed_request_content_type} |text/vcard|'" + +# Allow the data type 'application/octet-stream' +SecRule REQUEST_METHOD "@pm PUT MOVE" \ + "id:9003115,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQUEST_FILENAME "@rx /remote\.php/dav/(?:files|uploads)/" \ + "setvar:'tx.allowed_request_content_type=%{tx.allowed_request_content_type} |application/octet-stream|'" + +# Allow data types like video/mp4 +SecRule REQUEST_METHOD "@streq PUT" \ + "id:9003116,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQUEST_FILENAME "@rx /(?:public\.php/webdav|remote\.php/dav/uploads)/" \ + "ctl:ruleRemoveById=920340,\ + ctl:ruleRemoveById=920420" + +# Allow characters like /../ in files. +# Allow all kind of filetypes. +# Allow source code. +SecRule REQUEST_FILENAME "@contains /remote.php/dav/files/" \ + "id:9003120,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveById=930100-930110,\ + ctl:ruleRemoveById=951000-951999,\ + ctl:ruleRemoveById=953100-953130,\ + ctl:ruleRemoveById=920440,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Allow REPORT requests without Content-Type header (at least the iOS app does this) +SecRule REQUEST_METHOD "@streq REPORT" \ + "id:9003121,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule REQUEST_FILENAME "@contains /remote.php/dav/files/" \ + "t:none,\ + ctl:ruleRemoveById=920340" + + +# +# [ Searchengine ] +# + +# Nextcloud uses a search field for filename or content queries. +SecRule REQUEST_FILENAME "@contains /index.php/core/search" \ + "id:9003125,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=attack-injection-php;ARGS:query,\ + ctl:ruleRemoveTargetById=941000-942999;ARGS:query,\ + ctl:ruleRemoveTargetById=932000-932999;ARGS:query,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ DAV ] +# + +# Nextcloud uses DAV methods with index.php and remote.php to do many things +# The default ones in ModSecurity are: GET HEAD POST OPTIONS +# +# Looking through the code, and via testing, I found these: +# +# File manager: PUT DELETE MOVE PROPFIND PROPPATCH +# Calendars: REPORT +# Others in the code or js files: PATCH MKCOL MOVE TRACE +# Others that I added just in case, and they seem related: +# CHECKOUT COPY LOCK MERGE MKACTIVITY UNLOCK. +SecRule REQUEST_FILENAME "@rx /(?:remote|index|public)\.php/" \ + "id:9003130,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_methods=%{tx.allowed_methods} PUT PATCH CHECKOUT COPY DELETE LOCK MERGE MKACTIVITY MKCOL MOVE PROPFIND PROPPATCH SEARCH UNLOCK REPORT TRACE jsonp'" + +# We need to allow DAV methods for sharing files, and removing shares +# DELETE - when the share is removed +# PUT - when setting a password / expiration time +SecRule REQUEST_FILENAME "@rx /ocs/v[0-9]+\.php/apps/files_sharing/" \ + "id:9003140,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_methods=%{tx.allowed_methods} PUT DELETE'" + + +# +# [ Preview and Thumbnails ] +# + +# Preview +SecRule REQUEST_FILENAME "@contains /index.php/core/preview.png" \ + "id:9003150,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=932150;ARGS:file,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Filepreview for trashbin +SecRule REQUEST_FILENAME "@contains /index.php/apps/files_trashbin/ajax/preview.php" \ + "id:9003155,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=932150;ARGS:file,\ + ctl:ruleRemoveTargetById=942190;ARGS:file,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Thumbnails +SecRule REQUEST_FILENAME "@contains /index.php/apps/gallery/thumbnails" \ + "id:9003160,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=941120;ARGS:requesttoken,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ Ownnote ] +# + +SecRule REQUEST_FILENAME "@contains /index.php/apps/ownnote/" \ + "id:9003300,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveById=941150,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ Text Editor ] +# + +# This file can save anything, and it's name could be lots of things. +SecRule REQUEST_FILENAME "@contains /index.php/apps/files_texteditor/" \ + "id:9003310,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:filecontents,\ + ctl:ruleRemoveTargetById=921110-921160;ARGS:filecontents,\ + ctl:ruleRemoveTargetById=932150;ARGS:filename,\ + ctl:ruleRemoveTargetById=920370-920390;ARGS:filecontents,\ + ctl:ruleRemoveTargetById=920370-920390;ARGS_COMBINED_SIZE,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ Address Book ] +# + +# Allow the data type 'text/vcard' +SecRule REQUEST_FILENAME "@contains /remote.php/dav/addressbooks/" \ + "id:9003320,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_request_content_type=%{tx.allowed_request_content_type} |text/vcard|'" + +# Allow modifying contacts via the web interface +SecRule REQUEST_METHOD "@streq PUT" \ + "id:9003321,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQUEST_FILENAME "@contains /remote.php/dav/addressbooks/" \ + "t:none,\ + ctl:ruleRemoveById=200002" + + +# +# [ Calendar ] +# + +# Allow the data type 'text/calendar' +SecRule REQUEST_FILENAME "@contains /remote.php/dav/calendars/" \ + "id:9003330,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_request_content_type=%{tx.allowed_request_content_type} |text/calendar|'" + +# Allow modifying calendar events via the web interface +SecRule REQUEST_METHOD "@streq PUT" \ + "id:9003331,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQUEST_FILENAME "@contains /remote.php/dav/calendars/" \ + "t:none,\ + ctl:ruleRemoveById=200002" + +# Fix for iOS Calender not syncing +SecRule REQUEST_METHOD "@streq PROPFIND" \ + "id:9003332,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQUEST_FILENAME "@rx /remote\.php/dav/(?:principals/users|calendars)/" \ + "t:none,\ + ctl:ruleRemoveById=921110" + + +# +# [ Notes ] +# + +# We want to allow a lot of things as the user is +# allowed to note on anything. +SecRule REQUEST_FILENAME "@contains /index.php/apps/notes/" \ + "id:9003340,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveByTag=attack-injection-php,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ Bookmarks ] +# + +# Allow urls in data. +SecRule REQUEST_FILENAME "@contains /index.php/apps/bookmarks/" \ + "id:9003350,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveById=931130,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ Login forms and Logout ] +# +# This removes checks on the 'password' and related fields: + +# User login password. +SecRule REQUEST_FILENAME "@contains /index.php/login" \ + "id:9003400,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=941100;ARGS:requesttoken,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:password,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Reset password. +SecRule REQUEST_FILENAME "@endsWith /index.php/login" \ + "id:9003410,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:action "@streq resetpass" \ + "t:none,\ + chain" + SecRule &ARGS:action "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass1,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass1-text,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass2" + +# Logout token +SecRule REQUEST_FILENAME "@endsWith /index.php/logout" \ + "id:9003420,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=941120;ARGS:requesttoken,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Change Password and Setting up a new user/password +SecRule REQUEST_FILENAME "@endsWith /index.php/settings/users" \ + "id:9003500,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:newuserpassword,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:password,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +# +# [ Settings pages ] +# +# This removes checks on multiple settings pages + +# Personal: Security + +# Fixes deletion of auth tokens +SecRule REQUEST_FILENAME "@contains /settings/personal/authtokens/" \ + "id:9003601,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_methods=%{tx.allowed_methods} DELETE'" + +# Administration: Overview + +# Fixed "Security & setup warnings" test +SecRule REQUEST_FILENAME "@contains /.well-known/caldav" \ + "id:9003602,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_methods=%{tx.allowed_methods} PROPFIND'" + +SecRule REQUEST_FILENAME "@contains /.well-known/carddav" \ + "id:9003603,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_methods=%{tx.allowed_methods} PROPFIND'" + + +# +# [ Notifications ] +# +# Nextcloud uses notifictions to inform the user of important new details. + +# Allows notifications to be deleted +SecRule REQUEST_FILENAME "@rx /ocs/v[0-9]+\.php/apps/notifications/api/v2/notifications" \ + "id:9003701,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_methods=%{tx.allowed_methods} DELETE'" + + +# +# [ API ] +# + +# Allow access to the shares API URL from mobile app +SecRule REQUEST_FILENAME "@rx /ocs/v[0-9]+\.php/apps/files_sharing/api/v1/shares" \ + "id:9003800,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQUEST_METHOD "@streq GET" \ + "t:none,\ + ctl:ruleRemoveById=200002" + + +# +# [ Users ] +# + +# Allow users to be deleted +SecRule REQUEST_FILENAME "@rx /ocs/v[0-9]+\.php/cloud/users/" \ + "id:9003850,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.allowed_methods=%{tx.allowed_methods} DELETE'" + + +# +# [ Extensions ] +# + +# Extension: Ransomware protection + +# Allow configure file extensions fields +SecRule REQUEST_FILENAME "@contains /config/apps/ransomware_protection/extension_additions" \ + "id:9003900,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=932130;ARGS:value,\ + ver:'OWASP_CRS/3.4.0-dev'" + + +SecMarker "END-NEXTCLOUD-ADMIN" + +SecMarker "END-NEXTCLOUD" diff --git a/samples/ModSecurity SecRule/REQUEST-903.9004-DOKUWIKI-EXCLUSION-RULES.conf b/samples/ModSecurity SecRule/REQUEST-903.9004-DOKUWIKI-EXCLUSION-RULES.conf new file mode 100644 index 0000000000..f2adb0a572 --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-903.9004-DOKUWIKI-EXCLUSION-RULES.conf @@ -0,0 +1,273 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# +# ------------------------------------------------------------------------ + +# These exclusions remedy false positives in a default Dokuwiki install. +# The exclusions are only active if crs_exclusions_dokuwiki=1 is set. +# See rule 900130 in crs-setup.conf.example for instructions. +# +# Note, if you want to relax the upload restrictions, +# see rule 900240. For Dokuwiki you can limit the exception +# to the ajax.php file: +# +# SecRule REQUEST_FILENAME "@endsWith /lib/exe/ajax.php" ... +# + + +SecRule &TX:crs_exclusions_dokuwiki|TX:crs_exclusions_dokuwiki "@eq 0" \ + "id:9004000,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-DOKUWIKI" + +SecRule &TX:crs_exclusions_dokuwiki|TX:crs_exclusions_dokuwiki "@eq 0" \ + "id:9004001,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-DOKUWIKI" + + +# +# -=[ Dokuwiki Front-End ]=- +# +# Note on files specified: +# /doku.php: shows pages, saves, edits, admin +# /lib/exe/ajax.php: autosave, uploads +# +# Allow pages to be edited, and ajax to save drafts. +# +# ARGS 'wikitext', 'suffix', and 'prefix' must allow the same things, +# as the page (in part or whole) is passed via 'suffix/prefix' at times. +# attack-protocol (921110-921160/920230): Allows odd characters on the page. +# CRS: (still need attack-protocol specified.) +# attack-injection-php (930000-933999): Allows code on page. +# attack-sqli (940000-942999): Allows SQL expressions on page. +# +# Others: +# 930100-930110;REQUEST_BODY: if there's a /../ in the text. +# +# ARGS:summary (the text in the 'summary' box on page edits.): +# Allowing 930120-930130 lets user save summaries with +# system file names. This should not be needed in normal +# use. But leaving a note here of how to allow in rule below: +# ctl:ruleRemoveTargetById=930120;ARGS:summary +# ctl:ruleRemoveTargetById=930130;ARGS:summary +# +# Also, can't specify: +# SecRule ARGS:do "@streq edit" \ +# SecRule REQUEST_FILENAME "@endsWith /lib/exe/ajax.php"\ +# because at times the do=edit can get dropped, so if we use +# above the edit will get blocked when the page is saved. + +# Hint: those using .htaccess rewrites can remove/replace +# this first 'SecRule...' line with 'SecAction \' (unsupported). + +SecRule REQUEST_FILENAME "@rx (?:/doku.php|/lib/exe/ajax.php)$" \ + "id:9004100,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQUEST_METHOD "@streq POST" \ + "t:none,\ + chain" + SecRule REQUEST_COOKIES:/S?DW[a-f0-9]+/ "@rx ^[%a-zA-Z0-9_-]+" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-protocol;ARGS:wikitext,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:wikitext,\ + ctl:ruleRemoveTargetByTag=attack-protocol;ARGS:suffix,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:suffix,\ + ctl:ruleRemoveTargetByTag=attack-protocol;ARGS:prefix,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:prefix,\ + ctl:ruleRemoveTargetById=930100-930110;REQUEST_BODY" + + +# Allow it to upload files. But check for cookies just to make sure. + +SecRule REQUEST_FILENAME "@endsWith /lib/exe/ajax.php" \ + "id:9004110,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + noauditlog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQUEST_METHOD "@streq POST" \ + "t:none,\ + chain" + SecRule REQUEST_COOKIES:/S?DW[a-f0-9]+/ "@rx ^[%a-zA-Z0-9_-]+" \ + "t:none,\ + setvar:'tx.allowed_request_content_type=%{tx.allowed_request_content_type} |application/octet-stream|'" + + +# Show the index, even if things like "postgresql" or other things show up. + +SecRule REQUEST_FILENAME "@endsWith /doku.php" \ + "id:9004130,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + noauditlog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:do "@streq index" \ + "t:none,\ + chain" + SecRule &ARGS:do "@eq 1" \ + "t:none,\ + ctl:ruleRemoveById=951240,\ + ctl:ruleRemoveById=953110" + + +# +# [ Login form ] +# + +# Turn off checks for password. + +SecRule REQUEST_FILENAME "@endsWith /doku.php" \ + "id:9004200,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + noauditlog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:do "@streq login" \ + "t:none,\ + chain" + SecRule &ARGS:do "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:p" + + +# +# [ Admin Area ] +# +# Skip this section for performance unless do=admin is in request + +SecRule ARGS_GET:do "!@streq admin" \ + "id:9004300,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-DOKUWIKI-ADMIN" + +SecRule ARGS:do "!@streq admin" \ + "id:9004310,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-DOKUWIKI-ADMIN" + + +# [ Reset password ] +# +# Turn off checks for pass1, pass1-text, pass2 + +SecRule REQUEST_FILENAME "@endsWith /doku.php" \ + "id:9004320,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + noauditlog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:do "@streq login" \ + "t:none,\ + chain" + SecRule &ARGS:do "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass1,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass1-text,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:pass2" + + +# [ Save config ] +# +# Allow the config to be saved: +# 942200: If the user adds "..." to tagline: ARGS:config[tagline] +# 942430: if ARGS:config[hidepages] has pages looking like sql statements +# 942430,942440: "--- //[[@MAIL@|@NAME@]] @DATE@//"]" in ARGS:config[signature] + +SecRule REQUEST_FILENAME "@endsWith /doku.php" \ + "id:9004370,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + noauditlog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:page "@streq config" \ + "t:none,\ + chain" + SecRule &ARGS:page "@eq 1" \ + "t:none,\ + chain" + SecRule REQUEST_METHOD "@streq POST" \ + "t:none,\ + chain" + SecRule REQUEST_COOKIES:/S?DW[a-f0-9]+/ "@rx ^[%a-zA-Z0-9_-]+" \ + "t:none,\ + ctl:ruleRemoveTargetById=920230;ARGS:config[dformat],\ + ctl:ruleRemoveTargetById=942200;ARGS:config[tagline],\ + ctl:ruleRemoveTargetById=942430;ARGS:config[hidepages],\ + ctl:ruleRemoveTargetById=942430-942440;ARGS:config[signature]" + + +# When the config loads after a save, it gets blocked because +# it has 'readdir' and lines that look like sql +# 942430,942440: "--- //[[@MAIL@|@NAME@]] @DATE@//"]" in ARGS:config[signature] +# 951240,953110: When the page reloads, it triggers +# postgres and php code disclosure rules. + +SecRule REQUEST_FILENAME "@endsWith /doku.php" \ + "id:9004380,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + noauditlog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:page "@streq config" \ + "t:none,\ + chain" + SecRule &ARGS:page "@eq 1" \ + "t:none,\ + chain" + SecRule REQUEST_COOKIES:/S?DW[a-f0-9]+/ "@rx ^[%a-zA-Z0-9_-]+" \ + "t:none,\ + ctl:ruleRemoveById=951240,\ + ctl:ruleRemoveById=953110" + + +# End [ Admin Area ] + +SecMarker "END-DOKUWIKI-ADMIN" + +SecMarker "END-DOKUWIKI" diff --git a/samples/ModSecurity SecRule/REQUEST-903.9005-CPANEL-EXCLUSION-RULES.conf b/samples/ModSecurity SecRule/REQUEST-903.9005-CPANEL-EXCLUSION-RULES.conf new file mode 100644 index 0000000000..51048f0385 --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-903.9005-CPANEL-EXCLUSION-RULES.conf @@ -0,0 +1,64 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# These exclusions remedy false positives in a default cPanel environment. +# The exclusions are only active if crs_exclusions_cpanel=1 is set. +# See rule 900130 in crs-setup.conf.example for instructions. + + +SecRule &TX:crs_exclusions_cpanel|TX:crs_exclusions_cpanel "@eq 0" \ + "id:9005000,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-CPANEL" + +SecRule &TX:crs_exclusions_cpanel|TX:crs_exclusions_cpanel "@eq 0" \ + "id:9005001,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-CPANEL" + + +# +# [ cPanel whm-server-status ] +# +# Cpanel's WHM auto generates requests to /whm-server-status from +# 127.0.0.1 (triggers rule 920280, non-blocking, log only) Once every 5 minutes. +# These false positives have a low impact (logged, non-blocking) to a large number of users (all cPanel admins). +# + +# +# Rule to allow cPanel whm-server-status requests from localhost without log entry. +# +SecRule REQUEST_LINE "@rx ^GET /whm-server-status(?:/|/\?auto)? HTTP/[12]\.[01]$" \ + "id:9005100,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-apache',\ + tag:'attack-generic',\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REMOTE_ADDR "@ipMatch 127.0.0.1,::1" \ + "t:none,\ + ctl:ruleRemoveById=920280,\ + ctl:ruleRemoveById=920350" + + +SecMarker "END-CPANEL" diff --git a/samples/ModSecurity SecRule/REQUEST-903.9006-XENFORO-EXCLUSION-RULES.conf b/samples/ModSecurity SecRule/REQUEST-903.9006-XENFORO-EXCLUSION-RULES.conf new file mode 100644 index 0000000000..98bde93245 --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-903.9006-XENFORO-EXCLUSION-RULES.conf @@ -0,0 +1,646 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# These exclusions remedy false positives in a default XenForo install. +# The exclusions are only active if crs_exclusions_xenforo=1 is set. +# See rule 900130 in crs-setup.conf.example for instructions. + +SecRule &TX:crs_exclusions_xenforo|TX:crs_exclusions_xenforo "@eq 0" \ + "id:9006000,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-XENFORO" + +SecRule &TX:crs_exclusions_xenforo|TX:crs_exclusions_xenforo "@eq 0" \ + "id:9006001,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-XENFORO" + + +# +# -=[ XenForo Front-End ]=- +# + +# Proxy for images and remote content embedded in forum posts +# GET /xf/proxy.php?image=https://example.com/some.jpg&hash=foo +# GET /xf/proxy.php?link=https://example.com&hash=foo +# POST /xf/proxy.php, body: referrer=... +SecRule REQUEST_FILENAME "@endsWith /proxy.php" \ + "id:9006100,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:image,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:link,\ + ctl:ruleRemoveTargetById=931130;ARGS:referrer,\ + ctl:ruleRemoveTargetById=942230;ARGS:referrer,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Store drafts for private message, forum post, thread reply +# POST /xf/conversations/draft +# POST /xf/conversations/convo-title.12345/draft +# POST /xf/forums/forum-title.12345/draft +# POST /xf/threads/thread-title-%E2%98%85.12345/draft +# +# attachment_hash_combined example: +# {"type":"post","context":{"post_id":12345},"hash":"0123456789abcdef..."} +SecRule REQUEST_FILENAME "@rx /(?:conversations|(?:conversations|forums|threads)/.*)/draft$" \ + "id:9006110,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=931130;ARGS:href,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:title,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:message,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:message_html,\ + ctl:ruleRemoveTargetById=942200;ARGS:attachment_hash_combined,\ + ctl:ruleRemoveTargetById=942260;ARGS:attachment_hash_combined,\ + ctl:ruleRemoveTargetById=942340;ARGS:attachment_hash_combined,\ + ctl:ruleRemoveTargetById=942370;ARGS:attachment_hash_combined,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Send PM, edit post, create thread, reply to thread +# POST /xf/conversations/add +# POST /xf/conversations/add-preview +# POST /xf/conversations/messages/1463947/edit +# POST /xf/posts/12345/edit +# POST /xf/posts/12345/preview +# POST /xf/posts/12345/save-inline +# POST /xf/conversations/convo-title.12345/add-reply +# POST /xf/threads/thread-title.12345/add-reply +# POST /xf/threads/thread-title.12345/reply-preview +# POST /xf/threads/thread-title.12345/save-draft +# POST /xf/forums/forum-title.12345/post-thread +# POST /xf/forums/blogs/post-thread +# POST /xf/forums/forum-title.12345/thread-preview +# POST /xf/forums/forum-title.12345/create-thread +# POST /xf/forums/forum-title.12345/add-thread +# POST /xf/forums/forum-title.12345/save-draft +# POST /xf/forums/forum-title.12345/create-thread/preview +SecRule REQUEST_FILENAME "@rx /(?:conversations/add(?:-preview)?|conversations/messages/\d+/edit|posts/\d+/(?:edit|preview|save-inline|save)|(?:conversations|threads)/.*\.\d+/(?:add-reply|reply-preview|save-draft)|forums/.*/(?:post-thread|thread-preview|create-thread|add-thread|save-draft)(?:/preview|))$" \ + "id:9006120,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:title,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:message,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:message_html,\ + ctl:ruleRemoveTargetById=942200;ARGS:attachment_hash_combined,\ + ctl:ruleRemoveTargetById=942260;ARGS:attachment_hash_combined,\ + ctl:ruleRemoveTargetById=942340;ARGS:attachment_hash_combined,\ + ctl:ruleRemoveTargetById=942370;ARGS:attachment_hash_combined,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Quote +# POST /xf/posts/12345/quote +SecRule REQUEST_FILENAME "@rx /posts/\d+/quote$" \ + "id:9006130,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:quoteHtml,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Multi quote +# POST /xf/conversations/convo-title.12345/multi-quote +# POST /xf/threads/thread-title.12345/multi-quote +# quotes={"12345":["quote-html"]} +SecRule REQUEST_FILENAME "@rx /(?:conversations|threads)/.*\.\d+/multi-quote$" \ + "id:9006140,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:quotes,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:insert[0][value],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:insert[1][value],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:insert[2][value],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:insert[3][value],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:insert[4][value],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:insert[5][value],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:insert[6][value],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:insert[7][value],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:insert[8][value],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:insert[9][value],\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Delete thread +# POST /xf/threads/thread-title.12345/delete +SecRule REQUEST_FILENAME "@rx /threads/.*\.\d+/delete$" \ + "id:9006150,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=942130;ARGS:starter_alert_reason,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Feature thread +# POST /xf/threads/thread-title.12345/feature-edit +SecRule REQUEST_FILENAME "@rx /threads/.*\.\d+/feature-edit$" \ + "id:9006155,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:feature[feature_excerpt]" + +# Inline moderate thread +# POST /xf/inline-mod/ +SecRule REQUEST_FILENAME "@endsWith /inline-mod/" \ + "id:9006160,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:author_alert_reason,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:message,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Warn member +# POST /xf/members/name.12345/warn +# POST /xf/posts/12345/warn +SecRule REQUEST_FILENAME "@rx /(?:members/.*\.\d+|posts/\d+)/warn$" \ + "id:9006170,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:conversation_message,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:notes,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Editor +SecRule REQUEST_URI "@endsWith /index.php?editor/to-html" \ + "id:9006200,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:bb_code,\ + ctl:ruleRemoveTargetById=942200;ARGS:attachment_hash_combined,\ + ctl:ruleRemoveTargetById=942260;ARGS:attachment_hash_combined,\ + ctl:ruleRemoveTargetById=942340;ARGS:attachment_hash_combined,\ + ctl:ruleRemoveTargetById=942370;ARGS:attachment_hash_combined,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Editor +SecRule REQUEST_URI "@endsWith /index.php?editor/to-bb-code" \ + "id:9006210,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:html,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Post attachment +# POST /xf/account/avatar +# POST /xf/attachments/upload?type=post&context[thread_id]=12345&hash=foo +SecRule REQUEST_FILENAME "@rx /(?:account/avatar|attachments/upload)$" \ + "id:9006220,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveById=200003,\ + ctl:ruleRemoveTargetById=942220;ARGS:flowChunkSize,\ + ctl:ruleRemoveTargetById=942440;ARGS:flowIdentifier,\ + ctl:ruleRemoveTargetById=942440;ARGS:flowFilename,\ + ctl:ruleRemoveTargetById=942440;ARGS:flowRelativePath,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Media +# POST /xf/index.php?editor/media +SecRule REQUEST_URI "@endsWith /index.php?editor/media" \ + "id:9006230,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=931130;ARGS:url,\ + ctl:ruleRemoveTargetById=942130;ARGS:url,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Emoji +# GET /xf/index.php?misc/find-emoji&q=(%0A%0A +SecRule REQUEST_URI "@rx /index\.php\?misc/find-emoji&q=" \ + "id:9006240,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=921151;ARGS:q,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Login +# POST /xf/login/login +SecRule REQUEST_FILENAME "@endsWith /login/login" \ + "id:9006300,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:login,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:password,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Register account +# POST /xf/register/register +# The password is passed in a variable-name form parameter. We don't +# want to exclude all parameters completely as this would cause an +# unacceptable bypass. So, we exclude only commonly hit rules. +SecRule REQUEST_FILENAME "@endsWith /register/register" \ + "id:9006310,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=942130;ARGS,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:reg_key,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Confirm account +# GET /xf/account-confirmation/name.12345/email?c=foo +SecRule REQUEST_FILENAME "@rx /account-confirmation/.*\.\d+/email$" \ + "id:9006315,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:c" + +# Edit account +# POST /xf/account/account-details +SecRule REQUEST_FILENAME "@endsWith /account/account-details" \ + "id:9006320,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=931130;ARGS:custom_fields[picture],\ + ctl:ruleRemoveTargetById=931130;ARGS:profile[website],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:about_html,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Lost password +# POST /xf/lost-password/user-name.12345/confirm?c=foo +SecRule REQUEST_FILENAME "@rx /lost-password/.*\.\d+/confirm$" \ + "id:9006330,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:c,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Set forum signature +# POST /xf/account/signature +SecRule REQUEST_FILENAME "@endsWith /account/signature" \ + "id:9006340,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:signature_html,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Search +# POST /xf/search/search +SecRule REQUEST_FILENAME "@endsWith /search/search" \ + "id:9006400,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:keywords,\ + ctl:ruleRemoveTargetById=942200;ARGS:constraints,\ + ctl:ruleRemoveTargetById=942260;ARGS:constraints,\ + ctl:ruleRemoveTargetById=942340;ARGS:constraints,\ + ctl:ruleRemoveTargetById=942370;ARGS:constraints,\ + ctl:ruleRemoveTargetById=942120;ARGS:c[users],\ + ctl:ruleRemoveTargetById=942370;ARGS:c[users],\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Search within thread +# GET /xf/threads/foo.12345/page12?highlight=foo +SecRule REQUEST_FILENAME "@rx /threads/.*\.\d+/(?:page\d+)?$" \ + "id:9006410,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:highlight,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Search within search result +# GET /xf/search/12345/?q=foo +SecRule REQUEST_FILENAME "@rx /search/\d+/$" \ + "id:9006420,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:q,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Contact form +# POST /xf/misc/contact +SecRule REQUEST_FILENAME "@endsWith /misc/contact" \ + "id:9006500,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:message,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:subject,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Report post +# POST /xf/posts/12345/report +SecRule REQUEST_FILENAME "@rx /posts/\d+/report$" \ + "id:9006510,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:message,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Alternate thread view route +# /xf/index.php?threads/title-having-some-sql.12345/ +# +# Especially threads with the HAVING sql keyword are FP prone. +# This rule has some chains to narrow down the exclusion, +# making it harder for an attacker to abuse the ARGS_NAMES +# exclusion on other endpoints. +SecRule REQUEST_FILENAME "@endsWith /index.php" \ + "id:9006600,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQUEST_METHOD "@streq GET" \ + "t:none,\ + chain" + SecRule &ARGS_GET "@eq 1" \ + "t:none,\ + chain" + SecRule REQUEST_URI "@rx /index\.php\?threads/.*\.\d+/$" \ + "t:none,\ + ctl:ruleRemoveTargetById=942100;ARGS_NAMES,\ + ctl:ruleRemoveTargetById=942230;ARGS_NAMES" + +# Browser fingerprint (DBTech security extension) +# May Contain various javascript/XSS false positives +SecRule REQUEST_URI "@endsWith /index.php?dbtech-security/fingerprint" \ + "id:9006700,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:components[14][value],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:components[15][value],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:components[16][value],\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Get location info +SecRule REQUEST_FILENAME "@endsWith /misc/location-info" \ + "id:9006710,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:location,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# +# -=[ XenForo Global Exclusions ]=- +# + +# _xfRedirect, _xfRequestUri can appear on various endpoints. +# Cookies can appear on all endpoints. + +SecAction \ + "id:9006800,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=931120;ARGS:_xfRedirect,\ + ctl:ruleRemoveTargetById=941150;ARGS:_xfRedirect,\ + ctl:ruleRemoveTargetById=942230;ARGS:_xfRedirect,\ + ctl:ruleRemoveTargetById=942260;ARGS:_xfRedirect,\ + ctl:ruleRemoveTargetById=931120;ARGS:_xfRequestUri,\ + ctl:ruleRemoveTargetById=941150;ARGS:_xfRequestUri,\ + ctl:ruleRemoveTargetById=942130;ARGS:_xfRequestUri,\ + ctl:ruleRemoveTargetById=942230;ARGS:_xfRequestUri,\ + ctl:ruleRemoveTargetById=942260;ARGS:_xfRequestUri,\ + ctl:ruleRemoveTargetById=942100;REQUEST_COOKIES:xf_csrf,\ + ctl:ruleRemoveTargetById=942210;REQUEST_COOKIES:xf_csrf,\ + ctl:ruleRemoveTargetById=942440;REQUEST_COOKIES:xf_csrf,\ + ctl:ruleRemoveTargetById=942100;REQUEST_COOKIES:xf_emoji_usage,\ + ctl:ruleRemoveTargetById=942150;REQUEST_COOKIES:xf_emoji_usage,\ + ctl:ruleRemoveTargetById=942410;REQUEST_COOKIES:xf_emoji_usage,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;REQUEST_COOKIES:xf_ls,\ + ctl:ruleRemoveTargetById=942100;REQUEST_COOKIES:xf_session,\ + ctl:ruleRemoveTargetById=942210;REQUEST_COOKIES:xf_session,\ + ctl:ruleRemoveTargetById=942100;REQUEST_COOKIES:xf_user,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# +# -=[ XenForo Administration Back-End ]=- +# + +# Skip this section for performance unless requested file is admin.php + +SecRule REQUEST_FILENAME "!@endsWith /admin.php" \ + "id:9006900,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-XENFORO-ADMIN" + +SecRule REQUEST_FILENAME "!@endsWith /admin.php" \ + "id:9006901,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-XENFORO-ADMIN" + +# Admin edit user +# POST /xf/admin.php?users/the-user-name.12345/edit +SecRule REQUEST_URI "@rx /admin\.php\?users/.*\.\d+/edit$" \ + "id:9006910,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:profile[about],\ + ctl:ruleRemoveTargetById=931130;ARGS:profile[website],\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Admin save user +# POST /xf/admin.php?users/the-user-name.12345/save +# Runs in phase 1 to be able to remove rule 200003. +SecRule REQUEST_URI "@rx /admin\.php\?users/.*\.\d+/save$" \ + "id:9006920,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveById=200003,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:custom_fields[occupation],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:custom_fields[personal_quote],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:profile[about],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:profile[signature],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:custom_fields[sexuality],\ + ctl:ruleRemoveTargetById=931130;ARGS:custom_fields[picture],\ + ctl:ruleRemoveTargetById=931130;ARGS:profile[website],\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Admin mass send messages +# POST /xf/admin.php?users/message/send +# POST /xf/admin.php?users/foo.12345/send +SecRule REQUEST_URI "@rx /admin\.php\?users/(?:message|.*\.\d+)/send$" \ + "id:9006925,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:json_criteria,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Admin edit forum notice +# POST /xf/admin.php?notices/0/save +# POST /xf/admin.php?notices/forum-name.12345/save +SecRule REQUEST_URI "@rx /admin\.php\?notices/(?:.*\.)?\d+/save$" \ + "id:9006930,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:message,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:title,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Admin batch thread update +# POST /xf/admin.php?threads/batch-update/action +SecRule REQUEST_URI "@rx /admin\.php\?(?:threads|users)/batch-update/action$" \ + "id:9006940,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=942200;ARGS:criteria,\ + ctl:ruleRemoveTargetById=942260;ARGS:criteria,\ + ctl:ruleRemoveTargetById=942330;ARGS:criteria,\ + ctl:ruleRemoveTargetById=942340;ARGS:criteria,\ + ctl:ruleRemoveTargetById=942370;ARGS:criteria,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Edit forum theme +# POST /xf/admin.php?styles/title.1234/style-properties/group&group=basic +SecRule REQUEST_URI "@rx /admin\.php\?styles/" \ + "id:9006950,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=942200;ARGS:json,\ + ctl:ruleRemoveTargetById=942260;ARGS:json,\ + ctl:ruleRemoveTargetById=942300;ARGS:json,\ + ctl:ruleRemoveTargetById=942330;ARGS:json,\ + ctl:ruleRemoveTargetById=942340;ARGS:json,\ + ctl:ruleRemoveTargetById=942370;ARGS:json,\ + ctl:ruleRemoveTargetById=942440;ARGS:json,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Set forum options +# POST /xf/admin.php?options/update +SecRule REQUEST_URI "@rx /admin\.php\?options/update$" \ + "id:9006960,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:options[allowedCodeLanguages],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:options[boardInactiveMessage],\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:options[spamPhrases][phrases],\ + ctl:ruleRemoveTargetById=931130;ARGS:options[ipInfoUrl],\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Edit pages/templates +# POST /xf/admin.php?pages/0/save +# POST /xf/admin.php?pages/foo.12345/save +# POST /xf/admin.php?templates/foo.1234/save +SecRule REQUEST_URI "@rx /admin\.php\?(?:pages|templates)/.*/save$" \ + "id:9006970,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:template,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Merge templates +# POST /xf/admin.php?templates/thread_list_macros.12345/merge-outdated +SecRule REQUEST_URI "@rx /admin\.php\?templates/.*/merge-outdated$" \ + "id:9006980,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:merged[],\ + ver:'OWASP_CRS/3.4.0-dev'" + +# User groups +# POST /xf/admin.php?user-groups/foo.20/save +SecRule REQUEST_URI "@rx /admin\.php\?user-groups/.*\.\d+/save$" \ + "id:9006990,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=941320;ARGS:user_title,\ + ctl:ruleRemoveTargetById=942130;ARGS:user_title,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Donation +# POST /xf/admin.php?navigation/Donations/save +SecRule REQUEST_URI "@endsWith /admin.php?navigation/Donations/save" \ + "id:9006991,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=931130;ARGS:config[basic][link],\ + ver:'OWASP_CRS/3.4.0-dev'" + +SecMarker "END-XENFORO-ADMIN" + +SecMarker "END-XENFORO" diff --git a/samples/ModSecurity SecRule/REQUEST-903.9007-PHPBB-EXCLUSION-RULES.conf b/samples/ModSecurity SecRule/REQUEST-903.9007-PHPBB-EXCLUSION-RULES.conf new file mode 100644 index 0000000000..4692ce5e00 --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-903.9007-PHPBB-EXCLUSION-RULES.conf @@ -0,0 +1,273 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. (not) All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# These exclusions remedy false positives in a default phpBB install. +# The exclusions are only active if crs_exclusions_phpbb=1 is set. +# See rule 900130 in crs-setup.conf.example for instructions. + +SecRule &TX:crs_exclusions_phpbb|TX:crs_exclusions_phpbb "@eq 0" \ + "id:9007000,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-PHPBB" + +SecRule &TX:crs_exclusions_phpbb|TX:crs_exclusions_phpbb "@eq 0" \ + "id:9007001,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-PHPBB" + +SecRule &TX:crs_exclusions_phpbb|TX:crs_exclusions_phpbb "@eq 0" \ + "id:9007003,\ + phase:4,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-PHPBB" + +# Login +SecRule REQUEST_FILENAME "@endsWith /ucp.php" \ + "id:9007100,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:mode "@streq login" \ + "t:none,\ + chain" + SecRule &ARGS:mode "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=930100;ARGS:redirect,\ + ctl:ruleRemoveTargetById=930110;ARGS:redirect,\ + ctl:ruleRemoveTargetById=941100;ARGS:redirect,\ + ctl:ruleRemoveTargetById=942432;ARGS:redirect,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:password" + +# Registration +SecRule REQUEST_FILENAME "@endsWith /ucp.php" \ + "id:9007110,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:mode "@streq register" \ + "t:none,\ + chain" + SecRule &ARGS:mode "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:new_password,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:password_confirm" + +# Password change +SecRule REQUEST_FILENAME "@endsWith /ucp.php" \ + "id:9007120,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:mode "@streq reg_details" \ + "t:none,\ + chain" + SecRule &ARGS:mode "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:new_password,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:cur_password,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:password_confirm" + +# Redirect after admin login +SecRule REQUEST_FILENAME "@endsWith /adm/index.php" \ + "id:9007130,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule &ARGS:mode "@eq 0" \ + "t:none,\ + chain" + SecRule &ARGS:username "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=930100;ARGS:redirect,\ + ctl:ruleRemoveTargetById=930110;ARGS:redirect" + +# Creating and editing posts +# Argument 'subject_checked' is part or Prime Subject Check extension: +# https://www.phpbb.com/community/viewtopic.php?t=1056865 +SecRule REQUEST_FILENAME "@endsWith /posting.php" \ + "id:9007140,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:mode "@rx ^(?:post|edit|quote|reply)$" \ + "t:none,\ + chain" + SecRule &ARGS:mode "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:message,\ + ctl:ruleRemoveTargetByTag=attack-injection-php;ARGS:subject,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:subject,\ + ctl:ruleRemoveTargetByTag=attack-injection-php;ARGS:subject_checked,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:subject_checked" + +# Creating and editing posts +# Rule 200004 (phase 2) was triggered when ARGS 'subject' or +# 'message' starts with two hyphen characters (--). This exclusion rule +# must be processed BEFORE rule 200004, so it needs to be in phase 1. +# Rule 200004 comes with ModSecurity and is defined in modsecurity.conf. +# It is trying to search for unmatched boundaries if multipart/form-data +# Content-Type is used (phpBB is using it when creating/editing posts +# via logged [non-guest] accounts). According to RFC 2046, boundaries +# must start with two hyphen characters and that's where FPs are coming +# from - with multipart/form-data as Content-Type, values of arguments +# send by a user are put on a new line. +SecRule REQUEST_FILENAME "@endsWith /posting.php" \ + "id:9007141,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveById=200004,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Private messages +SecRule REQUEST_FILENAME "@endsWith /ucp.php" \ + "id:9007150,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:mode "@rx ^(?:compose|drafts)$" \ + "t:none,\ + chain" + SecRule &ARGS:mode "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:message,\ + ctl:ruleRemoveTargetByTag=attack-injection-php;ARGS:subject,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:subject" + +# Saving draft of a private message +# In this case, phpBB is sending both GET and POST 'mode' parameters togather (maybe a bug in phpBB?) +SecRule REQUEST_FILENAME "@endsWith /ucp.php" \ + "id:9007160,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS_GET:mode "@streq compose" \ + "t:none,\ + chain" + SecRule ARGS_POST:mode "@streq compose" \ + "t:none,\ + chain" + SecRule &ARGS:mode "@eq 2" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=OWASP_CRS;ARGS:message,\ + ctl:ruleRemoveTargetByTag=attack-injection-php;ARGS:subject,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:subject" + +# Profile - setting signature +SecRule REQUEST_FILENAME "@endsWith /ucp.php" \ + "id:9007170,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:mode "@streq signature" \ + "t:none,\ + chain" + SecRule &ARGS:mode "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-xss;ARGS:signature,\ + ctl:ruleRemoveTargetByTag=attack-injection-php;ARGS:signature,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:signature" + +# Settings - adding Google AdSense code using AdSense extension +# https://www.phpbb.com/customise/db/extension/adsense/ +SecRule REQUEST_FILENAME "@endsWith /adm/index.php" \ + "id:9007180,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule ARGS:mode "@streq settings" \ + "t:none,\ + chain" + SecRule &ARGS:mode "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-xss;ARGS:config[google_adsense_html],\ + ctl:ruleRemoveTargetByTag=attack-rce;ARGS:config[google_adsense_html]" + +# Installation +SecRule REQUEST_FILENAME "@endsWith /install/app.php/install" \ + "id:9007190,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=931130;ARGS:server_protocol,\ + ver:'OWASP_CRS/3.4.0-dev'" + +# Viewing a post +SecRule REQUEST_FILENAME "@endsWith /viewtopic.php" \ + "id:9007200,\ + phase:4,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQUEST_METHOD "@streq GET" \ + "ctl:ruleRemoveTargetById=951240;TX:sql_error_match" + +SecRule REQUEST_FILENAME "@endsWith /adm/index.php" \ + "id:9007210,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0',\ + chain" + SecRule ARGS:mode "@streq bbcodes" \ + "t:none,\ + chain" + SecRule &ARGS:mode "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=941100;ARGS:bbcode_tpl,\ + ctl:ruleRemoveTargetById=941110;ARGS:bbcode_tpl,\ + ctl:ruleRemoveTargetById=941160;ARGS:bbcode_tpl,\ + ctl:ruleRemoveTargetById=941180;ARGS:bbcode_tpl,\ + ctl:ruleRemoveTargetById=942230;ARGS:bbcode_tpl" + +SecMarker "END-PHPBB" diff --git a/samples/ModSecurity SecRule/REQUEST-903.9008-PHPMYADMIN-EXCLUSION-RULES.conf b/samples/ModSecurity SecRule/REQUEST-903.9008-PHPMYADMIN-EXCLUSION-RULES.conf new file mode 100644 index 0000000000..a5e87315fd --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-903.9008-PHPMYADMIN-EXCLUSION-RULES.conf @@ -0,0 +1,416 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. (not) All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# These exclusions remedy false positives in a default phpMyAdmin install. +# The exclusions are only active if crs_exclusions_phpmyadmin=1 is set. +# See rule 900130 in crs-setup.conf.example for instructions. + +SecRule &TX:crs_exclusions_phpmyadmin|TX:crs_exclusions_phpmyadmin "@eq 0" \ + "id:9008000,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-PHPMYADMIN" + +SecRule &TX:crs_exclusions_phpmyadmin|TX:crs_exclusions_phpmyadmin "@eq 0" \ + "id:9008001,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-PHPMYADMIN" + +# Editing / copying a row - loading row data +SecRule REQUEST_FILENAME "@endsWith /tbl_change.php" \ + "id:9008100,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:where_clause,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:sql_query" + +# Editing / copying a row - saving row data +SecRule REQUEST_FILENAME "@endsWith /tbl_replace.php" \ + "id:9008110,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=920230;ARGS:err_url,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:err_url,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:where_clause[],\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:where_clause[0],\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:sql_query,\ + ctl:ruleRemoveTargetByTag=attack-xss;ARGS,\ + ctl:ruleRemoveTargetById=921110;ARGS,\ + ctl:ruleRemoveTargetById=921130;ARGS,\ + ctl:ruleRemoveTargetById=932100;ARGS,\ + ctl:ruleRemoveTargetById=932105;ARGS,\ + ctl:ruleRemoveTargetById=932110;ARGS,\ + ctl:ruleRemoveTargetById=932115;ARGS,\ + ctl:ruleRemoveTargetById=932130;ARGS,\ + ctl:ruleRemoveTargetById=932140;ARGS,\ + ctl:ruleRemoveTargetById=932150;ARGS,\ + ctl:ruleRemoveTargetById=933100;ARGS,\ + ctl:ruleRemoveTargetById=933120;ARGS,\ + ctl:ruleRemoveTargetById=933130;ARGS,\ + ctl:ruleRemoveTargetById=933160;ARGS,\ + ctl:ruleRemoveTargetById=934100;ARGS,\ + ctl:ruleRemoveTargetById=942100;ARGS,\ + ctl:ruleRemoveTargetById=942170;ARGS,\ + ctl:ruleRemoveTargetById=942190;ARGS,\ + ctl:ruleRemoveTargetById=930100;ARGS:fields[multi_edit][0][],\ + ctl:ruleRemoveTargetById=930110;ARGS:fields[multi_edit][0][],\ + ctl:ruleRemoveTargetById=933210;ARGS:fields[multi_edit][0][]" + +# Downloading row data +SecRule REQUEST_FILENAME "@endsWith /tbl_get_field.php" \ + "id:9008120,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:where_clause,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:sql_query" + +# Deleting a row +SecRule REQUEST_FILENAME "@endsWith /sql.php" \ + "id:9008130,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=942140;ARGS:db,\ + ctl:ruleRemoveTargetById=920230;ARGS:goto,\ + ctl:ruleRemoveTargetById=942200;ARGS:goto,\ + ctl:ruleRemoveTargetById=942260;ARGS:goto,\ + ctl:ruleRemoveTargetById=942430;ARGS:goto,\ + ctl:ruleRemoveTargetById=942510;ARGS:goto,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:sql_query" + +# Mass actions on rows +SecRule REQUEST_FILENAME "@endsWith /tbl_row_action.php" \ + "id:9008140,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=932105;ARGS,\ + ctl:ruleRemoveTargetById=941100;ARGS,\ + ctl:ruleRemoveTargetById=942100;ARGS,\ + ctl:ruleRemoveTargetById=942110;ARGS,\ + ctl:ruleRemoveTargetById=942180;ARGS,\ + ctl:ruleRemoveTargetById=942370;ARGS,\ + ctl:ruleRemoveTargetById=942380;ARGS,\ + ctl:ruleRemoveTargetById=942430;ARGS,\ + ctl:ruleRemoveTargetById=942480;ARGS,\ + ctl:ruleRemoveTargetById=942510;ARGS" + +# Opening custom SQL editor (general) +SecRule REQUEST_FILENAME "@endsWith /lint.php" \ + "id:9008150,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=921110;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=941100;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=941310;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=941340;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=933210;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=932105;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=932115;ARGS:sql_query,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:sql_query" + +# Opening custom SQL editor (tables) +SecRule REQUEST_FILENAME "@endsWith /tbl_sql.php" \ + "id:9008160,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=932100;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=941100;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=941160;ARGS:sql_query" + +# Opening custom SQL editor (databases) +SecRule REQUEST_FILENAME "@endsWith /db_sql.php" \ + "id:9008170,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:sql_query" + +# Importing data and executing of custom SQL +SecRule REQUEST_FILENAME "@endsWith /import.php" \ + "id:9008180,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveById=200003,\ + ctl:ruleRemoveTargetById=942110;ARGS:csv_enclosed,\ + ctl:ruleRemoveTargetById=942330;ARGS:csv_enclosed,\ + ctl:ruleRemoveTargetById=942110;ARGS:csv_escaped,\ + ctl:ruleRemoveTargetById=942330;ARGS:csv_escaped,\ + ctl:ruleRemoveTargetById=942110;ARGS:ldi_enclosed,\ + ctl:ruleRemoveTargetById=942330;ARGS:ldi_enclosed,\ + ctl:ruleRemoveTargetById=942110;ARGS:ldi_terminated,\ + ctl:ruleRemoveTargetById=942140;ARGS:db,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:prev_sql_query,\ + ctl:ruleRemoveTargetById=932130;ARGS:prev_sql_query,\ + ctl:ruleRemoveTargetById=942110;ARGS:sql_delimiter,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=921110;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=932105;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=932130;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=932115;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=941100;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=941310;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=941340;ARGS:sql_query" + +# Adding / editing triggers +SecRule REQUEST_FILENAME "@endsWith /db_triggers.php" \ + "id:9008190,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=932115;ARGS:item_definition,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:item_definition" + +# Adding / editing events +SecRule REQUEST_FILENAME "@endsWith /db_events.php" \ + "id:9008200,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=932115;ARGS:item_definition,\ + ctl:ruleRemoveTargetById=942350;ARGS:item_definition" + +# Database export +SecRule REQUEST_FILENAME "@endsWith /export.php" \ + "id:9008210,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=942110;ARGS:csv_enclosed,\ + ctl:ruleRemoveTargetById=942330;ARGS:csv_enclosed,\ + ctl:ruleRemoveTargetById=942110;ARGS:csv_escaped,\ + ctl:ruleRemoveTargetById=942330;ARGS:csv_escaped,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:sql_query" + +# Table export +SecRule REQUEST_FILENAME "@endsWith /tbl_export.php" \ + "id:9008220,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:sql_query" + +# Sending error report +SecRule REQUEST_FILENAME "@endsWith /error_report.php" \ + "id:9008230,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=932130;ARGS,\ + ctl:ruleRemoveTargetById=932140;ARGS,\ + ctl:ruleRemoveTargetById=934100;ARGS,\ + ctl:ruleRemoveTargetById=942100;ARGS" + +# Session cookies allow list +SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "id:9008240,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=941100;REQUEST_COOKIES,\ + ctl:ruleRemoveTargetById=941120;REQUEST_COOKIES" + +# Creating view +SecRule REQUEST_FILENAME "@endsWith /view_create.php" \ + "id:9008250,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=941100;ARGS:view[as],\ + ctl:ruleRemoveTargetById=942100;ARGS:view[as],\ + ctl:ruleRemoveTargetById=942360;ARGS:view[as]" + +# Persistent cookies +# These cookies may persist beyond sessions and may block a user when trying +# to access phpMyAdmin for a second time. +SecAction \ + "id:9008260,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ctl:ruleRemoveTargetById=942100;REQUEST_COOKIES:auto_saved_sql,\ + ctl:ruleRemoveTargetById=942200;REQUEST_COOKIES:pmaAuth-1,\ + ctl:ruleRemoveTargetById=942340;REQUEST_COOKIES:pmaAuth-1,\ + ctl:ruleRemoveTargetById=942200;REQUEST_COOKIES:pmaUser-1,\ + ctl:ruleRemoveTargetById=942340;REQUEST_COOKIES:pmaUser-1,\ + ctl:ruleRemoveTargetById=942200;REQUEST_COOKIES:pma_console_config,\ + ctl:ruleRemoveTargetById=942260;REQUEST_COOKIES:pma_console_config" + +# Search rows in a table +# MySQL chars such as '%' trigger rules on ARGS:criteriaColumnTypes[n] +SecRule REQUEST_FILENAME "@endsWith /tbl_select.php" \ + "id:9008270,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=942440;ARGS:orderByColumn,\ + ctl:ruleRemoveTargetById=942470;ARGS,\ + ctl:ruleRemoveTargetById=942300;ARGS" + +# Structure -> Add columns +# Column types contain SQL keyword. One commonly adds multiple columns. +# The column types are in ARGS:field_type[n] and could get arbitrarily high. +SecRule REQUEST_FILENAME "@endsWith /tbl_addfield.php" \ + "id:9008280,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=942470;ARGS" + +# Structure -> Change columns +# Column types contain SQL keyword. One commonly adds multiple columns. +# The column types are in ARGS:field_type[n] and ARGS:field_type_orig[n]. +SecRule REQUEST_FILENAME "@endsWith /tbl_structure.php" \ + "id:9008290,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=942470;ARGS" + +# Query -> Multi-table query +SecRule REQUEST_FILENAME "@endsWith /db_multi_table_query.php" \ + "id:9008300,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=932115;ARGS:sql_query" + +# Query -> Query by example +SecRule REQUEST_FILENAME "@endsWith /db_qbe.php" \ + "id:9008310,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:sql_query,\ + ctl:ruleRemoveTargetById=932115;ARGS:sql_query" + +# Databases -> Check privileges +SecRule REQUEST_FILENAME "@endsWith /server_privileges.php" \ + "id:9008320,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetById=942140;ARGS:db,\ + ctl:ruleRemoveTargetById=942140;ARGS:checkprivsdb" + +# Routines -> Add routine +SecRule REQUEST_FILENAME "@endsWith /db_routines.php" \ + "id:9008330,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + chain" + SecRule &REQUEST_COOKIES_NAMES:'/^(?:phpMyAdmin|phpMyAdmin_https)$/' "@eq 1" \ + "t:none,\ + ctl:ruleRemoveTargetByTag=attack-sqli;ARGS:item_definition" + +SecMarker "END-PHPMYADMIN" diff --git a/samples/ModSecurity SecRule/REQUEST-905-COMMON-EXCEPTIONS.conf b/samples/ModSecurity SecRule/REQUEST-905-COMMON-EXCEPTIONS.conf new file mode 100644 index 0000000000..44909dc5af --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-905-COMMON-EXCEPTIONS.conf @@ -0,0 +1,55 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + + +# This file is used as an exception mechanism to remove common false positives +# that may be encountered. +# +# Exception for Apache SSL pinger +# +SecRule REQUEST_LINE "@streq GET /" \ + "id:905100,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-apache',\ + tag:'attack-generic',\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REMOTE_ADDR "@ipMatch 127.0.0.1,::1" \ + "t:none,\ + ctl:ruleRemoveByTag=OWASP_CRS,\ + ctl:auditEngine=Off" + +# +# Exception for Apache internal dummy connection +# +SecRule REMOTE_ADDR "@ipMatch 127.0.0.1,::1" \ + "id:905110,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-apache',\ + tag:'attack-generic',\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule REQUEST_HEADERS:User-Agent "@endsWith (internal dummy connection)" \ + "t:none,\ + chain" + SecRule REQUEST_LINE "@rx ^(?:GET /|OPTIONS \*) HTTP/[12]\.[01]$" \ + "t:none,\ + ctl:ruleRemoveByTag=OWASP_CRS,\ + ctl:auditEngine=Off" diff --git a/samples/ModSecurity SecRule/REQUEST-910-IP-REPUTATION.conf b/samples/ModSecurity SecRule/REQUEST-910-IP-REPUTATION.conf new file mode 100644 index 0000000000..05f57a5afe --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-910-IP-REPUTATION.conf @@ -0,0 +1,302 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# +# -= Paranoia Level 0 (empty) =- (apply unconditionally) +# + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:910011,phase:1,pass,nolog,skipAfter:END-REQUEST-910-IP-REPUTATION" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:910012,phase:2,pass,nolog,skipAfter:END-REQUEST-910-IP-REPUTATION" +# +# -= Paranoia Level 1 (default) =- (apply only when tx.executing_paranoia_level is sufficiently high: 1 or higher) +# + +# +# -=[ IP Reputation Block Flag Check ]=- +# +# The first check we do is to see if the client IP address has already +# been deny listed by rules from previous requests. +# +# If the rule matches, it will do a skipAfter and pick up processing +# at the end of the request phase for actual blocking. +# +SecRule TX:DO_REPUT_BLOCK "@eq 1" \ + "id:910000,\ + phase:2,\ + block,\ + t:none,\ + msg:'Request from Known Malicious Client (Based on previous traffic violations)',\ + logdata:'Previous Block Reason: %{ip.reput_block_reason}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-ip',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + chain,\ + skipAfter:BEGIN-REQUEST-BLOCKING-EVAL" + SecRule IP:REPUT_BLOCK_FLAG "@eq 1" \ + "setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# -=[ GeoIP Checks ]=- +# +# This rule requires activating the SecGeoLookupDB directive +# in the crs-setup.conf file and specifying +# the list of blocked countries (tx.high_risk_country_codes). +# +# This rule does a GeoIP resolution on the client IP address. +# +SecRule TX:HIGH_RISK_COUNTRY_CODES "!@rx ^$" \ + "id:910100,\ + phase:1,\ + block,\ + t:none,\ + msg:'Client IP is from a HIGH Risk Country Location',\ + logdata:'%{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-ip',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + chain" + SecRule TX:REAL_IP "@geoLookup" \ + "chain" + SecRule GEO:COUNTRY_CODE "@within %{tx.high_risk_country_codes}" \ + "setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',\ + setvar:'ip.reput_block_flag=1',\ + setvar:'ip.reput_block_reason=%{rule.msg}',\ + expirevar:'ip.reput_block_flag=%{tx.reput_block_duration}'" + + +# +# -=[ IP Reputation Checks ]=- +# +# First check if we have already run an @rbl check for this IP by checking in IP collection. +# If we have, then skip doing another check. +# +SecRule IP:PREVIOUS_RBL_CHECK "@eq 1" \ + "id:910120,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-ip',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-RBL-LOOKUP" + +# +# Check Client IP against ProjectHoneypot's HTTP Deny List +# Ref: http://www.projecthoneypot.org/httpbl_api.php +# +# To use the deny list, you must register for an HttpBL API Key +# and choose the traffic types to block. See section +# "Project Honey Pot HTTP:BL" in crs-setup.conf. +# +# Ref: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#wiki-SecHttpBlKey +# + +# Skip HttpBL checks if user has not defined one of the TX:block_* variables. +# This prevents error "Operator error: RBL httpBl called but no key defined: set SecHttpBlKey" +SecRule &TX:block_suspicious_ip "@eq 0" \ + "id:910130,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain,\ + skipAfter:END-RBL-CHECK" + SecRule &TX:block_harvester_ip "@eq 0" \ + "chain" + SecRule &TX:block_spammer_ip "@eq 0" \ + "chain" + SecRule &TX:block_search_ip "@eq 0" + +SecRule TX:REAL_IP "@rbl dnsbl.httpbl.org" \ + "id:910140,\ + phase:2,\ + pass,\ + capture,\ + t:none,\ + nolog,\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-ip',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.httpbl_msg=%{tx.0}',\ + chain" + SecRule TX:httpbl_msg "@rx RBL lookup of .*?.dnsbl.httpbl.org succeeded at TX:checkip. (.*?): .*" \ + "capture,\ + t:none,\ + setvar:'tx.httpbl_msg=%{tx.1}'" + +# The following regexs are generated based off re_operators.c +SecRule TX:block_search_ip "@eq 1" \ + "id:910150,\ + phase:2,\ + block,\ + t:none,\ + msg:'HTTP Deny List match for search engine IP',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-ip',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + chain,\ + skipAfter:END-RBL-CHECK" + SecRule TX:httpbl_msg "@rx Search Engine" \ + "setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',\ + setvar:'ip.reput_block_flag=1',\ + setvar:'ip.reput_block_reason=%{rule.msg}',\ + setvar:'ip.previous_rbl_check=1',\ + expirevar:'ip.reput_block_flag=%{tx.reput_block_duration}',\ + expirevar:'ip.previous_rbl_check=86400'" + +SecRule TX:block_spammer_ip "@eq 1" \ + "id:910160,\ + phase:2,\ + block,\ + t:none,\ + msg:'HTTP Deny List match for spammer IP',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-ip',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + chain,\ + skipAfter:END-RBL-CHECK" + SecRule TX:httpbl_msg "@rx (?i)^.*? spammer .*?$" \ + "setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',\ + setvar:'ip.reput_block_flag=1',\ + setvar:'ip.reput_block_reason=%{rule.msg}',\ + setvar:'ip.previous_rbl_check=1',\ + expirevar:'ip.reput_block_flag=%{tx.reput_block_duration}',\ + expirevar:'ip.previous_rbl_check=86400'" + +SecRule TX:block_suspicious_ip "@eq 1" \ + "id:910170,\ + phase:2,\ + block,\ + t:none,\ + msg:'HTTP Deny List match for suspicious IP',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-ip',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + chain,\ + skipAfter:END-RBL-CHECK" + SecRule TX:httpbl_msg "@rx (?i)^.*? suspicious .*?$" \ + "setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',\ + setvar:'ip.reput_block_flag=1',\ + setvar:'ip.reput_block_reason=%{rule.msg}',\ + setvar:'ip.previous_rbl_check=1',\ + expirevar:'ip.reput_block_flag=%{tx.reput_block_duration}',\ + expirevar:'ip.previous_rbl_check=86400'" + +SecRule TX:block_harvester_ip "@eq 1" \ + "id:910180,\ + phase:2,\ + block,\ + t:none,\ + msg:'HTTP Deny List match for harvester IP',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-ip',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + chain,\ + skipAfter:END-RBL-CHECK" + SecRule TX:httpbl_msg "@rx (?i)^.*? harvester .*?$" \ + "setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',\ + setvar:'ip.reput_block_flag=1',\ + setvar:'ip.reput_block_reason=%{rule.msg}',\ + setvar:'ip.previous_rbl_check=1',\ + expirevar:'ip.reput_block_flag=%{tx.reput_block_duration}',\ + expirevar:'ip.previous_rbl_check=86400'" + +SecAction \ + "id:910190,\ + phase:2,\ + pass,\ + t:none,\ + nolog,\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-ip',\ + tag:'paranoia-level/1',\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'ip.previous_rbl_check=1',\ + expirevar:'ip.previous_rbl_check=86400'" + +SecMarker "END-RBL-LOOKUP" + +SecMarker "END-RBL-CHECK" + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:910013,phase:1,pass,nolog,skipAfter:END-REQUEST-910-IP-REPUTATION" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:910014,phase:2,pass,nolog,skipAfter:END-REQUEST-910-IP-REPUTATION" +# +# -= Paranoia Level 2 =- (apply only when tx.executing_paranoia_level is sufficiently high: 2 or higher) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:910015,phase:1,pass,nolog,skipAfter:END-REQUEST-910-IP-REPUTATION" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:910016,phase:2,pass,nolog,skipAfter:END-REQUEST-910-IP-REPUTATION" +# +# -= Paranoia Level 3 =- (apply only when tx.executing_paranoia_level is sufficiently high: 3 or higher) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:910017,phase:1,pass,nolog,skipAfter:END-REQUEST-910-IP-REPUTATION" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:910018,phase:2,pass,nolog,skipAfter:END-REQUEST-910-IP-REPUTATION" +# +# -= Paranoia Level 4 =- (apply only when tx.executing_paranoia_level is sufficiently high: 4 or higher) +# + + + +# +# -= Paranoia Levels Finished =- +# +SecMarker "END-REQUEST-910-IP-REPUTATION" diff --git a/samples/ModSecurity SecRule/REQUEST-911-METHOD-ENFORCEMENT.conf b/samples/ModSecurity SecRule/REQUEST-911-METHOD-ENFORCEMENT.conf new file mode 100644 index 0000000000..ae208db92a --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-911-METHOD-ENFORCEMENT.conf @@ -0,0 +1,76 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# +# -= Paranoia Level 0 (empty) =- (apply unconditionally) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:911011,phase:1,pass,nolog,skipAfter:END-REQUEST-911-METHOD-ENFORCEMENT" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:911012,phase:2,pass,nolog,skipAfter:END-REQUEST-911-METHOD-ENFORCEMENT" +# +# -= Paranoia Level 1 (default) =- (apply only when tx.executing_paranoia_level is sufficiently high: 1 or higher) +# + +# +# -=[ Allowed Request Methods ]=- +# +# tx.allowed_methods is defined in the crs-setup.conf file +# +SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" \ + "id:911100,\ + phase:1,\ + block,\ + msg:'Method is not allowed by policy',\ + logdata:'%{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-generic',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/272/220/274',\ + tag:'PCI/12.1',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:911013,phase:1,pass,nolog,skipAfter:END-REQUEST-911-METHOD-ENFORCEMENT" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:911014,phase:2,pass,nolog,skipAfter:END-REQUEST-911-METHOD-ENFORCEMENT" +# +# -= Paranoia Level 2 =- (apply only when tx.executing_paranoia_level is sufficiently high: 2 or higher) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:911015,phase:1,pass,nolog,skipAfter:END-REQUEST-911-METHOD-ENFORCEMENT" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:911016,phase:2,pass,nolog,skipAfter:END-REQUEST-911-METHOD-ENFORCEMENT" +# +# -= Paranoia Level 3 =- (apply only when tx.executing_paranoia_level is sufficiently high: 3 or higher) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:911017,phase:1,pass,nolog,skipAfter:END-REQUEST-911-METHOD-ENFORCEMENT" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:911018,phase:2,pass,nolog,skipAfter:END-REQUEST-911-METHOD-ENFORCEMENT" +# +# -= Paranoia Level 4 =- (apply only when tx.executing_paranoia_level is sufficiently high: 4 or higher) +# + + + +# +# -= Paranoia Levels Finished =- +# +SecMarker "END-REQUEST-911-METHOD-ENFORCEMENT" diff --git a/samples/ModSecurity SecRule/REQUEST-912-DOS-PROTECTION.conf b/samples/ModSecurity SecRule/REQUEST-912-DOS-PROTECTION.conf new file mode 100644 index 0000000000..c8df7ba65c --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-912-DOS-PROTECTION.conf @@ -0,0 +1,327 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# +# Anti-Automation rules to detect Denial of Service attacks. +# +# Description of mechanics: +# When a request hits a non-static resource (TX:STATIC_EXTENSIONS), then a counter for the IP +# address is being raised (IP:DOS_COUNTER). If the counter (IP:DOS_COUNTER) hits a limit +# (TX:DOS_COUNTER_THRESHOLD), then a burst is identified (IP:DOS_BURST_COUNTER) and the +# counter (IP:DOS_COUNTER) is reset. The burst counter expires within a timeout period +# (TX:DOS_BURST_TIME_SLICE). +# If the burst counter (IP:DOS_BURST_COUNTER) is greater equal 2, then the blocking flag +# is being set (IP:DOS_BLOCK). The blocking flag (IP:DOS_BLOCK) expires within a timeout +# period (TX:DOS_BLOCK_TIMEOUT). All this counting happens in phase 5. +# There is a stricter sibling to this rule (912170) in paranoia level 2, where the +# burst counter check (IP:DOS_BURST_COUNTER) hits at greater equal 1. +# +# The blocking is done in phase 1: When the blocking flag is encountered (IP:DOS_BLOCK), +# then the request is dropped without sending a response. If this happens, then a +# counter is # raised (IP:DOS_BLOCK_COUNTER). +# When an IP address is blocked for the first time, then the blocking is reported in a +# message and a flag (IP:DOS_BLOCK_FLAG) is set. This flag expires in 60 seconds. +# When an IP address is blocked and the flag (IP:DOS_BLOCK_FLAG) is set, then the +# blocking is not being reported (to prevent a flood of alerts). When the flag +# (IP:DOS_BLOCK_FLAG) has expired and a new request is being blocked, then the +# counter (IP:DOS_BLOCK_COUNTER) is being reset to 0 and the block is being treated +# as the first block (-> alert). +# In order to be able to display the counter (IP:DOS_BLOCK_COUNTER) and resetting +# it at the same time, we copy the counter (IP:DOS_BLOCK_COUNTER) into a different +# variable (TX:DOS_BLOCK_COUNTER), which is then displayed in turn. +# +# Variables: +# IP:DOS_BLOCK Flag if an IP address should be blocked +# IP:DOS_BLOCK_COUNTER Counter of blocked requests +# IP:DOS_BLOCK_FLAG Flag keeping track of alert. Flag expires after 60 seconds. +# IP:DOS_BURST_COUNTER Burst counter +# IP:DOS_COUNTER Request counter (static resources are ignored) +# TX:DOS_BLOCK_COUNTER Copy of IP:DOS_BLOCK_COUNTER (needed for display reasons) +# TX:DOS_BLOCK_TIMEOUT Period in seconds a blocked IP will be blocked +# TX:DOS_COUNTER_THRESHOLD Limit of requests, where a burst is identified +# TX:DOS_BURST_TIME_SLICE Period in seconds when we will forget a burst +# TX:STATIC_EXTENSIONS Paths which can be ignored with regards to DoS +# +# As a precondition for these rules, please set the following three variables: +# - TX:DOS_BLOCK_TIMEOUT +# - TX:DOS_COUNTER_THRESHOLD +# - TX:DOS_BURST_TIME_SLICE +# +# And make sure that TX:STATIC_EXTENSIONS is also set. +# + +# +# -= Paranoia Level 0 (empty) =- (apply unconditionally) +# + +# +# Skip if variables defining DoS protection are not set +# +SecRule &TX:dos_burst_time_slice "@eq 0" \ + "id:912100,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain,\ + skipAfter:END-DOS-PROTECTION-CHECKS" + SecRule &TX:dos_counter_threshold "@eq 0" \ + "chain" + SecRule &TX:dos_block_timeout "@eq 0" + +SecRule &TX:dos_burst_time_slice "@eq 0" \ + "id:912110,\ + phase:5,\ + pass,\ + t:none,\ + nolog,\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain,\ + skipAfter:END-DOS-PROTECTION-CHECKS" + SecRule &TX:dos_counter_threshold "@eq 0" \ + "chain" + SecRule &TX:dos_block_timeout "@eq 0" + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:912011,phase:1,pass,nolog,skipAfter:END-REQUEST-912-DOS-PROTECTION" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:912012,phase:2,pass,nolog,skipAfter:END-REQUEST-912-DOS-PROTECTION" +# +# -= Paranoia Level 1 (default) =- (apply only when tx.executing_paranoia_level is sufficiently high: 1 or higher) +# + +# +# -=[ Anti-Automation / DoS Protection : Block ]=- +# + +# +# Block and track # of requests and log +# +SecRule IP:DOS_BLOCK "@eq 1" \ + "id:912120,\ + phase:1,\ + drop,\ + msg:'Denial of Service (DoS) attack identified from %{tx.real_ip} (%{tx.dos_block_counter} hits since last alert)',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'paranoia-level/1',\ + tag:'attack-dos',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/227/469',\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule &IP:DOS_BLOCK_FLAG "@eq 0" \ + "setvar:'ip.dos_block_counter=+1',\ + setvar:'ip.dos_block_flag=1',\ + setvar:'tx.dos_block_counter=%{ip.dos_block_counter}',\ + setvar:'ip.dos_block_counter=0',\ + expirevar:'ip.dos_block_flag=60'" + + +# +# Block and track # of requests but don't log +# +SecRule IP:DOS_BLOCK "@eq 1" \ + "id:912130,\ + phase:1,\ + drop,\ + t:none,\ + nolog,\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'paranoia-level/1',\ + tag:'attack-dos',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/227/469',\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'ip.dos_block_counter=+1'" + + +# +# -=[ Anti-Automation / DoS Protection: Count requests ]=- +# + +# +# Skip if we have blocked the request +# +SecRule IP:DOS_BLOCK "@eq 1" \ + "id:912140,\ + phase:5,\ + pass,\ + t:none,\ + nolog,\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'paranoia-level/1',\ + tag:'attack-dos',\ + ver:'OWASP_CRS/3.4.0-dev',\ + skipAfter:END-DOS-PROTECTION-CHECKS" + + +# +# DOS Counter: Count the number of requests to non-static resources +# +SecRule REQUEST_BASENAME "@rx .*?(\.[a-z0-9]{1,10})?$" \ + "id:912150,\ + phase:5,\ + pass,\ + capture,\ + t:none,t:lowercase,\ + nolog,\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'paranoia-level/1',\ + tag:'attack-dos',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/227/469',\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'tx.extension=/%{TX.1}/',\ + chain" + SecRule TX:EXTENSION "!@within %{tx.static_extensions}" \ + "setvar:'ip.dos_counter=+1'" + + +# +# Check DOS Counter +# If the request count is greater than or equal to user settings, +# we raise the burst counter. This happens via two separate rules: +# - 912160: raise from 0 to 1 +# - 912161: raise from 1 to 2 +# +# This approach with two rules avoids raising the burst counter +# from 0 to 2 via two concurrent requests. We do not raise the +# burst counter beyond 2. +# +# +SecRule IP:DOS_COUNTER "@ge %{tx.dos_counter_threshold}" \ + "id:912160,\ + phase:5,\ + pass,\ + t:none,\ + nolog,\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'paranoia-level/1',\ + tag:'attack-dos',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/227/469',\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule &IP:DOS_BURST_COUNTER "@eq 0" \ + "setvar:'ip.dos_burst_counter=1',\ + setvar:'!ip.dos_counter',\ + expirevar:'ip.dos_burst_counter=%{tx.dos_burst_time_slice}'" + + +SecRule IP:DOS_COUNTER "@ge %{tx.dos_counter_threshold}" \ + "id:912161,\ + phase:5,\ + pass,\ + t:none,\ + nolog,\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'paranoia-level/1',\ + tag:'attack-dos',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/227/469',\ + ver:'OWASP_CRS/3.4.0-dev',\ + chain" + SecRule &IP:DOS_BURST_COUNTER "@ge 1" \ + "setvar:'ip.dos_burst_counter=2',\ + setvar:'!ip.dos_counter',\ + expirevar:'ip.dos_burst_counter=%{tx.dos_burst_time_slice}'" + + +# +# Check DOS Burst Counter and set Block +# Check the burst counter - if greater than or equal to 2, then we set the IP +# block variable for a given expiry and issue an alert. +# +SecRule IP:DOS_BURST_COUNTER "@ge 2" \ + "id:912170,\ + phase:5,\ + pass,\ + t:none,\ + msg:'Potential Denial of Service (DoS) Attack from %{tx.real_ip} - # of Request Bursts: %{ip.dos_burst_counter}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'paranoia-level/1',\ + tag:'attack-dos',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/227/469',\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'ip.dos_block=1',\ + expirevar:'ip.dos_block=%{tx.dos_block_timeout}'" + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:912013,phase:1,pass,nolog,skipAfter:END-REQUEST-912-DOS-PROTECTION" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:912014,phase:2,pass,nolog,skipAfter:END-REQUEST-912-DOS-PROTECTION" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:912019,phase:5,pass,nolog,skipAfter:END-REQUEST-912-DOS-PROTECTION" +# +# -= Paranoia Level 2 =- (apply only when tx.executing_paranoia_level is sufficiently high: 2 or higher) +# + +# +# Check DOS Burst Counter and set Block +# Check the burst counter - if greater than or equal to 1, then we set the IP +# block variable for a given expiry and issue an alert. +# +# This is a stricter sibling of rule 912170. +# +SecRule IP:DOS_BURST_COUNTER "@ge 1" \ + "id:912171,\ + phase:5,\ + pass,\ + t:none,\ + msg:'Potential Denial of Service (DoS) Attack from %{tx.real_ip} - # of Request Bursts: %{ip.dos_burst_counter}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-dos',\ + tag:'paranoia-level/2',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/227/469',\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'ip.dos_block=1',\ + expirevar:'ip.dos_block=%{tx.dos_block_timeout}'" + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:912015,phase:1,pass,nolog,skipAfter:END-REQUEST-912-DOS-PROTECTION" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:912016,phase:2,pass,nolog,skipAfter:END-REQUEST-912-DOS-PROTECTION" +# +# -= Paranoia Level 3 =- (apply only when tx.executing_paranoia_level is sufficiently high: 3 or higher) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:912017,phase:1,pass,nolog,skipAfter:END-REQUEST-912-DOS-PROTECTION" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:912018,phase:2,pass,nolog,skipAfter:END-REQUEST-912-DOS-PROTECTION" +# +# -= Paranoia Level 4 =- (apply only when tx.executing_paranoia_level is sufficiently high: 4 or higher) +# + + + +# +# -= Paranoia Levels Finished =- +# +SecMarker "END-REQUEST-912-DOS-PROTECTION" + +SecMarker "END-DOS-PROTECTION-CHECKS" diff --git a/samples/ModSecurity SecRule/REQUEST-913-SCANNER-DETECTION.conf b/samples/ModSecurity SecRule/REQUEST-913-SCANNER-DETECTION.conf new file mode 100644 index 0000000000..d190506dfb --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-913-SCANNER-DETECTION.conf @@ -0,0 +1,204 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# +# -= Paranoia Level 0 (empty) =- (apply unconditionally) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:913011,phase:1,pass,nolog,skipAfter:END-REQUEST-913-SCANNER-DETECTION" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:913012,phase:2,pass,nolog,skipAfter:END-REQUEST-913-SCANNER-DETECTION" +# +# -= Paranoia Level 1 (default) =- (apply only when tx.executing_paranoia_level is sufficiently high: 1 or higher) +# + +# +# -=[ Vulnerability Scanner Checks ]=- +# +# These rules inspect the default User-Agent and Header values sent by +# various commercial and open source vuln scanners. +# +# The following rules contain User-Agent lists: +# 913100 - security scanners (data file scanners-user-agents.data) +# 913101 - scripting/generic HTTP clients (data file scripting-user-agents.data) +# 913102 - web crawlers/bots (data file crawlers-user-agents.data) +# +# Chained rule is allow listing: +# YUM package manager of CentOS / Fedore: User-Agent: urlgrabber/3.10 yum/3.4.3 +# eCairn service: User-Agent: mozilla/5.0 ecairn-grabber/1.0 (+http://ecairn.com/grabber) +SecRule REQUEST_HEADERS:User-Agent "@pmFromFile scanners-user-agents.data" \ + "id:913100,\ + phase:1,\ + block,\ + capture,\ + t:none,\ + msg:'Found User-Agent associated with security scanner',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-scanner',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/118/224/541/310',\ + tag:'PCI/6.5.10',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + chain" + SecRule MATCHED_VARS "!@rx ^(?:urlgrabber/[0-9\.]+ yum/[0-9\.]+|mozilla/[0-9\.]+ ecairn-grabber/[0-9\.]+ \(\+http://ecairn.com/grabber\))$" \ + "setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',\ + setvar:'ip.reput_block_flag=1',\ + setvar:'ip.reput_block_reason=%{rule.msg}',\ + expirevar:'ip.reput_block_flag=%{tx.reput_block_duration}'" + +SecRule REQUEST_HEADERS_NAMES|REQUEST_HEADERS "@pmFromFile scanners-headers.data" \ + "id:913110,\ + phase:1,\ + block,\ + capture,\ + t:none,\ + msg:'Found request header associated with security scanner',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-scanner',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/118/224/541/310',\ + tag:'PCI/6.5.10',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',\ + setvar:'ip.reput_block_flag=1',\ + setvar:'ip.reput_block_reason=%{rule.msg}',\ + expirevar:'ip.reput_block_flag=%{tx.reput_block_duration}'" + + + +SecRule REQUEST_FILENAME|ARGS "@pmFromFile scanners-urls.data" \ + "id:913120,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Found request filename/argument associated with security scanner',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-scanner',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/118/224/541/310',\ + tag:'PCI/6.5.10',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',\ + setvar:'ip.reput_block_flag=1',\ + setvar:'ip.reput_block_reason=%{rule.msg}',\ + expirevar:'ip.reput_block_flag=%{tx.reput_block_duration}'" + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:913013,phase:1,pass,nolog,skipAfter:END-REQUEST-913-SCANNER-DETECTION" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:913014,phase:2,pass,nolog,skipAfter:END-REQUEST-913-SCANNER-DETECTION" +# +# -= Paranoia Level 2 =- (apply only when tx.executing_paranoia_level is sufficiently high: 2 or higher) +# + + +# +# -=[ Scripting/Generic User-Agents ]=- +# +# This rule detects user-agents associated with various HTTP client libraries +# and scripting languages. Detection suggests attempted access by some +# automated tool. +# +# This rule is a sibling of rule 913100. +# +SecRule REQUEST_HEADERS:User-Agent "@pmFromFile scripting-user-agents.data" \ + "id:913101,\ + phase:1,\ + block,\ + capture,\ + t:none,\ + msg:'Found User-Agent associated with scripting/generic HTTP client',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-scripting',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/118/224/541/310',\ + tag:'PCI/6.5.10',\ + tag:'paranoia-level/2',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}',\ + setvar:'ip.reput_block_flag=1',\ + setvar:'ip.reput_block_reason=%{rule.msg}',\ + expirevar:'ip.reput_block_flag=%{tx.reput_block_duration}'" + + + +# +# -=[ Crawler User-Agents ]=- +# +# This rule detects user-agents associated with various crawlers, SEO tools, +# and bots, which have been reported to potentially misbehave. +# These crawlers can have legitimate uses when used with authorization. +# +# This rule is a sibling of rule 913100. +# +SecRule REQUEST_HEADERS:User-Agent "@pmFromFile crawlers-user-agents.data" \ + "id:913102,\ + phase:1,\ + block,\ + capture,\ + t:none,\ + msg:'Found User-Agent associated with web crawler/bot',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-reputation-crawler',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/118/116/150',\ + tag:'PCI/6.5.10',\ + tag:'paranoia-level/2',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}',\ + setvar:'ip.reput_block_flag=1',\ + setvar:'ip.reput_block_reason=%{rule.msg}',\ + expirevar:'ip.reput_block_flag=%{tx.reput_block_duration}'" + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:913015,phase:1,pass,nolog,skipAfter:END-REQUEST-913-SCANNER-DETECTION" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:913016,phase:2,pass,nolog,skipAfter:END-REQUEST-913-SCANNER-DETECTION" +# +# -= Paranoia Level 3 =- (apply only when tx.executing_paranoia_level is sufficiently high: 3 or higher) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:913017,phase:1,pass,nolog,skipAfter:END-REQUEST-913-SCANNER-DETECTION" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:913018,phase:2,pass,nolog,skipAfter:END-REQUEST-913-SCANNER-DETECTION" +# +# -= Paranoia Level 4 =- (apply only when tx.executing_paranoia_level is sufficiently high: 4 or higher) +# + + + +# +# -= Paranoia Levels Finished =- +# +SecMarker "END-REQUEST-913-SCANNER-DETECTION" diff --git a/samples/ModSecurity SecRule/REQUEST-920-PROTOCOL-ENFORCEMENT.conf b/samples/ModSecurity SecRule/REQUEST-920-PROTOCOL-ENFORCEMENT.conf new file mode 100644 index 0000000000..16e1b4242f --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-920-PROTOCOL-ENFORCEMENT.conf @@ -0,0 +1,1688 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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: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. +# +# Regexp generated from util/regexp-assemble/data/920100.data using Regexp::Assemble. +# To rebuild the regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 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)^(?:(?:[a-z]{3,10}\s+(?:\w{3,7}?://[\w\-\./]*(?::\d+)?)?\/[^?#]*(?:\?[^#\s]*)?(?:#[\S]*)?|connect (?:(?:\d{1,3}\.){3}\d{1,3}\.?(?::\d+)?|[\w\-\./]+:\d+)|options \*)\s+[\w\./]+|get \/[^?#]*(?:\?[^#\s]*)?(?:#[\S]*)?)$" \ + "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/3.4.0-dev',\ + severity:'WARNING',\ + setvar:'tx.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. +# +# To rebuild the regular expression: +# cd util/regexp-assemble +# ./regexp-assemble.py 920120 +# +SecRule FILES|FILES_NAMES "!@rx (?i)^(?:&(?:(?:[aeiouclnrszg]acut|[aeiou]grav|[anoi]tild)e|(?:[cgklnrst]cedi|[aeiouy]um)l|[aeioucghjswy]circ|[cdelnrstz]caron|a(?:pos|mp)|[au]ring|oslash|nbsp);|[^'\";=])*$" \ + "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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + chain" + SecRule REQUEST_HEADERS:Content-Length "!@rx ^0?$" \ + "t:none,\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + chain" + SecRule &REQUEST_HEADERS:Transfer-Encoding "!@eq 0" \ + "t:none,\ + setvar:'tx.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/3.4.0-dev',\ + 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.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/3.4.0-dev',\ + severity:'WARNING',\ + chain" + SecRule &REQUEST_HEADERS:Content-Length "!@eq 0" \ + "t:none,\ + setvar:'tx.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/3.4.0-dev',\ + severity:'WARNING',\ + chain" + SecRule TX:2 "@lt %{tx.1}" \ + "setvar:'tx.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/3.4.0-dev',\ + severity:'WARNING',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'WARNING',\ + chain" + SecRule REQUEST_URI "@validateUrlEncoding" \ + "setvar:'tx.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/3.4.0-dev',\ + severity:'WARNING',\ + chain" + SecRule REQUEST_BODY "@rx \x25" \ + "chain" + SecRule REQUEST_BODY "@validateUrlEncoding" \ + "setvar:'tx.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/3.4.0-dev',\ + severity:'WARNING',\ + chain" + SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "@validateUtf8Encoding" \ + "setvar:'tx.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/3.4.0-dev',\ + severity:'WARNING',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'WARNING',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'WARNING',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'NOTICE',\ + chain" + SecRule REQUEST_METHOD "!@rx ^OPTIONS$" \ + "chain" + SecRule REQUEST_HEADERS:User-Agent "!@pm AppleWebKit Android Business Enterprise Entreprise" \ + "t:none,\ + setvar:'tx.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/3.4.0-dev',\ + severity:'NOTICE',\ + chain" + SecRule REQUEST_METHOD "!@rx ^OPTIONS$" \ + "chain" + SecRule &REQUEST_HEADERS:User-Agent "@eq 0" \ + "t:none,\ + setvar:'tx.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/3.4.0-dev',\ + severity:'NOTICE',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'NOTICE',\ + chain" + SecRule &REQUEST_HEADERS:Content-Type "@eq 0" \ + "t:none,\ + setvar:'tx.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/3.4.0-dev',\ + severity:'WARNING',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + chain" + SecRule &ARGS "@gt %{tx.max_num_args}" \ + "t:none,\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + chain" + SecRule ARGS_NAMES "@gt %{tx.arg_name_length}" \ + "t:none,t:length,\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + chain" + SecRule ARGS "@gt %{tx.arg_length}" \ + "t:none,t:length,\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + chain" + SecRule ARGS_COMBINED_SIZE "@gt %{tx.total_arg_length}" \ + "t:none,\ + setvar:'tx.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/3.4.0-dev',\ + 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.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/3.4.0-dev',\ + severity:'CRITICAL',\ + chain" + SecRule FILES_COMBINED_SIZE "@gt %{tx.combined_file_sizes}" \ + "t:none,\ + setvar:'tx.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" +# +SecRule REQUEST_HEADERS:Content-Type "!@rx ^[\w/.+-]+(?:\s?;\s?(?:action|boundary|charset|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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.content_type=|%{tx.0}|',\ + chain" + SecRule TX:content_type "!@within %{tx.allowed_request_content_type}" \ + "t:lowercase,\ + setvar:'tx.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/3.4.0-dev',\ + 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.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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.extension=.%{tx.1}/',\ + chain" + SecRule TX:EXTENSION "@within %{tx.restricted_extensions}" \ + "t:none,t:urlDecodeUni,t:lowercase,\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.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_. 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: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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.header_name_%{tx.0}=/%{tx.0}/',\ + chain" + SecRule TX:/^header_name_/ "@within %{tx.restricted_headers}" \ + "setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +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: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/3.4.0-dev',\ + severity:'WARNING',\ + chain" + SecRule REQUEST_BASENAME "!@endsWith .pdf" \ + "setvar:'tx.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/3.4.0-dev',\ + severity:'WARNING',\ + chain" + SecRule REQUEST_HEADERS:Range|REQUEST_HEADERS:Request-Range "@rx ^bytes=(?:(?:\d+)?-(?:\d+)?\s*,?\s*){63}" \ + "setvar:'tx.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/3.4.0-dev',\ + severity:'WARNING',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'NOTICE',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + chain" + SecRule &REQUEST_HEADERS:Content-Type "@eq 0" \ + "t:none,\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}'" + + +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 +# +# 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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'NOTICE',\ + chain" + SecRule REQUEST_METHOD "!@rx ^(?:OPTIONS|CONNECT)$" \ + "chain" + SecRule REQUEST_HEADERS:User-Agent "!@pm AppleWebKit Android" \ + "t:none,\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + chain" + SecRule REQUEST_HEADERS:User-Agent "@rx ^(?i)up" \ + "t:none,\ + setvar:'tx.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= +# - max-stale[=] +# - min-fresh= +# - 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/3.4.0-dev',\ + 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.anomaly_score_pl3=+%{tx.critical_anomaly_score}'" + +# +# This rule checks for valid Accept-Encoding headers +# +# This rule has a less strict sibling: 920520 +# +# Regexp generated from util/regexp-assemble/data/920521.data using Regexp::Assemble. +# To rebuild the regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 920521 +# +SecRule REQUEST_HEADERS:Accept-Encoding "!@rx (?:x-(?:compress|gzip)|(?:pack200-)?gzip|aes128gcm|compress|identity|deflate|zstd|exi|^$|br|\*)" \ + "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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.anomaly_score_pl3=+%{tx.critical_anomaly_score}'" + +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: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/3.4.0-dev',\ + severity:'WARNING',\ + chain" + SecRule REQUEST_HEADERS:Range|REQUEST_HEADERS:Request-Range "@rx ^bytes=(?:(?:\d+)?-(?:\d+)?\s*,?\s*){6}" \ + "setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.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/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.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',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.http_violation_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl4=+%{tx.critical_anomaly_score}'" + + +# +# -= Paranoia Levels Finished =- +# +SecMarker "END-REQUEST-920-PROTOCOL-ENFORCEMENT" diff --git a/samples/ModSecurity SecRule/REQUEST-921-PROTOCOL-ATTACK.conf b/samples/ModSecurity SecRule/REQUEST-921-PROTOCOL-ATTACK.conf new file mode 100644 index 0000000000..7329dd2ae8 --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-921-PROTOCOL-ATTACK.conf @@ -0,0 +1,365 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# +# -= Paranoia Level 0 (empty) =- (apply unconditionally) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:921011,phase:1,pass,nolog,skipAfter:END-REQUEST-921-PROTOCOL-ATTACK" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:921012,phase:2,pass,nolog,skipAfter:END-REQUEST-921-PROTOCOL-ATTACK" +# +# -= Paranoia Level 1 (default) =- (apply only when tx.executing_paranoia_level is sufficiently high: 1 or higher) +# + +# +# -=[ HTTP Request Smuggling ]=- +# +# [ Rule Logic ] +# This rule looks for a HTTP / WEBDAV method name in combination with the word http/\d or a CR/LF character. +# This would point to an attempt to inject a 2nd request into the request, thus bypassing +# tests carried out on the primary request. +# +# [ References ] +# http://projects.webappsec.org/HTTP-Request-Smuggling +# +SecRule ARGS_NAMES|ARGS|REQUEST_BODY|XML:/* "@rx (?:get|post|head|options|connect|put|delete|trace|track|patch|propfind|propatch|mkcol|copy|move|lock|unlock)\s+[^\s]+\s+http/\d" \ + "id:921110,\ + phase:2,\ + block,\ + capture,\ + t:none,t:htmlEntityDecode,t:lowercase,\ + msg:'HTTP Request Smuggling Attack',\ + 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/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/272/220/33',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.http_violation_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + +# +# -=[ HTTP Response Splitting ]=- +# +# [ Rule Logic ] +# These rules look for Carriage Return (CR) %0d and Linefeed (LF) %0a characters. +# These characters may cause problems if the data is returned in a respones header and +# may be interpreted by an intermediary proxy server and treated as two separate +# responses. +# +# [ References ] +# http://projects.webappsec.org/HTTP-Response-Splitting +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx [\r\n]\W*?(?:content-(?:type|length)|set-cookie|location):\s*\w" \ + "id:921120,\ + phase:2,\ + block,\ + capture,\ + t:none,t:lowercase,\ + msg:'HTTP Response Splitting Attack',\ + 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/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/272/220/34',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.http_violation_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?:\bhttp/\d|<(?:html|meta)\b)" \ + "id:921130,\ + phase:2,\ + block,\ + capture,\ + t:none,t:htmlEntityDecode,t:lowercase,\ + msg:'HTTP Response Splitting Attack',\ + 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/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/272/220/34',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.http_violation_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + +# +# -=[ HTTP Header Injection ]=- +# +# [ Rule Logic ] +# These rules look for Carriage Return (CR) %0d and Linefeed (LF) %0a characters, +# on their own or in combination with header field names. +# These characters may cause problems if the data is returned in a response header +# and interpreted by the client. +# The rules are similar to rules defending against the HTTP Request Splitting and +# Request Smuggling rules. +# +# [ References ] +# https://en.wikipedia.org/wiki/HTTP_header_injection +# +SecRule REQUEST_HEADERS_NAMES|REQUEST_HEADERS "@rx [\n\r]" \ + "id:921140,\ + phase:1,\ + block,\ + capture,\ + t:none,t:htmlEntityDecode,\ + msg:'HTTP Header Injection Attack via headers',\ + 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/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/272/220/273',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.http_violation_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# Detect newlines in argument names. +# Checking for GET arguments has been moved to paranoia level 2 (921151) +# in order to mitigate possible false positives. +# +# 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_NAMES "@rx [\n\r]" \ + "id:921150,\ + phase:2,\ + block,\ + capture,\ + t:none,t:htmlEntityDecode,\ + msg:'HTTP Header Injection Attack via payload (CR/LF detected)',\ + 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/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/272/220/33',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.http_violation_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +SecRule ARGS_GET_NAMES|ARGS_GET "@rx [\n\r]+(?:\s|location|refresh|(?:set-)?cookie|(?:x-)?(?:forwarded-(?:for|host|server)|host|via|remote-ip|remote-addr|originating-IP))\s*:" \ + "id:921160,\ + phase:1,\ + block,\ + capture,\ + t:none,t:htmlEntityDecode,t:lowercase,\ + msg:'HTTP Header Injection Attack via payload (CR/LF and header-name detected)',\ + 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/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/272/220/33',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.http_violation_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# -=[ HTTP Splitting ]=- +# +# This rule detect \n or \r in the REQUEST FILENAME +# Reference: https://www.owasp.org/index.php/Testing_for_HTTP_Splitting/Smuggling_(OTG-INPVAL-016) +# +SecRule REQUEST_FILENAME "@rx [\n\r]" \ + "id:921190,\ + phase:1,\ + block,\ + t:none,t:urlDecodeUni,\ + msg:'HTTP Splitting (CR/LF in request filename detected)',\ + 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/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/272/220/34',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.http_violation_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# -=[ LDAP Injection ]=- +# +# [ Rule Logic ] +# +# This is a rule trying to prevent LDAP injection. It is based on a BlackHat presentation by Alonso Parada +# and regex writing by Denis Kolegov. +# +# [ References ] +# * https://www.blackhat.com/presentations/bh-europe-08/Alonso-Parada/Whitepaper/bh-eu-08-alonso-parada-WP.pdf +# * https://blog.ripstech.com/2017/joomla-takeover-in-20-seconds-with-ldap-injection-cve-2017-14596/ +# * https://github.com/SpiderLabs/owasp-modsecurity-crs/issues/276#issue-126581660 + +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx ^[^:\(\)\&\|\!\<\>\~]*\)\s*(?:\((?:[^,\(\)\=\&\|\!\<\>\~]+[><~]?=|\s*[&!|]\s*(?:\)|\()?\s*)|\)\s*\(\s*[\&\|\!]\s*|[&!|]\s*\([^\(\)\=\&\|\!\<\>\~]+[><~]?=[^:\(\)\&\|\!\<\>\~]*)" \ + "id:921200,\ + phase:2,\ + block,\ + capture,\ + t:none,t:htmlEntityDecode,\ + msg:'LDAP Injection Attack',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-ldap',\ + tag:'platform-multi',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/136',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:921013,phase:1,pass,nolog,skipAfter:END-REQUEST-921-PROTOCOL-ATTACK" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:921014,phase:2,pass,nolog,skipAfter:END-REQUEST-921-PROTOCOL-ATTACK" +# +# -= Paranoia Level 2 =- (apply only when tx.executing_paranoia_level is sufficiently high: 2 or higher) +# + + +# Detect newlines in GET argument values. +# These may point to a HTTP header injection attack, but can also sometimes +# occur in benign query parameters. +# +# See also: rule 921140, 921150 +# +SecRule ARGS_GET "@rx [\n\r]" \ + "id:921151,\ + phase:1,\ + block,\ + capture,\ + t:none,t:urlDecodeUni,t:htmlEntityDecode,\ + msg:'HTTP Header Injection Attack via payload (CR/LF detected)',\ + 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/2',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/210/272/220/33',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.http_violation_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}'" + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:921015,phase:1,pass,nolog,skipAfter:END-REQUEST-921-PROTOCOL-ATTACK" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:921016,phase:2,pass,nolog,skipAfter:END-REQUEST-921-PROTOCOL-ATTACK" +# +# -= Paranoia Level 3 =- (apply only when tx.executing_paranoia_level is sufficiently high: 3 or higher) +# +# + +# -=[ HTTP Parameter Pollution ]=- +# +# [ Rule Logic ] +# These rules look for multiple parameters with the same name. +# 921170 counts the occurrences of the individual parameters. +# 921180 checks if any counter is > 1. +# +# One HPP attack vector is to try evade signature filters by distributing the +# attack payload across multiple parameters with the same name. +# This works as many security devices only apply signatures to individual +# parameter payloads, however the back-end web application may (in the case +# of ASP.NET) consolidate all of the payloads into one thus making the +# attack payload active. +# +# [ References ] +# http://tacticalwebappsec.blogspot.com/2009/05/http-parameter-pollution.html +# https://capec.mitre.org/data/definitions/460.html +# +SecRule ARGS_NAMES "@rx ." \ + "id:921170,\ + phase:2,\ + pass,\ + nolog,\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-protocol',\ + tag:'paranoia-level/3',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/137/15/460',\ + ver:'OWASP_CRS/3.4.0-dev',\ + setvar:'TX.paramcounter_%{MATCHED_VAR_NAME}=+1'" + +SecRule TX:/paramcounter_.*/ "@gt 1" \ + "id:921180,\ + phase:2,\ + pass,\ + msg:'HTTP Parameter Pollution (%{TX.1})',\ + 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:'OWASP_CRS',\ + tag:'capec/1000/152/137/15/460',\ + tag:'paranoia-level/3',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + chain" + SecRule MATCHED_VARS_NAMES "@rx TX:paramcounter_(.*)" \ + "capture,\ + setvar:'tx.http_violation_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl3=+%{tx.critical_anomaly_score}'" + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:921017,phase:1,pass,nolog,skipAfter:END-REQUEST-921-PROTOCOL-ATTACK" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:921018,phase:2,pass,nolog,skipAfter:END-REQUEST-921-PROTOCOL-ATTACK" +# +# -= Paranoia Level 4 =- (apply only when tx.executing_paranoia_level is sufficiently high: 4 or higher) +# + + + +# +# -= Paranoia Levels Finished =- +# +SecMarker "END-REQUEST-921-PROTOCOL-ATTACK" diff --git a/samples/ModSecurity SecRule/REQUEST-930-APPLICATION-ATTACK-LFI.conf b/samples/ModSecurity SecRule/REQUEST-930-APPLICATION-ATTACK-LFI.conf new file mode 100644 index 0000000000..80bc9a8795 --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-930-APPLICATION-ATTACK-LFI.conf @@ -0,0 +1,177 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# +# -= Paranoia Level 0 (empty) =- (apply unconditionally) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:930011,phase:1,pass,nolog,skipAfter:END-REQUEST-930-APPLICATION-ATTACK-LFI" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:930012,phase:2,pass,nolog,skipAfter:END-REQUEST-930-APPLICATION-ATTACK-LFI" +# +# -= Paranoia Level 1 (default) =- (apply only when tx.executing_paranoia_level is sufficiently high: 1 or higher) +# + +# +# -=[ Directory Traversal Attacks ]=- +# +# Ref: https://github.com/wireghoul/dotdotpwn +# +# [ Encoded /../ Payloads ] +# +# Regexp generated from util/regexp-assemble/data/930100-slashes.data and +# util/regexp-assemble/data/930100-dots.data using Regexp::Assemble. +# To rebuild the regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 930100-slashes # SLASHES PATTERN +# ./regexp-assemble.py 930100-dots # DOTS PATTERN +# Prepend `(?i)` to the two patterns above to produce the following sequence +# SLASHES PATTERN before *and* after DOTS PATTERN): +# (?i)[SLASHES PATTERN][DOTS PATTERN][SLASHES PATTERN] +# +SecRule REQUEST_URI_RAW|ARGS|REQUEST_HEADERS|!REQUEST_HEADERS:Referer|FILES|XML:/* "@rx (?i)(?:%(?:c(?:0%(?:[2aq]f|5c|9v)|1%(?:[19p]c|8s|af))|2(?:5(?:c(?:0%25af|1%259c)|2f|5c)|%46|f)|(?:(?:f(?:8%8)?0%8|e)0%80%a|bg%q)f|%3(?:2(?:%(?:%6|4)6|F)|5%%63)|u(?:221[56]|002f|EFC8|F025)|1u|5c)|0x(?:2f|5c)|\/|\x5c)(?:%(?:(?:f(?:(?:c%80|8)%8)?0%8|e)0%80%ae|2(?:(?:5(?:c0%25a|2))?e|%45)|u(?:(?:002|ff0)e|2024)|%32(?:%(?:%6|4)5|E)|c0(?:%[256aef]e|\.))|\.(?:%0[01]|\?)?|\?\.?|0x2e){2,3}(?:%(?:c(?:0%(?:[2aq]f|5c|9v)|1%(?:[19p]c|8s|af))|2(?:5(?:c(?:0%25af|1%259c)|2f|5c)|%46|f)|(?:(?:f(?:8%8)?0%8|e)0%80%a|bg%q)f|%3(?:2(?:%(?:%6|4)6|F)|5%%63)|u(?:221[56]|002f|EFC8|F025)|1u|5c)|0x(?:2f|5c)|\/|\x5c)" \ + "id:930100,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Path Traversal Attack (/../) or (/.../)',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-lfi',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/255/153/126',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',\ + setvar:'tx.lfi_score=+%{tx.critical_anomaly_score}'" + +# +# [ Decoded /../ Payloads ] +# +# To prevent '..' from triggering, the regexp is split into two parts: +# - ../ +# - /.. +# OR +# - .../ +# - /... + +SecRule REQUEST_URI|ARGS|REQUEST_HEADERS|!REQUEST_HEADERS:Referer|FILES|XML:/* "@rx (?:(?:^|[\x5c/])\.{2,3}[\x5c/]|[\x5c/]\.{2,3}(?:[\x5c/]|$))" \ + "id:930110,\ + phase:2,\ + block,\ + capture,\ + t:none,t:utf8toUnicode,t:urlDecodeUni,t:removeNulls,t:cmdLine,\ + msg:'Path Traversal Attack (/../) or (/.../)',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-lfi',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/255/153/126',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + multiMatch,\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',\ + setvar:'tx.lfi_score=+%{tx.critical_anomaly_score}'" + +# +# -=[ OS File Access ]=- +# +# We check for OS file access with the help of a local file with OS files data. +# +# Ref: https://github.com/lightos/Panoptic/blob/master/cases.xml +# +# If you wonder where support for Google OAuth2 has gone, see: +# https://github.com/coreruleset/google-oauth2-plugin +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@pmFromFile lfi-os-files.data" \ + "id:930120,\ + phase:2,\ + block,\ + capture,\ + t:none,t:utf8toUnicode,t:urlDecodeUni,t:normalizePathWin,\ + msg:'OS File Access Attempt',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-lfi',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/255/153/126',\ + tag:'PCI/6.5.4',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.lfi_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + +# +# -=[ Restricted File Access ]=- +# +# Detects attempts to retrieve application source code, metadata, +# credentials and version control history possibly reachable in a web root. +# +SecRule REQUEST_FILENAME "@pmFromFile restricted-files.data" \ + "id:930130,\ + phase:1,\ + block,\ + capture,\ + t:none,t:utf8toUnicode,t:urlDecodeUni,t:normalizePathWin,\ + msg:'Restricted File Access Attempt',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-lfi',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/255/153/126',\ + tag:'PCI/6.5.4',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.lfi_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:930013,phase:1,pass,nolog,skipAfter:END-REQUEST-930-APPLICATION-ATTACK-LFI" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:930014,phase:2,pass,nolog,skipAfter:END-REQUEST-930-APPLICATION-ATTACK-LFI" +# +# -= Paranoia Level 2 =- (apply only when tx.executing_paranoia_level is sufficiently high: 2 or higher) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:930015,phase:1,pass,nolog,skipAfter:END-REQUEST-930-APPLICATION-ATTACK-LFI" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:930016,phase:2,pass,nolog,skipAfter:END-REQUEST-930-APPLICATION-ATTACK-LFI" +# +# -= Paranoia Level 3 =- (apply only when tx.executing_paranoia_level is sufficiently high: 3 or higher) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:930017,phase:1,pass,nolog,skipAfter:END-REQUEST-930-APPLICATION-ATTACK-LFI" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:930018,phase:2,pass,nolog,skipAfter:END-REQUEST-930-APPLICATION-ATTACK-LFI" +# +# -= Paranoia Level 4 =- (apply only when tx.executing_paranoia_level is sufficiently high: 4 or higher) +# + + + +# +# -= Paranoia Levels Finished =- +# +SecMarker "END-REQUEST-930-APPLICATION-ATTACK-LFI" diff --git a/samples/ModSecurity SecRule/REQUEST-931-APPLICATION-ATTACK-RFI.conf b/samples/ModSecurity SecRule/REQUEST-931-APPLICATION-ATTACK-RFI.conf new file mode 100644 index 0000000000..1896ea2dc9 --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-931-APPLICATION-ATTACK-RFI.conf @@ -0,0 +1,153 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ +# +# RFI Attacks +# + +# +# -= Paranoia Level 0 (empty) =- (apply unconditionally) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:931011,phase:1,pass,nolog,skipAfter:END-REQUEST-931-APPLICATION-ATTACK-RFI" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:931012,phase:2,pass,nolog,skipAfter:END-REQUEST-931-APPLICATION-ATTACK-RFI" +# +# -= Paranoia Level 1 (default) =- (apply only when tx.executing_paranoia_level is sufficiently high: 1 or higher) +# + +# -=[ Rule Logic ]=- +# These rules look for common types of Remote File Inclusion (RFI) attack methods. +# - URL Contains an IP Address +# - The PHP "include()" Function +# - RFI Data Ends with Question Mark(s) (?) +# - RFI Host Doesn't Match Local Host +# +# -=[ References ]=- +# http://projects.webappsec.org/Remote-File-Inclusion +# http://tacticalwebappsec.blogspot.com/2009/06/generic-remote-file-inclusion-attack.html +# +SecRule ARGS "@rx ^(?i:file|ftps?|https?)://(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" \ + "id:931100,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Possible Remote File Inclusion (RFI) Attack: URL Parameter using IP Address',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-rfi',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/175/253',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rfi_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + +SecRule QUERY_STRING|REQUEST_BODY "@rx (?i)(?:\binclude\s*\([^)]*|mosConfig_absolute_path|_CONF\[path\]|_SERVER\[DOCUMENT_ROOT\]|GALLERY_BASEDIR|path\[docroot\]|appserv_root|config\[root_dir\])=(?:file|ftps?|https?)://" \ + "id:931110,\ + phase:2,\ + block,\ + capture,\ + t:none,t:urlDecodeUni,\ + msg:'Possible Remote File Inclusion (RFI) Attack: Common RFI Vulnerable Parameter Name used w/URL Payload',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-rfi',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/175/253',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rfi_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + +SecRule ARGS "@rx ^(?i:file|ftps?|https?).*?\?+$" \ + "id:931120,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Possible Remote File Inclusion (RFI) Attack: URL Payload Used w/Trailing Question Mark Character (?)',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-rfi',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/175/253',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rfi_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:931013,phase:1,pass,nolog,skipAfter:END-REQUEST-931-APPLICATION-ATTACK-RFI" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:931014,phase:2,pass,nolog,skipAfter:END-REQUEST-931-APPLICATION-ATTACK-RFI" +# +# -= Paranoia Level 2 =- (apply only when tx.executing_paranoia_level is sufficiently high: 2 or higher) +# + +SecRule ARGS "@rx ^(?i:file|ftps?|https?)://([^/]*).*$" \ + "id:931130,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Possible Remote File Inclusion (RFI) Attack: Off-Domain Reference/Link',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-rfi',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/175/253',\ + tag:'paranoia-level/2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rfi_parameter_%{MATCHED_VAR_NAME}=.%{tx.1}',\ + chain" + SecRule TX:/rfi_parameter_.*/ "!@endsWith .%{request_headers.host}" \ + "setvar:'tx.rfi_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}'" + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:931015,phase:1,pass,nolog,skipAfter:END-REQUEST-931-APPLICATION-ATTACK-RFI" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:931016,phase:2,pass,nolog,skipAfter:END-REQUEST-931-APPLICATION-ATTACK-RFI" +# +# -= Paranoia Level 3 =- (apply only when tx.executing_paranoia_level is sufficiently high: 3 or higher) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:931017,phase:1,pass,nolog,skipAfter:END-REQUEST-931-APPLICATION-ATTACK-RFI" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:931018,phase:2,pass,nolog,skipAfter:END-REQUEST-931-APPLICATION-ATTACK-RFI" +# +# -= Paranoia Level 4 =- (apply only when tx.executing_paranoia_level is sufficiently high: 4 or higher) +# + + + +# +# -= Paranoia Levels Finished =- +# +SecMarker "END-REQUEST-931-APPLICATION-ATTACK-RFI" diff --git a/samples/ModSecurity SecRule/REQUEST-932-APPLICATION-ATTACK-RCE.conf b/samples/ModSecurity SecRule/REQUEST-932-APPLICATION-ATTACK-RCE.conf new file mode 100644 index 0000000000..9dd616fca3 --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-932-APPLICATION-ATTACK-RCE.conf @@ -0,0 +1,982 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# +# -= Paranoia Level 0 (empty) =- (apply unconditionally) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:932011,phase:1,pass,nolog,skipAfter:END-REQUEST-932-APPLICATION-ATTACK-RCE" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:932012,phase:2,pass,nolog,skipAfter:END-REQUEST-932-APPLICATION-ATTACK-RCE" +# +# -= Paranoia Level 1 (default) =- (apply only when tx.executing_paranoia_level is sufficiently high: 1 or higher) +# + + +# [ Unix command injection ] +# +# This rule detects Unix command injections. +# A command injection takes a form such as: +# +# foo.jpg;uname -a +# foo.jpg||uname -a +# +# The vulnerability exists when an application executes a shell command +# without proper input escaping/validation. +# +# This rule is also triggered by an Oracle WebLogic Remote Command Execution exploit: +# [ Oracle WebLogic vulnerability CVE-2017-10271 - Exploit tested: https://www.exploit-db.com/exploits/43458 ] +# +# To prevent false positives, we look for a 'starting sequence' that +# precedes a command in shell syntax, such as: ; | & $( ` <( >( +# Anatomy of the regexp with examples of patterns caught: +# +# 1. Starting tokens +# +# ; ;ifconfig +# \{ {ifconfig} +# \| |ifconfig +# \|\| ||ifconfig +# & &ifconfig +# && &&ifconfig +# \n ;\nifconfig +# \r ;\rifconfig +# \$\( $(ifconfig) +# $\(\( $((ifconfig)) +# ` `ifconfig` +# \${ ${ifconfig} +# <\( <( ifconfig ) +# >\( >( ifconfig ) +# \(\s*\) a() ( ifconfig; ); a +# +# 2. Command prefixes +# +# { { ifconfig } +# \s*\(\s* ( ifconfig ) +# \w+=(?:[^\s]*|\$.*|\$.*|<.*|>.*|\'.*\'|\".*\")\s+ VARNAME=xyz ifconfig +# !\s* ! ifconfig +# \$ $ifconfig +# +# 3. Quoting +# +# ' 'ifconfig' +# \" "ifconfig" +# +# 4. Paths +# +# [\?\*\[\]\(\)\-\|+\w'\"\./\x5c]+/ /sbin/ifconfig, /s?in/./ifconfig, /s[a-b]in/ifconfig etc. +# +# This rule is case-sensitive to prevent FP ("Cat" vs. "cat"). +# +# An effort was made to combat evasions by shell quoting (e.g. 'ls', +# 'l'"s", \l\s are all valid). ModSecurity has a t:cmdLine +# transformation built-in to deal with this, but unfortunately, it +# replaces ';' characters and lowercases the payload, which is less +# useful for this case. However, emulating the transformation makes +# the regexp more complex. +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 932100 +# +# This is the base Rule to prevent Unix Command Injection +# Please refer other rules 932105,932106,932101 to know more. +# +# .932100 (base rule, PL1) +# ├── 932101 (stricter sibling of base rule containing the source-shortcut ".", PL2) +# ├── 932105 (2nd part of base rule, PL1) +# ├── 932106 (stricter sibling of base rule, PL3 +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?:;|\{|\||\|\||&|&&|\n|\r|\$\(|\$\(\(|`|\${|<\(|>\(|\(\s*\))\s*(?:{|\s*\(\s*|\w+=(?:[^\s]*|\$.*|\$.*|<.*|>.*|\'.*\'|\".*\")\s+|!\s*|\$)*\s*(?:'|\")*(?:[\?\*\[\]\(\)\-\|+\w'\"\./\x5c]+/)?[\x5c'\"]*(?:l[\x5c'\"]*(?:w[\x5c'\"]*p[\x5c'\"]*-[\x5c'\"]*(?:d[\x5c'\"]*(?:o[\x5c'\"]*w[\x5c'\"]*n[\x5c'\"]*l[\x5c'\"]*o[\x5c'\"]*a[\x5c'\"]*d|u[\x5c'\"]*m[\x5c'\"]*p)|r[\x5c'\"]*e[\x5c'\"]*q[\x5c'\"]*u[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*t|m[\x5c'\"]*i[\x5c'\"]*r[\x5c'\"]*r[\x5c'\"]*o[\x5c'\"]*r)|s(?:[\x5c'\"]*(?:b[\x5c'\"]*_[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*l[\x5c'\"]*e[\x5c'\"]*a[\x5c'\"]*s[\x5c'\"]*e|c[\x5c'\"]*p[\x5c'\"]*u|m[\x5c'\"]*o[\x5c'\"]*d|p[\x5c'\"]*c[\x5c'\"]*i|u[\x5c'\"]*s[\x5c'\"]*b|-[\x5c'\"]*F|h[\x5c'\"]*w|o[\x5c'\"]*f))?|z[\x5c'\"]*(?:(?:[ef][\x5c'\"]*)?g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p|c[\x5c'\"]*(?:a[\x5c'\"]*t|m[\x5c'\"]*p)|m[\x5c'\"]*(?:o[\x5c'\"]*r[\x5c'\"]*e|a)|d[\x5c'\"]*i[\x5c'\"]*f[\x5c'\"]*f|l[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*s)|e[\x5c'\"]*s[\x5c'\"]*s[\x5c'\"]*(?:(?:f[\x5c'\"]*i[\x5c'\"]*l|p[\x5c'\"]*i[\x5c'\"]*p)[\x5c'\"]*e|e[\x5c'\"]*c[\x5c'\"]*h[\x5c'\"]*o|(?:\s|<|>).*)|a[\x5c'\"]*s[\x5c'\"]*t[\x5c'\"]*(?:l[\x5c'\"]*o[\x5c'\"]*g(?:[\x5c'\"]*i[\x5c'\"]*n)?|c[\x5c'\"]*o[\x5c'\"]*m[\x5c'\"]*m|(?:\s|<|>).*)|o[\x5c'\"]*(?:c[\x5c'\"]*a[\x5c'\"]*(?:t[\x5c'\"]*e|l)[\x5c'\"]*(?:\s|<|>).*|g[\x5c'\"]*n[\x5c'\"]*a[\x5c'\"]*m[\x5c'\"]*e)|d[\x5c'\"]*(?:c[\x5c'\"]*o[\x5c'\"]*n[\x5c'\"]*f[\x5c'\"]*i[\x5c'\"]*g|d[\x5c'\"]*(?:\s|<|>).*)|(?:[np]|i[\x5c'\"]*n[\x5c'\"]*k[\x5c'\"]*s|y[\x5c'\"]*n[\x5c'\"]*x)[\x5c'\"]*(?:\s|<|>).*|u[\x5c'\"]*a[\x5c'\"]*(?:5[\x5c'\"]*\.[\x5c'\"]*[1234]|(?:\s|<|>).*)|f[\x5c'\"]*t[\x5c'\"]*p(?:[\x5c'\"]*g[\x5c'\"]*e[\x5c'\"]*t)?)|b[\x5c'\"]*(?:z[\x5c'\"]*(?:(?:[ef][\x5c'\"]*)?g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p|d[\x5c'\"]*i[\x5c'\"]*f[\x5c'\"]*f|l[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*s|m[\x5c'\"]*o[\x5c'\"]*r[\x5c'\"]*e|c[\x5c'\"]*a[\x5c'\"]*t|i[\x5c'\"]*p[\x5c'\"]*2)|s[\x5c'\"]*d[\x5c'\"]*(?:c[\x5c'\"]*a[\x5c'\"]*t|i[\x5c'\"]*f[\x5c'\"]*f|t[\x5c'\"]*a[\x5c'\"]*r)|a[\x5c'\"]*(?:t[\x5c'\"]*c[\x5c'\"]*h[\x5c'\"]*(?:\s|<|>).*|s[\x5c'\"]*h)|r[\x5c'\"]*e[\x5c'\"]*a[\x5c'\"]*k[\x5c'\"]*s[\x5c'\"]*w|u[\x5c'\"]*i[\x5c'\"]*l[\x5c'\"]*t[\x5c'\"]*i[\x5c'\"]*n)|f[\x5c'\"]*(?:i(?:[\x5c'\"]*(?:l[\x5c'\"]*e[\x5c'\"]*(?:t[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*t|(?:\s|<|>).*)|n[\x5c'\"]*d[\x5c'\"]*(?:\s|<|>).*|s[\x5c'\"]*h))?|t[\x5c'\"]*p[\x5c'\"]*(?:s[\x5c'\"]*t[\x5c'\"]*a[\x5c'\"]*t[\x5c'\"]*s|w[\x5c'\"]*h[\x5c'\"]*o|(?:\s|<|>).*)|u[\x5c'\"]*n[\x5c'\"]*c[\x5c'\"]*t[\x5c'\"]*i[\x5c'\"]*o[\x5c'\"]*n|(?:e[\x5c'\"]*t[\x5c'\"]*c[\x5c'\"]*h|c)[\x5c'\"]*(?:\s|<|>).*|o[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*a[\x5c'\"]*c[\x5c'\"]*h|g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p)|c[\x5c'\"]*(?:o[\x5c'\"]*(?:m[\x5c'\"]*(?:p[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*s|m[\x5c'\"]*a[\x5c'\"]*n[\x5c'\"]*d)[\x5c'\"]*(?:\s|<|>).*|p[\x5c'\"]*r[\x5c'\"]*o[\x5c'\"]*c)|h[\x5c'\"]*(?:d[\x5c'\"]*i[\x5c'\"]*r[\x5c'\"]*(?:\s|<|>).*|f[\x5c'\"]*l[\x5c'\"]*a[\x5c'\"]*g[\x5c'\"]*s|a[\x5c'\"]*t[\x5c'\"]*t[\x5c'\"]*r|m[\x5c'\"]*o[\x5c'\"]*d)|r[\x5c'\"]*o[\x5c'\"]*n[\x5c'\"]*t[\x5c'\"]*a[\x5c'\"]*b|(?:[cp]|a[\x5c'\"]*t)[\x5c'\"]*(?:\s|<|>).*|u[\x5c'\"]*r[\x5c'\"]*l|s[\x5c'\"]*h)|e[\x5c'\"]*(?:n[\x5c'\"]*(?:v(?:[\x5c'\"]*-[\x5c'\"]*u[\x5c'\"]*p[\x5c'\"]*d[\x5c'\"]*a[\x5c'\"]*t[\x5c'\"]*e)?|d[\x5c'\"]*(?:i[\x5c'\"]*f|s[\x5c'\"]*w))|x[\x5c'\"]*(?:p[\x5c'\"]*(?:a[\x5c'\"]*n[\x5c'\"]*d|o[\x5c'\"]*r[\x5c'\"]*t|r)|e[\x5c'\"]*c[\x5c'\"]*(?:\s|<|>).*)|c[\x5c'\"]*h[\x5c'\"]*o[\x5c'\"]*(?:\s|<|>).*|g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p|s[\x5c'\"]*a[\x5c'\"]*c|v[\x5c'\"]*a[\x5c'\"]*l)|h[\x5c'\"]*(?:t[\x5c'\"]*(?:d[\x5c'\"]*i[\x5c'\"]*g[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*t|p[\x5c'\"]*a[\x5c'\"]*s[\x5c'\"]*s[\x5c'\"]*w[\x5c'\"]*d)|o[\x5c'\"]*s[\x5c'\"]*t[\x5c'\"]*(?:n[\x5c'\"]*a[\x5c'\"]*m[\x5c'\"]*e|i[\x5c'\"]*d)|(?:e[\x5c'\"]*a[\x5c'\"]*d|u[\x5c'\"]*p)[\x5c'\"]*(?:\s|<|>).*|i[\x5c'\"]*s[\x5c'\"]*t[\x5c'\"]*o[\x5c'\"]*r[\x5c'\"]*y)|i[\x5c'\"]*(?:p[\x5c'\"]*(?:(?:6[\x5c'\"]*)?t[\x5c'\"]*a[\x5c'\"]*b[\x5c'\"]*l[\x5c'\"]*e[\x5c'\"]*s|c[\x5c'\"]*o[\x5c'\"]*n[\x5c'\"]*f[\x5c'\"]*i[\x5c'\"]*g)|r[\x5c'\"]*b(?:[\x5c'\"]*(?:2[\x5c'\"]*[01234567]|1(?:[\x5c'\"]*[89])?|3[\x5c'\"]*0))?|f[\x5c'\"]*c[\x5c'\"]*o[\x5c'\"]*n[\x5c'\"]*f[\x5c'\"]*i[\x5c'\"]*g|d[\x5c'\"]*(?:\s|<|>).*)|a[\x5c'\"]*(?:l[\x5c'\"]*(?:i[\x5c'\"]*a[\x5c'\"]*s[\x5c'\"]*(?:\s|<|>).*|p[\x5c'\"]*i[\x5c'\"]*n[\x5c'\"]*e)|d[\x5c'\"]*d[\x5c'\"]*u[\x5c'\"]*s[\x5c'\"]*e[\x5c'\"]*r|p[\x5c'\"]*t[\x5c'\"]*-[\x5c'\"]*g[\x5c'\"]*e[\x5c'\"]*t|r[\x5c'\"]*(?:c[\x5c'\"]*h[\x5c'\"]*(?:\s|<|>).*|p)|w[\x5c'\"]*[ks][\x5c'\"]*(?:\s|<|>).*)|g[\x5c'\"]*(?:(?:e[\x5c'\"]*t[\x5c'\"]*f[\x5c'\"]*a[\x5c'\"]*c[\x5c'\"]*l|r[\x5c'\"]*e[\x5c'\"]*p|c[\x5c'\"]*c|i[\x5c'\"]*t|o)[\x5c'\"]*(?:\s|<|>).*|z[\x5c'\"]*(?:c[\x5c'\"]*a[\x5c'\"]*t|i[\x5c'\"]*p)|u[\x5c'\"]*n[\x5c'\"]*z[\x5c'\"]*i[\x5c'\"]*p|d[\x5c'\"]*b)|d[\x5c'\"]*(?:h[\x5c'\"]*c[\x5c'\"]*l[\x5c'\"]*i[\x5c'\"]*e[\x5c'\"]*n[\x5c'\"]*t|(?:i[\x5c'\"]*f[\x5c'\"]*f|u)[\x5c'\"]*(?:\s|<|>).*|(?:m[\x5c'\"]*e[\x5c'\"]*s|p[\x5c'\"]*k)[\x5c'\"]*g|o[\x5c'\"]*(?:a[\x5c'\"]*s|n[\x5c'\"]*e)|a[\x5c'\"]*s[\x5c'\"]*h)|m[\x5c'\"]*(?:(?:k[\x5c'\"]*d[\x5c'\"]*i[\x5c'\"]*r|o[\x5c'\"]*r[\x5c'\"]*e)[\x5c'\"]*(?:\s|<|>).*|a[\x5c'\"]*i[\x5c'\"]*l[\x5c'\"]*(?:x[\x5c'\"]*(?:\s|<|>).*|q)|l[\x5c'\"]*o[\x5c'\"]*c[\x5c'\"]*a[\x5c'\"]*t[\x5c'\"]*e)|j[\x5c'\"]*(?:(?:a[\x5c'\"]*v[\x5c'\"]*a|o[\x5c'\"]*b[\x5c'\"]*s)[\x5c'\"]*(?:\s|<|>).*|e[\x5c'\"]*x[\x5c'\"]*e[\x5c'\"]*c)|k[\x5c'\"]*(?:i[\x5c'\"]*l[\x5c'\"]*l[\x5c'\"]*(?:a[\x5c'\"]*l[\x5c'\"]*l|(?:\s|<|>).*)|s[\x5c'\"]*h)|G[\x5c'\"]*E[\x5c'\"]*T[\x5c'\"]*(?:\s|<|>).*|7[\x5c'\"]*z(?:[\x5c'\"]*[ar])?)\b" \ + "id:932100,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Remote Command Execution: Unix Command Injection',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-unix',\ + tag:'attack-rce',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + +# Apache 2.2 requires configuration file lines to be under 8kB. +# Therefore, some remaining commands have been split off to a separate rule. +# For explanation of this rule, see rule 932100. +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 932105 +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?:;|\{|\||\|\||&|&&|\n|\r|\$\(|\$\(\(|`|\${|<\(|>\(|\(\s*\))\s*(?:{|\s*\(\s*|\w+=(?:[^\s]*|\$.*|\$.*|<.*|>.*|\'.*\'|\".*\")\s+|!\s*|\$)*\s*(?:'|\")*(?:[\?\*\[\]\(\)\-\|+\w'\"\./\x5c]+/)?[\x5c'\"]*(?:s[\x5c'\"]*(?:e[\x5c'\"]*(?:t[\x5c'\"]*(?:(?:f[\x5c'\"]*a[\x5c'\"]*c[\x5c'\"]*l[\x5c'\"]*)?(?:\s|<|>).*|e[\x5c'\"]*n[\x5c'\"]*v|s[\x5c'\"]*i[\x5c'\"]*d)|n[\x5c'\"]*d[\x5c'\"]*m[\x5c'\"]*a[\x5c'\"]*i[\x5c'\"]*l|d[\x5c'\"]*(?:\s|<|>).*)|h[\x5c'\"]*(?:\.[\x5c'\"]*d[\x5c'\"]*i[\x5c'\"]*s[\x5c'\"]*t[\x5c'\"]*r[\x5c'\"]*i[\x5c'\"]*b|u[\x5c'\"]*t[\x5c'\"]*d[\x5c'\"]*o[\x5c'\"]*w[\x5c'\"]*n|(?:\s|<|>).*)|o[\x5c'\"]*(?:(?:u[\x5c'\"]*r[\x5c'\"]*c[\x5c'\"]*e|r[\x5c'\"]*t)[\x5c'\"]*(?:\s|<|>).*|c[\x5c'\"]*a[\x5c'\"]*t)|c[\x5c'\"]*(?:h[\x5c'\"]*e[\x5c'\"]*d|p[\x5c'\"]*(?:\s|<|>).*)|t[\x5c'\"]*r[\x5c'\"]*i[\x5c'\"]*n[\x5c'\"]*g[\x5c'\"]*s|(?:l[\x5c'\"]*e[\x5c'\"]*e|f[\x5c'\"]*t)[\x5c'\"]*p|y[\x5c'\"]*s[\x5c'\"]*c[\x5c'\"]*t[\x5c'\"]*l|u[\x5c'\"]*(?:(?:\s|<|>).*|d[\x5c'\"]*o)|d[\x5c'\"]*i[\x5c'\"]*f[\x5c'\"]*f|s[\x5c'\"]*h|v[\x5c'\"]*n)|p[\x5c'\"]*(?:k[\x5c'\"]*(?:g(?:(?:[\x5c'\"]*_)?[\x5c'\"]*i[\x5c'\"]*n[\x5c'\"]*f[\x5c'\"]*o)?|e[\x5c'\"]*x[\x5c'\"]*e[\x5c'\"]*c|i[\x5c'\"]*l[\x5c'\"]*l)|t[\x5c'\"]*a[\x5c'\"]*r(?:[\x5c'\"]*(?:d[\x5c'\"]*i[\x5c'\"]*f[\x5c'\"]*f|g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p))?|a[\x5c'\"]*(?:t[\x5c'\"]*c[\x5c'\"]*h[\x5c'\"]*(?:\s|<|>).*|s[\x5c'\"]*s[\x5c'\"]*w[\x5c'\"]*d)|r[\x5c'\"]*i[\x5c'\"]*n[\x5c'\"]*t[\x5c'\"]*(?:e[\x5c'\"]*n[\x5c'\"]*v|f[\x5c'\"]*(?:\s|<|>).*)|y[\x5c'\"]*t[\x5c'\"]*h[\x5c'\"]*o[\x5c'\"]*n(?:[\x5c'\"]*(?:3(?:[\x5c'\"]*m)?|2))?|e[\x5c'\"]*r[\x5c'\"]*(?:l(?:[\x5c'\"]*(?:s[\x5c'\"]*h|5))?|m[\x5c'\"]*s)|(?:u[\x5c'\"]*s[\x5c'\"]*h|o[\x5c'\"]*p)[\x5c'\"]*d|f[\x5c'\"]*(?:(?:\s|<|>).*|t[\x5c'\"]*p)|g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p|h[\x5c'\"]*p(?:[\x5c'\"]*[57])?|i[\x5c'\"]*n[\x5c'\"]*g|s[\x5c'\"]*(?:\s|<|>).*)|n[\x5c'\"]*(?:c[\x5c'\"]*(?:\.[\x5c'\"]*(?:t[\x5c'\"]*r[\x5c'\"]*a[\x5c'\"]*d[\x5c'\"]*i[\x5c'\"]*t[\x5c'\"]*i[\x5c'\"]*o[\x5c'\"]*n[\x5c'\"]*a[\x5c'\"]*l|o[\x5c'\"]*p[\x5c'\"]*e[\x5c'\"]*n[\x5c'\"]*b[\x5c'\"]*s[\x5c'\"]*d)|(?:\s|<|>).*|a[\x5c'\"]*t)|e[\x5c'\"]*t[\x5c'\"]*(?:k[\x5c'\"]*i[\x5c'\"]*t[\x5c'\"]*-[\x5c'\"]*f[\x5c'\"]*t[\x5c'\"]*p|(?:s[\x5c'\"]*t|c)[\x5c'\"]*a[\x5c'\"]*t|(?:\s|<|>).*)|s[\x5c'\"]*(?:l[\x5c'\"]*o[\x5c'\"]*o[\x5c'\"]*k[\x5c'\"]*u[\x5c'\"]*p|t[\x5c'\"]*a[\x5c'\"]*t)|(?:a[\x5c'\"]*n[\x5c'\"]*o|i[\x5c'\"]*c[\x5c'\"]*e)[\x5c'\"]*(?:\s|<|>).*|(?:o[\x5c'\"]*h[\x5c'\"]*u|m[\x5c'\"]*a)[\x5c'\"]*p|p[\x5c'\"]*i[\x5c'\"]*n[\x5c'\"]*g)|r[\x5c'\"]*(?:e[\x5c'\"]*(?:(?:p[\x5c'\"]*(?:l[\x5c'\"]*a[\x5c'\"]*c[\x5c'\"]*e|e[\x5c'\"]*a[\x5c'\"]*t)|n[\x5c'\"]*a[\x5c'\"]*m[\x5c'\"]*e)[\x5c'\"]*(?:\s|<|>).*|a[\x5c'\"]*l[\x5c'\"]*p[\x5c'\"]*a[\x5c'\"]*t[\x5c'\"]*h)|m[\x5c'\"]*(?:(?:d[\x5c'\"]*i[\x5c'\"]*r[\x5c'\"]*)?(?:\s|<|>).*|u[\x5c'\"]*s[\x5c'\"]*e[\x5c'\"]*r)|u[\x5c'\"]*b[\x5c'\"]*y(?:[\x5c'\"]*(?:1(?:[\x5c'\"]*[89])?|2[\x5c'\"]*[012]))?|(?:c[\x5c'\"]*p|p[\x5c'\"]*m)[\x5c'\"]*(?:\s|<|>).*|a[\x5c'\"]*(?:r[\x5c'\"]*(?:\s|<|>).*|k[\x5c'\"]*u)|n[\x5c'\"]*a[\x5c'\"]*n[\x5c'\"]*o|o[\x5c'\"]*u[\x5c'\"]*t[\x5c'\"]*e|s[\x5c'\"]*y[\x5c'\"]*n[\x5c'\"]*c)|t[\x5c'\"]*(?:c[\x5c'\"]*(?:p[\x5c'\"]*(?:t[\x5c'\"]*r[\x5c'\"]*a[\x5c'\"]*c[\x5c'\"]*e[\x5c'\"]*r[\x5c'\"]*o[\x5c'\"]*u[\x5c'\"]*t[\x5c'\"]*e|i[\x5c'\"]*n[\x5c'\"]*g)|s[\x5c'\"]*h)|r[\x5c'\"]*a[\x5c'\"]*c[\x5c'\"]*e[\x5c'\"]*r[\x5c'\"]*o[\x5c'\"]*u[\x5c'\"]*t[\x5c'\"]*e(?:[\x5c'\"]*6)?|e[\x5c'\"]*(?:l[\x5c'\"]*n[\x5c'\"]*e[\x5c'\"]*t|e[\x5c'\"]*(?:\s|<|>).*)|i[\x5c'\"]*m[\x5c'\"]*e[\x5c'\"]*(?:o[\x5c'\"]*u[\x5c'\"]*t|(?:\s|<|>).*)|a[\x5c'\"]*(?:i[\x5c'\"]*l(?:[\x5c'\"]*f)?|r[\x5c'\"]*(?:\s|<|>).*)|o[\x5c'\"]*(?:u[\x5c'\"]*c[\x5c'\"]*h[\x5c'\"]*(?:\s|<|>).*|p))|u[\x5c'\"]*(?:n[\x5c'\"]*(?:l[\x5c'\"]*(?:i[\x5c'\"]*n[\x5c'\"]*k[\x5c'\"]*(?:\s|<|>).*|z[\x5c'\"]*m[\x5c'\"]*a)|c[\x5c'\"]*o[\x5c'\"]*m[\x5c'\"]*p[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*s|a[\x5c'\"]*m[\x5c'\"]*e|r[\x5c'\"]*a[\x5c'\"]*r|s[\x5c'\"]*e[\x5c'\"]*t|z[\x5c'\"]*i[\x5c'\"]*p|x[\x5c'\"]*z)|s[\x5c'\"]*e[\x5c'\"]*r[\x5c'\"]*(?:(?:a[\x5c'\"]*d|m[\x5c'\"]*o)[\x5c'\"]*d|d[\x5c'\"]*e[\x5c'\"]*l)|l[\x5c'\"]*i[\x5c'\"]*m[\x5c'\"]*i[\x5c'\"]*t[\x5c'\"]*(?:\s|<|>).*)|m[\x5c'\"]*(?:y[\x5c'\"]*s[\x5c'\"]*q[\x5c'\"]*l(?:[\x5c'\"]*(?:d[\x5c'\"]*u[\x5c'\"]*m[\x5c'\"]*p(?:[\x5c'\"]*s[\x5c'\"]*l[\x5c'\"]*o[\x5c'\"]*w)?|h[\x5c'\"]*o[\x5c'\"]*t[\x5c'\"]*c[\x5c'\"]*o[\x5c'\"]*p[\x5c'\"]*y|a[\x5c'\"]*d[\x5c'\"]*m[\x5c'\"]*i[\x5c'\"]*n|s[\x5c'\"]*h[\x5c'\"]*o[\x5c'\"]*w))?|(?:(?:o[\x5c'\"]*u[\x5c'\"]*n|u[\x5c'\"]*t)[\x5c'\"]*t|v)[\x5c'\"]*(?:\s|<|>).*)|x[\x5c'\"]*(?:z[\x5c'\"]*(?:(?:[ef][\x5c'\"]*)?g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p|d[\x5c'\"]*(?:i[\x5c'\"]*f[\x5c'\"]*f|e[\x5c'\"]*c)|c[\x5c'\"]*(?:a[\x5c'\"]*t|m[\x5c'\"]*p)|l[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*s|m[\x5c'\"]*o[\x5c'\"]*r[\x5c'\"]*e|(?:\s|<|>).*)|a[\x5c'\"]*r[\x5c'\"]*g[\x5c'\"]*s|t[\x5c'\"]*e[\x5c'\"]*r[\x5c'\"]*m|x[\x5c'\"]*d[\x5c'\"]*(?:\s|<|>).*)|z[\x5c'\"]*(?:(?:[ef][\x5c'\"]*)?g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p|c[\x5c'\"]*(?:a[\x5c'\"]*t|m[\x5c'\"]*p)|d[\x5c'\"]*i[\x5c'\"]*f[\x5c'\"]*f|i[\x5c'\"]*p[\x5c'\"]*(?:\s|<|>).*|l[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*s|m[\x5c'\"]*o[\x5c'\"]*r[\x5c'\"]*e|r[\x5c'\"]*u[\x5c'\"]*n|s[\x5c'\"]*h)|o[\x5c'\"]*(?:p[\x5c'\"]*e[\x5c'\"]*n[\x5c'\"]*s[\x5c'\"]*s[\x5c'\"]*l|n[\x5c'\"]*i[\x5c'\"]*n[\x5c'\"]*t[\x5c'\"]*r)|w[\x5c'\"]*(?:h[\x5c'\"]*o[\x5c'\"]*(?:a[\x5c'\"]*m[\x5c'\"]*i|(?:\s|<|>).*)|g[\x5c'\"]*e[\x5c'\"]*t|3[\x5c'\"]*m)|v[\x5c'\"]*i[\x5c'\"]*(?:m[\x5c'\"]*(?:\s|<|>).*|g[\x5c'\"]*r|p[\x5c'\"]*w)|y[\x5c'\"]*u[\x5c'\"]*m)\b" \ + "id:932105,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Remote Command Execution: Unix Command Injection',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-unix',\ + tag:'attack-rce',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# [ Windows command injection ] +# +# This rule detects Windows shell command injections. +# If you are not running Windows, it is safe to disable this rule. +# +# A command injection takes a form such as: +# +# foo.jpg&ver /r +# foo.jpg|ver /r +# +# The vulnerability exists when an application executes a shell command +# without proper input escaping/validation. +# +# To prevent false positives, we look for a 'starting sequence' that +# precedes a command in CMD syntax, such as: ; | & ` +# +# Anatomy of the regexp: +# +# 1. Starting tokens +# +# ; ;cmd +# \{ {cmd +# \| |cmd +# \|\| ||cmd +# & &cmd +# && &&cmd +# \n \ncmd +# \r \rcmd +# ` `cmd +# +# 2. Command prefixes +# +# ( (cmd) +# , ,cmd +# @ @cmd +# ' 'cmd' +# " "cmd" +# \s spacing+cmd +# +# 3. Paths +# +# [\w'\"\./]+/ /path/cmd +# [\x5c'\"\^]*\w[\x5c'\"\^]*:.*\x5c C:\Program Files\cmd +# [\^\.\w '\"/\x5c]*\x5c)?[\"\^]* \\net\share\dir\cmd +# +# 4. Quoting +# +# \" "cmd" +# \^ ^cmd +# +# 5. Extension/switches +# +# \.[\"\^]*\w+ cmd.com, cmd.exe, etc. +# /b cmd/h +# +# An effort is made to combat evasions by CMD syntax; for example, +# the following strings are valid: c^md, @cmd, "c"md. ModSecurity +# has a t:cmdLine transformation built-in to deal with some of these, +# but unfortunately, that transformation replaces ';' characters (so +# we cannot match on the start of a command) and '\' characters (so we +# have trouble matching paths). This makes the regexp more complex. +# +# This rule is case-insensitive. +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 932110 +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?i)(?:;|\{|\||\|\||&|&&|\n|\r|`)\s*[\(,@\'\"\s]*(?:[\w'\"\./]+/|[\x5c'\"\^]*\w[\x5c'\"\^]*:.*\x5c|[\^\.\w '\"/\x5c]*\x5c)?[\"\^]*(?:m[\"\^]*(?:y[\"\^]*s[\"\^]*q[\"\^]*l(?:[\"\^]*(?:d[\"\^]*u[\"\^]*m[\"\^]*p(?:[\"\^]*s[\"\^]*l[\"\^]*o[\"\^]*w)?|h[\"\^]*o[\"\^]*t[\"\^]*c[\"\^]*o[\"\^]*p[\"\^]*y|a[\"\^]*d[\"\^]*m[\"\^]*i[\"\^]*n|s[\"\^]*h[\"\^]*o[\"\^]*w))?|s[\"\^]*(?:i[\"\^]*(?:n[\"\^]*f[\"\^]*o[\"\^]*3[\"\^]*2|e[\"\^]*x[\"\^]*e[\"\^]*c)|c[\"\^]*o[\"\^]*n[\"\^]*f[\"\^]*i[\"\^]*g|g[\"\^]*(?:[\s,;]|\.|/|<|>).*|t[\"\^]*s[\"\^]*c)|o[\"\^]*(?:u[\"\^]*n[\"\^]*t[\"\^]*(?:(?:[\s,;]|\.|/|<|>).*|v[\"\^]*o[\"\^]*l)|v[\"\^]*e[\"\^]*u[\"\^]*s[\"\^]*e[\"\^]*r|[dr][\"\^]*e[\"\^]*(?:[\s,;]|\.|/|<|>).*)|k[\"\^]*(?:d[\"\^]*i[\"\^]*r[\"\^]*(?:[\s,;]|\.|/|<|>).*|l[\"\^]*i[\"\^]*n[\"\^]*k)|d[\"\^]*(?:s[\"\^]*c[\"\^]*h[\"\^]*e[\"\^]*d|(?:[\s,;]|\.|/|<|>).*)|a[\"\^]*p[\"\^]*i[\"\^]*s[\"\^]*e[\"\^]*n[\"\^]*d|b[\"\^]*s[\"\^]*a[\"\^]*c[\"\^]*l[\"\^]*i|e[\"\^]*a[\"\^]*s[\"\^]*u[\"\^]*r[\"\^]*e|m[\"\^]*s[\"\^]*y[\"\^]*s)|d[\"\^]*(?:i[\"\^]*(?:s[\"\^]*k[\"\^]*(?:(?:m[\"\^]*g[\"\^]*m|p[\"\^]*a[\"\^]*r)[\"\^]*t|s[\"\^]*h[\"\^]*a[\"\^]*d[\"\^]*o[\"\^]*w)|r[\"\^]*(?:(?:[\s,;]|\.|/|<|>).*|u[\"\^]*s[\"\^]*e)|f[\"\^]*f[\"\^]*(?:[\s,;]|\.|/|<|>).*)|e[\"\^]*(?:l[\"\^]*(?:p[\"\^]*r[\"\^]*o[\"\^]*f|t[\"\^]*r[\"\^]*e[\"\^]*e|(?:[\s,;]|\.|/|<|>).*)|v[\"\^]*(?:m[\"\^]*g[\"\^]*m[\"\^]*t|c[\"\^]*o[\"\^]*n)|(?:f[\"\^]*r[\"\^]*a|b[\"\^]*u)[\"\^]*g)|s[\"\^]*(?:a[\"\^]*(?:c[\"\^]*l[\"\^]*s|d[\"\^]*d)|q[\"\^]*u[\"\^]*e[\"\^]*r[\"\^]*y|m[\"\^]*o[\"\^]*(?:v[\"\^]*e|d)|g[\"\^]*e[\"\^]*t|r[\"\^]*m)|(?:r[\"\^]*i[\"\^]*v[\"\^]*e[\"\^]*r[\"\^]*q[\"\^]*u[\"\^]*e[\"\^]*r|o[\"\^]*s[\"\^]*k[\"\^]*e)[\"\^]*y|(?:c[\"\^]*o[\"\^]*m[\"\^]*c[\"\^]*n[\"\^]*f|x[\"\^]*d[\"\^]*i[\"\^]*a)[\"\^]*g|a[\"\^]*t[\"\^]*e[\"\^]*(?:[\s,;]|\.|/|<|>).*|n[\"\^]*s[\"\^]*s[\"\^]*t[\"\^]*a[\"\^]*t)|c[\"\^]*(?:o[\"\^]*(?:m[\"\^]*(?:p[\"\^]*(?:(?:a[\"\^]*c[\"\^]*t[\"\^]*)?(?:[\s,;]|\.|/|<|>).*|m[\"\^]*g[\"\^]*m[\"\^]*t)|e[\"\^]*x[\"\^]*p)|n[\"\^]*(?:2[\"\^]*p|v[\"\^]*e)[\"\^]*r[\"\^]*t|p[\"\^]*y)|l[\"\^]*(?:e[\"\^]*a[\"\^]*(?:n[\"\^]*m[\"\^]*g[\"\^]*r|r[\"\^]*m[\"\^]*e[\"\^]*m)|u[\"\^]*s[\"\^]*t[\"\^]*e[\"\^]*r)|h[\"\^]*(?:k[\"\^]*(?:n[\"\^]*t[\"\^]*f[\"\^]*s|d[\"\^]*s[\"\^]*k)|d[\"\^]*i[\"\^]*r[\"\^]*(?:[\s,;]|\.|/|<|>).*)|s[\"\^]*(?:c[\"\^]*(?:r[\"\^]*i[\"\^]*p[\"\^]*t|c[\"\^]*m[\"\^]*d)|v[\"\^]*d[\"\^]*e)|e[\"\^]*r[\"\^]*t[\"\^]*(?:u[\"\^]*t[\"\^]*i[\"\^]*l|r[\"\^]*e[\"\^]*q)|a[\"\^]*(?:l[\"\^]*l[\"\^]*(?:[\s,;]|\.|/|<|>).*|c[\"\^]*l[\"\^]*s)|m[\"\^]*d(?:[\"\^]*k[\"\^]*e[\"\^]*y)?|i[\"\^]*p[\"\^]*h[\"\^]*e[\"\^]*r|u[\"\^]*r[\"\^]*l)|f[\"\^]*(?:o[\"\^]*r[\"\^]*(?:m[\"\^]*a[\"\^]*t[\"\^]*(?:[\s,;]|\.|/|<|>).*|f[\"\^]*i[\"\^]*l[\"\^]*e[\"\^]*s|e[\"\^]*a[\"\^]*c[\"\^]*h)|i[\"\^]*n[\"\^]*d[\"\^]*(?:(?:[\s,;]|\.|/|<|>).*|s[\"\^]*t[\"\^]*r)|s[\"\^]*(?:m[\"\^]*g[\"\^]*m[\"\^]*t|u[\"\^]*t[\"\^]*i[\"\^]*l)|t[\"\^]*(?:p[\"\^]*(?:[\s,;]|\.|/|<|>).*|y[\"\^]*p[\"\^]*e)|r[\"\^]*e[\"\^]*e[\"\^]*d[\"\^]*i[\"\^]*s[\"\^]*k|c[\"\^]*(?:[\s,;]|\.|/|<|>).*|g[\"\^]*r[\"\^]*e[\"\^]*p)|n[\"\^]*(?:e[\"\^]*t[\"\^]*(?:s[\"\^]*(?:t[\"\^]*a[\"\^]*t|v[\"\^]*c|h)|(?:[\s,;]|\.|/|<|>).*|c[\"\^]*a[\"\^]*t|d[\"\^]*o[\"\^]*m)|t[\"\^]*(?:b[\"\^]*a[\"\^]*c[\"\^]*k[\"\^]*u[\"\^]*p|r[\"\^]*i[\"\^]*g[\"\^]*h[\"\^]*t[\"\^]*s)|(?:s[\"\^]*l[\"\^]*o[\"\^]*o[\"\^]*k[\"\^]*u|m[\"\^]*a)[\"\^]*p|c[\"\^]*(?:(?:[\s,;]|\.|/|<|>).*|a[\"\^]*t)|b[\"\^]*t[\"\^]*s[\"\^]*t[\"\^]*a[\"\^]*t)|e[\"\^]*(?:x[\"\^]*p[\"\^]*(?:a[\"\^]*n[\"\^]*d[\"\^]*(?:[\s,;]|\.|/|<|>).*|l[\"\^]*o[\"\^]*r[\"\^]*e[\"\^]*r)|v[\"\^]*e[\"\^]*n[\"\^]*t[\"\^]*(?:c[\"\^]*r[\"\^]*e[\"\^]*a[\"\^]*t[\"\^]*e|v[\"\^]*w[\"\^]*r)|n[\"\^]*d[\"\^]*l[\"\^]*o[\"\^]*c[\"\^]*a[\"\^]*l|g[\"\^]*r[\"\^]*e[\"\^]*p|r[\"\^]*a[\"\^]*s[\"\^]*e|c[\"\^]*h[\"\^]*o)|g[\"\^]*(?:a[\"\^]*t[\"\^]*h[\"\^]*e[\"\^]*r[\"\^]*n[\"\^]*e[\"\^]*t[\"\^]*w[\"\^]*o[\"\^]*r[\"\^]*k[\"\^]*i[\"\^]*n[\"\^]*f[\"\^]*o|p[\"\^]*(?:(?:r[\"\^]*e[\"\^]*s[\"\^]*u[\"\^]*l|e[\"\^]*d[\"\^]*i)[\"\^]*t|u[\"\^]*p[\"\^]*d[\"\^]*a[\"\^]*t[\"\^]*e)|i[\"\^]*t[\"\^]*(?:[\s,;]|\.|/|<|>).*|e[\"\^]*t[\"\^]*m[\"\^]*a[\"\^]*c)|i[\"\^]*(?:r[\"\^]*b(?:[\"\^]*(?:1(?:[\"\^]*[89])?|2[\"\^]*[012]))?|f[\"\^]*m[\"\^]*e[\"\^]*m[\"\^]*b[\"\^]*e[\"\^]*r|p[\"\^]*c[\"\^]*o[\"\^]*n[\"\^]*f[\"\^]*i[\"\^]*g|n[\"\^]*e[\"\^]*t[\"\^]*c[\"\^]*p[\"\^]*l|c[\"\^]*a[\"\^]*c[\"\^]*l[\"\^]*s)|a[\"\^]*(?:d[\"\^]*(?:d[\"\^]*u[\"\^]*s[\"\^]*e[\"\^]*r[\"\^]*s|m[\"\^]*o[\"\^]*d[\"\^]*c[\"\^]*m[\"\^]*d)|r[\"\^]*p[\"\^]*(?:[\s,;]|\.|/|<|>).*|t[\"\^]*t[\"\^]*r[\"\^]*i[\"\^]*b|s[\"\^]*s[\"\^]*o[\"\^]*c|z[\"\^]*m[\"\^]*a[\"\^]*n)|l[\"\^]*(?:o[\"\^]*g[\"\^]*(?:e[\"\^]*v[\"\^]*e[\"\^]*n[\"\^]*t|t[\"\^]*i[\"\^]*m[\"\^]*e|m[\"\^]*a[\"\^]*n|o[\"\^]*f[\"\^]*f)|a[\"\^]*b[\"\^]*e[\"\^]*l[\"\^]*(?:[\s,;]|\.|/|<|>).*|u[\"\^]*s[\"\^]*r[\"\^]*m[\"\^]*g[\"\^]*r)|b[\"\^]*(?:(?:c[\"\^]*d[\"\^]*(?:b[\"\^]*o[\"\^]*o|e[\"\^]*d[\"\^]*i)|r[\"\^]*o[\"\^]*w[\"\^]*s[\"\^]*t[\"\^]*a)[\"\^]*t|i[\"\^]*t[\"\^]*s[\"\^]*a[\"\^]*d[\"\^]*m[\"\^]*i[\"\^]*n|o[\"\^]*o[\"\^]*t[\"\^]*c[\"\^]*f[\"\^]*g)|h[\"\^]*(?:o[\"\^]*s[\"\^]*t[\"\^]*n[\"\^]*a[\"\^]*m[\"\^]*e|d[\"\^]*w[\"\^]*w[\"\^]*i[\"\^]*z)|j[\"\^]*a[\"\^]*v[\"\^]*a[\"\^]*(?:[\s,;]|\.|/|<|>).*|7[\"\^]*z(?:[\"\^]*[ar])?)(?:\.[\"\^]*\w+)?\b" \ + "id:932110,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Remote Command Execution: Windows Command Injection',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-windows',\ + tag:'attack-rce',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + +# Apache 2.2 requires configuration file lines to be under 8kB. +# Therefore, some remaining commands have been split off to a separate rule. +# For explanation of this rule, see rule 932110. +# +# This rule is also triggered by an Oracle WebLogic Remote Command Execution exploit: +# [ Oracle WebLogic vulnerability CVE-2017-10271 - Exploit tested: https://www.exploit-db.com/exploits/43458 ] +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 932115 +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?i)(?:;|\{|\||\|\||&|&&|\n|\r|`)\s*[\(,@\'\"\s]*(?:[\w'\"\./]+/|[\x5c'\"\^]*\w[\x5c'\"\^]*:.*\x5c|[\^\.\w '\"/\x5c]*\x5c)?[\"\^]*(?:s[\"\^]*(?:y[\"\^]*s[\"\^]*(?:t[\"\^]*e[\"\^]*m[\"\^]*(?:p[\"\^]*r[\"\^]*o[\"\^]*p[\"\^]*e[\"\^]*r[\"\^]*t[\"\^]*i[\"\^]*e[\"\^]*s[\"\^]*(?:d[\"\^]*a[\"\^]*t[\"\^]*a[\"\^]*e[\"\^]*x[\"\^]*e[\"\^]*c[\"\^]*u[\"\^]*t[\"\^]*i[\"\^]*o[\"\^]*n[\"\^]*p[\"\^]*r[\"\^]*e[\"\^]*v[\"\^]*e[\"\^]*n[\"\^]*t[\"\^]*i[\"\^]*o[\"\^]*n|(?:p[\"\^]*e[\"\^]*r[\"\^]*f[\"\^]*o[\"\^]*r[\"\^]*m[\"\^]*a[\"\^]*n[\"\^]*c|h[\"\^]*a[\"\^]*r[\"\^]*d[\"\^]*w[\"\^]*a[\"\^]*r)[\"\^]*e|a[\"\^]*d[\"\^]*v[\"\^]*a[\"\^]*n[\"\^]*c[\"\^]*e[\"\^]*d)|i[\"\^]*n[\"\^]*f[\"\^]*o)|k[\"\^]*e[\"\^]*y|d[\"\^]*m)|h[\"\^]*(?:o[\"\^]*(?:w[\"\^]*(?:g[\"\^]*r[\"\^]*p|m[\"\^]*b[\"\^]*r)[\"\^]*s|r[\"\^]*t[\"\^]*c[\"\^]*u[\"\^]*t)|e[\"\^]*l[\"\^]*l[\"\^]*r[\"\^]*u[\"\^]*n[\"\^]*a[\"\^]*s|u[\"\^]*t[\"\^]*d[\"\^]*o[\"\^]*w[\"\^]*n|r[\"\^]*p[\"\^]*u[\"\^]*b[\"\^]*w|a[\"\^]*r[\"\^]*e|i[\"\^]*f[\"\^]*t)|e[\"\^]*(?:t[\"\^]*(?:(?:x[\"\^]*)?(?:[\s,;]|\.|/|<|>).*|l[\"\^]*o[\"\^]*c[\"\^]*a[\"\^]*l)|c[\"\^]*p[\"\^]*o[\"\^]*l|l[\"\^]*e[\"\^]*c[\"\^]*t)|c[\"\^]*(?:h[\"\^]*t[\"\^]*a[\"\^]*s[\"\^]*k[\"\^]*s|l[\"\^]*i[\"\^]*s[\"\^]*t)|u[\"\^]*b[\"\^]*(?:i[\"\^]*n[\"\^]*a[\"\^]*c[\"\^]*l|s[\"\^]*t)|(?:t[\"\^]*a|o)[\"\^]*r[\"\^]*t[\"\^]*(?:[\s,;]|\.|/|<|>).*|i[\"\^]*g[\"\^]*v[\"\^]*e[\"\^]*r[\"\^]*i[\"\^]*f|l[\"\^]*(?:e[\"\^]*e[\"\^]*p|m[\"\^]*g[\"\^]*r)|f[\"\^]*c|v[\"\^]*n)|p[\"\^]*(?:s[\"\^]*(?:s[\"\^]*(?:h[\"\^]*u[\"\^]*t[\"\^]*d[\"\^]*o[\"\^]*w[\"\^]*n|e[\"\^]*r[\"\^]*v[\"\^]*i[\"\^]*c[\"\^]*e|u[\"\^]*s[\"\^]*p[\"\^]*e[\"\^]*n[\"\^]*d)|l[\"\^]*(?:o[\"\^]*g[\"\^]*(?:g[\"\^]*e[\"\^]*d[\"\^]*o[\"\^]*n|l[\"\^]*i[\"\^]*s[\"\^]*t)|i[\"\^]*s[\"\^]*t)|p[\"\^]*(?:a[\"\^]*s[\"\^]*s[\"\^]*w[\"\^]*d|i[\"\^]*n[\"\^]*g)|g[\"\^]*e[\"\^]*t[\"\^]*s[\"\^]*i[\"\^]*d|e[\"\^]*x[\"\^]*e[\"\^]*c|f[\"\^]*i[\"\^]*l[\"\^]*e|i[\"\^]*n[\"\^]*f[\"\^]*o|k[\"\^]*i[\"\^]*l[\"\^]*l)|o[\"\^]*(?:w[\"\^]*e[\"\^]*r[\"\^]*(?:s[\"\^]*h[\"\^]*e[\"\^]*l[\"\^]*l(?:[\"\^]*_[\"\^]*i[\"\^]*s[\"\^]*e)?|c[\"\^]*f[\"\^]*g)|r[\"\^]*t[\"\^]*q[\"\^]*r[\"\^]*y|p[\"\^]*d)|r[\"\^]*(?:i[\"\^]*n[\"\^]*t[\"\^]*(?:(?:[\s,;]|\.|/|<|>).*|b[\"\^]*r[\"\^]*m)|n[\"\^]*(?:c[\"\^]*n[\"\^]*f[\"\^]*g|m[\"\^]*n[\"\^]*g[\"\^]*r)|o[\"\^]*m[\"\^]*p[\"\^]*t)|a[\"\^]*t[\"\^]*h[\"\^]*(?:p[\"\^]*i[\"\^]*n[\"\^]*g|(?:[\s,;]|\.|/|<|>).*)|e[\"\^]*r[\"\^]*(?:l(?:[\"\^]*(?:s[\"\^]*h|5))?|f[\"\^]*m[\"\^]*o[\"\^]*n)|y[\"\^]*t[\"\^]*h[\"\^]*o[\"\^]*n(?:[\"\^]*(?:3(?:[\"\^]*m)?|2))?|k[\"\^]*g[\"\^]*m[\"\^]*g[\"\^]*r|h[\"\^]*p(?:[\"\^]*[57])?|u[\"\^]*s[\"\^]*h[\"\^]*d|i[\"\^]*n[\"\^]*g)|r[\"\^]*(?:e[\"\^]*(?:(?:p[\"\^]*l[\"\^]*a[\"\^]*c[\"\^]*e|n(?:[\"\^]*a[\"\^]*m[\"\^]*e)?|s[\"\^]*e[\"\^]*t)[\"\^]*(?:[\s,;]|\.|/|<|>).*|g[\"\^]*(?:s[\"\^]*v[\"\^]*r[\"\^]*3[\"\^]*2|e[\"\^]*d[\"\^]*i[\"\^]*t|(?:[\s,;]|\.|/|<|>).*|i[\"\^]*n[\"\^]*i)|c[\"\^]*(?:d[\"\^]*i[\"\^]*s[\"\^]*c|o[\"\^]*v[\"\^]*e[\"\^]*r)|k[\"\^]*e[\"\^]*y[\"\^]*w[\"\^]*i[\"\^]*z)|u[\"\^]*(?:n[\"\^]*(?:d[\"\^]*l[\"\^]*l[\"\^]*3[\"\^]*2|a[\"\^]*s)|b[\"\^]*y[\"\^]*(?:1(?:[\"\^]*[89])?|2[\"\^]*[012]))|a[\"\^]*(?:s[\"\^]*(?:p[\"\^]*h[\"\^]*o[\"\^]*n[\"\^]*e|d[\"\^]*i[\"\^]*a[\"\^]*l)|r[\"\^]*(?:[\s,;]|\.|/|<|>).*)|m[\"\^]*(?:(?:d[\"\^]*i[\"\^]*r[\"\^]*)?(?:[\s,;]|\.|/|<|>).*|t[\"\^]*s[\"\^]*h[\"\^]*a[\"\^]*r[\"\^]*e)|o[\"\^]*(?:u[\"\^]*t[\"\^]*e[\"\^]*(?:[\s,;]|\.|/|<|>).*|b[\"\^]*o[\"\^]*c[\"\^]*o[\"\^]*p[\"\^]*y)|s[\"\^]*(?:t[\"\^]*r[\"\^]*u[\"\^]*i|y[\"\^]*n[\"\^]*c)|d[\"\^]*(?:[\s,;]|\.|/|<|>).*)|t[\"\^]*(?:a[\"\^]*(?:s[\"\^]*k[\"\^]*(?:k[\"\^]*i[\"\^]*l[\"\^]*l|l[\"\^]*i[\"\^]*s[\"\^]*t|s[\"\^]*c[\"\^]*h[\"\^]*d|m[\"\^]*g[\"\^]*r)|k[\"\^]*e[\"\^]*o[\"\^]*w[\"\^]*n)|(?:i[\"\^]*m[\"\^]*e[\"\^]*o[\"\^]*u|p[\"\^]*m[\"\^]*i[\"\^]*n[\"\^]*i|e[\"\^]*l[\"\^]*n[\"\^]*e|l[\"\^]*i[\"\^]*s)[\"\^]*t|s[\"\^]*(?:d[\"\^]*i[\"\^]*s[\"\^]*c[\"\^]*o|s[\"\^]*h[\"\^]*u[\"\^]*t[\"\^]*d)[\"\^]*n|y[\"\^]*p[\"\^]*e[\"\^]*(?:p[\"\^]*e[\"\^]*r[\"\^]*f|(?:[\s,;]|\.|/|<|>).*)|r[\"\^]*(?:a[\"\^]*c[\"\^]*e[\"\^]*r[\"\^]*t|e[\"\^]*e))|w[\"\^]*(?:i[\"\^]*n[\"\^]*(?:d[\"\^]*i[\"\^]*f[\"\^]*f|m[\"\^]*s[\"\^]*d[\"\^]*p|v[\"\^]*a[\"\^]*r|r[\"\^]*[ms])|u[\"\^]*(?:a[\"\^]*(?:u[\"\^]*c[\"\^]*l[\"\^]*t|p[\"\^]*p)|s[\"\^]*a)|s[\"\^]*c[\"\^]*(?:r[\"\^]*i[\"\^]*p[\"\^]*t|u[\"\^]*i)|e[\"\^]*v[\"\^]*t[\"\^]*u[\"\^]*t[\"\^]*i[\"\^]*l|m[\"\^]*i[\"\^]*(?:m[\"\^]*g[\"\^]*m[\"\^]*t|c)|a[\"\^]*i[\"\^]*t[\"\^]*f[\"\^]*o[\"\^]*r|h[\"\^]*o[\"\^]*a[\"\^]*m[\"\^]*i|g[\"\^]*e[\"\^]*t)|u[\"\^]*(?:s[\"\^]*(?:e[\"\^]*r[\"\^]*a[\"\^]*c[\"\^]*c[\"\^]*o[\"\^]*u[\"\^]*n[\"\^]*t[\"\^]*c[\"\^]*o[\"\^]*n[\"\^]*t[\"\^]*r[\"\^]*o[\"\^]*l[\"\^]*s[\"\^]*e[\"\^]*t[\"\^]*t[\"\^]*i[\"\^]*n[\"\^]*g[\"\^]*s|r[\"\^]*s[\"\^]*t[\"\^]*a[\"\^]*t)|n[\"\^]*(?:r[\"\^]*a[\"\^]*r|z[\"\^]*i[\"\^]*p))|q[\"\^]*(?:u[\"\^]*e[\"\^]*r[\"\^]*y[\"\^]*(?:[\s,;]|\.|/|<|>).*|p[\"\^]*r[\"\^]*o[\"\^]*c[\"\^]*e[\"\^]*s[\"\^]*s|w[\"\^]*i[\"\^]*n[\"\^]*s[\"\^]*t[\"\^]*a|g[\"\^]*r[\"\^]*e[\"\^]*p)|o[\"\^]*(?:d[\"\^]*b[\"\^]*c[\"\^]*(?:a[\"\^]*d[\"\^]*3[\"\^]*2|c[\"\^]*o[\"\^]*n[\"\^]*f)|p[\"\^]*e[\"\^]*n[\"\^]*f[\"\^]*i[\"\^]*l[\"\^]*e[\"\^]*s)|v[\"\^]*(?:o[\"\^]*l[\"\^]*(?:[\s,;]|\.|/|<|>).*|e[\"\^]*r[\"\^]*i[\"\^]*f[\"\^]*y)|x[\"\^]*c[\"\^]*(?:a[\"\^]*c[\"\^]*l[\"\^]*s|o[\"\^]*p[\"\^]*y)|z[\"\^]*i[\"\^]*p[\"\^]*(?:[\s,;]|\.|/|<|>).*)(?:\.[\"\^]*\w+)?\b" \ + "id:932115,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Remote Command Execution: Windows Command Injection',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-windows',\ + tag:'attack-rce',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# [ Windows PowerShell, cmdlets and options ] +# +# Detect some common PowerShell commands, cmdlets and options. +# These commands should be relatively uncommon in normal text, but +# potentially useful for code injection. +# +# If you are not running Windows, it is safe to disable this rule. +# +# https://technet.microsoft.com/en-us/magazine/ff714569.aspx +# https://msdn.microsoft.com/en-us/powershell/scripting/core-powershell/console/powershell.exe-command-line-help +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@pmFromFile windows-powershell-commands.data" \ + "id:932120,\ + phase:2,\ + block,\ + capture,\ + t:none,t:cmdLine,\ + msg:'Remote Command Execution: Windows PowerShell Command Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'language-powershell',\ + tag:'platform-windows',\ + tag:'attack-rce',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# [ Unix shell expressions ] +# +# Detects the following patterns which are common in Unix shell scripts +# and one-liners: +# +# $(foo) Command substitution +# ${foo} Parameter expansion +# <(foo) Process substitution +# >(foo) Process substitution +# $((foo)) Arithmetic expansion +# +# Regexp generated from util/regexp-assemble/data/932130.data using Regexp::Assemble. +# See https://coreruleset.org/20190826/optimizing-regular-expressions/ for usage. +# To rebuild the regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 932130 +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?:\$(?:\((?:\(.*\)|.*)\)|\{.*})|[<>]\(.*\))" \ + "id:932130,\ + phase:2,\ + block,\ + capture,\ + t:none,t:cmdLine,\ + msg:'Remote Command Execution: Unix Shell Expression Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-unix',\ + tag:'attack-rce',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# [ Windows FOR, IF commands ] +# +# This rule detects Windows command shell FOR and IF commands. +# If you are not running Windows, it is safe to disable this rule. +# +# Examples: +# +# FOR %a IN (set) DO +# FOR /D %a IN (dirs) DO +# FOR /F "options" %a IN (text|"text") DO +# FOR /L %a IN (start,step,end) DO +# FOR /R C:\dir %A IN (set) DO +# +# IF [/I] [NOT] EXIST filename | DEFINED define | ERRORLEVEL n | CMDEXTVERSION n +# IF [/I] [NOT] item1 [==|EQU|NEQ|LSS|LEQ|GTR|GEQ] item2 +# IF [/I] [NOT] (item1) [==|EQU|NEQ|LSS|LEQ|GTR|GEQ] (item2) +# +# http://ss64.com/nt/if.html +# http://ss64.com/nt/for.html +# +# Regexp generated from util/regexp-assemble/data/932140.data using Regexp::Assemble. +# See https://coreruleset.org/20190826/optimizing-regular-expressions/ for usage. +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx \b(?:if(?:/i)?(?: not)?(?: exist\b| defined\b| errorlevel\b| cmdextversion\b|(?: |\().*(?:\bgeq\b|\bequ\b|\bneq\b|\bleq\b|\bgtr\b|\blss\b|==))|for(?:/[dflr].*)? %+[^ ]+ in\(.*\)\s?do)" \ + "id:932140,\ + phase:2,\ + block,\ + capture,\ + t:none,t:cmdLine,\ + msg:'Remote Command Execution: Windows FOR/IF Command Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-windows',\ + tag:'attack-rce',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# [ Unix direct remote command execution ] +# +# Detects Unix commands at the start of a parameter (direct RCE). +# Example: foo=wget%20www.example.com +# +# This case is different from command injection (rule 932100), where a +# command string is appended (injected) to a regular parameter, and then +# passed to a shell unescaped. +# +# This rule is also triggered by an Oracle WebLogic Remote Command Execution exploit: +# [ Oracle WebLogic vulnerability CVE-2017-10271 - Exploit tested: https://www.exploit-db.com/exploits/43458 ] +# +# Due to a higher risk of false positives, the following changes have been +# made relative to rule 932100: +# 1) the set of commands is smaller +# 2) we require a trailing space (denoting command parameters) or command +# separator character after the command +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 932150 +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?:^|=)\s*(?:{|\s*\(\s*|\w+=(?:[^\s]*|\$.*|\$.*|<.*|>.*|\'.*\'|\".*\")\s+|!\s*|\$)*\s*(?:'|\")*(?:[\?\*\[\]\(\)\-\|+\w'\"\./\x5c]+/)?[\x5c'\"]*(?:l[\x5c'\"]*(?:s(?:[\x5c'\"]*(?:b[\x5c'\"]*_[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*l[\x5c'\"]*e[\x5c'\"]*a[\x5c'\"]*s[\x5c'\"]*e|c[\x5c'\"]*p[\x5c'\"]*u|m[\x5c'\"]*o[\x5c'\"]*d|p[\x5c'\"]*c[\x5c'\"]*i|u[\x5c'\"]*s[\x5c'\"]*b|-[\x5c'\"]*F|o[\x5c'\"]*f))?|z[\x5c'\"]*(?:(?:[ef][\x5c'\"]*)?g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p|c[\x5c'\"]*(?:a[\x5c'\"]*t|m[\x5c'\"]*p)|m[\x5c'\"]*(?:o[\x5c'\"]*r[\x5c'\"]*e|a)|d[\x5c'\"]*i[\x5c'\"]*f[\x5c'\"]*f|l[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*s)|e[\x5c'\"]*s[\x5c'\"]*s[\x5c'\"]*(?:(?:f[\x5c'\"]*i[\x5c'\"]*l|p[\x5c'\"]*i[\x5c'\"]*p)[\x5c'\"]*e|e[\x5c'\"]*c[\x5c'\"]*h[\x5c'\"]*o)|a[\x5c'\"]*s[\x5c'\"]*t[\x5c'\"]*(?:l[\x5c'\"]*o[\x5c'\"]*g(?:[\x5c'\"]*i[\x5c'\"]*n)?|c[\x5c'\"]*o[\x5c'\"]*m[\x5c'\"]*m)|w[\x5c'\"]*p(?:[\x5c'\"]*-[\x5c'\"]*d[\x5c'\"]*o[\x5c'\"]*w[\x5c'\"]*n[\x5c'\"]*l[\x5c'\"]*o[\x5c'\"]*a[\x5c'\"]*d)?|f[\x5c'\"]*t[\x5c'\"]*p(?:[\x5c'\"]*g[\x5c'\"]*e[\x5c'\"]*t)?|y[\x5c'\"]*n[\x5c'\"]*x)|s[\x5c'\"]*(?:e[\x5c'\"]*(?:t[\x5c'\"]*(?:e[\x5c'\"]*n[\x5c'\"]*v|s[\x5c'\"]*i[\x5c'\"]*d)|n[\x5c'\"]*d[\x5c'\"]*m[\x5c'\"]*a[\x5c'\"]*i[\x5c'\"]*l|d)|h(?:[\x5c'\"]*\.[\x5c'\"]*d[\x5c'\"]*i[\x5c'\"]*s[\x5c'\"]*t[\x5c'\"]*r[\x5c'\"]*i[\x5c'\"]*b)?|o[\x5c'\"]*(?:u[\x5c'\"]*r[\x5c'\"]*c[\x5c'\"]*e|c[\x5c'\"]*a[\x5c'\"]*t)|t[\x5c'\"]*r[\x5c'\"]*i[\x5c'\"]*n[\x5c'\"]*g[\x5c'\"]*s|y[\x5c'\"]*s[\x5c'\"]*c[\x5c'\"]*t[\x5c'\"]*l|c[\x5c'\"]*(?:h[\x5c'\"]*e[\x5c'\"]*d|p)|d[\x5c'\"]*i[\x5c'\"]*f[\x5c'\"]*f|f[\x5c'\"]*t[\x5c'\"]*p|u[\x5c'\"]*d[\x5c'\"]*o|s[\x5c'\"]*h|v[\x5c'\"]*n)|p[\x5c'\"]*(?:t[\x5c'\"]*a[\x5c'\"]*r(?:[\x5c'\"]*(?:d[\x5c'\"]*i[\x5c'\"]*f[\x5c'\"]*f|g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p))?|y[\x5c'\"]*t[\x5c'\"]*h[\x5c'\"]*o[\x5c'\"]*n(?:[\x5c'\"]*(?:3(?:[\x5c'\"]*m)?|2))?|k[\x5c'\"]*(?:e[\x5c'\"]*x[\x5c'\"]*e[\x5c'\"]*c|i[\x5c'\"]*l[\x5c'\"]*l)|r[\x5c'\"]*i[\x5c'\"]*n[\x5c'\"]*t[\x5c'\"]*e[\x5c'\"]*n[\x5c'\"]*v|(?:g[\x5c'\"]*r[\x5c'\"]*e|f[\x5c'\"]*t)[\x5c'\"]*p|e[\x5c'\"]*r[\x5c'\"]*l(?:[\x5c'\"]*5)?|h[\x5c'\"]*p(?:[\x5c'\"]*[57])?|i[\x5c'\"]*n[\x5c'\"]*g|o[\x5c'\"]*p[\x5c'\"]*d)|n[\x5c'\"]*(?:c(?:[\x5c'\"]*(?:\.[\x5c'\"]*(?:t[\x5c'\"]*r[\x5c'\"]*a[\x5c'\"]*d[\x5c'\"]*i[\x5c'\"]*t[\x5c'\"]*i[\x5c'\"]*o[\x5c'\"]*n[\x5c'\"]*a[\x5c'\"]*l|o[\x5c'\"]*p[\x5c'\"]*e[\x5c'\"]*n[\x5c'\"]*b[\x5c'\"]*s[\x5c'\"]*d)|a[\x5c'\"]*t))?|e[\x5c'\"]*t[\x5c'\"]*(?:k[\x5c'\"]*i[\x5c'\"]*t[\x5c'\"]*-[\x5c'\"]*f[\x5c'\"]*t[\x5c'\"]*p|(?:s[\x5c'\"]*t|c)[\x5c'\"]*a[\x5c'\"]*t)|o[\x5c'\"]*h[\x5c'\"]*u[\x5c'\"]*p|p[\x5c'\"]*i[\x5c'\"]*n[\x5c'\"]*g|s[\x5c'\"]*t[\x5c'\"]*a[\x5c'\"]*t)|t[\x5c'\"]*(?:c[\x5c'\"]*(?:p[\x5c'\"]*(?:t[\x5c'\"]*r[\x5c'\"]*a[\x5c'\"]*c[\x5c'\"]*e[\x5c'\"]*r[\x5c'\"]*o[\x5c'\"]*u[\x5c'\"]*t[\x5c'\"]*e|i[\x5c'\"]*n[\x5c'\"]*g)|s[\x5c'\"]*h)|r[\x5c'\"]*a[\x5c'\"]*c[\x5c'\"]*e[\x5c'\"]*r[\x5c'\"]*o[\x5c'\"]*u[\x5c'\"]*t[\x5c'\"]*e(?:[\x5c'\"]*6)?|i[\x5c'\"]*m[\x5c'\"]*e(?:[\x5c'\"]*o[\x5c'\"]*u[\x5c'\"]*t)?|a[\x5c'\"]*(?:i[\x5c'\"]*l(?:[\x5c'\"]*f)?|r)|e[\x5c'\"]*l[\x5c'\"]*n[\x5c'\"]*e[\x5c'\"]*t)|r[\x5c'\"]*(?:e[\x5c'\"]*(?:p[\x5c'\"]*(?:l[\x5c'\"]*a[\x5c'\"]*c[\x5c'\"]*e|e[\x5c'\"]*a[\x5c'\"]*t)|a[\x5c'\"]*l[\x5c'\"]*p[\x5c'\"]*a[\x5c'\"]*t[\x5c'\"]*h|n[\x5c'\"]*a[\x5c'\"]*m[\x5c'\"]*e)|u[\x5c'\"]*b[\x5c'\"]*y(?:[\x5c'\"]*(?:1(?:[\x5c'\"]*[89])?|2[\x5c'\"]*[012]))?|m[\x5c'\"]*(?:u[\x5c'\"]*s[\x5c'\"]*e|d[\x5c'\"]*i)[\x5c'\"]*r|n[\x5c'\"]*a[\x5c'\"]*n[\x5c'\"]*o|s[\x5c'\"]*y[\x5c'\"]*n[\x5c'\"]*c|c[\x5c'\"]*p)|b[\x5c'\"]*(?:z[\x5c'\"]*(?:(?:[ef][\x5c'\"]*)?g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p|d[\x5c'\"]*i[\x5c'\"]*f[\x5c'\"]*f|l[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*s|m[\x5c'\"]*o[\x5c'\"]*r[\x5c'\"]*e|c[\x5c'\"]*a[\x5c'\"]*t)|s[\x5c'\"]*d[\x5c'\"]*(?:c[\x5c'\"]*a[\x5c'\"]*t|i[\x5c'\"]*f[\x5c'\"]*f|t[\x5c'\"]*a[\x5c'\"]*r)|u[\x5c'\"]*i[\x5c'\"]*l[\x5c'\"]*t[\x5c'\"]*i[\x5c'\"]*n|a[\x5c'\"]*s[\x5c'\"]*h)|m[\x5c'\"]*(?:y[\x5c'\"]*s[\x5c'\"]*q[\x5c'\"]*l[\x5c'\"]*(?:d[\x5c'\"]*u[\x5c'\"]*m[\x5c'\"]*p(?:[\x5c'\"]*s[\x5c'\"]*l[\x5c'\"]*o[\x5c'\"]*w)?|h[\x5c'\"]*o[\x5c'\"]*t[\x5c'\"]*c[\x5c'\"]*o[\x5c'\"]*p[\x5c'\"]*y|a[\x5c'\"]*d[\x5c'\"]*m[\x5c'\"]*i[\x5c'\"]*n|s[\x5c'\"]*h[\x5c'\"]*o[\x5c'\"]*w)|l[\x5c'\"]*o[\x5c'\"]*c[\x5c'\"]*a[\x5c'\"]*t[\x5c'\"]*e|a[\x5c'\"]*i[\x5c'\"]*l[\x5c'\"]*q)|u[\x5c'\"]*(?:n[\x5c'\"]*(?:c[\x5c'\"]*o[\x5c'\"]*m[\x5c'\"]*p[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*s|l[\x5c'\"]*z[\x5c'\"]*m[\x5c'\"]*a|a[\x5c'\"]*m[\x5c'\"]*e|r[\x5c'\"]*a[\x5c'\"]*r|s[\x5c'\"]*e[\x5c'\"]*t|z[\x5c'\"]*i[\x5c'\"]*p|x[\x5c'\"]*z)|s[\x5c'\"]*e[\x5c'\"]*r[\x5c'\"]*(?:(?:a[\x5c'\"]*d|m[\x5c'\"]*o)[\x5c'\"]*d|d[\x5c'\"]*e[\x5c'\"]*l))|x[\x5c'\"]*(?:z(?:[\x5c'\"]*(?:(?:[ef][\x5c'\"]*)?g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p|d[\x5c'\"]*(?:i[\x5c'\"]*f[\x5c'\"]*f|e[\x5c'\"]*c)|c[\x5c'\"]*(?:a[\x5c'\"]*t|m[\x5c'\"]*p)|l[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*s|m[\x5c'\"]*o[\x5c'\"]*r[\x5c'\"]*e))?|a[\x5c'\"]*r[\x5c'\"]*g[\x5c'\"]*s)|z[\x5c'\"]*(?:(?:(?:[ef][\x5c'\"]*)?g[\x5c'\"]*r[\x5c'\"]*e|i)[\x5c'\"]*p|c[\x5c'\"]*(?:a[\x5c'\"]*t|m[\x5c'\"]*p)|d[\x5c'\"]*i[\x5c'\"]*f[\x5c'\"]*f|l[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*s|m[\x5c'\"]*o[\x5c'\"]*r[\x5c'\"]*e|r[\x5c'\"]*u[\x5c'\"]*n|s[\x5c'\"]*h)|f[\x5c'\"]*(?:t[\x5c'\"]*p[\x5c'\"]*(?:s[\x5c'\"]*t[\x5c'\"]*a[\x5c'\"]*t[\x5c'\"]*s|w[\x5c'\"]*h[\x5c'\"]*o)|i[\x5c'\"]*l[\x5c'\"]*e[\x5c'\"]*t[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*t|e[\x5c'\"]*t[\x5c'\"]*c[\x5c'\"]*h|g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p)|c[\x5c'\"]*(?:o[\x5c'\"]*(?:m[\x5c'\"]*m[\x5c'\"]*a[\x5c'\"]*n[\x5c'\"]*d|p[\x5c'\"]*r[\x5c'\"]*o[\x5c'\"]*c)|u[\x5c'\"]*r[\x5c'\"]*l|s[\x5c'\"]*h|c)|e[\x5c'\"]*(?:g[\x5c'\"]*r[\x5c'\"]*e[\x5c'\"]*p|c[\x5c'\"]*h[\x5c'\"]*o|v[\x5c'\"]*a[\x5c'\"]*l|x[\x5c'\"]*e[\x5c'\"]*c|n[\x5c'\"]*v)|d[\x5c'\"]*(?:m[\x5c'\"]*e[\x5c'\"]*s[\x5c'\"]*g|a[\x5c'\"]*s[\x5c'\"]*h|i[\x5c'\"]*f[\x5c'\"]*f|o[\x5c'\"]*a[\x5c'\"]*s)|g[\x5c'\"]*(?:z[\x5c'\"]*(?:c[\x5c'\"]*a[\x5c'\"]*t|i[\x5c'\"]*p)|r[\x5c'\"]*e[\x5c'\"]*p|c[\x5c'\"]*c)|j[\x5c'\"]*(?:o[\x5c'\"]*b[\x5c'\"]*s[\x5c'\"]*\s+[\x5c'\"]*-[\x5c'\"]*x|a[\x5c'\"]*v[\x5c'\"]*a)|w[\x5c'\"]*(?:h[\x5c'\"]*o[\x5c'\"]*a[\x5c'\"]*m[\x5c'\"]*i|g[\x5c'\"]*e[\x5c'\"]*t|3[\x5c'\"]*m)|i[\x5c'\"]*r[\x5c'\"]*b(?:[\x5c'\"]*(?:1(?:[\x5c'\"]*[89])?|2[\x5c'\"]*[012]))?|o[\x5c'\"]*n[\x5c'\"]*i[\x5c'\"]*n[\x5c'\"]*t[\x5c'\"]*r|h[\x5c'\"]*(?:e[\x5c'\"]*a[\x5c'\"]*d|u[\x5c'\"]*p)|v[\x5c'\"]*i[\x5c'\"]*(?:g[\x5c'\"]*r|p[\x5c'\"]*w)|G[\x5c'\"]*E[\x5c'\"]*T)[\x5c'\"]*(?:\s|;|\||&|<|>)" \ + "id:932150,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Remote Command Execution: Direct Unix Command Execution',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-unix',\ + tag:'attack-rce',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# [ Unix shell snippets ] +# +# Detect some common sequences found in shell commands and scripts. +# +# Some commands which were restricted in earlier rules due to FP, +# have been added here with their full path, in order to catch some +# cases where the full path is sent. +# +# This rule is also triggered by an Apache Struts Remote Code Execution exploit: +# [ Apache Struts vulnerability CVE-2017-9805 - Exploit tested: https://www.exploit-db.com/exploits/42627 ] +# +# This rule is also triggered by an Oracle WebLogic Remote Command Execution exploit: +# [ Oracle WebLogic vulnerability CVE-2017-10271 - Exploit tested: https://www.exploit-db.com/exploits/43458 ] + +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@pmFromFile unix-shell.data" \ + "id:932160,\ + phase:2,\ + block,\ + capture,\ + t:none,t:cmdLine,t:normalizePath,\ + msg:'Remote Command Execution: Unix Shell Code Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-unix',\ + tag:'attack-rce',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# [ Shellshock vulnerability (CVE-2014-6271 and CVE-2014-7169) ] +# +# Detect exploitation of "Shellshock" GNU Bash RCE vulnerability. +# +# Based on ModSecurity rules created by Red Hat. +# Permission for use was granted by Martin Prpic +# +# https://access.redhat.com/articles/1212303 +# +SecRule REQUEST_HEADERS|REQUEST_LINE "@rx ^\(\s*\)\s+{" \ + "id:932170,\ + phase:1,\ + block,\ + capture,\ + t:none,t:urlDecode,\ + msg:'Remote Command Execution: Shellshock (CVE-2014-6271)',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-unix',\ + tag:'attack-rce',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + +SecRule ARGS_NAMES|ARGS|FILES_NAMES "@rx ^\(\s*\)\s+{" \ + "id:932171,\ + phase:2,\ + block,\ + capture,\ + t:none,t:urlDecode,t:urlDecodeUni,\ + msg:'Remote Command Execution: Shellshock (CVE-2014-6271)',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-unix',\ + tag:'attack-rce',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# -=[ Restricted File Upload ]=- +# +# Detects attempts to upload a file with a forbidden filename. +# +# Many application contain Unrestricted File Upload vulnerabilities. +# https://www.owasp.org/index.php/Unrestricted_File_Upload +# +# These might be abused to upload configuration files or other files +# that affect the behavior of the web server, possibly causing remote +# code execution. +# +SecRule FILES|REQUEST_HEADERS:X-Filename|REQUEST_HEADERS:X_Filename|REQUEST_HEADERS:X-File-Name \ + "@pmFromFile restricted-upload.data" \ + "id:932180,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Restricted File Upload Attempt',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-rce',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:932013,phase:1,pass,nolog,skipAfter:END-REQUEST-932-APPLICATION-ATTACK-RCE" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:932014,phase:2,pass,nolog,skipAfter:END-REQUEST-932-APPLICATION-ATTACK-RCE" +# +# -= Paranoia Level 2 =- (apply only when tx.executing_paranoia_level is sufficiently high: 2 or higher) +# + +# This is a stricter sibling of rule 932100. +# The source command, dot character was moved from 932100 at PL1 to +# this stricter sibling 932101 at PL2 because of false positives. +# For explanation of this rule, see rule 932100. +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 932101 +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?:;|\{|\||\|\||&|&&|\n|\r|\$\(|\$\(\(|`|\${|<\(|>\(|\(\s*\))\s*(?:{|\s*\(\s*|\w+=(?:[^\s]*|\$.*|\$.*|<.*|>.*|\'.*\'|\".*\")\s+|!\s*|\$)*\s*(?:'|\")*(?:[\?\*\[\]\(\)\-\|+\w'\"\./\x5c]+/)?[\x5c'\"]*\.\s.*\b" \ + "id:932101,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Remote Command Execution: Unix Command Injection',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-unix',\ + tag:'attack-rce',\ + tag:'paranoia-level/2',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# -=[ Rule 932200 ]=- +# +# Block RCE Bypass using different techniques: +# - uninitialized variables (https://www.secjuice.com/web-application-firewall-waf-evasion/) +# - string concatenations (https://medium.com/secjuice/web-application-firewall-waf-evasion-techniques-2-125995f3e7b0) +# - globbing patterns (https://medium.com/secjuice/waf-evasion-techniques-718026d693d8) +# +# Examples: +# - foo;cat$u+/etc$u/passwd +# - bar;cd+/etc;/bin$u/ca*+passwd +# - foo;ca\t+/et\c/pa\s\swd +# - foo;c'at'+/etc/pa's'swd +# - foo;c$@at+/et$@c/pas$@swd +# - foo;c$!at+/et$!c/pas$!swd +# - foo;c$*at+/et$*c/pas$*swd +# - foo;c$?at+/et$?c/pas$?swd +# - foo;c$-at+/et$-c/pas$-swd +# - foo;c$_at+/et$_c/pas$_swd +# - foo;c$$at+/et$$c/pas$$swd +# +# Regex notes: https://regex101.com/r/V6wrCO/1 +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?:[*?`\x5c'][^/\n]+/|\$[({\[#@!?*\-_$a-zA-Z0-9]|/[^/]+?[*?`\x5c'])" \ + "id:932200,\ + phase:2,\ + block,\ + capture,\ + t:none,t:lowercase,t:urlDecodeUni,\ + msg:'RCE Bypass Technique',\ + logdata:'Matched Data: %{TX.0} found within %{TX.932200_MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-rce',\ + tag:'paranoia-level/2',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.932200_matched_var_name=%{matched_var_name}',\ + chain" + SecRule MATCHED_VAR "@rx /" "t:none,t:urlDecodeUni,chain" + SecRule MATCHED_VAR "@rx \s" "t:none,t:urlDecodeUni,\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}'" + + +# [ Sqlite System Command Execution ] +# +# This rule prevents execution of SQLite CLI commands like .system and .shell +# +# You can find a vulnerable script and a sample payload here: +# https://github.com/qxxxb/ctf/tree/master/2021/zer0pts_ctf/baby_sqli +# +# List of sqlite3 CLI commands: +# https://sqlite.org/cli.html +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 932210 +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx ;\s*\.\s*(?:s(?:e(?:parator|lftest)|c(?:anstats|hema)|h(?:a3sum|ell|ow)|ystem|tats|ave)|t(?:estc(?:ase|trl)|ime(?:out|r)|ables|race)|e(?:x(?:p(?:lain|ert)|cel|it)|cho|qp)|p(?:r(?:o(?:gress|mpt)|int)|arameter)|d(?:b(?:config|info)|atabases|ump)|c(?:h(?:anges|eck)|lone|d)|i(?:mpo(?:ster|rt)|ndexes)|l(?:i(?:mi|n)t|o(?:ad|g))|b(?:a(?:ckup|il)|inary)|f(?:ullschema|ilectrl)|vfs(?:info|list|name)|re(?:cover|store|ad)|o(?:utput|nce|pen)|(?:nullvalu|mod)e|a(?:rchive|uth)|he(?:aders|lp)|width|quit)" \ + "id:932210,\ + phase:2,\ + block,\ + t:none,t:escapeSeqDecode,t:compressWhitespace,\ + msg:'Remote Command Execution: SQLite System Command Execution',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-unix',\ + tag:'attack-rce',\ + tag:'paranoia-level/2',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}'" + +# -=[ SMTP/IMAP/POP3 Command Execution ]=- +# +# Rationale +# ========= +# +# The rules for email command execution are based on the RFCs for each protocol. +# Some of the commands have optional and/or additional parameters, so we tried to be +# precise to avoid as many FP in PL2 rules. +# For those commands that resemble common English words, and may pose a higher risk of false positives, +# they have been split off to a sibiling rule in PL3. + +# =[ SMTP Command Execution ]= +# +# This rule prevents execution of SMTP related system commands. +# +# List of SMTP commands: from rfc 5321 (https://www.rfc-editor.org/rfc/rfc5321) +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py data/932300.data +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "(?is)\r\n.*?\b(?:AUTH [A-Z0-9-_]{1,20} (?:=|(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=))|R(?:CPT TO:(?:<.{1,64}@.{1,255}>|(?: ))?<.{1,64}>|SET\b)|VRFY (?:.{1,64} <.{1,64}@.{1,255}>|.{1,64}@.{1,255})|E(?:HLO [a-zA-Z-\.]{1,255}|XPN (?:.{1,64}))|MAIL FROM:<.{1,64}@.{1,255}>|HELO [a-zA-Z-\.]{1,255}|NOOP\b(?: .{1,255})?|STARTTLS\b)" \ + "id:932300,\ + phase:2,\ + block,\ + t:none,t:escapeSeqDecode,\ + msg:'Remote Command Execution: SMTP Command Execution',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'platform-multi',\ + tag:'attack-rce',\ + tag:'paranoia-level/2',\ + tag:'OWASP_CRS',\ + tag:'capec/137/134',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}'" + +# =[ IMAP Command Execution ]= +# +# This rule prevents execution of IMAP4 related system commands. +# +# List of IMAP4 commands: from rfc 3501 (https://datatracker.ietf.org/doc/html/rfc3501#section-9) +# +# Note: Mailbox International Naming Convention uses UTF-7, so it was left out explicitly. +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py data/932310.data +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "(?is)\r\n\w{1,50}\b\ (?:A(?:PPEND (?:[\w\"\.\-\x5c\/%\*&#]+)?(?: \((?:[A-Za-z\x5c\ ])+\))?(?: \"?\d{1,2}-\w{3}-\d{4} \d{2}:\d{2}:\d{2} [+-]\d{4}\"?)? \{\d{1,20}\+?}|UTHENTICATE [A-Z0-9-_]{1,20}\r\n)|S(?:TATUS (?:[\w\"\.\-\x5c\/%\*&]+)? \((?:UNSEEN|UIDNEXT|MESSAGES|UIDVALIDITY|RECENT| )+\)|ETACL (?:[\w\"\.\-\x5c\/%\*&]+)? [+-][lrswipckdxtea]+?)|L(?:SUB (?:[\w\"~\/\*#\.]+)? (?:[\w\"\.\x5c\/%\*&]+)?|ISTRIGHTS (?:[\w\"\.\-\x5c\/%\*&]+)?)|(?:(?:DELETE|GET)ACL|MYRIGHTS) (?:[\w\"\.\-\x5c\/%\*&]+)?|UID (?:COPY|FETCH|STORE) (?:[0-9,:\*]+)?)" \ + "id:932310,\ + phase:2,\ + block,\ + t:none,t:escapeSeqDecode,\ + msg:'Remote Command Execution: IMAP Command Execution',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'platform-multi',\ + tag:'attack-rce',\ + tag:'paranoia-level/2',\ + tag:'OWASP_CRS',\ + tag:'capec/137/134',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}'" + +# =[ POP3 Command Execution ]= +# +# This rule prevents execution of POP3 related system commands. +# +# List of POP3 commands: +# - from rfc 1939 (https://www.rfc-editor.org/rfc/rfc1939#appendix-B) +# - extensions from rfc 2449 (https://www.rfc-editor.org/rfc/rfc2449) +# +# These commands all have some kind of parameter that makes them a good PL2 target. +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py data/932320.data +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "(?si)\r\n.*?\b(?:A(?:UTH [A-Z0-9-_]{1,20} (?:=|(?:[\w+/]{4})*(?:[\w+/]{2}==|[\w+/]{3}=))|POP [\w]+ [a-f0-9]{32})|(?:TOP \d+|LIST)(?: \d+)?|U(?:IDL(?: \d+)?|SER .+?)|(?:DELE|RETR) \d+?|PASS .+?)" \ + "id:932320,\ + phase:2,\ + block,\ + t:none,t:escapeSeqDecode,\ + msg:'Remote Command Execution: POP3 Command Execution',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'platform-multi',\ + tag:'attack-rce',\ + tag:'paranoia-level/2',\ + tag:'OWASP_CRS',\ + tag:'capec/137/134',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}'" + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:932015,phase:1,pass,nolog,skipAfter:END-REQUEST-932-APPLICATION-ATTACK-RCE" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:932016,phase:2,pass,nolog,skipAfter:END-REQUEST-932-APPLICATION-ATTACK-RCE" +# +# -= Paranoia Level 3 =- (apply only when tx.executing_paranoia_level is sufficiently high: 3 or higher) +# + +# Missing Unix commands have been added to a new word list i.e. +# util/regexp-assemble/data/932106.data +# These commands may have a higher risk of false positives. +# Therefore, they have been split off to a separate rule in PL3. +# For explanation of this rule, see rule 932100. +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 932106 +# +# This rule is a stricter sibling of rule 932100. +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?:;|\{|\||\|\||&|&&|\n|\r|\$\(|\$\(\(|`|\${|<\(|>\(|\(\s*\))\s*(?:{|\s*\(\s*|\w+=(?:[^\s]*|\$.*|\$.*|<.*|>.*|\'.*\'|\".*\")\s+|!\s*|\$)*\s*(?:'|\")*(?:[\?\*\[\]\(\)\-\|+\w'\"\./\x5c]+/)?[\x5c'\"]*(?:(?:(?:a[\x5c'\"]*p[\x5c'\"]*t[\x5c'\"]*i[\x5c'\"]*t[\x5c'\"]*u[\x5c'\"]*d|u[\x5c'\"]*p[\x5c'\"]*2[\x5c'\"]*d[\x5c'\"]*a[\x5c'\"]*t)[\x5c'\"]*e|d[\x5c'\"]*n[\x5c'\"]*f|v[\x5c'\"]*i)[\x5c'\"]*(?:\s|<|>).*|p[\x5c'\"]*(?:a[\x5c'\"]*c[\x5c'\"]*m[\x5c'\"]*a[\x5c'\"]*n[\x5c'\"]*(?:\s|<|>).*|w[\x5c'\"]*d|s)|w[\x5c'\"]*(?:(?:\s|<|>).*|h[\x5c'\"]*o))\b" \ + "id:932106,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Remote Command Execution: Unix Command Injection',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-unix',\ + tag:'attack-rce',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + tag:'paranoia-level/3',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl3=+%{tx.critical_anomaly_score}'" + +# +# -=[ Bypass Rule 930120 (wildcard) ]=- +# +# When Paranoia Level is set to 1 and 2, a Remote Command Execution +# could be exploited bypassing rule 930120 (OS File Access Attempt) +# by using wildcard characters. +# +# In some other cases, it could be bypassed even if the Paranoia Level is set to 3. +# Please, keep in mind that this rule could lead to many false positives. +# +# The following two blog posts explain the evasions this rule is designed to detect: +# - https://medium.com/secjuice/waf-evasion-techniques-718026d693d8 +# - https://medium.com/secjuice/web-application-firewall-waf-evasion-techniques-2-125995f3e7b0 + +SecRule ARGS "@rx /(?:[?*]+[a-z/]+|[a-z/]+[?*]+)" \ + "id:932190,\ + phase:2,\ + block,\ + capture,\ + t:none,t:urlDecode,t:urlDecodeUni,t:normalizePath,t:cmdLine,\ + msg:'Remote Command Execution: Wildcard bypass technique attempt',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-unix',\ + tag:'attack-rce',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/248/88',\ + tag:'PCI/6.5.2',\ + tag:'paranoia-level/3',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl3=+%{tx.critical_anomaly_score}'" + + +# -=[ SMTP commands ]=- +# +# This rule prevents execution of SMTP related system commands. +# +# These commands may have a higher risk of false positives. +# For explanation of this rule, see above rule 932300. +# +# Rule 932301 is a stricter sibling of rule 932300. +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py data/932301.data +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "(?s)\r\n.*?\b(?:HELP(?: .{1,255})?|DATA|QUIT)" \ + "id:932301,\ + phase:2,\ + block,\ + t:none,t:escapeSeqDecode,\ + msg:'Remote Command Execution: SMTP Command Execution',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'platform-multi',\ + tag:'attack-rce',\ + tag:'paranoia-level/3',\ + tag:'OWASP_CRS',\ + tag:'capec/137/134',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl3=+%{tx.critical_anomaly_score}'" + +# =[ IMAP4 Command Execution ]= +# +# This rule prevents execution of IMAP4 related system commands. +# +# These commands may have a higher risk of false positives. +# For explanation of this rule, see above rule 932310. +# +# Rule 932311 is a stricter sibling of rule 932310. +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py data/932311.data +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "(?is)\r\n\w{1,50}\b\ (?:S(?:E(?:ARCH(?: CHARSET [\w\-_\.]{1,40})? (?:(KEYWORD \x5c)?(?:ALL|ANSWERED|BCC|DELETED|DRAFT|FLAGGED|RECENT|SEEN|UNANSWERED|UNDELETED|UNDRAFT|UNFLAGGED|UNSEEN|NEW|OLD)|(?:TEXT .{1,255}|TO .{1,255}|UID [0-9,:\*]+?|UNKEYWORD (?:\x5c(Seen|Answered|Flagged|Deleted|Draft|Recent)))|(?:BEFORE|ON|SENTBEFORE|SENTON|SENTSINCE|SINCE) \"?\d{1,2}-\w{3}-\d{4}\"?|(?:OR .{1,255} .{1,255}|SMALLER \d{1,20}|SUBJECT .{1,255})|(?:(?:BODY|CC|FROM)|HEADER .{1,100}) .{1,255}|(?:LARGER \d{1,20}|NOT .{1,255}|[0-9,:\*]+))|LECT [\w\"\.\-\x5c\/%\*&#]+)|T(?:ORE [0-9,:\*]+? [+-]?FLAGS(?:\.SILENT)? (?:\(\x5c[A-Za-z]{1,20}\)){0,100}|ARTTLS)|UBSCRIBE [\w\"\.\-\x5c\/%\*&#]+)|L(?:IST [\w\"~\-\x5c\/\*#\.]+? [\w\"\.\-\x5c\/%\*&#]+|OG(?:IN [A-Z0-9-_\.\@]{1,40} .*? |OUT))|C(?:(?:OPY [0-9,:\*]+|REATE) [\w\"\.\-\x5c\/%\*&#]+|APABILITY|HECK|LOSE)|RENAME [\w\"\.\-\x5c\/%\*&#]+? [\w\"\.\-\x5c\/%\*&#]+|UN(?:SUBSCRIBE [\w\"\.\-\x5c\/%\*&#]+|AUTHENTICATE)|EX(?:AMINE [\w\"\.\-\x5c%\*&#]+|PUNGE)|DELETE [\w\"\.\-\x5c%\*&#]+|FETCH [0-9,:\*]+|NOOP)" \ + "id:932311,\ + phase:2,\ + block,\ + t:none,t:escapeSeqDecode,\ + msg:'Remote Command Execution: IMAP Command Execution',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'platform-multi',\ + tag:'attack-rce',\ + tag:'paranoia-level/3',\ + tag:'OWASP_CRS',\ + tag:'capec/137/134',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl3=+%{tx.critical_anomaly_score}'" + +# =[ POP3 Command Execution ]= +# +# This rule prevents execution of POP3 related system commands. +# +# These commands may have a higher risk of false positives. +# For explanation of this rule, see above rule 932320. +# +# Rule 932321 is a stricter sibling of rule 932320. +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py data/932321.data +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "(?si)\r\n.*?\b(?:(?:QUI|RSE|STA)T|CAPA|NOOP)" \ + "id:932321,\ + phase:2,\ + block,\ + t:none,t:escapeSeqDecode,\ + msg:'Remote Command Execution: POP3 Command Execution',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'platform-multi',\ + tag:'attack-rce',\ + tag:'paranoia-level/3',\ + tag:'OWASP_CRS',\ + tag:'capec/137/134',\ + tag:'PCI/6.5.2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl3=+%{tx.critical_anomaly_score}'" + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:932017,phase:1,pass,nolog,skipAfter:END-REQUEST-932-APPLICATION-ATTACK-RCE" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:932018,phase:2,pass,nolog,skipAfter:END-REQUEST-932-APPLICATION-ATTACK-RCE" +# +# -= Paranoia Level 4 =- (apply only when tx.executing_paranoia_level is sufficiently high: 4 or higher) +# + + + +# +# -= Paranoia Levels Finished =- +# +SecMarker "END-REQUEST-932-APPLICATION-ATTACK-RCE" diff --git a/samples/ModSecurity SecRule/REQUEST-933-APPLICATION-ATTACK-PHP.conf b/samples/ModSecurity SecRule/REQUEST-933-APPLICATION-ATTACK-PHP.conf new file mode 100644 index 0000000000..10bb2a237a --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-933-APPLICATION-ATTACK-PHP.conf @@ -0,0 +1,731 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# +# -= Paranoia Level 0 (empty) =- (apply unconditionally) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:933011,phase:1,pass,nolog,skipAfter:END-REQUEST-933-APPLICATION-ATTACK-PHP" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:933012,phase:2,pass,nolog,skipAfter:END-REQUEST-933-APPLICATION-ATTACK-PHP" +# +# -= Paranoia Level 1 (default) =- (apply only when tx.executing_paranoia_level is sufficiently high: 1 or higher) +# + +# +# -=[ PHP Injection Attacks ]=- +# +# [ References ] +# http://rips-scanner.sourceforge.net/ +# https://www.owasp.org/index.php/PHP_Top_5#P1:_Remote_Code_Executionh +# + +# +# [ PHP Open Tag Found ] +# +# Detects PHP open tags "', but +# this resulted in false positives which were difficult to prevent. +# Therefore, that pattern is now checked by rule 933190 in paranoia levels +# 3 or higher. +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?:<\?(?:[^x]|x[^m]|xm[^l]|xml[^\s]|xml$|$)|<\?php|\[(?:/|\x5c)?php\])" \ + "id:933100,\ + phase:2,\ + block,\ + capture,\ + t:none,t:lowercase,\ + msg:'PHP Injection Attack: PHP Open Tag Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + +# +# [ PHP Script Uploads ] +# +# Block file uploads with filenames ending in PHP related extensions +# (.php, .phps, .phtml, .php5 etc). +# +# Many application contain Unrestricted File Upload vulnerabilities. +# https://www.owasp.org/index.php/Unrestricted_File_Upload +# +# Attackers may use such a vulnerability to achieve remote code execution +# by uploading a .php file. If the upload storage location is predictable +# and not adequately protected, the attacker may then request the uploaded +# .php file and have the code within it executed on the server. +# +# Also block files with just dot (.) characters after the extension: +# https://community.rapid7.com/community/metasploit/blog/2013/08/15/time-to-patch-joomla +# +# Some AJAX uploaders use the nonstandard request headers X-Filename, +# X_Filename, or X-File-Name to transmit the file name to the server; +# scan these request headers as well as multipart/form-data file names. +# +SecRule FILES|REQUEST_HEADERS:X-Filename|REQUEST_HEADERS:X_Filename|REQUEST_HEADERS:X.Filename|REQUEST_HEADERS:X-File-Name "@rx .*\.(?:php\d*|phtml)\.*$" \ + "id:933110,\ + phase:2,\ + block,\ + capture,\ + t:none,t:lowercase,\ + msg:'PHP Injection Attack: PHP Script File Upload Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# [ PHP Configuration Directives ] +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@pmFromFile php-config-directives.data" \ + "id:933120,\ + phase:2,\ + block,\ + capture,\ + t:none,t:normalisePath,\ + msg:'PHP Injection Attack: Configuration Directive Found',\ + logdata:'Matched Data: %{TX.933120_TX_0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.933120_tx_0=%{tx.0}',\ + chain" + SecRule MATCHED_VARS "@pm =" \ + "capture,\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# [ PHP Variables ] +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@pmFromFile php-variables.data" \ + "id:933130,\ + phase:2,\ + block,\ + capture,\ + t:none,t:normalisePath,t:urlDecodeUni,\ + msg:'PHP Injection Attack: Variables Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# [ PHP I/O Streams ] +# +# The "php://" syntax can be used to refer to various objects, such as local files (for LFI), +# remote urls (for RFI), or standard input/request body. Its occurrence indicates a possible attempt +# to either inject PHP code or exploit a file inclusion vulnerability in a PHP web app. +# +# Examples: +# php://filter/resource=./../../../wp-config.php +# php://filter/resource=http://www.example.com +# php://stdin +# php://input +# +# http://php.net/manual/en/wrappers.php.php +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?i)php://(?:std(?:in|out|err)|(?:in|out)put|fd|memory|temp|filter)" \ + "id:933140,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'PHP Injection Attack: I/O Stream Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# [ PHP Wrappers ] +# +# PHP comes with many built-in wrappers for various URL-style protocols for use with the filesystem +# functions such as fopen(), copy(), file_exists() and filesize(). Abusing of PHP wrappers like phar:// +# could lead to RCE as describled by Sam Thomas at BlackHat USA 2018 (https://bit.ly/2yaKV5X), even +# wrappers like zlib://, glob://, rar://, zip://, etc... could lead to LFI and expect:// to RCE. +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?i:zlib|glob|phar|ssh2|rar|ogg|expect|zip)://" \ + "id:933200,\ + phase:2,\ + block,\ + t:none,t:utf8toUnicode,t:urlDecodeUni,t:removeNulls,t:cmdLine,\ + msg:'PHP Injection Attack: Wrapper scheme detected',\ + logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# [ PHP Functions ] +# +# Detecting PHP function names is useful to block PHP code injection attacks. +# There are many PHP functions. We have to strike a balance between robust detection +# of PHP code in content, and the risk of false positives. +# +# The list of PHP functions is divided into four groups of varying attack/false positive risk. +# Four separate rules are used to detect these groups of functions: +# +# - Rule 933150: ~40 words highly common to PHP injection payloads and extremely rare in +# natural language or other contexts. +# Examples: 'base64_decode', 'file_get_contents'. +# These words are detected as a match directly using @pmFromFile. +# Function names are defined in php-function-names-933150.data +# +# - Rule 933160: ~220 words which are common in PHP code, but have a higher chance to cause +# false positives in natural language or other contexts. +# Examples: 'chr', 'eval'. +# To mitigate false positives, a regexp looks for PHP function syntax, e.g. 'eval()'. +# Regexp is generated from function names in util/regexp-assemble/data/933160.data +# +# - Rule 933151: ~1300 words of lesser importance. This includes most PHP functions and keywords. +# Examples: 'addslashes', 'array_diff'. +# For performance reasons, the @pmFromFile operator is used, and many functions from lesser +# used PHP extensions are removed. +# To mitigate false positives, we only match when the '(' character is also found. +# This rule only runs in paranoia level 2 or higher. +# Function names are defined in php-function-names-933151.data +# +# - Rule 933161: ~200 words with short or trivial names, possibly leading to false positives. +# Examples: 'abs', 'cos'. +# To mitigate false positives, a regexp matches on function syntax, e.g. 'abs()'. +# This rule only runs in paranoia level 3 or higher. +# Regexp is generated from function names in util/regexp-assemble/data/933161.data +# + + +# +# [ PHP Functions: High-Risk PHP Function Names ] +# +# Rule 933150 contains a small list of function names which are highly indicative of a PHP +# injection attack, for example 'base64_decode'. +# We block these function names outright, without using a complex regexp or chain. +# This could make the detection a bit more robust against possible bypasses. +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "@pmFromFile php-function-names-933150.data" \ + "id:933150,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'PHP Injection Attack: High-Risk PHP Function Name Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# [ PHP Functions: High-Risk PHP Function Calls ] +# +# Some PHP function names have a certain risk of false positives, due to short +# names, full or partial overlap with common natural language terms, uses in +# other contexts, et cetera. Some examples are 'eval', 'exec', 'system'. +# +# For these function names, we apply a regexp to look for PHP function syntax. +# The regexp looks for a word boundary and adjoining parentheses. +# For instance, we want to block 'eval()', but we want to allow 'medieval()'. +# +# We have to be careful of possible bypasses using comment syntax. Examples: +# +# system(...) +# system (...) +# system\t(...) +# system /*comment*/ (...) +# system /*multiline \n comment*/ (...) +# system //comment \n (...) +# system #comment \n (...) +# +# This rule is also triggered by the following exploit(s): +# [ Apache Struts vulnerability CVE-2017-9791 - Exploit tested: https://www.exploit-db.com/exploits/42324 ] +# [ Apache Struts vulnerability CVE-2018-11776 - Exploit tested: https://www.exploit-db.com/exploits/45260 ] +# [ SAP CRM Java vulnerability CVE-2018-2380 - Exploit tested: https://www.exploit-db.com/exploits/44292 ] +# +# Regexp generated from util/regexp-assemble/data/933160.data using Regexp::Assemble. +# See https://coreruleset.org/20190826/optimizing-regular-expressions/ for usage. +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "@rx (?i)\b(?:s(?:e(?:t(?:_(?:e(?:xception|rror)_handler|magic_quotes_runtime|include_path)|defaultstub)|ssion_s(?:et_save_handler|tart))|qlite_(?:(?:(?:unbuffered|single|array)_)?query|create_(?:aggregate|function)|p?open|exec)|tr(?:eam_(?:context_create|socket_client)|ipc?slashes|rev)|implexml_load_(?:string|file)|ocket_c(?:onnect|reate)|h(?:ow_sourc|a1_fil)e|pl_autoload_register|ystem)|p(?:r(?:eg_(?:replace(?:_callback(?:_array)?)?|match(?:_all)?|split)|oc_(?:(?:terminat|clos|nic)e|get_status|open)|int_r)|o(?:six_(?:get(?:(?:e[gu]|g)id|login|pwnam)|mk(?:fifo|nod)|ttyname|kill)|pen)|hp(?:_(?:strip_whitespac|unam)e|version|info)|g_(?:(?:execut|prepar)e|connect|query)|a(?:rse_(?:ini_file|str)|ssthru)|utenv)|r(?:unkit_(?:function_(?:re(?:defin|nam)e|copy|add)|method_(?:re(?:defin|nam)e|copy|add)|constant_(?:redefine|add))|e(?:(?:gister_(?:shutdown|tick)|name)_function|ad(?:(?:gz)?file|_exif_data|dir))|awurl(?:de|en)code)|i(?:mage(?:createfrom(?:(?:jpe|pn)g|x[bp]m|wbmp|gif)|(?:jpe|pn)g|g(?:d2?|if)|2?wbmp|xbm)|s_(?:(?:(?:execut|write?|read)ab|fi)le|dir)|ni_(?:get(?:_all)?|set)|terator_apply|ptcembed)|g(?:et(?:_(?:c(?:urrent_use|fg_va)r|meta_tags)|my(?:[gpu]id|inode)|(?:lastmo|cw)d|imagesize|env)|z(?:(?:(?:defla|wri)t|encod|fil)e|compress|open|read)|lob)|a(?:rray_(?:u(?:intersect(?:_u?assoc)?|diff(?:_u?assoc)?)|intersect_u(?:assoc|key)|diff_u(?:assoc|key)|filter|reduce|map)|ssert(?:_options)?)|h(?:tml(?:specialchars(?:_decode)?|_entity_decode|entities)|(?:ash(?:_(?:update|hmac))?|ighlight)_file|e(?:ader_register_callback|x2bin))|f(?:i(?:le(?:(?:[acm]tim|inod)e|(?:_exist|perm)s|group)?|nfo_open)|tp_(?:nb_(?:ge|pu)|connec|ge|pu)t|(?:unction_exis|pu)ts|write|open)|o(?:b_(?:get_(?:c(?:ontents|lean)|flush)|end_(?:clean|flush)|clean|flush|start)|dbc_(?:result(?:_all)?|exec(?:ute)?|connect)|pendir)|m(?:b_(?:ereg(?:_(?:replace(?:_callback)?|match)|i(?:_replace)?)?|parse_str)|(?:ove_uploaded|d5)_file|ethod_exists|ysql_query|kdir)|e(?:x(?:if_(?:t(?:humbnail|agname)|imagetype|read_data)|ec)|scapeshell(?:arg|cmd)|rror_reporting|val)|c(?:url_(?:file_create|exec|init)|onvert_uuencode|reate_function|hr)|u(?:n(?:serialize|pack)|rl(?:de|en)code|[ak]?sort)|(?:json_(?:de|en)cod|debug_backtrac|tmpfil)e|b(?:(?:son_(?:de|en)|ase64_en)code|zopen)|var_dump)(?:\s|/\*.*\*/|//.*|#.*)*\(.*\)" \ + "id:933160,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'PHP Injection Attack: High-Risk PHP Function Call Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# [ PHP Object Injection ] +# +# PHP Object Injection is an application level vulnerability that could allow +# an attacker to perform different kinds of malicious attacks, such as +# Code Injection, SQL Injection, Path Traversal and Application Denial of Service, +# depending on the context. +# +# The vulnerability occurs when user-supplied input is not properly sanitized +# before being passed to the unserialize() PHP function. Since PHP allows object +# serialization, attackers could pass ad-hoc serialized strings to a vulnerable +# unserialize() call, resulting in an arbitrary PHP object(s) injection into the +# application scope. +# +# https://www.owasp.org/index.php/PHP_Object_Injection +# +# In serialized form, PHP objects have the following format: +# +# O:8:"stdClass":1:{s:1:"a";i:2;} +# O:3:"Foo":0:{} +# +# Also detected are PHP objects with a custom unserializer: +# http://www.phpinternalsbook.com/classes_objects/serialization.html +# These have the following format: +# +# C:11:"ArrayObject":37:{x:i:0;a:1:{s:1:"a";s:1:"b";};m:a:0:{}} +# C:3:"Foo":23:{s:15:"My private data";} +# +# HTTP headers are inspected, since PHP object injection vulnerabilities have been +# found in applications parsing them: +# https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-8562 (User-Agent header) +# https://www.exploit-db.com/exploits/39033/ (X-Forwarded-For header) +# http://karmainsecurity.com/KIS-2015-10 (Host header) +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_HEADERS|ARGS_NAMES|ARGS|XML:/* "@rx [oOcC]:\d+:\".+?\":\d+:{.*}" \ + "id:933170,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'PHP Injection Attack: Serialized Object Injection',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + + +# +# [ PHP Functions: Variable Function Calls ] +# +# PHP 'variable functions' provide an alternate syntax for calling PHP functions. +# http://php.net/manual/en/functions.variable-functions.php +# +# An attacker may use variable function syntax to evade detection of function +# names during exploitation of a remote code execution vulnerability. +# An example to use the 'file_get_contents' function while evading rule 933150: +# +# $fn = 'file_' . 'get_' . 'contents'; +# echo $fn('wp-co' . 'nfig.php'); +# +# Some examples from obfuscated malware: +# +# $OOO0000O0(...) +# @$b374k(...) +# $_[@-_]($_[@!+_] ) +# +# A breakdown of the regular expression: +# +# \$+ +# The variable's '$' char, or multiple '$' for 'variable variables': +# http://php.net/manual/en/language.variables.variable.php +# (?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*|\s*{.+}) +# One of the following: +# - A variable name; regexp from http://php.net/language.variables.basics +# - A nonempty expression for variable variables: ${'fn'} or $ {'fn'} +# (?:\s|\[.+\]|{.+}|/\*.*\*/|//.*|#.*)* +# Optional whitespace, array access, or comments +# \(.*\) +# Parentheses optionally containing function parameters +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "@rx \$+(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*|\s*{.+})(?:\s|\[.+\]|{.+}|/\*.*\*/|//.*|#.*)*\(.*\)" \ + "id:933180,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'PHP Injection Attack: Variable Function Call Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + +# [ PHP Functions: Variable Function Prevent Bypass ] +# +# Referring to https://www.secjuice.com/php-rce-bypass-filters-sanitization-waf/ +# Regex test on https://regex101.com/r/x1tfXG/1 +# the rule 933180 could be bypassed by using the following payloads: +# +# - (system)('uname'); +# - (sy.(st).em)('uname'); +# - (string)"system"('uname'); +# - define('x', 'sys' . 'tem');(x)/* comment */('uname'); +# - $y = 'sys'.'tem';($y)('uname'); +# - define('z', [['sys' .'tem']]);(z)[0][0]('uname'); +# - (system)(ls); +# - (/**/system)(ls/**/); +# - (['system'])[0]('uname'); +# - (++[++system++][++0++])++{/*dsasd*/0}++(++ls++); +# +# This rule blocks all payloads above and avoids to block values like: +# +# - [ACME] this is a test (just a test) +# - Test (with two) rounded (brackets) +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "@rx (\(.+\)\(.+\)|\(.+\)['\"][a-zA-Z-_0-9]+['\"]\(.+\)|\[\d+\]\(.+\)|\{\d+\}\(.+\)|\$[^(\),.;\x5c/]+\(.+\)|[\"'][a-zA-Z0-9-_\x5c]+[\"']\(.+\)|\([^\)]*string[^\)]*\)[a-zA-Z-_0-9\"'.{}\[\]\s]+\([^\)]*\));" \ + "id:933210,\ + phase:2,\ + block,\ + capture,\ + t:none,t:urlDecode,t:replaceComments,t:removeWhitespace,\ + msg:'PHP Injection Attack: Variable Function Call Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:933013,phase:1,pass,nolog,skipAfter:END-REQUEST-933-APPLICATION-ATTACK-PHP" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:933014,phase:2,pass,nolog,skipAfter:END-REQUEST-933-APPLICATION-ATTACK-PHP" +# +# -= Paranoia Level 2 =- (apply only when tx.executing_paranoia_level is sufficiently high: 2 or higher) +# + +# +# [ PHP Functions: Medium-Risk PHP Function Names ] +# +# In paranoia level 2, we add additional checks for most PHP functions. +# +# The size of the PHP function list is considerable. +# Even after excluding the more obscure PHP extensions, 1300+ functions remain. +# For performance and maintenance reasons, this rule does not use a regexp, +# but uses a phrase file (@pmFromFile), and additionally looks for an '(' character +# in the matched variable. +# +# This approach carries some risk for false positives. Therefore, the function list +# has been curated to remove words closely matching natural language and terms often +# used in other contexts. +# +# This rule is a stricter sibling of rule 933150. +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "@pmFromFile php-function-names-933151.data" \ + "id:933151,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'PHP Injection Attack: Medium-Risk PHP Function Name Found',\ + logdata:'Matched Data: %{TX.933151_TX_0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + tag:'paranoia-level/2',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.933151_tx_0=%{tx.0}',\ + chain" + SecRule MATCHED_VARS "@pm (" \ + "capture,\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}'" + + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:933015,phase:1,pass,nolog,skipAfter:END-REQUEST-933-APPLICATION-ATTACK-PHP" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:933016,phase:2,pass,nolog,skipAfter:END-REQUEST-933-APPLICATION-ATTACK-PHP" +# +# -= Paranoia Level 3 =- (apply only when tx.executing_paranoia_level is sufficiently high: 3 or higher) +# + +# +# [ PHP Variables: Common Variable Indexes ] +# +# In paranoia level 3, we add additional checks for parameters to many PHP variables. +# +# +# One of the more common variables used within attacks on PHP is $_SERVER. Because +# of how many different ways PHP has for executing variables (variable variables, +# etc) often just looking for $_SERVER will be less effective than looking for the +# various indexes within $_SERVER. This rule checks for these indexes. +# This rule is located in PL 3 because often developers will use these names as +# parameter names or values and this will lead to false positives. +# Because this list is not expected to change and it is limited in size we use a +# regex in this case to look for these values whereas in its sibling rule we use +# @pmFromFile for flexibility and performance. +# +# To rebuild the regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 933131 +# +# This rule is a stricter sibling of rule 933130. +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?:HTTP_(?:ACCEPT(?:_(?:ENCODING|LANGUAGE|CHARSET))?|(?:X_FORWARDED_FO|REFERE)R|(?:USER_AGEN|HOS)T|CONNECTION|KEEP_ALIVE)|PATH_(?:TRANSLATED|INFO)|ORIG_PATH_INFO|QUERY_STRING|REQUEST_URI|AUTH_TYPE)" \ + "id:933131,\ + phase:2,\ + block,\ + capture,\ + t:none,t:normalisePath,t:urlDecodeUni,\ + msg:'PHP Injection Attack: Variables Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + tag:'paranoia-level/3',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl3=+%{tx.critical_anomaly_score}'" + + +# +# [ PHP Functions: Low-Value PHP Function Calls ] +# +# In paranoia level 3, we add additional checks for the remaining PHP functions. +# +# Most of these function names are likely to cause false positives in natural text +# or common parameter values, such as 'abs', 'copy', 'date', 'key', 'max', 'min'. +# Therefore, these function names are not scanned in lower paranoia levels. +# +# To mitigate the risk of false positives somewhat, a regexp is used to look for +# PHP function syntax. (See rule 933160 for a description.) +# +# This rule is a stricter sibling of rule 933160. +# +# This rule is also triggered by the following exploit(s): +# [ Apache Struts vulnerability CVE-2018-11776 - Exploit tested: https://www.exploit-db.com/exploits/45262 ] +# [ SAP CRM Java vulnerability CVE-2018-2380 - Exploit tested: https://www.exploit-db.com/exploits/44292 ] +# +# Regexp generated from util/regexp-assemble/data/933161.data using Regexp::Assemble. +# See https://coreruleset.org/20190826/optimizing-regular-expressions/ for usage. +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "@rx (?i)\b(?:i(?:s(?:_(?:in(?:t(?:eger)?|finite)|n(?:u(?:meric|ll)|an)|(?:calla|dou)ble|s(?:calar|tring)|f(?:inite|loat)|re(?:source|al)|l(?:ink|ong)|a(?:rray)?|object|bool)|set)|n(?:(?:clud|vok)e|t(?:div|val))|(?:mplod|dat)e|conv)|s(?:t(?:r(?:(?:le|sp)n|coll)|at)|(?:e(?:rializ|ttyp)|huffl)e|i(?:milar_text|zeof|nh?)|p(?:liti?|rintf)|(?:candi|ubst)r|y(?:mlink|slog)|o(?:undex|rt)|leep|rand|qrt)|f(?:ile(?:(?:siz|typ)e|owner|pro)|l(?:o(?:atval|ck|or)|ush)|(?:rea|mo)d|t(?:ell|ok)|unction|close|gets|stat|eof)|c(?:h(?:o(?:wn|p)|eckdate|root|dir|mod)|o(?:(?:(?:nsta|u)n|mpac)t|sh?|py)|lose(?:dir|log)|(?:urren|ryp)t|eil)|e(?:x(?:(?:trac|i)t|p(?:lode)?)|a(?:ster_da(?:te|ys)|ch)|r(?:ror_log|egi?)|mpty|cho|nd)|l(?:o(?:g(?:1[0p])?|caltime)|i(?:nk(?:info)?|st)|(?:cfirs|sta)t|evenshtein|trim)|d(?:i(?:(?:skfreespac)?e|r(?:name)?)|e(?:fined?|coct)|(?:oubleva)?l|ate)|r(?:e(?:(?:quir|cod|nam)e|adlin[ek]|wind|set)|an(?:ge|d)|ound|sort|trim)|m(?:b(?:split|ereg)|i(?:crotime|n)|a(?:i[ln]|x)|etaphone|y?sql|hash)|u(?:n(?:(?:tain|se)t|iqid|link)|s(?:leep|ort)|cfirst|mask)|a(?:s(?:(?:se|o)rt|inh?)|r(?:sort|ray)|tan[2h]?|cosh?|bs)|t(?:e(?:xtdomain|mpnam)|a(?:int|nh?)|ouch|ime|rim)|h(?:e(?:ader(?:s_(?:lis|sen)t)?|brev)|ypot|ash)|p(?:a(?:thinfo|ck)|r(?:intf?|ev)|close|o[sw]|i)|g(?:et(?:t(?:ext|ype)|date)|mdate)|o(?:penlog|ctdec|rd)|b(?:asename|indec)|n(?:atsor|ex)t|k(?:sort|ey)|quotemeta|wordwrap|virtual|join)(?:\s|/\*.*\*/|//.*|#.*)*\(.*\)" \ + "id:933161,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'PHP Injection Attack: Low-Value PHP Function Call Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + tag:'paranoia-level/3',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl3=+%{tx.critical_anomaly_score}'" + + +# +# [ PHP Script Uploads: Superfluous extension ] +# +# Block file uploads with PHP related extensions (.php, .phps, .phtml, +# .php5 etc) anywhere in the name, followed by a dot. +# +# Example: index.php.tmp +# +# Uploading of such files can lead to remote code execution if +# Apache is configured with AddType and MultiViews, as Apache will +# automatically do a filename match when the extension is unknown. +# This configuration is fortunately not common in modern installs. +# +# Blocking these file names might lead to more false positives. +# +# Some AJAX uploaders use the nonstandard request headers X-Filename, +# X_Filename, or X-File-Name to transmit the file name to the server; +# scan these request headers as well as multipart/form-data file names. +# +# This rule is a stricter sibling of rule 933110. +# +SecRule FILES|REQUEST_HEADERS:X-Filename|REQUEST_HEADERS:X_Filename|REQUEST_HEADERS:X.Filename|REQUEST_HEADERS:X-File-Name "@rx .*\.(?:php\d*|phtml)\..*$" \ + "id:933111,\ + phase:2,\ + block,\ + capture,\ + t:none,t:lowercase,\ + msg:'PHP Injection Attack: PHP Script File Upload Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + tag:'paranoia-level/3',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl3=+%{tx.critical_anomaly_score}'" + + +# [ PHP Closing Tag Found ] +# +# http://www.php.net/manual/en/language.basic-syntax.phptags.php +# +# This check was extracted from 933100 (paranoia level 1), since the +# checked sequence '?>' commonly causes false positives. +# See issue #654 for discussion. +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@pm ?>" \ + "id:933190,\ + phase:2,\ + block,\ + capture,\ + t:none,t:urlDecodeUni,\ + msg:'PHP Injection Attack: PHP Closing Tag Found',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-php',\ + tag:'platform-multi',\ + tag:'attack-injection-php',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + tag:'paranoia-level/3',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl3=+%{tx.critical_anomaly_score}'" + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:933017,phase:1,pass,nolog,skipAfter:END-REQUEST-933-APPLICATION-ATTACK-PHP" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:933018,phase:2,pass,nolog,skipAfter:END-REQUEST-933-APPLICATION-ATTACK-PHP" +# +# -= Paranoia Level 4 =- (apply only when tx.executing_paranoia_level is sufficiently high: 4 or higher) +# + + + +# +# -= Paranoia Levels Finished =- +# +SecMarker "END-REQUEST-933-APPLICATION-ATTACK-PHP" diff --git a/samples/ModSecurity SecRule/REQUEST-934-APPLICATION-ATTACK-GENERIC.conf b/samples/ModSecurity SecRule/REQUEST-934-APPLICATION-ATTACK-GENERIC.conf new file mode 100644 index 0000000000..01c4759d19 --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-934-APPLICATION-ATTACK-GENERIC.conf @@ -0,0 +1,243 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# +# -= Paranoia Level 0 (empty) =- (apply unconditionally) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:934011,phase:1,pass,nolog,skipAfter:END-REQUEST-934-APPLICATION-ATTACK-GENERIC" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:934012,phase:2,pass,nolog,skipAfter:END-REQUEST-934-APPLICATION-ATTACK-GENERIC" +# +# -= Paranoia Level 1 (default) =- (apply only when tx.executing_paranoia_level is sufficiently high: 1 or higher) +# + + +# [ NodeJS Insecure unserialization / generic RCE signatures ] +# +# Libraries performing insecure unserialization: +# - node-serialize: _$$ND_FUNC$$_ (CVE-2017-5941) +# - funcster: __js_function +# +# See: +# https://opsecx.com/index.php/2017/02/08/exploiting-node-js-deserialization-bug-for-remote-code-execution/ +# https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/ +# +# Some generic snippets used: +# - function() { +# - new Function( +# - eval( +# - String.fromCharCode( +# +# Last two are used by nodejsshell.py, +# https://github.com/ajinabraham/Node.Js-Security-Course/blob/master/nodejsshell.py +# +# As base64 is sometimes (but not always) used to encode serialized values, +# use multiMatch and t:base64decode. +# +# Regexp generated from util/regexp-assemble/data/934100.data using Regexp::Assemble. +# See https://coreruleset.org/20190826/optimizing-regular-expressions/ for usage. + +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?:_(?:\$\$ND_FUNC\$\$_|_js_function)|(?:new\s+Function|\beval)\s*\(|String\s*\.\s*fromCharCode|function\s*\(\s*\)\s*{|module\.exports\s*=|this\.constructor)" \ + "id:934100,\ + phase:2,\ + block,\ + capture,\ + t:none,t:urlDecodeUni,t:base64Decode,\ + msg:'Node.js Injection Attack',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-javascript',\ + tag:'platform-multi',\ + tag:'attack-rce',\ + tag:'attack-injection-generic',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + multiMatch,\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + +# -=[ SSRF Attacks ]=- +# +# We provide only partial protection to SSRF. DNS Rebinding attacks needs +# to be handled at application level, and even those might be difficult to catch. +# +# PL1 rules are based on common attacks on cloud providers, based on well-known URLs. +# +# -=[ References ]=- +# https://highon.coffee/blog/ssrf-cheat-sheet/ +# https://cwe.mitre.org/data/definitions/918.html +# https://capec.mitre.org/data/definitions/664.html) +# +# Preventing: https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html + +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "@pmFromFile ssrf.data" \ + "id:934110,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Possible Server Side Request Forgery (SSRF) Attack: Cloud provider metadata URL in Parameter',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-ssrf',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/225/664',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + +# JavaScript prototype pollution injection attempts +# +# Example from https://hackerone.com/reports/869574 critical +# vulnerability in the TypeORM library: +# {"text":"a","title":{"__proto__":{"where":{"name":"sqlinjection","where":null}}}} +# +# Test cases are based on this list of payloads: +# https://github.com/BlackFan/client-side-prototype-pollution/blob/master/README.md +# +# See also: https://cwe.mitre.org/data/definitions/1321.html +# +# Note: only server-based (not DOM-based) attacks are covered here. +# Stricter sibling: 934131 + +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?:__proto__|constructor\s*(?:\.|\[)\s*prototype)" \ + "id:934130,\ + phase:2,\ + block,\ + capture,\ + t:none,t:urlDecodeUni,t:base64Decode,\ + msg:'JavaScript Prototype Pollution',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-javascript',\ + tag:'platform-multi',\ + tag:'attack-rce',\ + tag:'attack-injection-generic',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1/180/77',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + multiMatch,\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:934013,phase:1,pass,nolog,skipAfter:END-REQUEST-934-APPLICATION-ATTACK-GENERIC" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 2" "id:934014,phase:2,pass,nolog,skipAfter:END-REQUEST-934-APPLICATION-ATTACK-GENERIC" +# +# -= Paranoia Level 2 =- (apply only when tx.executing_paranoia_level is sufficiently high: 2 or higher) +# + +# -=[ SSRF Attacks ]=- +# +# PL2 rules adds SSRF capture for common evasion techniques. +# +# We add captures for these evasion techniques: (see source in util/regexp-assemble/data/regexp-934120.data) +# http://425.510.425.510/ Dotted decimal with overflow (already covered by RFI rule 931100) +# http://2852039166/ Dotless decimal - \d{10} +# http://7147006462/ Dotless decimal with overflow - \d{10} +# http://0xA9.0xFE.0xA9.0xFE/ Dotted hexadecimal - (?:0x[a-f0-9]{2}\.){3}0x[a-f0-9]{2} +# http://0xA9FEA9FE/ Dotless hexadecimal - 0x[a-f0-9]{8} +# http://0x41414141A9FEA9FE/ Dotless hexadecimal with overflow - 0x[a-f0-9]{16} +# http://0251.0376.0251.0376/ Dotted octal - Covered by the same below +# http://0251.00376.000251.0000376/ Dotted octal with padding - (?:0{1,4}\d{3}\.){3}0{1,4}\d{3}) +# http://169.254.43518/ - (?:\d{1,3}\.){2}\.\d{5} +# http://169.16689662/ - \d{1,3}\.\d{8} +# http://[::ffff:a9fe:a9fe] IPV6 Compressed - IPv6 regex from https://ihateregex.io/expr/ipv6/, with [0-9] converted to \d and with non-capturing groups (below) +# http://[0:0:0:0:0:ffff:a9fe:a9fe] IPV6 Expanded - (?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}\d){0,1}\d)\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}\d){0,1}\d)|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(2[0-4]|1{0,1}\d){0,1}\d)\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}\d){0,1}\d)) +# http://[0:0:0:0:0:ffff:169.254.169.254] IPV6/IPV4 - ((?:[0-9a-fA-F]{1,4}:){6}(?:(25[0-5]|(?:2[0-4]|1{0,1}\d){0,1}\d)\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}\d){0,1}\d)) +# http://[::] +# http://127.88.23.245:22/+&@google.com:80#+@google.com:80/ (already covered by RFI rule 931100) +# http://127.88.23.245:22/?@google.com:80/ (already covered by RFI rule 931100) +# http://127.88.23.245:22/#@www.google.com:80/ (already covered by RFI rule 931100) +# http://google.com:80\\@127.88.23.245:22/ (already covered by RFI rule 931100) +# http://google.com:80+&@127.88.23.245:22/#+@google.com:80/ +# http://google.com:80+&@google.com:80#+@127.88.23.245:22/ +# +# To rebuild the word list regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py data/934120.data + +SecRule ARGS "@rx (?i)(?:acap|bitcoin|blob|cap|cvs|svn|svn\+ssh|turn|udp|vnc|xmpp|webcal|xri|dav|dns|feed|finger|h323|icap|ipp|ipps|jabber|mms|mumble|maven|nfs|rmi|rsync|rtmp|rtm|rtmfp|http|https|ftp|ftps|git|gopher|irc|fd|ldap|ldapi|ldaps|expect|netdoc|jar|ogg|tftp|dict|ssh2|ssh|imap|pop2|pop3|mailto|nntp|news|snews|snmp|smtp|sftp|telnet|s3|phar|sip|sips|smb)://(?:\[(?:((?:[0-9a-f]{1,4}:){7,7}[a-f0-9]{1,4}|(?:[a-f0-9]{1,4}:){1,7}:|(?:[a-f0-9]{1,4}:){1,6}:[a-f0-9]{1,4}|(?:[a-f0-9]{1,4}:){1,5}(?::[a-f0-9]{1,4}){1,2}|(?:[a-f0-9]{1,4}:){1,4}(?::[a-f0-9]{1,4}){1,3}|(?:[a-f0-9]{1,4}:){1,3}(?::[a-f0-9]{1,4}){1,4}|(?:[a-f0-9]{1,4}:){1,2}(?::[a-f0-9]{1,4}){1,5}|[a-f0-9]{1,4}:(?:(?::[a-f0-9]{1,4}){1,6})|:(?:(?::[a-f0-9]{1,4}){1,7}|:)|fe80:(?::[a-f0-9]{0,4}){0,4}%[a-f0-9]{1,}|::(?:ffff(:0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[a-f0-9]{1,4}:){1,4}:(?:(25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))|(?:(?:[0-9a-f]{1,4}:){6}(?:(25[0-5]|(?:2[0-4]|1{0,1}\d){0,1}\d)\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}\d){0,1}\d))\])|\xe2(?:\x93(?:\x81\xe2\x93\x84\xe2\x92\xb8\xe2\x92\xb6\xe2\x93\x81\xe2\x92\xbd\xe2\x93\x84\xe2\x93\x88\xe2\x93\x89|\x9b\xe2\x93\x9e\xe2\x93\x92\xe2\x93\x90\xe2\x93\x9b\xe2\x93\x97\xe2\x93\x9e\xe2\x93\xa2\xe2\x93\xa3)|\x91\xa0\xe2\x91(?:\xa1\xe2\x91\xa6|\xa5\xe2\x91\xa8)\.)|[a-zA-Z][\w\-\.]{1,255}:\d{1,5}\+&@(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|[a-zA-Z][\w\-\.]{1,255}):\d{1,5}\/?#\+@(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|[a-zA-Z][\w\-\.]{1,255}):\d{1,5}\/?|(?:0x[a-f0-9]{2}\.){3}0x[a-f0-9]{2}|(?:0{1,4}\d{1,3}\.){3}0{1,4}\d{1,3}|\d{1,3}\.(?:\d{1,3}\.\d{5}|\d{8})|0x(?:[a-f0-9]{16}|[a-f0-9]{8})|\d{10})" \ + "id:934120,\ + phase:2,\ + block,\ + capture,\ + t:none,\ + msg:'Possible Server Side Request Forgery (SSRF) Attack: URL Parameter using IP Address',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-ssrf',\ + tag:'paranoia-level/2',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/225/664',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}'" + +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx \[\s*constructor\s*\]" \ + "id:934131,\ + phase:2,\ + block,\ + capture,\ + t:none,t:urlDecodeUni,t:base64Decode,\ + msg:'JavaScript Prototype Pollution',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-javascript',\ + tag:'platform-multi',\ + tag:'attack-rce',\ + tag:'attack-injection-generic',\ + tag:'paranoia-level/2',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + multiMatch,\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}'" + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:934015,phase:1,pass,nolog,skipAfter:END-REQUEST-934-APPLICATION-ATTACK-GENERIC" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 3" "id:934016,phase:2,pass,nolog,skipAfter:END-REQUEST-934-APPLICATION-ATTACK-GENERIC" +# +# -= Paranoia Level 3 =- (apply only when tx.executing_paranoia_level is sufficiently high: 3 or higher) +# + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:934017,phase:1,pass,nolog,skipAfter:END-REQUEST-934-APPLICATION-ATTACK-GENERIC" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 4" "id:934018,phase:2,pass,nolog,skipAfter:END-REQUEST-934-APPLICATION-ATTACK-GENERIC" +# +# -= Paranoia Level 4 =- (apply only when tx.executing_paranoia_level is sufficiently high: 4 or higher) +# + + + +# +# -= Paranoia Levels Finished =- +# +SecMarker "END-REQUEST-934-APPLICATION-ATTACK-GENERIC" diff --git a/samples/ModSecurity SecRule/REQUEST-941-APPLICATION-ATTACK-XSS.conf b/samples/ModSecurity SecRule/REQUEST-941-APPLICATION-ATTACK-XSS.conf new file mode 100644 index 0000000000..c707e0735a --- /dev/null +++ b/samples/ModSecurity SecRule/REQUEST-941-APPLICATION-ATTACK-XSS.conf @@ -0,0 +1,963 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.3.4.0-dev +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021 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. +# ------------------------------------------------------------------------ + +# +# -= Paranoia Level 0 (empty) =- (apply unconditionally) +# + + + +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:941011,phase:1,pass,nolog,skipAfter:END-REQUEST-941-APPLICATION-ATTACK-XSS" +SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:941012,phase:2,pass,nolog,skipAfter:END-REQUEST-941-APPLICATION-ATTACK-XSS" +# +# -= Paranoia Level 1 (default) =- (apply only when tx.executing_paranoia_level is sufficiently high: 1 or higher) +# + + +# +# -=[ Libinjection - XSS Detection ]=- +# +# Ref: https://github.com/client9/libinjection +# Ref: https://speakerdeck.com/ngalbreath/libinjection-from-sqli-to-xss +# +# -=[ Targets ]=- +# +# 941100: PL1 : REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/| +# REQUEST_COOKIES_NAMES|REQUEST_HEADERS:User-Agent| +# ARGS_NAMES|ARGS|XML:/* +# +# 941101: PL2 : REQUEST_FILENAME|REQUEST_HEADERS:Referer +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_HEADERS:User-Agent|ARGS_NAMES|ARGS|XML:/* "@detectXSS" \ + "id:941100,\ + phase:2,\ + block,\ + t:none,t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls,\ + msg:'XSS Attack Detected via libinjection',\ + logdata:'Matched Data: XSS data found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-xss',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.xss_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# -=[ XSS Filters - Category 1 ]=- +# http://xssplayground.net23.net/xssfilter.html +# script tag based XSS vectors, e.g., +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_FILENAME|REQUEST_HEADERS:User-Agent|REQUEST_HEADERS:Referer|ARGS_NAMES|ARGS|XML:/* "@rx (?i)]*>[\s\S]*?" \ + "id:941110,\ + phase:2,\ + block,\ + capture,\ + t:none,t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls,\ + msg:'XSS Filter - Category 1: Script Tag Vector',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-xss',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.xss_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# -=[ XSS Filters - Category 3 ]=- +# +# Regexp generated from util/regexp-assemble/data/941130.data using Regexp::Assemble. +# To rebuild the regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 941130 +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_HEADERS:User-Agent|ARGS_NAMES|ARGS|XML:/* "@rx (?i)[\s\S](?:\b(?:x(?:link:href|html|mlns)|data:text\/html|pattern\b.*?=|formaction)|!ENTITY\s+(?:\S+|%\s+\S+)\s+(?:PUBLIC|SYSTEM)|;base64|@import)\b" \ + "id:941130,\ + phase:2,\ + block,\ + capture,\ + t:none,t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls,\ + msg:'XSS Filter - Category 3: Attribute Vector',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-xss',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.xss_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# -=[ XSS Filters - Category 4 ]=- +# XSS vectors making use of javascript uri and tags, e.g.,

+# https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#css-expressions-ie7 +# https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#behaviors-for-older-modes-of-ie +# examples: https://regex101.com/r/FFEpsh/1 +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_HEADERS:User-Agent|REQUEST_HEADERS:Referer|ARGS_NAMES|ARGS|XML:/* "@rx (?i)[a-z]+=(?:[^:=]+:.+;)*?[^:=]+:url\(javascript" \ + "id:941140,\ + phase:2,\ + block,\ + capture,\ + t:none,t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls,t:removeWhitespace,\ + msg:'XSS Filter - Category 4: Javascript URI Vector',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-xss',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.xss_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# -=[ NoScript XSS Filters ]=- +# Ref: http://noscript.net/ +# +# [NoScript InjectionChecker] HTML injection +# +# Regexp generated from util/regexp-assemble/data/941160.data using Regexp::Assemble. +# To rebuild the regexp: +# cd util/regexp-assemble +# ./regexp-assemble.py 941160 +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_HEADERS:User-Agent|REQUEST_HEADERS:Referer|ARGS_NAMES|ARGS|XML:/* "@rx (?i)(?:(?:<\w[\s\S]*[\s/]|['\"](?:[\s\S]*[\s/])?)(?:on(?:d(?:e(?:vice(?:(?:orienta|mo)tion|proximity|found|light)|livery(?:success|error)|activate)|r(?:ag(?:e(?:n(?:ter|d)|xit)|(?:gestur|leav)e|start|drop|over)|op)|i(?:s(?:c(?:hargingtimechange|onnect(?:ing|ed))|abled)|aling)|ata(?:setc(?:omplete|hanged)|(?:availabl|chang)e|error)|urationchange|ownloading|blclick)|Moz(?:M(?:agnifyGesture(?:Update|Start)?|ouse(?:PixelScroll|Hittest))|S(?:wipeGesture(?:Update|Start|End)?|crolledAreaChanged)|(?:(?:Press)?TapGestur|BeforeResiz)e|EdgeUI(?:C(?:omplet|ancel)|Start)ed|RotateGesture(?:Update|Start)?|A(?:udioAvailable|fterPaint))|c(?:o(?:m(?:p(?:osition(?:update|start|end)|lete)|mand(?:update)?)|n(?:t(?:rolselect|extmenu)|nect(?:ing|ed))|py)|a(?:(?:llschang|ch)ed|nplay(?:through)?|rdstatechange)|h(?:(?:arging(?:time)?ch)?ange|ecking)|(?:fstate|ell)change|u(?:echange|t)|l(?:ick|ose))|s(?:t(?:a(?:t(?:uschanged|echange)|lled|rt)|k(?:sessione|comma)nd|op)|e(?:ek(?:complete|ing|ed)|(?:lec(?:tstar)?)?t|n(?:ding|t))|(?:peech|ound)(?:start|end)|u(?:ccess|spend|bmit)|croll|how)|m(?:o(?:z(?:(?:pointerlock|fullscreen)(?:change|error)|(?:orientation|time)change|network(?:down|up)load)|use(?:(?:lea|mo)ve|o(?:ver|ut)|enter|wheel|down|up)|ve(?:start|end)?)|essage|ark)|a(?:n(?:imation(?:iteration|start|end)|tennastatechange)|fter(?:(?:scriptexecu|upda)te|print)|udio(?:process|start|end)|d(?:apteradded|dtrack)|ctivate|lerting|bort)|b(?:e(?:fore(?:(?:(?:de)?activa|scriptexecu)te|u(?:nload|pdate)|p(?:aste|rint)|c(?:opy|ut)|editfocus)|gin(?:Event)?)|oun(?:dary|ce)|l(?:ocked|ur)|roadcast|usy)|DOM(?:Node(?:Inserted(?:IntoDocument)?|Removed(?:FromDocument)?)|(?:CharacterData|Subtree)Modified|A(?:ttrModified|ctivate)|Focus(?:Out|In)|MouseScroll)|r(?:e(?:s(?:u(?:m(?:ing|e)|lt)|ize|et)|adystatechange|pea(?:tEven)?t|movetrack|trieving|ceived)|ow(?:s(?:inserted|delete)|e(?:nter|xit))|atechange)|p(?:op(?:up(?:hid(?:den|ing)|show(?:ing|n))|state)|a(?:ge(?:hide|show)|(?:st|us)e|int)|ro(?:pertychange|gress)|lay(?:ing)?)|t(?:ouch(?:(?:lea|mo)ve|en(?:ter|d)|cancel|start)|ransition(?:cancel|end|run)|ime(?:update|out)|ext)|u(?:s(?:erproximity|sdreceived)|p(?:gradeneeded|dateready)|n(?:derflow|load))|f(?:o(?:rm(?:change|input)|cus(?:out|in)?)|i(?:lterchange|nish)|ailed)|l(?:o(?:ad(?:e(?:d(?:meta)?data|nd)|start)|secapture)|evelchange|y)|g(?:amepad(?:(?:dis)?connected|button(?:down|up)|axismove)|et)|e(?:n(?:d(?:Event|ed)?|abled|ter)|rror(?:update)?|mptied|xit)|i(?:cc(?:cardlockerror|infochange)|n(?:coming|valid|put))|o(?:(?:(?:ff|n)lin|bsolet)e|verflow(?:changed)?|pen)|SVG(?:(?:Unl|L)oad|Resize|Scroll|Abort|Error|Zoom)|h(?:e(?:adphoneschange|l[dp])|ashchange|olding)|v(?:o(?:lum|ic)e|ersion)change|w(?:a(?:it|rn)ing|heel)|key(?:press|down|up)|(?:AppComman|Loa)d|no(?:update|match)|Request|zoom)|s(?:tyle|rc)|background|formaction|lowsrc|ping)[\s\x08]*?=|<[^\w<>]*(?:[^<>\"'\s]*:)?[^\w<>]*\W*?(?:(?:a\W*?(?:n\W*?i\W*?m\W*?a\W*?t\W*?e|p\W*?p\W*?l\W*?e\W*?t|u\W*?d\W*?i\W*?o)|b\W*?(?:i\W*?n\W*?d\W*?i\W*?n\W*?g\W*?s|a\W*?s\W*?e|o\W*?d\W*?y)|i?\W*?f\W*?r\W*?a\W*?m\W*?e|o\W*?b\W*?j\W*?e\W*?c\W*?t|i\W*?m\W*?a?\W*?g\W*?e?|e\W*?m\W*?b\W*?e\W*?d|p\W*?a\W*?r\W*?a\W*?m|v\W*?i\W*?d\W*?e\W*?o|l\W*?i\W*?n\W*?k)[^>\w]|s\W*?(?:c\W*?r\W*?i\W*?p\W*?t|t\W*?y\W*?l\W*?e|e\W*?t[^>\w]|v\W*?g)|m\W*?(?:a\W*?r\W*?q\W*?u\W*?e\W*?e|e\W*?t\W*?a[^>\w])|f\W*?o\W*?r\W*?m))" \ + "id:941160,\ + phase:2,\ + block,\ + capture,\ + t:none,t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls,\ + msg:'NoScript XSS InjectionChecker: HTML Injection',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-xss',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.xss_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# [NoScript InjectionChecker] Attributes injection +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_HEADERS:User-Agent|REQUEST_HEADERS:Referer|ARGS_NAMES|ARGS|XML:/* "@rx (?i)(?:\W|^)(?:javascript:(?:[\s\S]+[=\x5c\(\[\.<]|[\s\S]*?(?:\bname\b|\x5c[ux]\d))|data:(?:(?:[a-z]\w+/\w[\w+-]+\w)?[;,]|[\s\S]*?;[\s\S]*?\b(?:base64|charset=)|[\s\S]*?,[\s\S]*?<[\s\S]*?\w[\s\S]*?>))|@\W*?i\W*?m\W*?p\W*?o\W*?r\W*?t\W*?(?:/\*[\s\S]*?)?(?:[\"']|\W*?u\W*?r\W*?l[\s\S]*?\()|[^-]*?-\W*?m\W*?o\W*?z\W*?-\W*?b\W*?i\W*?n\W*?d\W*?i\W*?n\W*?g[^:]*?:\W*?u\W*?r\W*?l[\s\S]*?\(" \ + "id:941170,\ + phase:2,\ + block,\ + capture,\ + t:none,t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls,\ + msg:'NoScript XSS InjectionChecker: Attribute Injection',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-xss',\ + tag:'paranoia-level/1',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.xss_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'" + + +# +# [Deny List Keywords from Node-Validator] +# https://raw.github.com/chriso/node-validator/master/validator.js +# This rule has a stricter sibling 941181 (PL2) that covers the additional payload "-->" +# +SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@pm document.cookie document.write .parentnode .innerhtml window.location -moz-binding " \ + "id:941181,\ + phase:2,\ + block,\ + capture,\ + t:none,t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:lowercase,t:removeNulls,\ + msg:'Node-Validator Deny List Keywords',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-multi',\ + tag:'platform-multi',\ + tag:'attack-xss',\ + tag:'paranoia-level/2',\ + tag:'OWASP_CRS',\ + tag:'capec/1000/152/242',\ + ctl:auditLogParts=+E,\ + ver:'OWASP_CRS/3.4.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.xss_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.anomaly_score_pl2=+%{tx.critical_anomaly_score}'" + + + +# +# -=[ XSS Filters from IE ]=- + +# Detect tags that are the most common direct HTML injection points. +# +# +# +# +#