How the JSON API and XMLRPC are used for Brute Force Attacks Against WordPress

WordPress is the most popular Content Management System (CMS) – and because of its popularity, it is also the most attacked. One of the common attacks is brute forcing (i.e., trying to guess a users password), an attack that works to guess the password used by a user on the site (hopefully the administrator).

Every Brute Force attack requires two key components – the username and a password. They are two halves of the same pie.

But how do attackers know the usernames when it’s not admin?

Another aspect of these Brute Force attacks is the method in which they are performed – bot automation. It’s what brought about recommendations like – restricting access to wp-login.php or wp-admin, using IP authentication, leveraging CAPTCHA’s, and a slew of other hardening tips. All these recommendations are great, and do wonders in reducing the noise that comes from these types of attacks.

But are these the only areas we should be concerned with?

Finding Users in WordPress using the JSON API

To successfully brute force a site you need two key components – the username and password. You’d think finding the username might be tough, but it’s not. Things like the WordPress JSON API make it trivial for a bad actor to enumerate the users on a specific site and this is not theoretical, it’s actually happening.

It is such a trivial process, that here is an excerpt of a real attack on some of our customers sites. Here, a bad actor attempts to use WP API (WP-JSON) to enumerate available users:

2021-06-21 20:21:46 [site] cdn-edge-usa-east-atlanta1 129.213.160.208 403 196 HIT waf:spam_bot1 GET //wp-json/wp/v2/users/ HTTP/1.1 – Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36

When this fails, they attempt to call up users by their potential IDs:

2021-06-14 20:24:04 [site] cdn-edge-usa-east-atlanta1 129.213.160.208 403 210 HIT waf:virtual_hardening1 GET //?author=1 HTTP/1.1 – Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36

Here the attacker is trying to identify users that were added early in the installation process. They are most likely administrators. 🙂

They then use that information to send POST requests to XMLRPC:

2021-06-21 20:54:36 [site] cdn-edge-usa-east-atlanta1 129.213.160.208 200 726 HIT waf: POST //xmlrpc.php HTTP/1.1 – Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36

That’s interesting. Let’s look at what they’re doing with XMLRPC and why they are sending it POST requests.

How XMLRPC is Used in Brute Force Attacks

We see two main types of brute force attacks against /xmlrpc.

1- getUsersBlogs brute force

The first one is a single user testing, often against the wp.getUsersBlogs method. The attackers send a POST payload to /xmlrpc.php with the XMLRPC method, similar to this one:

<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>userx</value></param>
<param><value>author</value></param>
</params>
</methodCall>

WordPress XMLRPC supports many method names, but the attackers often choose the wp.getUsersBlogs method because it requires authentication and they leverage it to check if the user is valid. In this case, they are trying the username userx with password “author”.

2- system.multicall brute force

The second one is more popular and also more dangerous. It leverages the system.multicall method. It allows attackers to try multiple passwords at the same time. They send a similar POST request to /xmlrpc.php with the system.multicall method, followed by a list of actions, like this one:

<methodCall>
<methodName>system.multicall</methodName>
<params>
<param><value><array><data> <value><struct>
<member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member>
<member><name>params</name><value><array><data><value><array><data ><value>
<string>4seasons</string></value><value><string>z43218765z</string></value></data></array> </value></data></array></value></member></struct></value> </data></array></value>
</param>
</params>
</methodCall>

In this case, the attacker tried username 4seasons with password z43218765z against the same wp.getUsersBlogs action.

Hardening XMLRPC and WP-JSON in WordPress

Unfortunately, by default the most effective way to stop all this is to block access XMLRPC the same way you would wp-admin / wp-login.php.

Using .htaccess you can do something like this:

# Block WordPress xmlrpc.php requests
 <Files xmlrpc.php>
 order deny,allow
 deny from all
 allow from xxx.xxx.xxx.xxx
 </Files>

The problem, however, is what if you need it? And if you use JetPack, you need it. In this scenario, you harden XMLRPC by analyzing the POST requests, and stripping malicious requests like the ones against wp.getUsersBlogs and system.multicall. Doing this level of hardening is going to require some advanced skills, but it could be done using something like dump_io for Apache.

We also highly recommend hardening the WP API. Do you need it? Using .htaccess, here is one method available to help lock it down:

# BLOCK REQUEST TO WP REST API
# Block/Forbid Requests to: /wp-json/wp/
# WP REST API REQUEST METHODS: GET, POST, PUT, PATCH, DELETE
RewriteCond %{REQUEST_METHOD} ^(GET|POST|PUT|PATCH|DELETE) [NC]
RewriteCond %{REQUEST_URI} ^.*wp-json/wp/ [NC]
RewriteRule ^(.*)$ - [F]

Alternatively, if this is over your head, but don’t want to be a victim to it or just don’t want to worry about it, then something like a Web Application Firewalls (WAF) might be what you’re after. Most WAF technologies will offer some form of virtual hardening and patching, which essentially means you get peace of mind while it protects your site.