Skip to content
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

Shell false positives for rules 932260 and 932236 #3631

Open
superlgn opened this issue Mar 25, 2024 · 15 comments
Open

Shell false positives for rules 932260 and 932236 #3631

superlgn opened this issue Mar 25, 2024 · 15 comments
Labels
➕ False Positive v4 unix rce One of the many reports on FPs with the new unix rce rules in v4

Comments

@superlgn
Copy link
Contributor

Description

I've encountered some shell false positives for 932260 (PL1) , 932236 and 932239 (PL2) for commands like sudo, df, fd, and grc.

Some of these I'm obviously familiar with, but others like 'fd' or 'grc' I've never come across before. I'm not sure exactly what they are or how commonly they may be used in attacks/information leakage, or how relaxed is even appropriate. Would it be acceptable for 'sudo' to be broken out into sudo@ and sudoedit, sudoreplay, etc, fd to fd@, df to df@. I'd think this would be ok, but I also don't know if there are additional commands that these substrings were intended to match on. And more generally should these lists be revisited to ensure the matches are concise enough? I feel like there's some potential for additional FPs on unix-shell-upto3.ra and even some of the 4 character words on unix-shell-4andup.ra (expr, sched, uniq).

I didn't include any examples for grc. It just happens to match on the first 3 characters of a cookie prefix we use for a site and I've taken care of those individually. Just thought it was worth mentioning due to my unfamiliarity and the general questions.

How to reproduce the misbehavior (-> curl call)

932260 - PL1 on 'sudo' in REQUEST_COOKIES:fpestid

curl -H "x-format-output: txt-matched-rules" --cookie "fpestid=SUDoLongRandomString" https://sandbox.coreruleset.org/
932260 PL1 Remote Command Execution: Direct Unix Command Execution
949110 PL1 Inbound Anomaly Score Exceeded (Total Score: 5)
980170 PL1 Anomaly Scores: (Inbound Scores: blocking=5, detection=5, per_pl=5-0-0-0, threshold=5) - (Outbound Scores: blocking=0, detection=0, per_pl=0-0-0-0, threshold=4) - (SQLI=0, XSS=0, RFI=0, LFI=0, RCE=5, PHPI=0, HTTP=0, SESS=0, COMBINED_SCORE=5)
-
[Mon Feb 26 20:14:58.290634 2024] [:error] [pid 32480] [client 1.2.3.4:56492] [client 1.2.3.4] ModSecurity: Access denied with code 403 (phase 2). Pattern match "(?i)(?:^|b[\\"'\\\\)\\\\[-\\\\x5c]*(?:(?:(?:\\\\|\\\\||&&)[\\\\s\\\\v]*)?\\\\$[!#\\\\(\\\\*\\\\-0-9\\\\?-@_a-\\\\{]*)?\\\\x5c?u[\\"'\\\\)\\\\[-\\\\x5c]*(?:(?:(?:\\\\|\\\\||&&)[\\\\s\\\\v]*)?\\\\$[!#\\\\(\\\\*\\\\-0-9\\\\?-@_a-\\\\{]*)?\\\\x5c?s[\\"'\\\\)\\\\[-\\\\x5c]*(?:(?:(?:\\\\|\\\\||&&)[\\\\s\\\\v]*)?\\\\$[!#\\\\(\\\\*\\\\-0-9 ..." at REQUEST_COOKIES:fpestid. [file "crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf"] [line "498"] [id "932260"] [msg "Remote Command Execution: Direct Unix Command Execution"] [data "Matched Data: SUDo found within REQUEST_COOKIES:fpestid: SUDoLongRandomString"] [severity "CRITICAL"] [ver "OWASP_CRS/4.0.1-dev"] [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"] [hostname "www.example.com"] [uri "/abc/"] [unique_id "Zd1ForZabQ9-ap78Ly4PHQAAAAs"], referer: https://www.example.com/xyz/

932236 - PL2 on 'df', 'fd', in ARGS (uniqid, uuid, or hex type values)

curl -H "x-format-output: txt-matched-rules" -H "x-crs-paranoia-level: 2" "https://sandbox.coreruleset.org/get?abc=dfc987c2-72e2-4a8e-ad98-e0bf1bc3a01c"
932236 PL2 Remote Command Execution: Unix Command Injection (command without evasion)
949110 PL1 Inbound Anomaly Score Exceeded (Total Score: 5)
980170 PL1 Anomaly Scores: (Inbound Scores: blocking=5, detection=5, per_pl=0-5-0-0, threshold=5) - (Outbound Scores: blocking=0, detection=0, per_pl=0-0-0-0, threshold=4) - (SQLI=0, XSS=0, RFI=0, LFI=0, RCE=5, PHPI=0, HTTP=0, SESS=0, COMBINED_SCORE=5)
-
[Sat Mar 23 18:36:57.137426 2024] [:error] [pid 30042] [client 1.2.3.4:53294] [client 1.2.3.4] ModSecurity: Access denied with code 403 (phase 2). Pattern match "(?i)(?:^|b[\\"'\\\\)\\\\[\\\\x5c]*(?:(?:(?:\\\\|\\\\||&&)[\\\\s\\\\v]*)?\\\\$[!#\\\\(\\\\*\\\\-0-9\\\\?@_a-\\\\{]*)?\\\\x5c?u[\\"'\\\\)\\\\[\\\\x5c]*(?:(?:(?:\\\\|\\\\||&&)[\\\\s\\\\v]*)?\\\\$[!#\\\\(\\\\*\\\\-0-9\\\\?@_a-\\\\{]*)?\\\\x5c?s[\\"'\\\\)\\\\[\\\\x5c]*(?:(?:(?:\\\\|\\\\||&&)[\\\\s\\\\v]*)?\\\\$[!#\\\\(\\\\*\\\\-0-9\\\\?@_ ..." at ARGS:abc. [file "crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf"] [line "1368"] [id "932236"] [msg "Remote Command Execution: Unix Command Injection (command without evasion)"] [data "Matched Data: df found within ARGS:abc: dfc987c2-72e2-4a8e-ad98-e0bf1bc3a01c"] [severity "CRITICAL"] [ver "OWASP_CRS/4.0.1-dev"] [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"] [hostname "www.example.com"] [uri "/xyz/"] [unique_id "Zf9nmYvL5W2kWBKkT1JMpQAAAAI"]

and REQUEST_COOKIES (ids, hex values, etc)

curl -H "x-format-output: txt-matched-rules" -H "x-crs-paranoia-level: 2" --cookie "abc=fd01bfcfdbe02" https://sandbox.coreruleset.org/ 
932236 PL2 Remote Command Execution: Unix Command Injection (command without evasion)
949110 PL1 Inbound Anomaly Score Exceeded (Total Score: 5)
980170 PL1 Anomaly Scores: (Inbound Scores: blocking=5, detection=5, per_pl=0-5-0-0, threshold=5) - (Outbound Scores: blocking=0, detection=0, per_pl=0-0-0-0, threshold=4) - (SQLI=0, XSS=0, RFI=0, LFI=0, RCE=5, PHPI=0, HTTP=0, SESS=0, COMBINED_SCORE=5)
-
[Sat Mar 23 06:18:31.906424 2024] [:error] [pid 208032] [client 1.2.3.4:36324] [client 1.2.3.4] ModSecurity: Access denied with code 403 (phase 2). Pattern match "(?i)(?:^|b[\\"'\\\\)\\\\[\\\\x5c]*(?:(?:(?:\\\\|\\\\||&&)[\\\\s\\\\v]*)?\\\\$[!#\\\\(\\\\*\\\\-0-9\\\\?@_a-\\\\{]*)?\\\\x5c?u[\\"'\\\\)\\\\[\\\\x5c]*(?:(?:(?:\\\\|\\\\||&&)[\\\\s\\\\v]*)?\\\\$[!#\\\\(\\\\*\\\\-0-9\\\\?@_a-\\\\{]*)?\\\\x5c?s[\\"'\\\\)\\\\[\\\\x5c]*(?:(?:(?:\\\\|\\\\||&&)[\\\\s\\\\v]*)?\\\\$[!#\\\\(\\\\*\\\\-0-9\\\\?@_ ..." at REQUEST_COOKIES:abc. [file "crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf"] [line "1368"] [id "932236"] [msg "Remote Command Execution: Unix Command Injection (command without evasion)"] [data "Matched Data: fd found within REQUEST_COOKIES:abc: fd01bfcfdbe02"] [severity "CRITICAL"] [ver "OWASP_CRS/4.0.1-dev"] [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"] [hostname "www.example.com"] [uri "/xyz/"] [unique_id "Zf66hyl54eZv1SgtgUHGRQAAAA0"], referer: https://www.example.com/

932239 - PL2 for REQUEST_HEADERS:Referer

curl -H "x-format-output: txt-matched-rules" -H "x-crs-paranoia-level: 2" --referer "https://sandbox.coreruleset.org/get?abc=dfc987c2-72e2-4a8e-ad98-e0bf1bc3a01c" https://sandbox.coreruleset.org/
932239 PL2 Remote Command Execution: Unix Command Injection found in user-agent or referer header
949110 PL1 Inbound Anomaly Score Exceeded (Total Score: 5)
980170 PL1 Anomaly Scores: (Inbound Scores: blocking=5, detection=5, per_pl=0-5-0-0, threshold=5) - (Outbound Scores: blocking=0, detection=0, per_pl=0-0-0-0, threshold=4) - (SQLI=0, XSS=0, RFI=0, LFI=0, RCE=5, PHPI=0, HTTP=0, SESS=0, COMBINED_SCORE=5)
-
[Sat Mar 23 19:55:03.716728 2024] [:error] [pid 6278] [client 1.2.3.4:38730] [client 1.2.3.4] ModSecurity: Access denied with code 403 (phase 1). Pattern match "(?i)(?:^|b[\\"'\\\\)\\\\[\\\\x5c]*(?:(?:(?:\\\\|\\\\||&&)[\\\\s\\\\v]*)?\\\\$[!#\\\\(\\\\*\\\\-0-9\\\\?@_a-\\\\{]*)?\\\\x5c?u[\\"'\\\\)\\\\[\\\\x5c]*(?:(?:(?:\\\\|\\\\||&&)[\\\\s\\\\v]*)?\\\\$[!#\\\\(\\\\*\\\\-0-9\\\\?@_a-\\\\{]*)?\\\\x5c?s[\\"'\\\\)\\\\[\\\\x5c]*(?:(?:(?:\\\\|\\\\||&&)[\\\\s\\\\v]*)?\\\\$[!#\\\\(\\\\*\\\\-0-9\\\\?@_ ..." at REQUEST_HEADERS:Referer. [file "crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf"] [line "1430"] [id "932239"] [msg "Remote Command Execution: Unix Command Injection found in user-agent or referer header"] [data "Matched Data: =fd found within REQUEST_HEADERS:Referer: https://www.example.com/xyz/?abc=dfc987c2-72e2-4a8e-ad98-e0bf1bc3a01c"] [severity "CRITICAL"] [ver "OWASP_CRS/4.0.1-dev"] [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"] [hostname "www.example.com"] [uri "/news/"] [unique_id "Zf9556f1ZVkt2o@NOscW7gAAABc"], referer: https://www.example.com/xyz/

Your Environment

  • CRS version (e.g., v3.3.4): 4.0.1-dev
  • Paranoia level setting (e.g. PL1) : PL1+ (can change dynamically based on geolocation, rbl scores, etc)
  • ModSecurity version (e.g., 2.9.6): 2.9.7-1+b1
  • Web Server and version or cloud provider / CDN (e.g., Apache httpd 2.4.54): Apache 2.4.57-2
  • Operating System and version: Debian 12

Confirmation

[X] I have removed any personal data (email addresses, IP addresses,
passwords, domain names) from any logs posted.

@dune73
Copy link
Member

dune73 commented Mar 26, 2024

Thank you very much for an excellent bug report. And sorry for the hassles.

You are not on the full CRS 4.0.0 release, but I've looked up the 4 examples they gave and they are still set as reported by you.

So just make sure we're on the same page, you suggest the following:

./regex-assembly/include/unix-shell-upto3.ra

  • df -> df@
  • fd -> fd@
  • grc -> grc@

./regex-assembly/include/unix-shell-4andup.ra

  • sudo -> sudo@ sudoedit, sudoreplay, etc. (?)

But this is part of a larger problem for you.

You happen to use grc as a cookie prefix - well, that's just bad luck. But you also encounter FPs with uuids with the 2- and 3-letter combinations in unix-shell-upto3.ra, so you see a general problem there.

Most of the commands in unix-shell-upto3.ra come with the @ macro extension, but this was done based on English language use, but with uuid in mind as far as I can see.

This is substantial feedback and while the suggestions above are easy to implement, the general extension of all the commands in unix-shell-upto3.ra takes some thinking.

@superlgn
Copy link
Contributor Author

I don't have any suggestions for fd and grc at this time since I'm not really sure what they were originally intended to match on. These could be exact matches or partial prefixes for a whole suites of utilities behind it, covering things like fdisk, fd(u)mount, fdfind, etc. grc could be the generic colouriser at https://github.com/garabik/grc. If so, and this pattern is necessary, then it may be worth splitting those up into grc@ and grcat. I tried to find changelogs and checkins for these but didn't notice anything. If 'df' is meant to match on the actual df command then changing it to 'df@' may be prudent.

These are all PL2, meaning increased scrutiny and potentially increased false positives as well. I think most people understand that and that's fine. I don't want to presume anything or recommend changes that would drastically weaken the higher paranoia levels. I've already solved my issue with the grcxyz_ cookie prefix issue, and can make other changes to reduce the PL2 usage for suspicious traffic. I just thought this would be worth mentioning given what I've been seeing and the potential for 'df' and 'fd' to match on hex or UUID type values that may be on cookies or parameters and their related usage in the Referer header.

sudo is a PL1 so I do think that's worth considering. On Debian 12 the sudo package has 5 binaries that start with 'sudo'. If it was necessary to match on most or all of them then 'sudo' could be broken out into: sudo@, sudoedit, sudoreplay, sudo_logsrvd, and sudo_sendlog. sudoedit is a symlink to sudo. The longer binary names seem far less likely to hit on random strings like the SUDoXyZ... fpestid cookie I ran across the other day so they may not need the @. Who knows, maybe it's already very unlikely for someone to run across a cookie or other random value that starts with sudo. But generally speaking if we know or can make an educated guess on what the original intent was then maybe it's worth being more precise with some of them, especially the shorter strings.

@EsadCetiner
Copy link
Member

I've taken a crack at trying to solve this issue, and I don't think adding @ will be able to fully solve this false positive. It'll make it less frequent and therefore less painful, but it won't fundamentally solve the false positives with session cookies, tokens, UUIDs and such. I tried to at least take care of false positives with UUIDs by adding @ to any command that solely consists of characters between 0-9 a-f, but some commands like df can be executed without any arguments so that will lead to false negatives.

The real problem here is that invalid commands are being matched when neither @ or ~ is specified, if we stop matching invalid commands then that should fix all false positives with session cookies, tokens, and UUIDs while still blocking the execution of commands with no arguments. I've also taken a crack at trying to solve the invalid matching of commands, the closest I got was by adding a negated set at the end of each command sudo[^a-z0-9\-], however I don't think doing that will be very readable or good for performance. I'm not very experienced with the CRS toolchain, so maybe someone more knowledgeable than me may have a better solution.

Either way, it will take some time to find all the commands that each entry in the unix-shell-* was intended to match, and some commands will likely be missed (Simply due to human error) so a false negative may be unavoidable.

@niklasweimann
Copy link

I am not very familiar with CRS, but wouldn't it be enough to check whether there is at least one alphabetic or numeric characters before or after the match and if this is the case than this can not be a valid unix command so reject it.

There seems to be an equivalent discussion about the php functions, which sounds very similar: #3273 (comment)

@theseion
Copy link
Contributor

Thanks @EsadCetiner for trying. Indeed, this is only one of several reports of issues with the shell injection rules and we need to solve this on a conceptual level and not case by case.

@niklasweimann That's not a bad idea. @EsadCetiner do you want to give it a try? I currently have a few other things to finish.

@theseion theseion added the v4 unix rce One of the many reports on FPs with the new unix rce rules in v4 label May 22, 2024
@EsadCetiner
Copy link
Member

@niklasweimann Sorry for the late reply
If I understand you correctly, I think I've already tried that with a negated set without much success. I was able to prevent matches of invalid commands, but it didn't block the execution of commands without arguments.

There seems to be an equivalent discussion about the php functions, which sounds very similar: #3273 (comment)

we're already excluding commands at PL-1 that match with common english words, but thank you for the suggestion.

@theseion
I was able to come up with this after a bit more thinking which blocks execution of commands without arguments while also preventing invalid matches:

sudo(?:$|[^a-z0-9])

However I have to add this to end of all commands, and I don't know how bad the performance will be since I'm using a non capturing group.

Although in the past 3 weeks I did find all of the commands with 5 characters or less that don't require arguments and added @ to them. I tried to find all of the commands that they were originally intended to match, I found a fair few but I doubt I've found them all.

@theseion
Copy link
Contributor

Thanks @EsadCetiner. I'll get back to the RCE issue once I'm done with a couple of other things. If anybody else want's to take a crack at the Unix RCE rules in the mean time, I'll be happy to give some pointers.

@EsadCetiner
Copy link
Member

If nobody else has any other ideas, I'm going to open a PR that prevents invalid matches on commands with 5 characters or less. I'm not sure how ideal this solution is, but it does work.

@theseion
Copy link
Contributor

Go for it, I think it's a good idea. Be careful though, @ and ~ will add suffixes to some commands, so an expression may end up looking like this:

perf[\s<>&|)](?:$|[^a-z0-9])

This is probably not what you want. Your solution needs to take the use of these suffixes into account. Maybe they can be dropped, be applied conditionally, .... Ping me if you have questions.

@EsadCetiner
Copy link
Member

@theseion wdym by conditionals, I thought they weren't allowed because of ReDoS risk?

@theseion
Copy link
Contributor

theseion commented Jun 1, 2024

Ah 😄. I wasn't talking about regex conditionals. I mean conditionally in the sense that the crs-toolchain could, theoretically, be used to append a suffix conditionally. The condition would then be part of the regex-assembly macro language.

@EsadCetiner
Copy link
Member

@theseion I know I can use a suffix marker ##!$ to append regex unconditionally, but then I have the issue you mentioned earlier. I don't know how to append a suffix conditionaly and I don't see it documented anywhere.

@theseion
Copy link
Contributor

theseion commented Jun 1, 2024

Well, as I've said elsewhere, I think we need to solve this with an altered approach, which will probably require some changes to the setup of the way the regular expressions are generated for the 932xxx rules, and / or changes to the crs-toolchain.

For example, we could extend the cmdline processor to append a suffix (which could be user defined). Imagine something like this:

##!> cmdline unix (?:$|[^a-z0-9])
  <list of commands>
##!<

Then the crs-toolchain could generate the regular expression for some command cmd by first appending the supplied suffix, followed by expansion of @ or ~. So cmd@ would become cmd[\s<>&|)](?:$|[^a-z0-9]).

There are two things to consider here:

  1. For 932260 we don't actually use the cmdline processor, but replace @ and ~ as part of the include-except processor
  2. It probably makes more sense to configure the suffix in toolchain.yaml instead of allowing it to be specified as part of the cmdline processor.

There are probably other ways to go about this, I'm just trying to give you some idea where this could go. I know this might all seem a bit much, I'll try to put more energy into this soon.

@EsadCetiner
Copy link
Member

@theseion

For example, we could extend the cmdline processor to append a suffix (which could be user defined). Imagine something like this:

I thought about this too, but I'm not sure if there's a way to specify a default suffix (i.e when neither @ or ~ is specified, append (?:$|[^a-z0-9]) to the end of a command), just adding (?:$|[^a-z0-9]) to all commands causes false negatives and tests to fail as mentioned earlier.

I don't know if it's a good idea to get rid of @, append(?:$|[^a-z0-9]) to all commands by default and keep ~ for when we want to match permutations of a commands. It would be clean and the performance shouldn't suffer if there's nothing else that I'm missing.

@theseion
Copy link
Contributor

theseion commented Jun 4, 2024

I think you need the semantics of @, i.e., I would probably merge your suffix with the definition for @. We have to be pretty careful how we do this though, because adding a suffix to every command, without being able to group, will lead to an explosion in the length of the regular expressions. However, the generator does a pretty good job of reducing expressions, so you could simply give it a try by creating an example file with just a couple of commands and then passing that to the toolchain: crs-toolchain regex generate - < some_file.ra. Then you could estimate how big the effect on the length would be.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
➕ False Positive v4 unix rce One of the many reports on FPs with the new unix rce rules in v4
Projects
None yet
Development

No branches or pull requests

5 participants