-
Notifications
You must be signed in to change notification settings - Fork 282
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix response body (based on #84) #326
base: master
Are you sure you want to change the base?
Conversation
Is there any update on when this PR will be reviewed/merged? This solution also solves another issue, wherein if the response size is large, even if the blocking rule is triggered, the response still goes through to the client. |
Hi @g00g1, @Dr-Lazarus-V2, although all tests were passed, it would be nice to explain the exact problem. @Dr-Lazarus-V2 mentioned this PR in his issue, but unfortunately I wasn't able to reproduce the problem - and then I can't try the fix. Also this comment mentions that the requested feature is not available yet - this PR adds it? |
@airween how did you try to reproduce it? Have you seen my comment here: #41 (comment) ? |
Hey @airween, yes I believe this PR resolves the issue. As the response body is fully examined before the response is sent out. |
@g00g1: I saw your comment, but I'm afraid it's not enough to reproduce the issue. Could you share some more details, eg. (relevant) configuration, and a @Dr-Lazarus-V2: I'm sure the PR solves the problem, but first I would like to see the problem itself 😃 |
Hi guys, finally I had some time to reproduce the issue. Coreruleset issue #3277 helped me. I tried your patch, seems like it solves the problem - but unfortunately the audit log Could you take a look this issue? |
I am still not very familiar with modsecurity code, it might take some time before I figure out how this should work properly and what is lacking to fill If anyone wished to help or guide me regarding this, I will be very grateful. Am I right that this pull request somehow impacts the codepath taken at https://github.com/owasp-modsecurity/ModSecurity/blob/4a720004ddf37963ad63deb1f1a2fdd8113f7d41/src/rule_with_actions.cc#L495 ? |
Sure, I'm happy to help you. You can join to OWASP's Slack server, and choose |
@airween thanks, but I don't have an account there nor email at owasp.org domain. |
As I know you don't need any owasp.org email address. But if you send me your email me in private, I can send you an invitation. |
Or you can use this link. |
Hey, if it's all right, I can also have a look at the PR and try to debug what the issue might be |
I have noticed a weird behaviour:
Within a single transaction (identified by a unique ID), the same rule is triggered twice, but with inconsistent results: once with the HTTP method identified as POST and then as GET. This means that during the same request, ModSecurity is evaluating the method as both POST and GET at different points. This unusual behavior may indicate an issue with how ModSecurity is processing the request, possibly due to a configuration quirk or rule interpretation that causes the request method to appear inconsistent during evaluation. |
As an addition to the previously mentioned setup, I created the following custom rule to block responses containing the word "leak":
However, at Debug Level 9, when this rule is triggered and blocks the response, the transaction appears to restart unexpectedly. Below are the logs capturing this issue:
|
Hi @Dr-Lazarus-V2, this would be awesome! Thanks! |
Could you show your request with that I can reproduce this? Also please provide the used CRS version too. |
Hey @airween, the CRS Version I used is v4.7.0. In the application I am trying to protect, I had an endpoint which returns a JSON response with the word leak. I had used this custom rule to detect it:
If you'd like to reproduce this, I think you would need a simple POST endpoint in the Web Application which is protected by the WAF. The POST endpoint needs to return a JSON response with the word "leak". If you perform a Here is my modsecurity configuration for reference:
|
@airween, I have tested and realized the following: I have setup an endpoint: "/healthz" which returns "leak!" Bug Case:
Now if I perform the following
It seems to return the following log: {
"transaction": {
"client_ip": "10.103.253.191",
"time_stamp": "Thu Nov 14 03:29:54 2024",
"server_id": "88a6b3d6a5b5bc6b73061e112bad4a1309e1d4b2",
"client_port": 36616,
"host_ip": "192.168.32.3",
"host_port": 8080,
"unique_id": "bdcd2324b71e0518896ea843b61e488b",
"request": {
"method": "GET",
"http_version": 1.1,
"uri": "/healthz",
"headers": {
"Host": "waf.theviscousweb.com",
"User-Agent": "curl/7.81.0",
"Accept": "*/*",
"Content-Length": "0",
"Content-Type": "application/x-www-form-urlencoded"
}
},
"response": {
"http_code": 403,
"headers": {
"Server": "nginx/1.27.2",
"Date": "Thu, 14 Nov 2024 03:29:54 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.7.0\""
]
},
"messages": [
{
"message": "GET Request Detected",
"details": {
"match": "Matched \"Operator `StrEq' with parameter `GET' against variable `REQUEST_METHOD' (Value: `GET' )",
"reference": "v0,3",
"ruleId": "191",
"file": "/var/opt/modsecurity.d/custom-ruleset/Data-Leak/Data-Leak-RULES.conf",
"lineNumber": "18",
"data": "",
"severity": "0",
"ver": "",
"rev": "",
"tags": [],
"maturity": "0",
"accuracy": "0"
}
},
{
"message": "Inbound Score: 0 Outbound Score: 0",
"details": {
"match": "",
"reference": "",
"ruleId": "980145",
"file": "/var/opt/modsecurity.d/owasp-crs/waf.theviscousweb.com.crs_setup.conf",
"lineNumber": "40",
"data": "",
"severity": "0",
"ver": "",
"rev": "",
"tags": [],
"maturity": "0",
"accuracy": "0"
}
}
]
}
} You can see that even though the
Bug Free Case:If I modify the
It does not cause any issues and the request is logged as a POST. {
"transaction": {
"client_ip": "10.103.253.191",
"time_stamp": "Thu Nov 14 03:36:23 2024",
"server_id": "88a6b3d6a5b5bc6b73061e112bad4a1309e1d4b2",
"client_port": 43622,
"host_ip": "192.168.32.3",
"host_port": 8080,
"unique_id": "13fdd4d10d426b9e653cc22a833bd3df",
"request": {
"method": "POST",
"http_version": 1.1,
"uri": "/healthz",
"headers": {
"Host": "waf.theviscousweb.com",
"User-Agent": "curl/7.81.0",
"Accept": "*/*",
"Content-Length": "0",
"Content-Type": "application/x-www-form-urlencoded"
}
},
"response": {
"http_code": 200,
"headers": {
"Server": "nginx/1.27.2",
"Date": "Thu, 14 Nov 2024 03:36:23 GMT",
"Content-Length": "5",
"Content-Type": "application/octet-stream",
"Content-Type": "text/plain",
"Connection": "keep-alive"
}
},
"producer": {
"modsecurity": "ModSecurity v3.0.13 (Linux)",
"connector": "ModSecurity-nginx v1.0.3",
"secrules_engine": "Enabled",
"components": [
"OWASP_CRS/4.7.0\""
]
},
"messages": [
{
"message": "Inbound Score: 0 Outbound Score: 0",
"details": {
"match": "",
"reference": "",
"ruleId": "980145",
"file": "/var/opt/modsecurity.d/owasp-crs/waf.theviscousweb.com.crs_setup.conf",
"lineNumber": "40",
"data": "",
"severity": "0",
"ver": "",
"rev": "",
"tags": [],
"maturity": "0",
"accuracy": "0"
}
}
]
}
} Hence I see that when a rule is triggered in the response body, somehow the |
Hello, everyone. Apologies for the confusion regarding the recent PR. I want to clarify that the bug I observed – where malicious POST requests are being logged as GET requests – appears to be a redirection issue, which affects the logging. I had a redirect in my nginx code. This specific issue is not introduced by the recent code changes, as I found it to be present in the current active version of the ModSecurity-Nginx connector. This bug has already been raised and is still open in: #182. Here is my audit logs under: {
"transaction": {
"client_ip": "10.103.253.191",
"time_stamp": "Thu Nov 14 06:18:37 2024",
"server_id": "135e057d99ba46945f45e685b2bae7652ad9cc73",
"client_port": 54524,
"host_ip": "192.168.32.3",
"host_port": 8080,
"unique_id": "349dd1ee0e6e742bfc604e2732173015",
"request": {
"method": "POST",
"http_version": 1.1,
"uri": "/",
"headers": {
"Host": "waf.theviscousweb.com",
"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 06:18:37 GMT",
"Content-Length": "153",
"Content-Type": "text/html",
"Connection": "keep-alive"
}
},
"producer": {
"modsecurity": "ModSecurity v3.0.13 (Linux)",
"connector": "ModSecurity-nginx v1.0.3",
"secrules_engine": "Enabled",
"components": [
"OWASP_CRS/4.7.0\""
]
},
"messages": [
{
"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,10v220,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.7.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,10v220,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.7.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": "v186,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.7.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,8v186,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.7.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,7v186,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.7.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,6v186,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.7.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": "v157,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.7.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: 35)",
"details": {
"match": "Matched \"Operator `Ge' with parameter `20' against variable `TX:BLOCKING_INBOUND_ANOMALY_SCORE' (Value: `35' )",
"reference": "",
"ruleId": "949110",
"file": "/etc/modsecurity.d/owasp-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf",
"lineNumber": "222",
"data": "",
"severity": "0",
"ver": "OWASP_CRS/4.7.0",
"rev": "",
"tags": [
"anomaly-evaluation",
"OWASP_CRS"
],
"maturity": "0",
"accuracy": "0"
}
},
{
"message": "Inbound Score: 35 Outbound Score: 0",
"details": {
"match": "",
"reference": "",
"ruleId": "980145",
"file": "/var/opt/modsecurity.d/owasp-crs/waf.theviscousweb.com.crs_setup.conf",
"lineNumber": "40",
"data": "",
"severity": "0",
"ver": "",
"rev": "",
"tags": [],
"maturity": "0",
"accuracy": "0"
}
}
]
}
} Here is the curl command:
|
@airween, I have also tested it with the custom rule mentioned in: #3277. I was not able to reproduce the bug raised earlier. Custom Rule:
I can see the {
"transaction": {
"client_ip": "10.103.253.240",
"time_stamp": "Thu Nov 14 06:33:40 2024",
"server_id": "135e057d99ba46945f45e685b2bae7652ad9cc73",
"client_port": 53601,
"host_ip": "192.168.32.3",
"host_port": 8080,
"unique_id": "bdcc99b802f390fe8f0f0d7119e3a0d5",
"request": {
"method": "POST",
"http_version": 1.1,
"uri": "/check",
"headers": {
"Content-Type": "application/json",
"User-Agent": "PostmanRuntime/7.42.0",
"Postman-Token": "90021de8-7a7e-4a2d-a78b-1339ea7a1061",
"Accept": "*/*",
"Host": "weborion.licensing.portal",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Content-Length": "21"
}
},
"response": {
"http_code": 403,
"headers": {
"Server": "nginx/1.27.2",
"Date": "Thu, 14 Nov 2024 06:33:40 GMT",
"Content-Length": "11",
"Content-Type": "text/plain; charset=utf-8",
"Connection": "keep-alive"
}
},
"producer": {
"modsecurity": "ModSecurity v3.0.13 (Linux)",
"connector": "ModSecurity-nginx v1.0.3",
"secrules_engine": "Enabled",
"components": [
"OWASP_CRS/4.7.0\""
]
},
"messages": [
{
"message": "multiline active",
"details": {
"match": "Matched \"Operator `Rx' with parameter `^}' against variable `RESPONSE_BODY' (Value: `} triggered' )",
"reference": "o0,1v259,11",
"ruleId": "189",
"file": "/var/opt/modsecurity.d/custom-ruleset/TestRukeset/TestRukeset-RULES.conf",
"lineNumber": "2",
"data": "",
"severity": "0",
"ver": "",
"rev": "",
"tags": [],
"maturity": "0",
"accuracy": "0"
}
},
{
"message": "Inbound Score: 0 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"
}
}
]
}
} |
Outside of that, I do not see any critical bugs in this PR and it looks stable to me. |
Hi @Dr-Lazarus-V2, thanks for the very detailed explanation. I tried - I think - the same config as you have:
But I still get empty {
"transaction": {
"client_ip": "127.0.0.1",
"time_stamp": "Thu Nov 14 16:05:58 2024",
"server_id": "0d4d434e96996020298a3e094b066f66e6634d3b",
"client_port": 53650,
"host_ip": "127.0.0.1",
"host_port": 8080,
"unique_id": "173159675881.617821",
"request": {
"method": "GET",
"http_version": 1.1,
"uri": "/healthz",
"headers": {
"Host": "waf.test:8080",
"User-Agent": "curl/8.10.1",
"Accept": "*/*",
"Content-Length": "0",
"Content-Type": "application/x-www-form-urlencoded"
}
},
"response": {
"http_code": 403,
"headers": {
"Server": "nginx/1.26.0",
"Date": "Thu, 14 Nov 2024 15:05:58 GMT",
"Content-Length": "101",
"Content-Type": "text/html",
"Last-Modified": "Thu, 17 Oct 2024 11:36:28 GMT",
"Connection": "keep-alive",
"ETag": "\"6710f6bc-65\""
}
},
"producer": {
"modsecurity": "ModSecurity v3.0.13 (Linux)",
"connector": "ModSecurity-nginx v1.0.3",
"secrules_engine": "Enabled",
"components": [
"OWASP_CRS/4.9.0-dev\""
]
},
"messages": []
}
} In the
I continue the investigation. |
With the given The question is to me why can't I see the |
That's interesting. In my system, I use the Old Line New Line I've set up this configuration as my WAF, and based on your audit logs, it’s evident that our configurations are largely similar, except for the Nginx server version. I'm running version 1.27.2 on my end, which I don’t believe should cause any issues. I have used the same rule:
Here is the audit log:
Here is the error log:
Modsecurity-CRS Docker Image
|
Hi @Dr-Lazarus-V2, thanks again for this very detailed report. I try to review that soon and reconstruct the expected behavior. |
Guys, seems like I found the root cause of the issue why isn't there any lines under If I have a custom configuration like this:
then (I think) this block starts a new transaction, which clears all collected messages inside of the transaction. In the second transaction there is no rule match so the Could you confirm this? |
Yes, I can confirm this. With the following configuration, I also noticed that in the second transaction (after the new transaction) there is no rule match and the audit logs are empty. I believe it is linked to this comment: #326 (comment) |
I believe this issue relates to the previously raised bug in #182, as the problem appears to be recurring here. Since the rule is in Phase 4, there is no body due to the error redirect, which seems to prevent the rule from being triggered. Based on this, I do not believe this PR introduces the bug. As metioned in #182, this seems to be a potential workaround:
However from a deeper investigation, it is clear that the this is not a full workaround but a temporary one. There does seem to be a PR relating to it: #204 |
Yes, I saw this and other redirect related fixes. Now I try to make a new PR which merges all of these. |
Is there anything I can do to help? |
If you have any C experiences, you can try to pick up any of related PR from the pending ones and try to apply those on this one. Or later (if I can progress with the patch) you can help to test that. |
I tried to add these modifications. The good news is that there is no new transaction, I see only one transaction in debug.log. The bad news that there is no audit.log at all. (Audit.log had created but it's empty.) |
Hey was caught up in other work, I will have a look at it now! |
This PR is an attempt to revive previously developed fix for #41. It seems like its' developer abandoned PR, therefore I would like to try my best to finish it.
I have updated the code base to a current master, therefore it is possible to continue discussion regarding this patch.