Reducing WordPress Load from XMLRPC
Every so often my website would get hit with high load but when I looked at the website analytics, I wouldn’t see any traffic that would explain it. It wasn’t until I was analyzing the nginx access logs through splunk that I realized what was causing it. Every couple days the xmlrpc.php file was getting hit hard by a bot.
What is XMLRPC?
This file is basically an old xml based remote procedure call (RPC) that allows 3rd party sites to query and interact with your wordpress via XML. You might get pingbacks from other sites or you might be using a plugin on your site that uses xmlrpc.
What Commands are Available:
blogger.deletePost blogger.editPost blogger.getPost blogger.getRecentPosts blogger.getUserInfo blogger.getUsersBlogs blogger.newPost demo.addTwoNumbers demo.sayHello metaWeblog.deletePost metaWeblog.editPost metaWeblog.getCategories metaWeblog.getPost metaWeblog.getRecentPosts metaWeblog.getUsersBlogs metaWeblog.newMediaObject metaWeblog.newPost mt.getCategoryList mt.getPostCategories mt.getRecentPostTitles mt.getTrackbackPings mt.publishPost mt.setPostCategories mt.supportedMethods mt.supportedTextFilters pingback.extensions.getPingbacks pingback.ping system.getCapabilities system.listMethods system.multicall wp.deleteCategory wp.deleteComment wp.deleteFile wp.deletePage wp.deletePost wp.deleteTerm wp.editComment wp.editPage wp.editPost wp.editProfile wp.editTerm wp.getAuthors wp.getCategories wp.getComment wp.getCommentCount wp.getComments wp.getCommentStatusList wp.getMediaItem wp.getMediaLibrary wp.getOptions wp.getPage wp.getPageList wp.getPages wp.getPageStatusList wp.getPageTemplates wp.getPost wp.getPostFormats wp.getPosts wp.getPostStatusList wp.getPostType wp.getPostTypes wp.getProfile wp.getRevisions wp.getTags wp.getTaxonomies wp.getTaxonomy wp.getTerm wp.getTerms wp.getUser wp.getUsers wp.getUsersBlogs wp.newCategory wp.newComment wp.newPage wp.newPost wp.newTerm wp.restoreRevision wp.setOptions wp.suggestCategories wp.uploadFile
Start with a Honeypot
In the world of security, a honeypot is a fake page that looks like the real thing. It is to lure in bad actors and then log what they do. So I moved my xmlrpc.php out of the way and wrote a honeypot that logs out everything that is trying to connect to xmlrpc. I left it for a few weeks.
If you are interested in running your own honeypot, replace your xmlrpc.php with:
<?php
$body = file_get_contents('php://input');
$trimedBody = preg_replace('/\s*/m', '',$body);
$logline = date("Y/m/d.H:i:s")." IP:".$_SERVER['REMOTE_ADDR']. ' BODY:'.$trimedBody.PHP_EOL;
file_put_contents('/var/www/xmlrpc.log', $logline, FILE_APPEND);
echo '<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>-32601</int></value>
</member>
<member>
<name>faultString</name>
<value><string>server error. requested method does not exist.</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>';
The xmlrpc has lots of slow, legitimate uses. Then, about once a week, a bot comes in that runs thousands of large queries.
Blocking XMLRPC
The simplest way to deal with this is to just rename or delete the xmlrpc.php file from the root of your wordpress install. The bulk hits from this file are probably automated bots searching for vulnerable wordpress installations.
Don’t want to actually delete it, you can also tell nginx config to deny traffic to it by adding this to the virtual host:
// server * {
location xmlrpc.php {
deny all;
}
// }
Filtering XMLRPC
However, instead of restricting all visitors of xmlrpc.php, what if instead we made it filter out common attacks. Most of the attacks are against system.multicall
and attacking the `admin` user.
<?
php$body = file_get_contents('php://input');
if(str_contains($body, "admin")){ exit 1; } include("xmlrpc-original.php")
Throttling XMLRPC
But what if you have a plugin or real traffic that needs to use xmlrpc page? Throttling might be the best route.
This is still a work in progress post, throttling is difficult to keep track of state.