WordPress xmlprc.php DDoS and brute-force attacks. How to identify, block, mitigate and leverage these xmlrpc.php scans, brute-force, and user enumeration attacks on WordPress sites… Secure WordPress xmlprc.php interface and reduce service disruption.

Advertisement:

WordPress xmlrpc.php attack characteristics #

WordPress <= 3.9.2 XML-PRC brute-force.

All attacked WordPress sites share the same characteristics: HTTP POST requests to /xmlrpc.php, all with an HTTP_USER_AGENT Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5), by random IP addresses (probably hacked and part of a botnet).

As shown below:

2014-07-07 09:16:59 77.94.248.174 POST /xmlrpc.php - 80 - 
  203.126.164.157 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  www.example.com 200 0 64 0 471 32901
2014-07-07 09:16:59 77.94.248.174 POST /xmlrpc.php - 80 -
  122.167.183.21 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  www.example.com 500 0 64 0 471 30108
2014-07-07 09:17:00 77.94.248.174 POST /xmlrpc.php - 80 - 
  39.51.10.73 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  www.example.com 200 0 64 0 471 32292
2014-07-07 09:17:00 77.94.248.174 POST /xmlrpc.php - 80 - 
  94.67.115.29 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  www.example.com 200 0 64 0 471 32105
2014-07-07 09:17:02 77.94.248.174 GET / - 80 - 
  203.126.164.157 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  www.example.com 500 0 64 0 145 30405
2014-07-07 09:17:02 77.94.248.174 GET / - 80 - 
  76.71.193.113 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  www.example.com 500 0 64 0 145 30155

Notice the POST payload is about 10 times bigger than the wp.getUsersBlogs bruteforce attacks mentioned further down below.

The attacks come at such high rates that the webservers sometimes return HTTP 500 Service Unavailable error messages for those websites. How to reduce this service disruption is explained in the next section. Attacked websites don’t run the same WordPress version. I’ve seen these attacks on WordPress versions 3.0.2 (*sigh* yep…) through 3.9.1.

About WordPress XML-RPC support
WordPress uses an XML-RPC interface. With WordPress XML-RPC support, you can post to your WordPress blog using many popular Weblog Clients. The XML-RPC system can be extended by WordPress Plugins to modify its behavior. All built-in XML-RPC methods use the action xmlrpc_call, with a parameter equal to the method’s name (e.g., wp.newPost). The action is performed after the user has been authenticated but before the rest of the method logic begins.

2014-07-07 10:46:23 77.94.250.138 POST /xmlrpc.php - 80 - 
  93.174.93.204 Mozilla/4.0+(compatible:+MSIE+7.0;+Windows+NT+6.0) - 
  www.example.net 200 0 64 0 417 359
2014-07-07 10:55:59 77.94.250.138 POST /xmlrpc.php - 80 - 
  93.174.93.204 Mozilla/4.0+(compatible:+MSIE+7.0;+Windows+NT+6.0) - 
  www.example.net 200 0 64 0 417 343
2014-07-07 11:01:28 77.94.250.138 POST /xmlrpc.php - 80 - 
  93.174.93.204 Mozilla/4.0+(compatible:+MSIE+7.0;+Windows+NT+6.0) - 
  www.example.net 200 0 64 0 417 406
2014-07-07 11:06:57 77.94.250.138 POST /xmlrpc.php - 80 - 
  93.174.93.204 Mozilla/4.0+(compatible:+MSIE+7.0;+Windows+NT+6.0) - 
  www.example.net 200 0 64 0 417 343

As you can see, this XML-RPC payload is much smaller, and it’s always the same IP address. The IP address is known to be a comment spammer, so I suspect this to be a different -spamming- attack. But I’ll keep an close eye on it.

How To Log XMLRPC.php POST Data to a Logfile #

Log WordPress XML-RPC brute-force data to a logfile for analysis and inspection: on a number of WordPress blogs I run, I wanted to log the HTTP POST information send to xmlrpc.php. Just to see what is POST’ed.

I made a quick ‘n dirty logger in WordPress’ xmlrpc.php file, it’s ugly… Open up xmlrcp.php, and add on line 2, after the PHP open tag <?php:


// Quick 'n dirty XMLRPC logger
$xmlrpc_log = "../database/xmlrpc_log.txt";
$text = "";
if ( $_SERVER['REQUEST_METHOD'] === 'POST' )
{
	foreach ( $_POST as $key => $value ) {
		$text .= $key ." : ". $value ."\r\n";
	}
	if( file_exists( $xmlrpc_log ) && is_writable( $xmlrpc_log ) )
	{
		// our XMLRPC "honeypot" logfile is writeable, let's continue
		$f = fopen( $xmlrpc_log,"ab" );
		fwrite( $f, $text );
		fclose( $f );
		clearstatcache();
	}
}

Now we wait…

XMLRPC-logger result
The result of the above XMLRPC-logger is quite interesting. It appears it could be a WordPress user blog enumeration scan. On one of the websites I enabled the logger, I noticed HTTP POST requests in the HTTP logfile:

2014-07-09 09:51:28 77.94.248.175 POST /xmlrpc.php - 80 - 
  84.40.123.39 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  example.com 200 0 0 588 500 202
2014-07-09 09:51:29 77.94.248.175 POST /xmlrpc.php - 80 - 
  182.186.89.187 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  example.com 200 0 0 588 503 592
2014-07-09 09:51:42 77.94.248.175 POST /xmlrpc.php - 80 - 
  77.31.97.165 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  example.com 200 0 0 588 502 249
2014-07-09 09:51:45 77.94.248.175 POST /xmlrpc.php - 80 - 
  162.198.57.86 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  example.com 200 0 0 588 499 343
2014-07-09 09:51:52 77.94.248.175 POST /xmlrpc.php - 80 - 
  213.233.104.10 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  example.com 200 0 0 588 515 265
2014-07-09 09:52:05 77.94.248.175 POST /xmlrpc.php - 80 - 
  112.135.143.66 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  example.com 200 0 0 588 501 390
2014-07-09 09:52:05 77.94.248.175 POST /xmlrpc.php - 80 - 
  109.93.159.232 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  example.com 200 0 0 588 503 234
2014-07-09 09:52:08 77.94.248.175 POST /xmlrpc.php - 80 - 
  111.68.38.156 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  example.com 200 0 0 588 503 686
2014-07-09 09:52:15 77.94.248.175 POST /xmlrpc.php - 80 - 
  181.188.88.148 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  example.com 200 0 64 0 501 374
2014-07-09 09:52:16 77.94.248.175 POST /xmlrpc.php - 80 - 
  96.36.130.121 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  example.com 200 0 0 588 501 265
2014-07-09 09:52:18 77.94.248.175 POST /xmlrpc.php - 80 - 
  176.73.72.206 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  example.com 200 0 0 588 501 218
2014-07-09 09:52:36 77.94.248.175 POST /xmlrpc.php - 80 - 
  196.29.199.46 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  example.com 200 0 0 588 502 358
2014-07-09 09:52:41 77.94.248.175 POST /xmlrpc.php - 80 - 
  121.54.54.50 Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5) - 
  example.com 200 0 0 588 503 873

Notice that, in this case, the POST payload is much smaller, so this could be the result from a different type of attack. The logged POST data is:

<?xml_version"1.0" encoding="iso-8859-1"?>
<methodCall>
  <methodName>wp.getUsersBlogs</methodName>
  <params>
    <param>
      <value>
        <string>example_com</string>
      </value>
    </param>
    <param>
      <value>
        <string>nickie</string>
      </value>
    </param>
  </params>
</methodCall>
<?xml_version"1.0" encoding="iso-8859-1"?>
<methodCall>
  <methodName>wp.getUsersBlogs</methodName>
  <params>
    <param>
      <value>
        <string>example_com</string>
      </value>
    </param>
    <param>
    <value>
      <string>nicola</string>
    </value>
    </param>
  </params>
</methodCall>
<?xml_version"1.0" encoding="iso-8859-1"?>
<methodCall>
  <methodName>wp.getUsersBlogs</methodName>
  <params>
    <param>
      <value>
        <string>example_com</string>
      </value>
    </param>
    <param>
      <value>
        <string>nick</string>
      </value>
    </param>
  </params>
</methodCall>
<?xml_version"1.0" encoding="iso-8859-1"?>
<methodCall>
  <methodName>wp.getUsersBlogs</methodName>
  <params>
    <param>
      <value>
        <string>example_com</string>
      </value>
    </param>
    <param>
      <value>
        <string>nicolas</string>
      </value>
    </param>
  </params>
</methodCall>
<?xml_version"1.0" encoding="iso-8859-1"?>
<methodCall>
  <methodName>wp.getUsersBlogs</methodName>
  <params>
    <param>
      <value>
        <string>example_com</string>
      </value>
    </param>
    <param>
      <value>
        <string>nico</string>
      </value>
    </param>
  </params>
</methodCall>
<?xml_version"1.0" encoding="iso-8859-1"?>
<methodCall>
  <methodName>wp.getUsersBlogs</methodName>
  <params>
    <param>
      <value>
        <string>example_com</string>
      </value>
    </param>
    <param>
      <value>
        <string>nicole1</string>
      </value>
    </param>
  </params>
</methodCall>
[...]

wp.getUsersBlogs is used to retrieve the blogs of the users.

Mitigate WordPress XML-RPC attacks and wp.getUserBlogs enumeration scans – How to Disable XML-RPC in WordPress #

How to mitigate attacks and degraded web server performance…

Unfortunately you can’t just simply remove the WordPress xmlrpc.php file. It should be possible to mitigate against this wp.getUserBlogs enumeration scan with a filter, which we put in our THEME functions.php file. Basically it’s the same as the filter below to disable the pingback.ping function.

Here is an example of removing wp.getUsersBlogs method:

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

But of course HTTP POST requests on xmlrpc.php will still come in.

Reduce web server load caused by XMLRPC.php attacks on websites #

The performance of a web server might severely degrade when you host multiple WordPress sites that are under attack. Therefore we need to take action to reduce the disturbance caused by these attacks and the inconvenience for other sites. More than one action to take is possible and recommended.

Update WordPress – security release 3.9.2 fixes XML-RPC DoS #

Update 2014-08-07: Both WordPress and Drupal released security updates to fix an XML-RPC DoS vulnerability in the XML-RPC implementation. WordPress 3.9.2, Drupal 7.31 and Drupal 6.33 fixes this severe XML-RPC Denial of Service (DoS).

Disable WordPress wp_cron in wp-config.php #

A good starting point in reducing the service disruption is to disable WordPress’ WP_CRON functionality. The wp_cron.php file is executed after each HTTP request and thus it’s executed a lot. This is a drain on your database; it makes an extra database connection for each time it’s executed.

Open up your wp-config.php file and add:

define( 'DISABLE_WP_CRON', 'true' );

While you’re at it, verify WordPress’ WP_DEBUG is disabled too: define('WP_DEBUG', false);

Another good method to reduce server loads during an attack is to optimize WordPress wp_options table and autoload feature. This micro-optimization retrieves autoloaded options much faster using less resources.

How to Disable XMLRPC.PHP in WordPress Completely #

You can disable the XML-RPC functionality in WordPress by adding to your theme’s functions.php file:

add_filter( 'xmlrpc_enabled', '__return_false' );

Disable only XMLRPC pingback functionality #

Another action is to disable WordPress’ XMLRPC pingback function. You can achieve this by adding the following filter to your THEME’s functions.php file:

function saotn_remove_xmlrpc_pingback( $methods ) {
    unset( $methods['pingback.ping'] );
    return $methods;   
}
add_filter( 'xmlrpc_methods', 'saotn_remove_xmlrpc_pingback' );

Deny WinHttpRequest User-Agent in .htaccess #

We don’t want shady user-agents requesting information on our website. Since this attack on mulitple sites has in common that the User-Agent is always Mozilla/4.0+(compatible;+Win32;+WinHttp.WinHttpRequest.5), I believe it’s wise to block this User-Agent on the website under siege.

Add to your .htaccess file:

RewriteCond %{HTTP_USER_AGENT} .*WinHttp.WinHttpRequest.* [NC]
RewriteRule .* - [F,L]

Remove WordPress spam comments and disable WordPress comments #

It is recommended to check your WordPress database for spam comments, and to delete spam comments if they are present. The larger your WordPress MySQL database is, the bigger the disturbance. When your WordPress website is under attack, quickly disable WordPress comments on all posts, for the same reason. You can re-enable them later.

WordPress .htaccess protection from XMLRPC-, comment- and trackback spam #

when the visitor isn’t coming from your website as referer

It has always been important to protect your WordPress site from comment- and trackback spammers, and to block spammers manually if necessary. Especially if they don’t come from your website (e.g. don’t have your domain name as referer). To block all POST requests on xmlrpc.php, wp-comments-post.php and wp-trackback.php, without your domain name as referrer, add to your .htaccess file:

RewriteCond %{REQUEST_METHOD} POST [NC]
RewriteCond %{REQUEST_FILENAME} (xmlrpc|wp-comments-post|wp-trackback)\.php [NC]
RewriteCond %{HTTP_REFERER} !^http://www.example.com [NC]
Rewriterule .* - [F,L]

A fast and easy method is to secure WordPress with a captcha on comment- and log in forms.

IIS requestFiltering, protect and deny access to xmlrpc.php, wp-comments-post.php and wp-trackback.php #

The <denyUrlSequences> element contains a collection of <add> elements that specify sequences of URL characters that IIS will deny, which helps prevent URL-based attacks on the Web server. Access to xmlrpc.php on IIS web servers can be denied with a requestFiltering rule. Just add a denyUrlSequences:

<security>
  <requestFiltering>
    <denyUrlSequences>
      <add sequence="xmlrpc.php" />
      <add sequence="wp-comments-post.php" />
      <add sequence="wp-trackback.php" />
    </denyUrlSequences>
  </requestFiltering>
</security>

When request filtering blocks an HTTP request because of a denied URL sequence, IIS 7 will return an HTTP 404 error to the client and log the following HTTP status with a unique sub status that identifies the reason why the request was denied.

Yes, the point of XMLRPC.php is for remote communiation, but we don’t want that now during these attacks.

The Internet Storm Center report #

The Internet Storm Center (ISC) also reports on this WordPress brute force attack via wp.getUsersBlogs.

WordPress 3.9.2 security release fixes XML-RPC DoS #

Update 2014-08-07: WordPress and Drupal security updates fixes XML-RPC DoS

XML-RPC Brute-force amplification attacks through WordPress xmlrpc.php #

Just a small note: Sucuri writes about ongoing amplified brute-force attacks through the WordPress xmlrpc.php file. Attackers managed to find a way to send a brute-force attack, trying multiple (tens or hundreds) username/password combinations within one request. This way, an XML-RPC brute-force attack might stay under the radar of ordinary Web Application Firewalls or rate-limiting settings.