[wp-hackers] New Security Vulnerability?

Owen Winkler ringmaster at midnightcircus.com
Thu Mar 9 20:26:03 GMT 2006


Denis de Bernardy wrote:
> +1. an advisory with proof of concept code to mass-produce users. how
> __evil__. at this rate, we'll soon see an advisory with proof of concept
> code to mass-produce comments. ;)

Along those lines, here's a proof of concept to mass block user 
registrations from the same IP within 5 minutes.  (It also blocks brute 
force password cracks.)

The comment blocking thing is already done, I think.  ;)

Owen


-------------- next part --------------
<?php
/*
Plugin Name: Armor
Plugin URI: http://redalt.com/wiki/Armor Plugin
Description: Add some security-related features to WP.
Author: Owen Winkler
Version: 0.1
Author URI: http://asymptomatic.net
*/

class Armor 
{
	function Armor()
	{
		load_plugin_textdomain('armor');
		
		add_action('wp_authenticate', array(&$this, 'wp_authenticate'), 99999);
		add_filter('validate_username', array(&$this, 'validate_username'), 99999, 2);
	}

	function login_delay($content) 
	{
		$error = __('ERROR: Too many retries.  Login disabled for 10 minutes.', 'armor');
		$content = preg_replace('/<div id=\'login_error\'>(.*?)<\/div>/i', "<div id='login_error'>$error</div>", $content);
		return $content;
	}

	function wp_authenticate(&$auth_user, &$auth_pass) 
	{
		global $using_cookie, $error;
		
		wp_cache_flush();

		$index = $_SERVER['REMOTE_ADDR'];
		
		$timeouts = get_settings('login_failure_timeouts');
		if(isset($timeouts[$index])) {
			if(time() < $timeouts[$index]) {
				$auth_pass = '';
				ob_start(array(&$this, 'login_delay'));
			}
		} 
		
		if (!wp_login($auth_user, $auth_pass, $using_cookie) ) {
			// Login failed
			$retries = get_settings('login_failure_retries');
			if(isset($retries[$index])) {
				$retries[$index] ++;
			}
			else {
				$retries[$index] = 1;
			}
			if($retries[$index] > 3) {
				$auth_pass = '';
				$retries[$index] = 0;
				$timeouts[$index] = time() + 600;  // Ten minute timeout				
				ob_start(array(&$this, 'login_delay'));

				$message = sprintf(__('3 failed login attempts from IP: %s', 'armor'), $_SERVER['REMOTE_ADDR']) . "\r\n\r\n";
				$message .= sprintf(__('Last user attempted: %s.', 'armor'), $auth_user) . "\r\n\r\n";
				$message .= __('IP was blocked for ten minutes.', 'armor') . "\r\n";
				
				wp_mail(get_settings('admin_email'), sprintf(__('[%s] Excessive failed login attempts', 'armor'), get_settings('blogname')), $message);
				
				//$this->debug(get_settings('admin_email'), sprintf(__('[%s] Excessive failed login attempts', 'armor'), get_settings('blogname')), $message);
			}
			update_option('login_failure_retries', $retries);
			update_option('login_failure_timeouts', $timeouts);
		}
	}
	
	function validate_username($valid, $username)
	{
		global $errors;
		$minsbetweenregs = 5;
		if($valid) {
			$index = $_SERVER['REMOTE_ADDR'];
			$regs = get_settings('registration_retries');
			if(isset($regs[$index]) && ((time() - $regs[$index]) < $minsbetweenregs * 60 )) {
				$errors['armor'] = sprintf(__('Only one registration is allowed per IP address per %s minutes.'), $minsbetweenregs);
			}
			$regs[$index] = time();
		}
		update_option('registration_retries', $regs);
		return $valid;
	}
	
	function debug($foo)
	{
		$args = func_get_args();
		echo "<pre style=\"background-color:#ffeeee;border:1px solid red;\">";
		foreach($args as $arg1)
		{
			echo htmlentities(print_r($arg1, 1)) . "<br/>";
		}
		echo "</pre>";
	}
}

$armor = new Armor();

?>


More information about the wp-hackers mailing list