WordPress REST API user enumeration

  • Tomas Antal
  • /
  • 28/11/2017
  • /
  • 11
  • /
  • 1

What is user enumeration?

Hackers and attackers at first usually try to gather information about your website and search for possible vulnerabilities with active plugins, themes or the WP core. Part of this is the user enumeration process, when the hacker tries to find the admin username that can be later used for a bruteforce attack.

Hackers usually try to enumerate users the following way:

https://wtsn.eu/?author=1

The request will be redirected to the author’s page with the corresponding user ID:

https://wtsn.eu/author/example_admin

Since we block this type of user enumeration, this will not work. However, many websites reveal user and possible admin details.

The REST API has a /users endpoint that is useful for getting more information about the post authors. Only the authors, therefore the users with published, publicly-available post are listed. It is generally advised to use only an account with lowered capabilities for posting and commenting on a website. The admin accounts should be used for administrative tasks.

The /users endpoint for our website is the following:

https://wtsn.eu/wp-json/wp/v2/users

Since we blocked this type of user enumeration as well, it will not work. But you can check the results on the official WordPress.org site which reveals information about Barry, Douglas and many other users:

https://wordpress.org/showcase/wp-json/wp/v2/users

So how do I block this?

You can block the traditional query string based user enumeration with the following code:

if (!is_admin()) {
	if (preg_match('/author=([0-9]*)/i', $_SERVER['QUERY_STRING'])) die();
	add_filter('redirect_canonical', 'stop_enum', 10, 2);
}
function stop_enum($redirect, $request) {
	if (preg_match('/\?author=([0-9]*)(\/*)/i', $request)) die();
	else return $redirect;
}

To unset the user endpoints of the REST API, your can use the following code:

add_filter( 'rest_endpoints', function( $endpoints ){
  if ( isset( $endpoints['/wp/v2/users'] ) ) {
    unset( $endpoints['/wp/v2/users'] );
  }
  if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) ) {
    unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
  }
  return $endpoints;
});

You can add both code snippets to your theme’s function.php file or include them in a custom plugin.

Comments

Johnny
22/12/2017: 20:13

Great stuff

Add your comment