<< there's no place like 127.0.0.1

Back in February of 2012, I first wrote about a Wordpress attack that was aimed at Mage-style websites. A few months ago, it looked like it returned in an evolved form. However, this time the attacks weren’t limited to mage sites. Random Wordpress sites were being targeted at nearly every hosting company I heard of. Worst yet was that the attackers’ user agent wasn’t ‘Bing bot’ anymore. This time they masqueraded as regular user browsers such as Mozilla. No, this attack was different, further reaching across the Internet, and far more effective. I’d like to share with you what I saw and the solutions I’ve found.

Background of the Attack

This Wordpress attack is directed at the Wordpress login page. Numerous bots will slam the wp-admin URL with POST requests. Whether they are actually trying to brute force their way in by actually submitting real data is hard to say. However, that wouldn’t actually matter for my purposes. I would see entire servers brought to their knees, unable to serve up any other web pages, due to the extreme CPU load caused by these WP admin area login requests.

It didn’t even require a large amount of bots. With only a few extra ‘visitors’, these requests would cripple the CPU. This is because Wordpress admin pages still have to query the database, usually can’t be cached, and it consumes a considerable amount of CPU. A few extra requests targeted just at that login page are quite effective.

Even if you have brute force detection firewalls such as CSF or APF, they don’t track POST requests and are no help here. Preventing the attack also wasn’t as quick as reusing the Bing-bot protection from before. They had changed their user agent to mimic regular home browsers, instead of pretending to be search engines. Here are a couple samples from my own Apache domlogs:

    [31/Oct/2013:12:17:09 -0400] "GET /wp-login.php HTTP/1.1" 401 383 "-" "Mozilla/4.0"  
    [31/Oct/2013:17:06:42 -0400] "GET /wp-login.php HTTP/1.1" 401 383 "http://boomshadow.net/wp-login.php" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0.1) Gecko/20100101 Firefox/8.0.1"  

At my company, it took about three days to fully identify this trend as a mass attack and few days more to implement a solid, deployable solution.

Identifying the Attack

This Wordpress attack can be identified very easily. You’ll notice an extremely high CPU load on the server, the output of the top command shows that that dozens or hundreds of Apache processes are to blame. Another thing to look for is a lack of reported traffic rise to coincide with the numerous additional Apache processes. The traffic hasn’t spiked up suddenly like you would expect from a normal DOS attack.

The basics to look for:

  • High CPU load from Apache
  • No extreme traffic spike

At this point, due to how common these attacks were, I would jump straight to checking for the Wordpress attack. Meeting the above two criteria was suspect enough for me.

Parse the logs for quick identification

You can easily confirm this kind of attack by parsing the domlogs. I recommend that you shut down the Apache service first. If suspect this attack to be happening, then the following command could take a long time to run unless you’ve stopped Apache.

    # cPanel servers:  
    egrep 'wp-login.php' /usr/local/apache/domlogs/* | grep -v ftp_log | awk -F : '{print $2}' | awk '{print $1}' | sort | uniq -c | sort -n
    
    # Plesk servers:  
    egrep 'wp-login.php' /var/www/vhosts/*/statistics/logs/access_log |awk -F : '{print $2}' | awk '{print $1}' | sort | uniq -c | sort -n  

That will parse the domlogs for all domains, looking for wp-login.php, while ignoring the ftp logs. It then grabs just the first column (the IP address of the visitor), and finds out how many hits there are.

Here is an example output from one such attack:

    root@server [~]# egrep 'wp-login.php' /usr/local/apache/domlogs/* | grep -v ftp_log | awk -F : '{print $2}' | awk '{print $1}' | sort | uniq -c | sort -n  
      9 5.39.219.25  
     11 5.39.219.27  
     12 70.160.25.219  
     12 95.7.156.62  
     20 146.0.74.170  
     36 146.0.74.204  
     53 182.93.186.115  
     84 146.0.74.208  
    147 146.0.74.212  
    156 176.42.149.247  
    189 109.2.224.78  
    212 213.238.175.22  

No legitimate user should be trying to hit the login page that many times.

Protecting the Wordpress admin page

As I mentioned before, you can’t simply block the IP addresses in the firewall. The attack is normally from hundreds of different IP address. Blocking a few will stop the problem for a short while, but in a few hours you’ll have issues again.

The best method is to protect that admin page directly. There is a simple solution: an ‘htpasswd’ password prompt. Here’s how:

Generate the htpasswd file, placing it in a centralized location:

    htpasswd -c /home/wp-admin-attack-htpasswd-file human  

When it prompts you for the password, literally type the word ‘password’. Now configure Apache to load the password prompt to anyone hitting a wp-login.php page, anywhere on the server. Edit your Apache include file:

/usr/local/apache/conf/includes/pre_virtualhost_global.conf  

And add the following section:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# BEGIN BLOCK-WP-ADMIN-ATTACK

<Files wp-login.php>  
AuthType basic  
AuthName "Human Check - U: human P: password"  
AuthBasicProvider file  
AuthUserFile /home/wp-admin-attack-htpasswd-file  
Require valid-user  
ErrorDocument 401 "<center>  
# Warning! #You failed to authenticate.
  Extra security has been temporarily enabled due to an ongoing attack against Wordpress logins on this server.   <b>If you are a real user, please refresh the page and enter the username and password that are provided on the pop-up.</b>
If you are still having troubles, please contact your hosting provider.</center>"  
</Files>

# END BLOCK-WP-ADMIN-ATTACK #  

Save it and restart Apache. That’s it! The CPU drain of the attack has now been undermined.

Explanation of the Implementation

Anyone that tries to access the login page is prompted with a login popup. It tells them the user name and password on the screen. The credentials literally are:

  • User Name: human
  • Password: password

Legitimate human visitors will be able to read that; a bot will not. This kind of implementation has successfully stopped the CPU usage of the attack on all the affected servers I’ve encountered. There are a few benefits to this:

  1. It’s safe. This will not affect the main site itself. Regular site visitors will never notice any changes. It only affect the admin login. Bots should never need access to the admin page (not even Googlebot); humans only.
  2. It’s less intrusive. The credentials are given on screen. The user enters them once, and can choose to have their browser save it for the future. They’ll never see it again.
  3. Failed logins can be blocked by CSF. The is the biggest one of them all. Obviously, blocking connections at the firewall is much better. This will allow you to do that once again. While CSF may not protect against failed POST logins, it will protect against failed htpasswd logins. Bots that can’t authenticate will get auto-blocked now.

Other Implementations

I’ve seen some implementations that had Apache also protect the wp-admin folder by using the “LocationMatch” directive. This is a mistake; don’t do it! There are many Wordpress plugins out there, albeit poorly coded ones, that directly call things from the wp-admin folder. If you protect the entire wp-admin folder, your regular site visitors will see a password prompt when visiting the main site.

Notes

If you had CloudFlare, you would never even experience this problem. See their blog post here. I’m a big fan of CloudFlare and will regularly recommend it to people. This is only one more reason why they are awesome!

Running Gitit Wiki with Upstart

I know I've been rather quiet lately. I've been busy travelling for a little bit and found myself at a new job. Well, now I'm getting bac...… Continue reading

Installing ImageMagick & PHP Imagick

Published on March 26, 2015