eval.blog

Internal IP Address leak in Misconfigured WordPress to bypass WAF

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:

logout.png

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.