This is an evolving blog post with infos about the role of CRS in defending against the log4j vulnerabilities that threatens quite all logging JAVA applications. We believe the mitigations and rules suggested below will have you covered up to and including CVE-2021-45105.
In January 2022, we have consolidated our knowledge into a pull request with new rules to be merged into CRS for the next major release. The pull request can be applied to your existing installation for immediate use of the new rules.
Quick Fix
CRS rule 932130 is able to detect all known exploits targetting arbitrary GET and POST parameters. However, the rule is not inspecting HTTP headers such as the User-Agent and Referer. Starting Friday we saw attacks on these headers, so 932130 is not really adequate.
The quick fix is to add the User-Agent and the Referer to the targets of the rule. Do this by adding the following two directives after the CRS include in your configuration:
<pre class="wp-block-preformatted"># Defense against CVE-2021-44228
SecRuleUpdateTargetById 932130 "REQUEST_HEADERS:User-Agent"
SecRuleUpdateTargetById 932130 "REQUEST_HEADERS:Referer"
There is a certain chance this update will trigger new false positives. Personally I do not expect too many, but you have been warned.
If you want to extend the coverage to all HTTP headers, then the following should be applied:
<pre class="wp-block-preformatted"># Defense against CVE-2021-44228
SecRuleUpdateTargetById 932130 "REQUEST_HEADERS"
The new rule to detect all log4shell payloads
There have been so many payloads published those last few days leading to more complicated rules and what not. But I have now come to the conclusion a very simple rule is the better approach. In fact that’s the effectiveness of 932130: It’s not overly smart, but it catches literally every payload with one exception.
So here is the approach for the new rule:
- Detect nested use of
${
- this literally kills all the evasions we have seen - Detect use of
${jndi:...
without the closing bracket (this is the payload that 932130 misses)
There might be legitimate uses of nested ${
, but that will simply lead to false positives and they are acceptable in light of a highly critical vulnerability.
The second item can literally scan for the string jndi, as any obfuscation would depend on nested ${
make the first item hit again.
So here is your rule that does just this. It also includes the ctx
lookup that can be exploited in a similar way like jndi
. We also introduce a range limiter with the nesting. We have thought about whitespace in this position, but a closer look at the log4j source code revealed that whitespace will ruin the lookup and thus the exploit. Here you go (please place before your CRS include):
<pre class="wp-block-preformatted"># Generic rule against CVE-2021-44228 (Log4j / Log4Shell)
# See https://coreruleset.org/20211213/crs-and-log4j-log4shell-cve-2021-44228/
SecRule REQUEST_LINE|ARGS|ARGS_NAMES|REQUEST_COOKIES|REQUEST_COOKIES_NAMES|REQUEST_HEADERS|XML://*<em>|XML://@*</em> "@rx (?:\${[^}]{0,4}\${|\${(?:jndi|ctx))" \
"id:1005,\
phase:2,\
block,\
t:none,t:urlDecodeUni,t:cmdline,\
log,\
msg:'Potential Remote Command Execution: Log4j CVE-2021-44228', \
tag:'application-multi',\
tag:'language-java',\
tag:'platform-multi',\
tag:'attack-rce',\
tag:'OWASP_CRS',\
tag:'capec/1000/152/137/6',\
tag:'PCI/6.5.2',\
tag:'paranoia-level/1',\
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}'"
Warning: Apache throws a warning when loading this rule. But I am confident it does not lead to any problems.
There is a certain chance for false positives for this rule, but so far we have not heard any reports about it. Other users confirm they did not get any so far.
Please notice that the t:cmdline
transformation also puts everything in lowercase, so we do not have to do this explictly. And that we do not log the exact payload via logdata
anymore. This makes it harder to identify false positives, but it also protects your logviewer from making jndi calls.
Update 2021-12-23: We’ve received a bypass for this rule (see our log4j hall-of-fame). The attack was detected by 932130 however. So we really suggest to address the whole problem with this rule, but also the extension of 932130 described above.
Mitigation via WAF? Should you not just patch your software?
Patching your servers should be your number 1 priority. Of course it should.
CRS can only buy you time or support you by taking out simple attacks in a layered defense. This will allow you to concentrate on the hard stuff.
We dub our project as “The 1st line of defense”. It does not say the only line of defense for a good reason.
Illustration of the CVE-2021-44228 and the different mitigation techniques by GovCert.ch. Notice how a WAF is depicted as a first line of defense.
And finally, if you really think our detection is m00t, then proof it via our log4j detection bypass hallo-of-fame contest.
Former approaches to a new rule - left here for archive and further inspiration
CRS is a generic rule set that does not update when a new CVE comes out. Usually, we are covering it already, but it is true that we fear false positives on User-Agents and Referers. As a consequence a lot of very strong rules are not applied to these headers since they can take crazy forms, really. Loginjection attacks are therefore a bit of a weak spot.
Maybe this CVE is such a clusterf**k that a separate update of the rule set is due. We have not made up our mind yet, but there is now consensus, that a new rule has to be developed. New rules are always tricky because of false positives and the flexible JNDI interface that the exploits abuse make evasions really simple (and hard to spot!).
So here are two a basic rules contributed by one of our integrators and then a third one with an alternative regex from our github issue on the topic. They are candidates for a possible inclusion in the rule set:
<pre class="wp-block-preformatted"># Generic rule against CVE-2021-44228 (Log4j)
SecRule REQUEST_LINE|ARGS|ARGS_NAMES|REQUEST_COOKIES|REQUEST_COOKIES_NAMES|REQUEST_HEADERS|XML://*|XML://@* "@rx \${[^}]*\${" \
"id:1000,\
phase:2,\
block,\
t:none,t:urlDecodeUni,t:cmdline,\
log,\
msg:'Potential Remote Command Execution: Log4j CVE-2021-44228', \
logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
tag:'application-multi',\
tag:'language-java',\
tag:'platform-multi',\
tag:'attack-rce',\
tag:'OWASP_CRS',\
tag:'capec/1000/152/137/6',\
tag:'PCI/6.5.2',\
tag:'paranoia-level/1',\
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}'"
# Targetted rule against CVE-2021-44228 (Log4j)
# Can be evaded
SecRule REQUEST_LINE|ARGS|ARGS_NAMES|REQUEST_COOKIES|REQUEST_COOKIES_NAMES|REQUEST_HEADERS|XML://*|XML://@* "@rx \${jndi:(?:ldaps?|iiop|dns|rmi)://" \
"id:1001,\
phase:2,\
block,\
t:none,t:lowercase,t:urlDecodeUni,\
log,\
msg:'Remote Command Execution: Log4j CVE-2021-44228', \
logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
tag:'application-multi',\
tag:'language-java',\
tag:'platform-multi',\
tag:'attack-rce',\
tag:'OWASP_CRS',\
tag:'capec/1000/152/137/6',\
tag:'PCI/6.5.2',\
tag:'paranoia-level/1',\
ver:'OWASP_CRS/3.3.x',\
severity:'CRITICAL',\
setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\
setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
# Targetted rule against CVE-2021-44228 (Log4j)
# Alternative generic regex
SecRule REQUEST_LINE|ARGS|ARGS_NAMES|REQUEST_COOKIES|REQUEST_COOKIES_NAMES|REQUEST_HEADERS|XML://*|XML://@* "@rx \${[\w${}\-:]*j[\w${}\-:]*n[\w${}\-:]*d[\w${}\-:]*i[\w${}\-:]*:.*}" \
"id:1002,\
phase:2,\
block,\
t:none,t:lowercase,t:urlDecodeUni,\
log,\
msg:'Remote Command Execution: Log4j CVE-2021-44228', \
logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
tag:'application-multi',\
tag:'language-java',\
tag:'platform-multi',\
tag:'attack-rce',\
tag:'OWASP_CRS',\
tag:'capec/1000/152/137/6',\
tag:'PCI/6.5.2',\
tag:'paranoia-level/1',\
ver:'OWASP_CRS/3.3.x',\
severity:'CRITICAL',\
setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\
setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
Both rules have to be added to the configuration before the CRS include. While the 2nd more targetted rule is unlikely to trigger false positives, they have to be expected with the first one.
Please note that rule 1000 and rule 1002 trigger several warnings on Apache when starting, but it seems to work.
If you extended the target list of 932130 as described under “Quick Fix”, then the rule 1000 does not really add much value on a system that blocks at an anomaly threshold of 5 as it should. If you have a higher anomaly limit, then adding a 2nd rule that follows a very similar pattern will push the request to an anomaly score of 10.
False Positives
It’s very hard to give substantial information about false positives here. However, I did run everything above against a little User-Agent value zoo of 7.5K UAs from a production server. I did not get a single false positives on the User-Agent HTTP header for rule 932130 and the three new rule proposals above.
Things might look different on other headers or cookies depending on the application. So you can not know unless you test it yourself, or you really, really know your traffic.
The Basic Auth Problem
The so called basic auth header in HTTP has a very special format with a value that starts with “Basic” and then a base64 encoded concatenation of username and password, separated by a colon. This colon is what saves the day here. Because the exploits all contain colons, splitting username and password would cut the exploit apart. So the username can not really contain the exploit. Besides RFC 7617 forbids colons in usernames for Basic Auth.
So you are pretty much save from that angle with two exceptions:
- An application that logs passwords via log4j
- You URL-decode the username before you log it via log4j (since an attacker could URL-decode the colons)
I think these two exceptions are quite far fetched, so I won’t pursue them here.
Changelog:
2021-12-13 09:00 CET : Publication
2021-12-13 09:30 CET : Fix linebreaks in rules, add notice about Apache warning for rule 1000
2021-12-13 12:30 CET : Added rule 1002, added section on false positives
2021-12-13 21:40 CET : Added comma after msg action in rules 1000, 1001 1002 and transformations in 1000; added info about potential memory leak
2021-12-13 22:00 CET : Dropped the case insensitive prefix for the regex (since we have t:lowercase)
2021-12-13 22:10 CET : Added a variant for all HTTP headers to the Quick Fix section
2021-12-13 23:10 CET : Added section “The Basic Auth problem”
2021-12-14 22:40 CET : Removed REQUEST_BODY after reports of false positives and multimatch because it’s likely no use with the transformations being used here. Remove remark about potential memory leak. Said report has been withdrawn and no further information about a memory leak.
2021-12-15 09:00 CET : Added rule 1005 as new generic rule.
2021-12-15 09:10 CET : Added section about “Mitigation via WAF? …”
2021-12-15 11:15 CET : 3 small typos and reformatting
2021-12-15 22:30 CET : Added ctx to rule 1005, fixed backtracking / ReDoS problem of rule by introducing a range limiter after we rules out whitespace can be used to evade the pattern.
2021-12-15 22:50 CET : Add GovCert.ch illustration of mitigation options including WAF as 1st line of defense
2021-12-15 22:55 CET : Explain that t:cmdline
also does t:lowercase
in the same run
Christian Folini, CRS Co-Lead