Closed Dr-Lazarus-V2 closed 11 hours ago
I have conducted a further investigation and can note that even a request which triggerers a CRS rule has the same issue.
curl -X POST http://weborion.licensing.portal/check \ with lazarus@weborion-test-server at 05:41:12 AM
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin' OR '1'='1;--&password=<script>alert('XSS')</script>&cmd=;cat /etc/passwd;"
{
"transaction": {
"client_ip": "10.103.253.191",
"time_stamp": "Thu Nov 14 05:41:12 2024",
"server_id": "327c12d21520fbed95c8764ad076e59b7ea5d783",
"client_port": 48752,
"host_ip": "192.168.32.3",
"host_port": 8080,
"unique_id": "6e46a0dff39cbcc3e1f3a1d396afaa22",
"request": {
"method": "GET",
"http_version": 1.1,
"uri": "/check",
"headers": {
"Host": "weborion.licensing.portal",
"User-Agent": "curl/7.81.0",
"Accept": "*/*",
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": "89"
}
},
"response": {
"http_code": 403,
"headers": {
"Server": "nginx/1.27.2",
"Date": "Thu, 14 Nov 2024 05:41:11 GMT",
"Content-Type": "text/html",
"Connection": "keep-alive",
"ETag": "\"66dfdff2-93d6\""
}
},
"producer": {
"modsecurity": "ModSecurity v3.0.13 (Linux)",
"connector": "ModSecurity-nginx v1.0.3",
"secrules_engine": "Enabled",
"components": [
"OWASP_CRS/4.8.0\""
]
},
"messages": [
{
"message": "GET or HEAD Request with Body Content",
"details": {
"match": "Matched \"Operator `Rx' with parameter `^0?$' against variable `REQUEST_HEADERS:Content-Length' (Value: `89' )",
"reference": "o0,3v0,3v152,2",
"ruleId": "920170",
"file": "/etc/modsecurity.d/owasp-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf",
"lineNumber": "171",
"data": "89",
"severity": "2",
"ver": "OWASP_CRS/4.8.0",
"rev": "",
"tags": [
"application-multi",
"language-multi",
"platform-multi",
"attack-protocol",
"paranoia-level/1",
"OWASP_CRS",
"capec/1000/210/272"
],
"maturity": "0",
"accuracy": "0"
}
},
{
"message": "OS File Access Attempt",
"details": {
"match": "Matched \"Operator `PmFromFile' with parameter `lfi-os-files.data' against variable `ARGS:cmd' (Value: `;cat /etc/passwd;' )",
"reference": "o6,10v228,17t:utf8toUnicode,t:urlDecodeUni,t:normalizePathWin",
"ruleId": "930120",
"file": "/etc/modsecurity.d/owasp-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf",
"lineNumber": "97",
"data": "Matched Data: etc/passwd found within ARGS:cmd: ;cat /etc/passwd;",
"severity": "2",
"ver": "OWASP_CRS/4.8.0",
"rev": "",
"tags": [
"application-multi",
"language-multi",
"platform-multi",
"attack-lfi",
"paranoia-level/1",
"OWASP_CRS",
"capec/1000/255/153/126",
"PCI/6.5.4"
],
"maturity": "0",
"accuracy": "0"
}
},
{
"message": "Remote Command Execution: Unix Shell Code Found",
"details": {
"match": "Matched \"Operator `PmFromFile' with parameter `unix-shell.data' against variable `ARGS:cmd' (Value: `;cat /etc/passwd;' )",
"reference": "o5,10v228,17t:cmdLine,t:normalizePath",
"ruleId": "932160",
"file": "/etc/modsecurity.d/owasp-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf",
"lineNumber": "596",
"data": "Matched Data: etc/passwd found within ARGS:cmd: cat/etc/passwd ",
"severity": "2",
"ver": "OWASP_CRS/4.8.0",
"rev": "",
"tags": [
"application-multi",
"language-shell",
"platform-unix",
"attack-rce",
"paranoia-level/1",
"OWASP_CRS",
"capec/1000/152/248/88",
"PCI/6.5.2"
],
"maturity": "0",
"accuracy": "0"
}
},
{
"message": "XSS Attack Detected via libinjection",
"details": {
"match": "detected XSS using libinjection.",
"reference": "v194,29t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls",
"ruleId": "941100",
"file": "/etc/modsecurity.d/owasp-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf",
"lineNumber": "82",
"data": "Matched Data: XSS data found within ARGS:password: <script>alert('XSS')</script>",
"severity": "2",
"ver": "OWASP_CRS/4.8.0",
"rev": "",
"tags": [
"application-multi",
"language-multi",
"platform-multi",
"attack-xss",
"xss-perf-disable",
"paranoia-level/1",
"OWASP_CRS",
"capec/1000/152/242"
],
"maturity": "0",
"accuracy": "0"
}
},
{
"message": "XSS Filter - Category 1: Script Tag Vector",
"details": {
"match": "Matched \"Operator `Rx' with parameter `(?i)<script[^>]*>[\\s\\S]*?' against variable `ARGS:password' (Value: `<script>alert('XSS')</script>' )",
"reference": "o0,8v194,29t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls",
"ruleId": "941110",
"file": "/etc/modsecurity.d/owasp-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf",
"lineNumber": "108",
"data": "Matched Data: <script> found within ARGS:password: <script>alert('XSS')</script>",
"severity": "2",
"ver": "OWASP_CRS/4.8.0",
"rev": "",
"tags": [
"application-multi",
"language-multi",
"platform-multi",
"attack-xss",
"xss-perf-disable",
"paranoia-level/1",
"OWASP_CRS",
"capec/1000/152/242"
],
"maturity": "0",
"accuracy": "0"
}
},
{
"message": "NoScript XSS InjectionChecker: HTML Injection",
"details": {
"match": "Matched \"Operator `Rx' with parameter `(?i)<[^0-9<>A-Z_a-z]*(?:[^\\s\\x0b\\\"'<>]*:)?[^0-9<>A-Z_a-z]*[^0-9A-Z_a-z]*?(?:s[^0-9A-Z_a-z]*?(?:c[^0-9A-Z_a-z]*?r[^0-9A-Z_a-z]*?i[^0-9A-Z_a-z]*?p[^0-9A-Z_a-z]*?t|t[^0-9A-Z_a-z]*?y[^0-9A-Z_a-z]*?l[^0-9A (4378 characters omitted)' against variable `ARGS:password' (Value: `<script>alert('XSS')</script>' )",
"reference": "o0,7v194,29t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls",
"ruleId": "941160",
"file": "/etc/modsecurity.d/owasp-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf",
"lineNumber": "200",
"data": "Matched Data: <script found within ARGS:password: <script>alert('XSS')</script>",
"severity": "2",
"ver": "OWASP_CRS/4.8.0",
"rev": "",
"tags": [
"application-multi",
"language-multi",
"platform-multi",
"attack-xss",
"xss-perf-disable",
"paranoia-level/1",
"OWASP_CRS",
"capec/1000/152/242"
],
"maturity": "0",
"accuracy": "0"
}
},
{
"message": "Javascript method detected",
"details": {
"match": "Matched \"Operator `Rx' with parameter `(?i)\\b(?:eval|set(?:timeout|interval)|new[\\s\\x0b]+Function|a(?:lert|tob)|btoa|prompt|confirm)[\\s\\x0b]*\\(' against variable `ARGS:password' (Value: `<script>alert('XSS')</script>' )",
"reference": "o8,6v194,29t:htmlEntityDecode,t:jsDecode",
"ruleId": "941390",
"file": "/etc/modsecurity.d/owasp-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf",
"lineNumber": "713",
"data": "Matched Data: alert( found within ARGS:password: <script>alert('XSS')</script>",
"severity": "2",
"ver": "OWASP_CRS/4.8.0",
"rev": "",
"tags": [
"application-multi",
"language-multi",
"attack-xss",
"xss-perf-disable",
"paranoia-level/1",
"OWASP_CRS",
"capec/1000/152/242"
],
"maturity": "0",
"accuracy": "0"
}
},
{
"message": "SQL Injection Attack Detected via libinjection",
"details": {
"match": "detected SQLi using libinjection.",
"reference": "v165,19",
"ruleId": "942100",
"file": "/etc/modsecurity.d/owasp-crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf",
"lineNumber": "46",
"data": "Matched Data: s&sos found within ARGS:username: admin' OR '1'='1;--",
"severity": "2",
"ver": "OWASP_CRS/4.8.0",
"rev": "",
"tags": [
"application-multi",
"language-multi",
"platform-multi",
"attack-sqli",
"paranoia-level/1",
"OWASP_CRS",
"capec/1000/152/248/66",
"PCI/6.5.2"
],
"maturity": "0",
"accuracy": "0"
}
},
{
"message": "Inbound Anomaly Score Exceeded (Total Score: 40)",
"details": {
"match": "Matched \"Operator `Ge' with parameter `20' against variable `TX:BLOCKING_INBOUND_ANOMALY_SCORE' (Value: `40' )",
"reference": "",
"ruleId": "949110",
"file": "/etc/modsecurity.d/owasp-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf",
"lineNumber": "222",
"data": "",
"severity": "0",
"ver": "OWASP_CRS/4.8.0",
"rev": "",
"tags": [
"anomaly-evaluation",
"OWASP_CRS"
],
"maturity": "0",
"accuracy": "0"
}
},
{
"message": "Inbound Score: 40 Outbound Score: 0",
"details": {
"match": "",
"reference": "",
"ruleId": "980145",
"file": "/var/opt/modsecurity.d/owasp-crs/weborion.licensing.portal.crs_setup.conf",
"lineNumber": "40",
"data": "",
"severity": "0",
"ver": "",
"rev": "",
"tags": [],
"maturity": "0",
"accuracy": "0"
}
}
]
}
}
In the audit logs this is logged as a GET and not a POST.
I believe this issue relates to the REQUEST_METHOD being reset on redirection: Issue
My Nginx configuration involves a redirect:
error_page 403 /error.html;
location = /error.html {
ssi on;
internal;
auth_basic off;
root /etc/nginx/template;
}
There is a workaround metioned by @hator
I think I found one more way that might help.
It is to use a named location for the error page instead of a URL, like so:
error_page 403 @error;
location @error {
...
}
Then NGINX won't change the request method to GET and full audit log will appear with correct messages in part H.
If there is no need to change URI and method during internal redirection it is possible to pass error processing into a named location: [...]
(http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page)
EDIT: I just noticed the reference to https://github.com/owasp-modsecurity/ModSecurity-nginx/issues/152#issuecomment-593259386 which seems to use the same solution (although in a little more complicated way)
This issue seems to be a duplicate of another issue opened earlier in the Modsecurity-Nginx connector Issue Hence I am closing it.
Describe the bug
I have created a custom rule to detect the word
confidential
in theREQUEST_BODY
. If the word is detected, the action to undertake isdeny
. However when I make thisPOST
request the request is logged as aGET
in the audit logs.Here is the custom rule:
Logs and dumps Audit Logs
Debug Logs (Level 9) Modsecurity-Debug-Logs-Level-9.txt
A more closer view:
In the logs above you can see that for the same
unique_id
8f198db9483934603d69ce8654326042, theREQUEST_METHOD
variable value changes fromPOST
toGET
Error Logs:
In the error log the same request gets logged twice, it gets logged as POST request.
To Reproduce
POST
endpoint in the server/webApp protected by the WAF.SecRuleEngine On
SecRequestBodyAccess on SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap+|/)|text/)xml" \ "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" SecRule REQUEST_HEADERS:Content-Type "^application/json" \ "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" SecRequestBodyLimit 10485760 SecRequestBodyNoFilesLimit 1048576 SecRequestBodyLimitAction ProcessPartial SecRequestBodyJsonDepthLimit 512 SecArgumentsLimit 1000
SecRule &ARGS "@ge 1000" \ "id:'200007', phase:2,t:none,log,deny,status:400,msg:'Failed to fully parse request body due to large argument count',severity:2"
SecRule REQBODY_ERROR "!@eq 0" \ "id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ "id:'200003',phase:2,t:none,log,deny,status:400, \ msg:'Multipart request body failed strict validation: \ PE %{REQBODY_PROCESSOR_ERROR}, \ BQ %{MULTIPART_BOUNDARY_QUOTED}, \ BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ DB %{MULTIPART_DATA_BEFORE}, \ DA %{MULTIPART_DATA_AFTER}, \ HF %{MULTIPART_HEADER_FOLDING}, \ LF %{MULTIPART_LF_LINE}, \ SM %{MULTIPART_MISSING_SEMICOLON}, \ IQ %{MULTIPART_INVALID_QUOTING}, \ IP %{MULTIPART_INVALID_PART}, \ IH %{MULTIPART_INVALID_HEADER_FOLDING}, \ FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
SecPcreMatchLimit 100000 SecPcreMatchLimitRecursion 100000 SecRule TX:/^MSC_/ "!@streq 0" \ "id:'200005',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
SecResponseBodyAccess on SecResponseBodyMimeType text/plain text/html text/xml application/JSON SecResponseBodyLimit 537600 SecResponseBodyLimitAction Reject
SecTmpDir /tmp/ SecDataDir /tmp/
SecAuditEngine On SecAuditLogType Serial SecAuditLogRelevantStatus "^(?:5|4(?!04))" SecAuditLog /var/log/modsec_logs/modsec_audit_weborion.licensing.portal.log SecAuditLogFormat JSON SecAuditLogParts ABFHZ
SecArgumentSeparator & SecCookieFormat 0 SecUnicodeMapFile unicode.mapping 20127 SecStatusEngine Off
SecGeoLookupDb /var/GeoLite2-Country/GeoLite2-Country.mmdb
SecDebugLog /var/log/modsec_logs/modsec_debug.log SecDebugLogLevel 9
[8f198db9483934603d69ce8654326042] [/check] [4] (Rule: 1) Executing operator "Rx" with param "(?i)(\b|\W*)confidential" against REQUEST_BODY. [8f198db9483934603d69ce8654326042] [/check] [9] Target value: "{"confidential":true}" (Variable: REQUEST_BODY) [8f198db9483934603d69ce8654326042] [/check] [9] Matched vars updated. [8f198db9483934603d69ce8654326042] [/check] [4] Rule returned 1. [8f198db9483934603d69ce8654326042] [/check] [9] Saving msg: UNAUTHORIZED DATA ACCESS [8f198db9483934603d69ce8654326042] [/check] [9] Running action: log [8f198db9483934603d69ce8654326042] [/check] [9] Saving transaction to logs [8f198db9483934603d69ce8654326042] [/check] [4] Running (disruptive) action: deny. [8f198db9483934603d69ce8654326042] [/check] [8] Running action deny [8f198db9483934603d69ce8654326042] [/check] [8] Skipping this phase as this request was already intercepted. [8f198db9483934603d69ce8654326042] [] [4] Initializing transaction [8f198db9483934603d69ce8654326042] [] [4] Transaction context created. [8f198db9483934603d69ce8654326042] [] [4] Starting phase CONNECTION. (SecRules 0) [8f198db9483934603d69ce8654326042] [] [9] This phase consists of 29 rule(s). [8f198db9483934603d69ce8654326042] [] [4] Starting phase URI. (SecRules 0 + 1/2)