Login Failure Daemon (lfd) is a process that runs all the time and periodically (every X seconds) scans the latest log file entries for login attempts against your server that continually fail within a short period of time. Such attempts are often called “Brute-force attacks” and the daemon process responds very quickly to such patterns and blocks offending IP’s quickly. Other similar products run every x minutes via cron and as such often miss break-in attempts until after they’ve finished, our daemon eliminates such long waits and makes it much more effective at performing its task.
lfd is often used to detect login failures of SSH connections. It operates by monitoring the authentication log and matching all its lines against the set of regular expressions.
This is an example how lfd detects brute force attacks against SSH (password-based authentication):
if (($config{LF_SSHD}) and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($globlogs{SSHD_LOG}{$lgfile})) and ($line =~ /^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: Failed password for (invalid user |illegal user )?(\S*) from (\S+)( port \S+ \S+\s*)?/)) { my $ip = $5; my $acc = $4; $ip =~ s/^::ffff://; if (checkip(\$ip)) {return ("Failed SSH login from","$ip|$acc","sshd")} else {return} }
This is the regular expression:
/^(\S+|\S+\s+\d+\s+\S+) (\S+ )?sshd\[\d+\]: Failed password for (invalid user |illegal user )?(\S*) from (\S+)( port \S+ \S+\s*)?/
The interesting parts of this regular expression are those in parentheses (so called “capture groups”):
(\S+|\S+\s+\d+\s+\S+)
matches the timestamp;(\S+ )?
matches the name of the host;(invalid user |illegal user )?
catches cases when the attacker tries non-existent usernames;(\S*)
will match the username;(\S+)
will be the attacker’s IP address;( port \S+ \S+\s*)?
will match the port number and additional information like[preauth]
or SSH version.
lfd uses the fourth (my $acc = $4
) and fifth (my $ip = $5
) capture groups to get the name of the user (account) and attacker’s IP address, respectively.
Given this line:
May 21 23:14:26 nostalgia-for-infinity sshd[11835]: Failed password for invalid user root from 127.0.0.1 port 60002 ssh2
The username will be root
, and the attacker’s IP address will be 127.0.0.1
(yes, I played with my local SSH first).
Now comes the most interesting part. Attackers do not play nicely, and if lfd expects that the username will not contain spaces, this does not mean that attackers will not try username with spaces.
What happens if I try to log in as, say, root from 8.8.8.8 port 123 ssh2
?
Like this:
ssh 'root from 8.8.8.8 port 123 ssh2'@127.0.0.1
sshd will log something like this:
May 23 16:56:32 nostalgia-for-infinity sshd[8541]: Invalid user root from 8.8.8.8 port 123 ssh2 from 127.0.0.1 port 50594 May 23 16:57:32 nostalgia-for-infinity sshd[8541]: pam_unix(sshd:auth): check pass; user unknown May 23 16:57:32 nostalgia-for-infinity sshd[8541]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=127.0.0.1 May 23 16:57:33 nostalgia-for-infinity sshd[8541]: Failed password for invalid user root from 8.8.8.8 port 123 ssh2 from 127.0.0.1 port 50594 ssh2 May 23 16:57:34 nostalgia-for-infinity sshd[8541]: pam_unix(sshd:auth): check pass; user unknown May 23 16:57:36 nostalgia-for-infinity sshd[8541]: Failed password for invalid user root from 8.8.8.8 port 123 ssh2 from 127.0.0.1 port 50594 ssh2 May 23 16:57:37 nostalgia-for-infinity sshd[8541]: pam_unix(sshd:auth): check pass; user unknown May 23 16:57:39 nostalgia-for-infinity sshd[8541]: Failed password for invalid user root from 8.8.8.8 port 123 ssh2 from 127.0.0.1 port 50594 ssh2 May 23 16:57:39 nostalgia-for-infinity sshd[8541]: Connection closed by invalid user root from 8.8.8.8 port 123 ssh2 127.0.0.1 port 50594 [preauth]
If we feed these lines to lfd, it will see the user as root
and the attacker’s IP address as 8.8.8.8
, not 127.0.0.1
(this link explains why):
If the administrator has not bothered adding their IP address into csf.ignore
and csf.allow
, an attacker can lock the administrator out of the server. If the LF_NETBLOCK
option is enabled, the consequences can be more disastrous.