The WordPress user enumeration attack

Tags: wordpress, apache, http logs, httplogbrowser

Recently I was contacted by a company that was developing a WordPress web site for a real estate agency but after making the web site public it was immediately hacked. The webmaster did not want to put the website back online until he new why it was compromised. So I was asked to determine how the attacker had gained control over the web site and then to secure it.

The investigation

I downloaded the Apache access log files and loaded them in the HttpLogBrowser and I found several suspicious activities:

Suspicious activity

First, a plugin (ubh) was uploaded from a foreign IP address (Spain) (The real estate agency was located in France). You can see that in the following screenshot (Chronological order is upwards).

A malicious WordPress plugin is uploaded by an internet user

Secondly, two themes (maxbusiness and fuence) were uploaded from a Ukrainian IP address

 A suspicious user is uploading two malicious WordPress themes

A botnet involved

After the first theme (maxbusiness) was uploaded a few hours later there was much activity on a PHP file added by this theme and the activity came from many IP addresses. You can see that in the following screenshot. 900 IP addresses requested the PHP file /wp-content/themes/maxbusiness/fonts/qfsqjiul.php installed by the uploaded theme with a rate of 50 requests per hour. Among these IP addresses only a few requested the file several times.

A botnet is accessing a PHP file installed by a malicious WordPress theme

When I was doing the investigation the rogue files were already removed so I could not see the code of this PHP file. However according to what the webmaster told me, the web hosting provider cut the hosting because the web site was sending too many E-mails. So it was most probable that the mails were sent by this PHP file controlled by a botnet.

The initial attack

It was also clear that the whole purpose of the attack was to use the web site to send spam E-Mail. However at this stage of the investigation I still didn’t know how the attacker could gain admin access to WordPress in order to upload themes. So I continued to analyze the traffic that came from outside the country and more particularly from Ukraine and I found the following suspicious activity from a second Ukrainian IP address just one hour before the malicious theme was uploaded.

Attack of a WordPress web site

I did not immediately understand the attack but there were requests to the XML-RPC module that is known to be used to brute force passwords. There were also requests to wp-login.php, the login page of WordPress. And there were also some other web requests that were cryptic to me because I was a newbie in WordPress security at that time.

I asked then to the webmaster to put the web site back online and when I ran a security audit with Hacker Target

I saw this in the result: Warning! User Enumeration is possible

Use enumeration detected in a default WordPress installation

That’s something I didn’t know. It’s possible to enumerate users on a default installation of WordPress.

If you take a look again at the screenshot of the attack (see below) you see several requests with author=N (with N=1 to 7) as query parameter. You also see that for author=1 the request is redirected to a URL that contains the name of the admin account (wpadmin). So it’s easy to guess the account used to administer WordPress. The other requests with author > 1 lead to a 404 (not found) error because there was only one account configured.

User enumeration attack of a WordPress web site

Weak password

What was worse is that when I got the password of the admin account I saw that it was identical to the account name. The Webmaster did this by thinking that the admin account name could not be guessed by someone outside and this was the mistake.

Trying the account name as password was probably the first thing the attacker did. A brute force attack to guess the password wasn’t even needed!

Securing the web site

Strong password

After understanding that, I immediately changed the admin password with a complex password generated by WordPress itself.

Generate a strong password in WordPress

A strong password generated in WordPress

The lesson to learn from this is to never use a password that can be guessed from the account name because the account name is not a secret. Today it’s easy enough to use a complex password stored in a password manager.

Avoid user enumeration

However if needed there is a way to disable the user enumeration in WordPress and this is explained in the following article: Stop User Enumeration in WordPress

One of the two methods proposed by this article is to add the following lines in the .htaccess file located at the root of the WordPress site (If hosted by an Apache web server).

# Block User ID Phishing Requests  
<IfModule mod_rewrite.c>  
RewriteCond %{QUERY_STRING} ^author=([0-9]*)  
RewriteRule .* http://example.com/? [L,R=302]  
</IfModule>

Replace http://example.com with the base URL of the WordPress site. The effect will be to automatically redirect any request with “author=?” as query string to the root of the web site instead of the author page. We can see the effect by checking the Apache logs after the modification was done. Take a look at the following screenshot:

The user enumeration is disabled in a WordPress web site

But this may not be enough because there is now a new way since WordPress 4.7 to enumerate users through the json API. The json API is only enabled if the Permalinks are not set to Plain in the settings. You can check if you are concerned by requesting the following URL on your WordPress site:
http://your-wordpress-site.com/wp-json/wp/v2/users/
If you are concerned you will get a json file with all authors with published content on the web site as in the following screenshot.

Retreves all user accounts of a WordPress web site through the json API

So here is an improved version of the rewrite rule that handles both ways to enumerate users and respond with a 403 status (forbidden). These lines need to be added before the line # BEGIN WordPress where the WordPress directives start in the .htaccess.

<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_URI} ^/wp-json/wp/v2/users [OR]
RewriteCond %{QUERY_STRING} ^author=([0-9]*)
RewriteRule ^ - [L,R=403]
</IfModule>

Now you get this when you try to get users through the json API:

The user enumeration of a WordPress web site through the json API is disabled

So the user enumeration can be easily stopped but what is more difficult is that user names may also be displayed by the WordPress theme at different places on the web site and if that’s the case you will need to change the code of the theme possibly at several locations in order to completely hide all account names.

Conclusion

We saw how a WordPress web site could be easily hacked by an attacker because the webmaster used the account name as password. We saw how it was possible to investigate the attack with the Apache access logs and the HttpLogBrowser to analyze them. Then we finally saw how to secure the web site against such an attack.

As last advice, it’s always important to understand how a web site was hacked. Otherwise you cannot learn from your mistakes. The webmaster was maybe young and unexperienced but he had the good reflex to not put the web site back online until the problem was understood and fixed.

3 Comments

  • jnh said

    Fixed some typos in the URL rewrite rule

  • Simon said

    Good analysis and nice how-to for HttpLogBrowser.

    Thanks also for the link to HackerTarget.

  • jnh said

    Added mention of the user enumeration with the WordPress json API and added an improved version of rewrite rules

You must log on to comment.