Internal IP Address leak in Misconfigured WordPress to bypass WAF
Web Application Firewalls like CloudFlare are pretty good at protecting websites by tunnelling traffic through their secure servers. But if the underlying IP address is leaked, such protection is usually bypassed and the attacker can directly target the application.
IP Disclosure in WordPress
WordPress stores the site URL and home URL in the database and uses them to serve content or redirect users. But sometimes the website is required to migrate to another domain. This can break the WordPress installation. One way to fix this is taking a backup and restoring it to a new domain and replacing every mention of the old domain in the database with the new domain. This is quite inconsistent and usually creates a lot of breaking if not done correctly. To overcome such inconsistency, WordPress allows two constants ie. WP_HOME
and WP_SITEURL
in wp-config.php
which can be used to override database entries.
If a WordPress site is using these two constants incorrectly, it can lead to old site url disclosure and in some cases, IP Address disclosure.
Environment
The exploitation assumes that a WordPress installation was done on a server without any domain ie. has been installed directly using an IP Address say 123.456.789.012
(This is quite common in VM hostings like AWS EC2 where an IP is allocated but the domain is not pointed yet). Further, it is assumed that the developer later defined the WP_HOME
constant in wp-config.php
to override the old urls definitions but did not configured WP_SITEURL
constant.
Exploitation
For target target.com
, the exploitation starts by following the logout route with the parameter redirect_to
to any other domain:
https://target.com/wp-login.php?action=logout&redirect_to=https://example.com
The WordPress CSRF protection triggers and shows a warning message with a link containing the nonce token:
Clicking on the logout link redirects the attacker to the URL in redirect_to
. But due to filtration for open redirect, WordPress redirects the attacker to the value of admin_url()
.
https://target.com/wp-login.php?redirect_to=https%3A%2F%2F123.456.789.012%2Fwp-admin%2F&reauth=1
Since the developer did not configure WP_SITEURL
the admin_url()
gave the real site URL from the database and in this case, the real ip stored within the database.
Internal Explanation
The logout functionality redirects the user using the function wp_safe_redirect
which calls wp_sanitize_redirect
, wp_validate_redirect
and in the end wp_redirect
ie. The given argument to wp_safe_redirect
is first sanitized, then validated and at the end redirected.
The function wp_safe_redirect
resides in wp-includes/pluggable.php
<?php
function wp_safe_redirect( $location, $status = 302, $x_redirect_by = 'WordPress' ) {
// Need to look at the URL the way it will end up in wp_redirect().
$location = wp_sanitize_redirect( $location );
/**
* Filters the redirect fallback URL for when the provided redirect is not safe (local).
*
* @since 4.3.0
*
* @param string $fallback_url The fallback URL to use by default.
* @param int $status The HTTP response status code to use.
*/
$location = wp_validate_redirect( $location, apply_filters( 'wp_safe_redirect_fallback', admin_url(), $status ) );
return wp_redirect( $location, $status, $x_redirect_by );
}
As you can see, the filter wp_safe_redirect_fallback
uses admin_url()
which falls back to the entry in the database on missing WP_SITEURL
.
Mitigation
For most WordPress installations, defining WP_SITEURL
along with WP_HOME
is enough to protect against such leaks. If you for some reason don't wish to define WP_SITEURL
, you should change the entries within the database appropriately. On the other hand, if you are trying to migrate your website to another domain, it would be best to use a migration plugin that automatically replaces the old references.