In many WordPress blogs, it’s possible to enumerate WordPress users using a well-known feature/bug related to author archives. This works if the following conditions are met:

  • WordPress permalinks are enabled. By default WordPress uses web URLs which have question marks and lots of numbers in them; however, WordPress offers the ability to create a custom URL structure for your permalinks and archives. Many blogs use this feature.
  • The user has to write at least one post in order to be listed.

How does it work?

When permalinks are enabled, WordPress provides a URL that lists all the posts written by a certain user. For example, the URL will list all the posts written by the first user (with id 1).  However, it will first redirect to a URL containing the username of this user id. In this example, WordPress will redirect to http://dev/wordpress-3.9.1/author/adm1n/.

adm1n is the username of the user with id 1. So, even if the WordPress installation followed security practices and renamed the administrator account, an attacker can use this trick to discover administrative account name.

Sample HTTP request:

With this, an attacker can iterate through all the user ids and list all the WordPress users that have at least one post. This represents a security risk that should be addressed, especially since WordPress doesn’t prevent repeated password-guessing attacks.

How to enumerate all WordPress users with the Acunetix WVS HTTP Fuzzer

The rest of this article shows you how to use the Acunetix HTTP Fuzzer to discover the usernames of a WordPress installation by exploiting the issue described above.

Start Acunetix WVS and change to Tools > HTTP Fuzzer. First, prepare the HTTP request. Start by creating a Number Generator that iterates through the first 10 user ids.The number Generator should have the following properties:

  • Name: author_id
  • Start number: 1
  • Stop number: 10
  • Increment: 1
  • Encoding: None
  • Padding: No Padding

This should list the first 10 WordPress users. Your Number Generator should look as shown in the screenshot.

Next, the author_id Number Generator needs to be inserted in the HTTP request after /?author= URI. This is also shown in the screenshot. You will need to define the Host to match the hostname of your wordpress installation, and the URL might change slightly depending on the version of WordPress you are using.

Click ‘Start’ and the HTTP Fuzzer will start generating an HTTP request for each iteration, replacing the value of author_id with the current value of the iterator. The results are shown below.

The first 3 requests returned the HTTP status code 301 (Moved Permanently) and the rest of the requests returned 404 Not Found. 301 means that this user id is valid and WordPress will redirect to the archive for this user. So, this WordPress installation has 3 users that wrote at least one post.

The username is returned in the Location header in the HTTP Response. It would be cool if we could extract the username from the response automatically so we don’t have to manually click on each result and copy and paste the username.

This is possible using the HTTP Fuzzer filters. Fuzzer filters can be used to show the results that only meet particular conditions (for example the HTTP status code is 301). The HTTP Fuzzer filters can also be configured to extract and log information from the response.

Click on Fuzzer Filters to create the Log Fuzzer Filter. The Fuzzer Filter should have the following properties:

  • Rule Description: Give it a meaningful name
  • Rule type: Log
  • Regular Expression: Location: .*?/author/(.*?)/
  • Log string: Username: $1

This filter is applied on the Response, and uses the regular expression Location: .*?/author/(.*?)/ to extract the username from the Location header. The user name will be logged in the activity window using the Log string definition.

When running the fuzzer again, you should receive a list of log entries (one for each listed username):

Fuzzer log: Username: adm1n (Generators : author_id = 1)
Fuzzer log: Username: johnsmith (Generators : author_id = 2)
Fuzzer log: Username: janesmith (Generators : author_id = 3)

Three users are listed; adm1n, johnsmith and janesmith. For each user, the log entry includes the values of the Generator. It’s possible to use multiple generators and the HTTP Fuzzer will iterate through each value of each generator.

Detection of WordPress Username Enumeration

Acunetix WVS can detect WordPress installations that allow enumerating of the WordPress users. This is detected as a Medium Severity vulnerability and would be reported as follows:

How do we prevent somebody from enumerating our WordPress users?

One workaround would be to block this attack from .htaccess:

RewriteCond %{QUERY_STRING} author=d
RewriteRule ^ /? [L,R=301]

However, this is not ideal as it’s highly likely that somebody will find a bypass for these .htaccess rules – essentially, it is a WordPress problem and should be fixed in WordPress.

A better solution would be to have very strong passwords for your WordPress accounts so even if an attacker can enumerate your usernames they cannot gain access to the WordPress Dashboard. However, an attacker trying to login using thousands of passwords will generate as much requests and will slow down your website considerably.

The best solution would be to have an official fix from WordPress combined with prevention of repeated password-guessing attacks (not implemented as some plugin).

Bogdan Calin

Acunetix developers and tech agents regularly contribute to the blog. All the Acunetix developers come with years of experience in the web security sphere.