Affected plugin | iThemes Security |
Active installs | 1+ million |
Vulnerable version | <= 8.1.2 |
Audited version | 8.1.2 |
Fully patched version | – |
Recommended remediation | Removal of the plugin |
Description
The plugin is wide open to IP spoofing, which an attacker can use to exploit to ban search-engine crawlers, the site’s reverse proxy, or legitimate users.
Proof of concept
The plugin uses the ITSEC_Lib::get_ip() method in almost all places where access to the current request’s IP address is needed.
Following the code path of ITSEC_Lib::get_ip() ultimately reveals that the plugin blindly reads the IP address from HTTP headers.
The relevant method is ITSEC_Lib_IP_Detector::get_ip()
private function get_ip() {
foreach ( $this->headers as list( $header, $position ) ) {
$ip = $this->get_for_header( $header, $position );
if ( ! $ip ) {
continue;
}
if ( $this->allow_private ) {
$ip = filter_var( $ip, FILTER_VALIDATE_IP );
} else {
$ip = filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE );
}
if ( $ip ) {
return (string) $ip;
}
}
return '';
}
“$this->headers” can have two values, depending on how the site owner configured the plugin.
By default “$this->headers” will be equal to the below array of HTTP header names:
'HTTP_CF_CONNECTING_IP', // Cloudflare
'HTTP_X_FORWARDED_FOR', // Squid, and most other forward and reverse proxies
'HTTP_X_REAL_IP',
'HTTP_X_CLIENT_IP',
'HTTP_CLIENT_IP',
'HTTP_X_CLUSTER_CLIENT_IP',
If the site owner manually configures an IP source, “$this->headers” will only contain the configured HTTP header name.
ITSEC_Lib_IP_Detector::get_for_header() looks like this:
private function get_for_header( $header, $position ) {
if ( empty( $this->server[ $header ] ) ) {
return '';
}
$value = trim( $this->server[ $header ] );
if ( - 1 === $position ) {
return explode( ',', $value )[0];
}
// EDITOR: Irrelevant, $position is always -1.
}}
The plugin is making the mistake of always returning the leftmost IP that is present in an “IP source header”.
Thus, IP spoofing is trivial.
The following scenarios are possible:
In automatic configuration (the default):
- Sites not behind a reverse proxy are wide open since any header is trusted blindly
- Sites behind Cloudflare are reasonably safe because the “CF-Connecting-IP” header is always checked first.
- Sites behind any other reverse proxy are also wide open because an attacker can spoof the “CF-Connecting-IP” header which will always be checked first.
In manual configuration:
- Sites behind Cloudflare are reasonably safe because the “CF-Connecting-IP” header is always checked first. Furthermore, Cloudflare will drop the header if an attacker sends a spoofed request to Cloudflare
- In all other scenarios, the site is wide open to IP spoofing since the plugin always used the leftmost IP address in the header instead of the rightmost.
Proposed patch
The needed patch is described in great length in this article of us.
Summary: Only ever use REMOTE_ADDR to access to current IP.
Timeline
Vendor contacted | September 07, 2022 |
First Response (from a developer) | – |
Fully patched at | – |
Publicly disclosed | April 24, 2023 |
Miscellaneous
- The vendor made us send the POC through third-party chat software to support staff despite asking explicitly for a security@ email.
Leave a Reply