WordPress and XML-RPC.

Being in WebHosting I see a number of WordPress sites being compromised even tho the sites are up to date being that the WordPress version is the latest, The theme and plugins are up to date with the latest releases. In situations like this, the usual culprit is XML-RPC

What is XML-RPC?
XML-RPC is a Remote Procedure Call (RPC) protocol that uses XML to encode its calls and uses HTTP as a transport mechanism. WordPress allows developers to use XML-RPC to update Posts, Pages and upload media programmatically.

Why do hackers use XML-RPC
Malicious actors (Hackers) choose to use XML-RPC to brute force as the direct method of attack (wp-login.php) can be blocked by WordPress itself when you enable “Limit login attempts”. This will deny access after a set number of tries to the wp-login page.

An example of a XML-RPC Bruteforce that would show up in your access logs.

192.168.0.1 - - [26/Apr/2022:01:24:22 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:22 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:22 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:22 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:22 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:22 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:22 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:22 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:22 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:22 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:22 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:22 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:23 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:23 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:23 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:23 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:23 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:23 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [26/Apr/2022:01:24:23 +1000] "POST /xmlrpc.php HTTP/1.1" 200 225 "-" "Python-xmlrpc/3.9"

Looking at an XML-RPC packet used in a brute-force attack we can see a POST request to /xmlrpc.php just above the domain name. We also see the content type is text/XML and the user agent being used which in this case was python-xmlrpc/3.9

What also is interesting is the password is passed to the server encoded as a base64 string. The last thing we see in the request in blue is the response from the server being 200 OK even tho the password attempt was wrong. These requests do not suffer the same fate as invalid logins to the wp-login.php file and therefore the malicious actor can keep hitting the site with different attempts.

As in my example, I am using python and what is returned back with an invalid login is wordpress_xmlrpc.exceptions.InvalidCredentialsError: Incorrect username or password.

Once you have the right username and password combination and can successfully log in, As an example with XML-RPC you can pull out the Post Data

[<WordPressPost: b'Example wordpress post123434'>, <WordPressPost: b'Hello world!'>]

You are able to also publish Posts, Pages and upload images to WordPress, You however are unable to upload direct PHP files as WordPress has denied access to this on the grounds of the security concern.

How do we stop hackers from using XML-RPC
There are a few ways you can block XML-RPC requests. There is a number of plugins available through ‘add plugins’ within WordPress. You are also able to install Wordfence security plugin however the option to disable ‘XMLRPC’ is disabled by default.

Alternatively, you can add the following lines of code to your .htaccess file which will have the same effect as the plugins.

<Files xmlrpc.php>
order deny,allow
deny from all
</Files>

Once you have disabled XML-RPC access logs would return a 404 error message.

192.168.0.1 - - [04/May/2022:06:33:35 +1000] "POST /xmlrpc.php HTTP/1.1" 404 9937 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [04/May/2022:06:33:35 +1000] "POST /xmlrpc.php HTTP/1.1" 404 9937 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [04/May/2022:06:33:35 +1000] "POST /xmlrpc.php HTTP/1.1" 404 9937 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [04/May/2022:06:33:35 +1000] "POST /xmlrpc.php HTTP/1.1" 404 9937 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [04/May/2022:06:33:36 +1000] "POST /xmlrpc.php HTTP/1.1" 404 9937 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [04/May/2022:06:33:36 +1000] "POST /xmlrpc.php HTTP/1.1" 404 9937 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [04/May/2022:06:33:36 +1000] "POST /xmlrpc.php HTTP/1.1" 404 9937 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [04/May/2022:06:33:36 +1000] "POST /xmlrpc.php HTTP/1.1" 404 9937 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [04/May/2022:06:33:36 +1000] "POST /xmlrpc.php HTTP/1.1" 404 9937 "-" "Python-xmlrpc/3.9"
192.168.0.1 - - [04/May/2022:06:33:36 +1000] "POST /xmlrpc.php HTTP/1.1" 404 9937 "-" "Python-xmlrpc/3.9"

The malicious actor would also receive an error message back advising they cannot make an XML-RPC call. This will also come down to how their error reporting is configured.

If XML-RPC is not required in the development or use of your website it is highly recommended that you disable the function.