Audit Log Tampering through IP spoofing – Stream <= 3.9.3

| in


Affected pluginStream
Active installs80,000+
Vulnerable version<= 3.9.3
Audited version3.9.3
Fully patched version4.0.0
Recommended remediationUpgrade to version 4.0.0 or higher.

Description


The plugin, utilized for audit and security logging, exhibits a vulnerability whereby malicious actors can easily spoof IP addresses.

This is less than ideal, given the plugin’s widespread use in compliance and security monitoring contexts.

In a compliance scenario, this undermines the reliability of logs, and in security contexts, this may compromise incident response and forensics efforts by providing misleading information.

Proof of concept


The plugin uses the following code to determine the client IP for each log record that is stored:

/**
	 * Class constructor.
	 *
	 * @param Plugin $plugin Instance of plugin object.
	 */
	public function __construct( $plugin ) {
		$this->plugin = $plugin;

		// Support proxy mode by checking the `X-Forwarded-For` header first.
		$ip_address = wp_stream_filter_input( INPUT_SERVER, 'HTTP_X_FORWARDED_FOR', FILTER_VALIDATE_IP );
		$ip_address = $ip_address ? $ip_address : wp_stream_filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP );

		$this->ip_address = $ip_address;

		// Ensure function used in various methods is pre-loaded.
		if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
			require_once ABSPATH . '/wp-admin/includes/plugin.php';
		}
	}

The wp_stream_filter_input function ultimately ends up calling Core’s
WP_Http::is_ip_address function, which returns the passed value if it’s a valid IPv4 or IPv6 IP address.

The issue here is that the plugin blindly reads from the X-Forwarded-For header, which can be spoofed.

This can be exploited in one of two ways:

1. Sites without a reverse proxy or behind a reverse proxy that does not use the X-Forwarded-For header:

If an attacker spoofs the X-Forwarded-For header for any action, it will be used in the log record.

curl 'https://wp.test/wp-login.php?action=lostpassword' 
-X POST 
--data-raw 'user_login=admin&redirect_to=&wp-submit=Get+New+Password' 
-H "X-Forwarded-For: 165.54.171.228"

The above request triggers a password reset for the admin user.

The plugin will use the spoofed IP 165.54.171.228 in the recorded log entry.

stream
The actual IP address on our dockerized test environment is 172.18.0.1

2. Sites behind a reverse proxy that uses the X-Forwarded-For header:

As per the HTTP spec, a reverse proxy always prepends an existing header to the left of the actual client IP.

If the real client IP is 165.54.171.221 and the attacker sends the IP 170.54.171.221 to the reverse proxy, WordPress will see the header as so:

X-Forworded-For: 170.54.171.221, 165.54.171.221 

In this case, wp_stream_filter_input will return false which means the value of $this->ip_address will always be the IP of the reverse proxy.

Proposed patch


The needed patch is described in great length in this article of us.

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

Everything else is conceptually insecure.

Timeline


Vendor contactedOctober 08, 2023
First ResponseOctober 09, 2023
Fully patched atJanuary 9, 2024
Publicly disclosedJanuary 25, 2024

Miscellaneous


XWP patched the vulnerability as per our recommendations and now only uses REMOTE_ADDR.

Leave a Reply

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