WordPress – brute force attack on xmlrpc.php

Over the weekend our server security software alerted us to an unusual brute force attack that was taking place. We run WordPress to power our site, and there was an exceedingly high level of traffic to the xmlrpc.php file – it transpires that this is a relatively (within the last week or so) new brute force attack.

For those who are unfamiliar with it, XML RPC is a remote procedure call (hence RPC) which uses the eXtensible Markup Language (hence XML). In practice, what it allows is remote publishing to you blog, so if you use a desktop blogging client like BlogMate or myWPEdit, then the communication between your client and your blog takes place through XML RPC, and through one specific file, called xmlrpc.php.

For security’s sake, sensibly enough, you can’t just send information to this file and expect your WordPress blog to process it. All requests have to be authorised, so when your desktop client passes the new blog post to your blog, it will pass with it the username and password you set up when you installed the client so your blog knows that it’s authorised to create the post. WordPress will reply if that username and password is correct or not – and that’s what the brute force approach is relying upon.

The evidence

Our logs showed thousands upon thousands of requests along the line of the following (genuine requests have been removed) :

125.60.243.125 - - [27/Jul/2014:13:36:34 +0100] "POST /xmlrpc.php HTTP/1.1" 200 438 "-" "-" "-"
95.92.14.103 - - [27/Jul/2014:13:36:35 +0100] "POST /xmlrpc.php HTTP/1.1" 200 438 "-" "-" "-"
46.13.172.172 - - [27/Jul/2014:13:36:37 +0100] "POST /xmlrpc.php HTTP/1.1" 200 438 "-" "-" "-"
5.0.30.165 - - [27/Jul/2014:13:36:38 +0100] "POST /xmlrpc.php HTTP/1.1" 200 438 "-" "-" "5.0.30.165"
117.199.146.67 - - [27/Jul/2014:13:36:39 +0100] "POST /xmlrpc.php HTTP/1.1" 200 438 "-" "-" "-"
110.39.184.211 - - [27/Jul/2014:13:36:43 +0100] "POST /xmlrpc.php HTTP/1.1" 200 438 "-" "-" "-"
186.44.145.99 - - [27/Jul/2014:13:36:44 +0100] "POST /xmlrpc.php HTTP/1.1" 200 438 "-" "-" "-"
117.216.178.102 - - [27/Jul/2014:13:36:45 +0100] "POST /xmlrpc.php HTTP/1.1" 200 438 "-" "-" "-"
85.97.37.196 - - [27/Jul/2014:13:36:47 +0100] "POST /xmlrpc.php HTTP/1.1" 200 438 "-" "-" "-"
186.207.20.148 - - [27/Jul/2014:13:36:52 +0100] "POST /xmlrpc.php HTTP/1.1" 200 438 "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)" "-"
50.171.69.223 - - [27/Jul/2014:13:36:56 +0100] "POST /xmlrpc.php HTTP/1.1" 200 438 "-" "-" "-"
213.243.168.119 - - [27/Jul/2014:13:36:57 +0100] "POST /xmlrpc.php HTTP/1.1" 200 438 "-" "-" "-"

Analysing one individual request out of these (note that they’re all the same size), showed that the request was trying to use the wp.getUsersBlogs method, passing a username and password as follows :

<methodCall><methodName>wp.getUsersBlogs</methodName><params><param><value>
 <string>admin</string></value></param>
  <param><value><string>123123</string></value></param></params>
</methodCall>

Why xmlrpc.php?

A lot of security plugins for WordPress will detect brute force attacks on the main login page – but it seems that very few will detect them on xmlrpc.php, and because xmlrpc.php responds to authentication requests the same way as login.php (ie. this is or is not a valid username / password combination) your attack will still have the same net effect.

In addition, it’s unlikely that a server owner will spot a brute force attack on xmlrpc.php, because it’s only generally logged to your web server’s access logs. So attacking xmlrpc.php, rather than login.php, gives you a greater likelihood that your attack won’t be spotted – the longer your attack goes on for, the more chance it has of succeeding.

How to prevent it

There are a number of ways to prevent brute force attacks, in general, and this attack, in particular, from being successful :

1. Foremost is to ensure that all users have a very secure password – use the excellent howsecureismypassword.net site to work out how secure your password is.

2. Use a module such as “Force Strong Passwords” to ensure that users are only allowed to have passwords which WordPress considers “strong”.

3. Use two-factor authentication (through Google Authenticator, or Duo Security, or others) to ensure that even if a username and password are compromised, one can still not login without a second factor (like a mobile phone) – although some will not protect against XML RPC attacks, only normal login attacks.

4. Do not use common usernames – like “admin”, the site’s name, or the site’s domain – for admin logins to WordPress.

5. If it’s not being used, disable XML RPC. In older (3.4 and before) versions of WordPress this can be done under Settings -> Writing from within WordPress. In later versions of WordPress you can disable it entirely with the use of a plugin.

6. To disable the specific method being targeted in the attack covered by this post, you could add the following to the bottom of your theme’s functions.php file :

add_filter('xmlrpc_methods', function($methods) {
  unset($methods['wp.getUsersBlogs']);
  return $methods;
 });

7. As an additional step, if using Fail2Ban (and if you’re not, why aren’t you?) then you should create a new filter (in /etc/fail2ban/filter.d) :

[Definition]
failregex = ^<HOST> .*POST .*xmlrpc\.php.*
ignoreregex =

To correspond to that you would also want to add a new entry in jail.conf :

[nginx-xmlrpc]
enabled = true
port = http,https
filter = nginx-xmlrpc
action = iptables[name=nginx-xmlrpc, port=http, protocol=tcp]
logpath = /path/to/log/to/be/watched
maxretry = 2
bantime = 100000

Then restart fail2ban. Within minutes the number of attack attempts had fallen from 300 / minute to around 60 / minute, and within half an hour was down to around 1 / minute. A day later and it’s maybe one every half hour or so, at best.

Hopefully this will be of use…!

Want to talk to us about your project?