DOS through IP spoofing – (SiteGuard <= 1.6.1)

| in


Affected pluginSiteGuard
Active installs500,000+
Vulnerable version<= 1.6.1
Audited version1.6.1
Fully patched versionPENDING
Recommended remediationPENDING

Description


An attacker can exploit an IP spoofing vulnerability in the plugin to ban arbitrary users or the site’s reverse proxies.

This is only possible under the following conditions:

  • The user configured the plugin to use the X-Forwarded-For header instead of REMOTE_ADDR.
  • The site is behind a reverse proxy but is also directly accessible from the internet (most WordPress sites fall into this category).

Proof of concept


The SiteGuard plugin handles IP detection better than 99% of WordPress security plugins.

However, it is vulnerable to any of the “advanced” scenarios outlined here:

The plugin uses the following function to detect the IP address when evaluating rate limits.

function get_ip( ) {
    global $siteguard_config;
    $ip_mode = $siteguard_config->get( 'ip_mode' );
    if ( ! in_array( $ip_mode, SiteGuard_Base::$ip_mode_items ) ) {
        $ip_mode = '0';
        $siteguard_config->set( 'ip_mode', $ip_mode );
        $siteguard_config->update( );
    }
    $ip_mode_num = intval( $ip_mode );
    $remote_addr = '127.0.0.1';
    if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
        $remote_addr = $_SERVER['REMOTE_ADDR'];
    }
    if ( '0' === $ip_mode ) {
        return $remote_addr;
    }
    if ( ! isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
        return $remote_addr;
    }
    $xff = $_SERVER['HTTP_X_FORWARDED_FOR'];
    if ( empty( $xff ) ) {
        return $remote_addr;
    }
    $ips = explode( ',', $xff );
    $count = count( $ips );
    $idx = $count - $ip_mode_num;
    if ( $idx < 0 ) {
        return $remote_addr;
    }
    $ip = $ips[ $idx ];
    if ( ! filter_var($ip, FILTER_VALIDATE_IP ) ) {
        return $remote_addr;
    }
    return $ip;
}

The relevant lines are highlighted above (20-34).

The plugin uses one of the two valid approaches for IP detection, described here.

“Using the Nth IP address from the right, where N is the number of reverse proxies”.

However, the plugin misses ensuring that the REMOTE_ADDR (the connecting IP) belongs to a trusted proxy each time it uses an HTTP header to determine the IP address.

This opens the possibility for the following (advanced) attack vector:

Most WordPress sites use Cloudflare as a proxy. But they do NOT use authenticated origin pulls to ensure that ONLY CloudFlare and nobody else can connect directly to the server.

If an attacker can determine the IP address of the target site, he can provide the plugin with a spoofed header that looks exactly like the one the plugin is expecting from the reverse proxy.

Unfortunately, this is not something out of the possibility when using automated tools like theHarvester.

Assuming the site owner configures the plugin to run behind ONE reverse proxy.

An attacker directly connecting to the server (using a local host entry) can submit the following request to ban random IP addresses.

curl -X POST https://target.com/wp-login.php -d "log=foo" "pwd=bar" -H "X-Forwarded-For: 147.93.33.75"

The relevant part of the “get_ip” method is:

$ips = explode( ',', $xff ); // EDITOR: This is an array with one element
$count = count( $ips); // EDITOR: (int) 1
$idx = $count - $ip_mode_n // EDITOR: $idx = (int) 0
if ( $idx < 0 ) {
 return $remote_addr;
}
$ip = $ips[ $idx ];

// EDITOR: $ip 147.93.33.75, spoofed. 

The same exploit also works if the user configured the plugin to expect more than one reverse proxy. The attacker must only increase the number of spoofed IP addresses in the curl request.

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 *