Skip to content

Instantly share code, notes, and snippets.

@jaydansand
Last active August 18, 2020 21:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaydansand/dcb66a0e36365c178a91 to your computer and use it in GitHub Desktop.
Save jaydansand/dcb66a0e36365c178a91 to your computer and use it in GitHub Desktop.
Quick and dirty WordPress plugin to rate-limit XMLRPC calls (especially on login failure)
<?php
/*
Plugin Name: XMLRPC Slowdown
Plugin URI:
Description: Deliberately introduce some delay into XMLRPC calls to rate limit brute-force and DoS attacks.
Version: 1.0
Author: Jay Dansand, Technology Services, Lawrence University
Author URI: https://gist.github.com/jaydansand
*/
/* Copyright 2014 Lawrence University
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2, as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Implement filters xmlrpc_enabled and xmlrpc_login_error.
*
* @see class-wp-xmlrpc-server.php
*/
function xmlrpc_slowdown_filter_sleep($arg) {
// Keep track of calls (should be 1 or 2) to increase delay
// when called after a login failure.
static $calls = 0;
++$calls;
$rand = function_exists('mt_rand') ? 'mt_rand' : 'rand';
usleep(call_user_func($rand, 500000 * $calls, 2000000 * $calls));
return $arg;
}
add_filter('xmlrpc_enabled', 'xmlrpc_slowdown_filter_sleep');
add_filter('xmlrpc_login_error', 'xmlrpc_slowdown_filter_sleep');
@f00stx
Copy link

f00stx commented Aug 9, 2020

FYI, I've seen this kind of mitigation in use before and if someone is bruteforcing xmlrpc, the results aren't pretty - you'll end up with all your workers occupied because each call is taking a lot longer. I'd simply no-op an increasing percentage of requests so the workers can finish a request and move onto the next etc.

@jaydansand
Copy link
Author

Thanks for the comment/concern! I get the theory, but I must be missing something: if each attacking node (for example, let's say 50 of them) limits itself to any finite number of simultaneous connections (let's say 5), then regardless of whether you're responding really quickly or really slowly, they're still going to be occupying on average 50 * 5 = 250 workers (or any other finite number) at a time, right? The only situation where this script would cause a logjam (where there wasn't nearly one already) is where the brute force attacker is simply spawning new requests as quickly as it can, not waiting for previous requests to finish. That's no longer someone trying to log in, that's someone trying to take you down. Admittedly, this script does not help with that at all!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment