Skip to content
This repository has been archived by the owner on May 14, 2020. It is now read-only.

Commit

Permalink
Improve request Content-Type checks (#1103)
Browse files Browse the repository at this point in the history
* 920420: always check request Content-Type, no matter the HTTP method

* 920420: fix incorrect tag

* introduce Content-Type whitelist regexp

* Content-Type: escape + char in regexp for clarity

* Content-Type: renumber

* 920470: add tests

* 920420: add tests
  • Loading branch information
lifeforms authored and csanders-git committed Jul 31, 2018
1 parent f9ffe1c commit cd407cb
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 42 deletions.
58 changes: 47 additions & 11 deletions rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf
Expand Up @@ -983,7 +983,44 @@ SecRule &TX:COMBINED_FILE_SIZES "@eq 1" \
#
# Restrict which content-types we accept.
#
SecRule REQUEST_METHOD "!@rx ^(?:GET|HEAD|PROPFIND|OPTIONS)$" \

# Restrict Content-Type header to established patterns.
#
# This provides generic whitelist 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
#
SecRule REQUEST_HEADERS:Content-Type "!@rx ^[\w\d/\.\-\+]+(?:\s?;\s?(?:boundary|charset)\s?=\s?['\"\w\d_\-]+)?$" \
"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:'OWASP_CRS/PROTOCOL_VIOLATION/CONTENT_TYPE',\
tag:'WASCTC/WASC-20',\
tag:'OWASP_TOP_10/A1',\
tag:'OWASP_AppSensor/EE2',\
tag:'PCI/12.1',\
rev:1,\
ver:'OWASP_CRS/3.0.0',\
severity:'CRITICAL',\
setvar:'tx.msg=%{rule.msg}',\
setvar:'tx.anomaly_score=+%{tx.critical_anomaly_score}',\
setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/CONTENT_TYPE-%{matched_var_name}=%{matched_var}'"

# 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:2,\
block,\
Expand All @@ -994,24 +1031,23 @@ SecRule REQUEST_METHOD "!@rx ^(?:GET|HEAD|PROPFIND|OPTIONS)$" \
tag:'language-multi',\
tag:'platform-multi',\
tag:'attack-protocol',\
tag:'OWASP_CRS/POLICY/ENCODING_NOT_ALLOWED',\
tag:'OWASP_CRS/POLICY/CONTENT_TYPE_NOT_ALLOWED',\
tag:'WASCTC/WASC-20',\
tag:'OWASP_TOP_10/A1',\
tag:'OWASP_AppSensor/EE2',\
tag:'PCI/12.1',\
rev:2,\
ver:'OWASP_CRS/3.0.0',\
severity:'CRITICAL',\
capture,\
chain"
SecRule REQUEST_HEADERS:Content-Type "@rx ^[^;\s]+" \
"capture,\
chain"
SecRule TX:0 "!@rx ^%{tx.allowed_request_content_type}$" \
"t:none,\
ctl:forceRequestBodyVariable=On,\
setvar:'tx.msg=%{rule.msg}',\
setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',\
setvar:'tx.%{rule.id}-OWASP_CRS/POLICY/CONTENT_TYPE_NOT_ALLOWED-%{matched_var_name}=%{matched_var}'"
SecRule TX:0 "!@rx ^%{tx.allowed_request_content_type}$" \
"t:none,\
ctl:forceRequestBodyVariable=On,\
setvar:'tx.msg=%{rule.msg}',\
setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',\
setvar:'tx.%{rule.id}-OWASP_CRS/POLICY/CONTENT_TYPE_NOT_ALLOWED-%{matched_var_name}=%{matched_var}'"


#
# Restrict protocol versions.
Expand Down
@@ -1,15 +1,15 @@
---
meta:
meta:
author: "csanders-git"
enabled: true
name: "920420.yaml"
description: "Description"
tests:
-
tests:
-
test_title: 920420-1
stages:
-
stage:
stages:
-
stage:
input:
dest_addr: "127.0.0.1"
port: 80
Expand All @@ -19,13 +19,13 @@
Host: "localhost"
Content-Type: "application/x-www-form-urlencoded"
data: "test=value"
output:
output:
no_log_contains: "id \"920420\""
-
-
test_title: 920420-2
stages:
-
stage:
stages:
-
stage:
input:
dest_addr: "127.0.0.1"
port: 80
Expand All @@ -35,13 +35,13 @@
Host: "localhost"
Content-Type: "my-new-content-type"
data: "test"
output:
output:
log_contains: "id \"920420\""
-
-
test_title: 920420-3
stages:
-
stage:
stages:
-
stage:
input:
dest_addr: "127.0.0.1"
port: 80
Expand All @@ -51,13 +51,13 @@
Host: "localhost"
Content-Type: "my-new-content-type"
data: "test"
output:
no_log_contains: "id \"920420\""
-
output:
log_contains: "id \"920420\""
-
test_title: 920420-4
stages:
-
stage:
stages:
-
stage:
input:
dest_addr: "127.0.0.1"
port: 80
Expand All @@ -67,13 +67,13 @@
Host: "localhost"
Content-Type: "my-new-content-type"
data: "test"
output:
no_log_contains: "id \"920420\""
-
output:
log_contains: "id \"920420\""
-
test_title: 920420-5
desc: Request content type is not allowed by policy (920420) from old modsec regressions
stages:
-
-
stage:
input:
dest_addr: 127.0.0.1
Expand Down Expand Up @@ -108,11 +108,11 @@
- --0000--
output:
log_contains: id "920420"
-
-
test_title: 920420-6
desc: Request content type is not allowed by policy (920420) from old modsec regressions
stages:
-
-
stage:
input:
dest_addr: 127.0.0.1
Expand Down Expand Up @@ -147,11 +147,11 @@
- --0000--
output:
log_contains: id "920420"
-
-
test_title: 920420-7
desc: Request content type is not allowed by policy (920420) from old modsec regressions
stages:
-
-
stage:
input:
dest_addr: 127.0.0.1
Expand Down Expand Up @@ -186,5 +186,36 @@
- --0000--
output:
log_contains: id "920420"

-
test_title: 920420-8
stages:
-
stage:
input:
dest_addr: "127.0.0.1"
port: 80
method: "HEAD"
headers:
User-Agent: "ModSecurity CRS 3 Tests"
Host: "localhost"
Content-Type: "my-new-content-type"
data: "test"
output:
log_contains: "id \"920420\""
-
test_title: 920420-9
stages:
-
stage:
input:
dest_addr: "127.0.0.1"
port: 80
method: "OPTIONS"
headers:
User-Agent: "ModSecurity CRS 3 Tests"
Host: "localhost"
Content-Type: "application/json"
data: "test"
output:
no_log_contains: "id \"920420\""

@@ -0,0 +1,115 @@
---
meta:
author: "lifeforms"
enabled: true
name: "920470.yaml"
description: "Content-Type header format checks"
tests:
- test_title: 920470-1
stages:
- stage:
input:
dest_addr: 127.0.0.1
port: 80
headers:
User-Agent: "ModSecurity CRS 3 Tests"
Host: "localhost"
Content-Type: "%{(#nike='multipart/form-data').(#dm=@ognl"
Content-Length: 0
output:
log_contains: "id \"920470\""
- test_title: 920470-2
stages:
- stage:
input:
dest_addr: 127.0.0.1
port: 80
headers:
User-Agent: "ModSecurity CRS 3 Tests"
Host: "localhost"
Content-Type: 'text/plain; charset="UTF-8"; garbage'
Content-Length: 0
output:
log_contains: "id \"920470\""
- test_title: 920470-3
stages:
- stage:
input:
dest_addr: 127.0.0.1
port: 80
headers:
User-Agent: "ModSecurity CRS 3 Tests"
Host: "localhost"
Content-Type: 'text/plain; charset=/gar/bage'
Content-Length: 0
output:
log_contains: "id \"920470\""
- test_title: 920470-4
stages:
- stage:
input:
dest_addr: 127.0.0.1
port: 80
method: POST
headers:
User-Agent: "ModSecurity CRS 3 Tests"
Host: "localhost"
Content-Type: "text/plain"
Content-Length: 0
output:
no_log_contains: "id \"920470\""
- test_title: 920470-5
stages:
- stage:
input:
dest_addr: 127.0.0.1
port: 80
method: POST
headers:
User-Agent: "ModSecurity CRS 3 Tests"
Host: "localhost"
Content-Type: 'text/plain; charset=UTF-8'
output:
no_log_contains: "id \"920470\""
- test_title: 920470-6
stages:
- stage:
input:
dest_addr: 127.0.0.1
port: 80
method: POST
headers:
User-Agent: "ModSecurity CRS 3 Tests"
Host: "localhost"
Content-Type: 'text/plain; charset="UTF-8"'
Content-Length: 0
output:
no_log_contains: "id \"920470\""
- test_title: 920470-7
stages:
- stage:
input:
dest_addr: 127.0.0.1
port: 80
method: POST
headers:
User-Agent: "ModSecurity CRS 3 Tests"
Host: "localhost"
Content-Type: 'multipart/form-data; boundary=----WebKitFormBoundary12345'
Content-Length: 0
output:
no_log_contains: "id \"920470\""
- test_title: 920470-8
stages:
- stage:
input:
dest_addr: 127.0.0.1
port: 80
method: POST
headers:
User-Agent: "ModSecurity CRS 3 Tests"
Host: "localhost"
Content-Type: 'application/json'
Content-Length: 0
output:
no_log_contains: "id \"920470\""

0 comments on commit cd407cb

Please sign in to comment.