On 1/27/2016 I noticed a large number of failed login attempts using the following account names: admin, administrator, and www.iac, coming from hundreds of different IP addresses. This is indicative of a botnet-driven attempt to crack a site admin password by guessing at the password -- also known as a brute force attack.
Fortunately none of those account names is valid so these particular attacks are guaranteed not to succeed. But the botnet may try to crack a different ID that is valid. Therefore it behooves us to take steps to thwart these attacks.
There is no way to know whether a given IP address belongs to a botnet at the time of first contact, therefore we can't identify an attacker until it does something that exposes its intent -- i.e., trying to log into one of the aforementioned accounts. Once that happens we can permanently block that IP address, neutralizing that attacker and reducing the effective size of the botnet one slave computer at a time.
Drupal has two built-in features that are designed to reduce the effectiveness of these attacks:
- All failed logins are recorded in the flood table. After five unsuccessful login attempts for a single user within six hours, or 50 unsuccessful login attempts for a single IP address within one hour, Drupal blocks the offender for six hours. (Click here for more details.) This prevents a single attacker from trying an infinite number of guesses. Unfortunately the current attack is paced well below these thresholds.
- All login attempts, successful or not, are recorded in the watchdog database table. This lets us detect and act on malicious behavior patterns that do not trigger the flood logic.
I created a script that retrieves failed login reports from the watchdog table, filters them by the known attack vectors (admin, administrator, www.iac) and compares the IP addresses to the blocked_ips database table. It then adds any new IP addresses to the blocked_ips table. The script is located in ~webmaster/scripts/hacker-blocker/hacker-logins.php. The cron daemon triggers it once an hour.
This approach has a significant limitation: Every attacker gets one free guess before we block them. Depending on the size of the botnet and frequency of the attempts, that still leaves room for thousands of attempts per hour. Fortunately we're only seeing about 20 attempts per hour at the moment.