Brute-force is the process of simply trying all possible username and password combinations in order to gain access to a service — finding valid credentials by force. Any publicly accessible service could become target of such assaults, with public web sites and servers on the very top of the list. This article briefly describes the security risk associated with such type of password guessing attacks, and it gives concrete guidance on possible counter-measures. While the sample refers to popular Open Source software ownCloud, the underlying concepts apply to any modern web application running on your Linux server.

Background

While some argue that brute-force is no longer the predominant attack vector these days, folks at Sucuri do regularly publish reports which prove that such attacks still need to be taken very seriously. If the (potential) value of access to a service is high enough then it will justify the necessary efforts for attackers to perform brute-force, among other types of attacks. A private cloud service hosting personal documents, contacts, and calendar information of its users is a very attractive target; and breaking in to such services is a profitable business nowadays.

The Problem

Passwords which are stolen during site hacks are traded and exchanged openly on the Internet. To make matters worse, humans all follow the same simple rules to come up with, and remember, pseudo-complex passwords. This means that the number of possible passwords to be tried in guessing attacks can be reduced significantly, in turn reducing the number of login attempts necessary to find the correct username and password combination. Instead of trying all possible password combinations, attackers first focus on passwords used for other sites which have been compromised. In addition to that, there are few basic rules which produce passwords which many users are likely to choose: adding a number and a special character to your cat's name after all is not as innovative as you may think it is…

Password Strength Webcomic by xkcd.com

As a result, brute-force attacks have a pretty decent chance of being successful. This is especially true if they remain unnoticed by the owner of the site for an extended period of time. You are regularly checking your relevant server log files, are you?

The Other Problem

In many cases, brute-force goes hand in hand with some kind of denial-of-service attack. Think about the resources required by your web application to process and verify a single login attempt: The web server has to serve the login page and accept the login request, PHP has to interpret the code on the login page, which in turn fetches some information from the database. Imagine the time and resources required to serve 100 such attempts. Is your server capable of serving 1.000 login attempts at once? 10.000?

Introducing Fail2Ban

Enter Fail2Ban. Fail2Ban is an intrusion prevention tool designed to automatically monitor log files on your server, and capable of taking appropriate actions if something suspicious is found. It is typically used to monitor the log files of, say, your SSH server. If Fail2Ban finds too many failed login attempts in this log then it will block ('ban') the IP address initiating such attempts for a certain period of time. This is typically done by adding drop rules to the Linux kernel firewall. All this occurs automatically, without any user intervention.

Fail2Ban Logo

Nowadays more and more web applications implement similar functionality themselves, typically by using 3rd party plugins. However, the resources required to verify and deny access to a site from within a web application (written in a high-level script language, interpreted by a web server) are significantly higher than what the very efficient Linux kernel firewall (Netfilter) uses. In scenarios in which large numbers of requests are sent to a server it can be an important advantage to block such requests upon arrival, without even executing a single line of PHP code.

The following section illustrates configuration and basic usage of Fail2Ban based on a typical LAMP stack application: ownCloud. This sample was tested with ownCloud Server version 9.1 on Ubuntu Linux. Note that other versions of ownCloud may require adaption of the filter rules used by Fail2Ban.

ownCloud Logo

Configuring ownCloud

First of all, ownCloud needs to be configured so that it writes the relevant messages to a log file in an appropriate format:

/var/www/owncloud/config/config.php:
  ...
  'logfile' => '/var/log/owncloud.log',
  'loglevel' => 2,
  'logtimezone' => 'Europe/Berlin',
  ...

It's important to understand that the logtimezone setting needs to match the operating system's configured timezone, otherwise Fail2Ban will not be able to properly interpret the log messages. Unless your system runs UTC, which is ownCloud's default, this line in the configuration file needs to be adapted accordingly.

That's it for configuring ownCloud. Failed login attempts should now be logged in the configured logfile, so that Fail2Ban can be configured to monitor such events and react accordingly. Please verify existence of the mentioned entries in the configured log file prior to…

Configuring Fail2Ban

If not done so already, now is a good time to install Python-based Fail2Ban. Packages for all major distributions should be readily available from the standard repositories — the following is all it takes to install the tool on Ubuntu Linux:

# sudo apt-get install fail2ban

Fail2Ban implements a concept of jails, filters and actions. A jail basically associates a filter (regular expressions used to detect break-in attempts) and actions to a log file. Let's start with adding a jail for ownCloud:

/etc/fail2ban/jail.local:
  ...
  [owncloud]
  enabled  = true
  filter   = owncloud
  action   = iptables-multiport[name=ownCloud, port="http,https"]
             sendmail-whois[name=ownCloud, dest=root@localhost, sender=fail2ban@localhost]
  logpath  = /var/log/owncloud.log
  ...

If a match is found according to the filter criteria (more on that in a second) then the iptables-multiport action will block the HTTP(S) ports of your server for the address initiating the requests. Furthermore, an email will be sent to the root account of the system.

The jail references a filter which needs to be defined like so:

/etc/fail2ban/filter.d/owncloud.conf:
  [Definition]
  failregex={"reqId":".*","remoteAddr":".*","app":"core","message":"Login failed: '.*' \(Remote IP: \'<HOST>\'\)","level":2,"time":".*"}
  ignoreregex=

This filter configuration essentially instructs Fail2Ban to monitor the log for lines which include the string "Login failed". In addition to that, it tells Fail2Ban where to find the IP of the initiator in such log messages (after the "Remote IP:" string) — this is the address that will get blocked by means of iptables drop rules.

Fail2Ban itself is implemented as a daemon which needs to be (re)started to activate the new configuration:

# sudo service fail2ban reload

That's it. If all goes well you now have Fail2Ban monitoring ownCloud for failed login attempts. You can consult /var/log/fail2ban.log for a pretty comprehensive status report. Furthermore, if you have configured email relaying on your server (which you really should) then you will get notified of banned IPs as per the jail configuration mentioned above.

Hints and Tips

To better understand if and how your filter criteria actually works you can manually test it against a given log file by using the following command. This is especially useful for debugging, without running the risk of locking yourself out:

# fail2ban-regex /var/log/owncloud.log /etc/fail2ban/filter.d/owncloud.conf -v

The output of fail2ban-regex tells you how many lines from the log file were matched and how many lines were missed by the given filter configuration. If no matches are found with your filter then you need to check and adapt the regular expression. Also keep in mind that Fail2Ban will only detect timestamps if the format used in the log file matches the system's timezone set via /etc/timezone.

Another important command, fail2ban-client, is used to monitor the current state of a given jail. This includes information on banned IPs. Furthermore, the same command is used to manually unban IPs from a given jail — which is especially useful after you've locked yourself out:

# fail2ban-client status owncloud
# fail2ban-client set owncloud unbanip YOURIP

Now that you have the basic functionality set up and running you might want to spend some additional time fine-tuning the configuration. The number of failed attempts in a given timeframe, as well as the duration for which IPs are banned is configured via options. There are some global options defined in the main configuration file /etc/fail2ban/jail.conf, but each jail can override these global settings with specific parameters. Information on the individual options can be found in the manual.

/etc/fail2ban/jail.local:
  ...
  maxretry = 4
  bantime  = 3000
  findtime = 600
  ...

It is generally recommended to start with a short bantime for testing, for example a few minutes, and then raise this duration once you feel reasonably confident that the filter criteria works as expected.

Conclusion

Fail2Ban is a powerful utility when it comes to hardening services accessible via untrusted networks. Even if attackers learn and work around the timings enforced by Fail2Ban this will at least slow down things significantly. For sure such a configuration doesn't yield absolute security, but you still increase your chances that attackers give up and turn to the next site...

Nevertheless, it should be noted at this point that a configuration like described in this article doesn't substitute, but only supplements a good overall security strategy. Such a strategy comprises the following aspects:

  • Update software regularly. Set up (email) notifications and treat security updates urgently.
  • Don't use default usernames. It goes without saying that default passwords should be avoided, too.
  • Enable two-factor authentication wherever possible.
  • Use a password manager to generate (and remember) strong, unique passwords for all your services.
developerWorks