WordPress and the evil RELOCATE.

A few days ago I found the following directive inside the WordPress configuration file, wp-config.php, of one of our customers:

define('RELOCATE', true);

Most WordPress users never heard of what is the RELOCATE constant. In fact, the only few lines of code related to it can be found in the wp-login.php script:

if ( defined( 'RELOCATE' ) && RELOCATE ) { // Move flag is set
   if ( isset( $_SERVER['PATH_INFO'] ) && ( $_SERVER['PATH_INFO'] != $_SERVER['PHP_SELF'] ) ) {
      $_SERVER['PHP_SELF'] = str_replace( $_SERVER['PATH_INFO'], '', $_SERVER['PHP_SELF'] );

   $url = dirname( set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] ) );
   if ( $url != get_option( 'siteurl' ) ) {
      update_option( 'siteurl', $url );

When that constant is set, WordPress will override the blog URL (siteurl) with the $_SERVER['HTTP_HOST'] value and save it to the database. It was last used by our customer when she migrated the site to another host.
The problem is that $_SERVER['HTTP_HOST'] is a user input and thus cannot be trusted. For instance, an attacker could send this request to example.com, if it has the RELOCATE constant set:

$ curl https://example.com/wp-login.php -H 'Host: evil.com'

Because the evil.com domain does not exist on the server, the HTTP server will return the main site instead. If it is example.com, the above simple GET request will be enough to change the blog URL in the database:

MariaDB > SELECT * FROM `wp_options` WHERE `option_name` LIKE 'siteurl';
| option_id | option_name | option_value    | autoload |
|         1 | siteurl     | http://evil.com | yes      |
1 row in set (0.001 sec)

All visitors coming to example.com will be redirected to evil.com.

Our customer hasn’t been hacked, she was very lucky: not only her blog was the main site on the server, but the RELOCATE directive has been set and left in the wp-config.php since the last relocation of her site which occurred… six months ago.


Don’t use RELOCATE.