WordPress is the most popular content management system in the world with over 40% of all websites running on this open-source platform. Given its popularity, WordPress’s core source code is regularly reviewed and heavily dissected by its community, making the core software relatively secure when compared to custom-built web applications. However, this doesn’t mean that WordPress is the Fort Knox of web platforms, in fact, common misconfigurations and poor security hygiene found in many WordPress deployments can leave the application and often the server itself vulnerable to exploitation.
In this blog post we’ll discuss the common WordPress security pitfalls, how these can be identified during penetration testing, and how they can be remediated. Please note, the tools and techniques shown in this article should not be used against any application for which you do not have explicit consent to test.
1. Testing for Vulnerabilities in WordPress Core, Plugins and Themes
WPScan is a common tool used to gather information on a WordPress site, and can enumerate several things, including plugins, themes, backup files and usernames. WPScan can be run with a simple command:
wpscan –url <URL>
However, if you want to ensure your scan is as complete as possible, you can use the following options:
wpscan -e ap,at,tt,cb,dbe,u1-20 –plugins-detection mixed –url <URL> –api-token <API Token>
|-e ap,at,tt,cb,dbe,u1-20,m||This option specifies to check for all plugins, themes, TimThumb scripts, config backups, database exports and to enumerate up to 20 WordPress usernames.|
|–plugins-detection mixed||This will turn on the aggressive mode for plugin detection (passive by default).|
|–url||Used to specify the target WordPress URL.|
|–api-token||You can provide an API token, to match discovered component versions against WPScan’s vulnerability database. You can get a free token here.|
Once your scan has finished, take some time to look through the output for any points of interest. Make a particular note of the version of WordPress Core, the plugins and themes identified, and the versions of those plugins and themes.
With this information you can look for publically disclosed security flaws in these WordPress components. We recommend using the command line tool SearchSploit and to consider searching Google to identify any CVEs (Common Vulnerabilities and Exposures).
You should also make note of any obscure or custom plugins discovered during testing. Vulnerabilities in well-known plugins are typically caught quickly by the WordPress community, but custom or more obscure plugins may have undiscovered vulnerabilities that are waiting to be exploited. If you have the time, take a look at these plugins and see if you can exploit their functionality.
- Ensure that WordPress Core and all plugins/themes are regularly updated. This will make sure that all WordPress components have the latest security patches.
- Evaluate the installed plugins and remove those that are no longer required by the application. This will reduce the potential vulnerability surface.
2. Enumerating WordPress Usernames
WordPress usernames are simple to identify and there are several methods available for enumeration. Discovering valid usernames can help an attacker when it comes to brute-force attacks against XMLRPC and wp-login.php. In this section we’ll explore techniques that can be used to identify usernames. Some of these techniques can be automated with WPScan.
Enumerating Usernames via JSON API
Where enabled, you can get a list of usernames from WordPress’s JSON API. You can reach these by browsing to the following URL path:
https://<Target WordPress Site>/wp-json/wp/v2/users
Enumerating Usernames via Author ID Parameter
It’s possible to enumerate usernames by sending a request to the landing page and appending the ‘author’ parameter with an ID number. This can be done in a browser or automated with a tool, and can help you build a list of potential usernames by incrementing the ID value (starting with ‘1’):
https://<Target WordPress Site>/?author=1
Where not restricted, you should be redirected to each author’s page within WordPress. The username will be displayed in the ‘Location’ header of the redirecting HTTP response, which can be captured using a HTTP proxy such as Burp Suite or by opening your browser’s web development tools and going to the network tab. The username will also show up in the URL at the top of the browser.
Enumerating/Validating Usernames Through wp-login Error Messages
With WordPress’s default login error messages, it’s possible to directly enumerate usernames and validate any you’ve gathered. Please see below the error message when entering an incorrect username and password:
This contrasts to the error message received for a valid username but incorrect password:
The error message indicates that the username is valid. If there are no rate limiting controls in place, it may be possible to create a list of likely or common usernames and use a tool such as Burp Suite’s Intruder to derive a list through a brute-force attack. Better yet, WordPress supports both emails and usernames, so if Joe Bloggs’s email is email@example.com, that email could be used in place of the username. This can be valuable when testing as you are likely to have gathered a list of email addresses when gathering open-source intelligence and can try these against the WordPress login page.
Please see below a demonstration of Burp Suite’s Intruder being used to enumerate valid emails/usernames:
- Block access to the wp-json API by unauthenticated users. There are several plugins that can do this, but we recommend restricting access without plugins if possible.
- Block author ID lookup at a server level. On apache, this can be done adding a rewrite condition to the .htaccess file to return a 403 error when a request has ‘author=<ID>’ in the request. Please find an example here.
- Change the error messages received through invalid wp-login requests and ensure they are all exactly the same. This will make it harder for an attacker to know which usernames are valid. Please see here for more information.
3. Gaining Access Through Brute-Force Login Attacks
By this point you may have an extensive list of valid usernames and possibly even potential passwords that may have been obtained from breached credentials dumps available online. When testing WordPress installations there are two main vectors for brute-force login attacks, via/wp-login.php and /XMLRPC.php functionality. And like WPScan, these attacks will generate a lot of noise on the server.
Brute-Force Login Attack via XMLRPC
The XMLRPC functionality within WordPress allows other applications and systems to communicate with WordPress and perform certain actions such as publishing a blog post. Unrestricted access XMLRPC can leave the WordPress application vulnerable to a brute-force login attack. The attacks is performed by sending a POST request to XMLRPC referencing the wp.getUsersBlogs method:
<?xml version=”1.0″ encoding=”UTF-8″?>
Please see below a login request sent with invalid credentials to demonstrate the response received from an invalid request (this request was sent via Burp Suite):
Although we get a 403 error response code, the xml in the response suggests that a login attempt occurred but the credentials are incorrect. Since we now know XMLRPC is accessible and that authentication is possible, we can attempt a brute-force attack using an automated tool (in this example I’ve used Burp Suite’s Intruder to automate the requests):
We can see that our brute-force attack was successful when provided with the username ‘wp-admin’ and the password ‘SuperSecretPassword123#’. If we take a look at the request itself, the response confirms that we were able to successfully authenticate as the user ‘wp-admin’. We can now use the credentials to log into the wp-admin panel if accessible.
Access to XMLRPC must be restricted to ensure that it is protected from exploitation. In addition to login brute-force attacks, XMLRPC also has a vulnerable method known as pingback.ping which can be used in distributed denial of service (DDOS) attacks.
Brute-Force Login Attack via wp-login
Perhaps the more obvious authentication mechanism that may be vulnerable to brute-force attacks is the wp-login form, which by default is found on the /wp-admin login page (where access is not restricted). If the rate limiting controls in place (such as a lockout policy) are weak or non-existent, you may be able to conduct a brute-force attack with your username list to gain access to the WordPress admin dashboard.
Please see below the POST request sent to /wp-login.php which we will use to attempt to brute force a number of passwords:
As per XMLRPC we provided a list of common passwords. The wordlist used in practical brute-force attacks will be a lot more thorough compared to our demonstration wordlist. Where authentication is successful, the HTTP response will contain a 301 redirect to the WordPress admin dashboard. We can see from the results of the attack that authentication was possible using ‘wp-admin’ as the username and ‘SuperSecretPassword123#’ as the password. Again, for this demonstration Burp Suite’s Intruder was used.
If we open the response in the browser, we can see that we have successfully gained access to the WordPress admin dashboard and should now be able to compromise the web server itself with relative ease.
- Ensure that XMLRPC is either disabled or that access is restricted to unauthenticated users.
- Ensure that the wp-login form is protected by rate limiting controls, this could be via a web application firewall, implementing Fail2Ban or a plugin. Rate limiting controls will reduce the likelihood of a successful brute-force login attack.
- Ensure that users that no longer require access are removed.
- Ensure that users have a strong password.
4. Testing for Access to Sensitive Files
Testing for Exposed Backup Files
WordPress Core’s file structure is well known and therefore it often doesn’t make sense to try and discover hidden files and directories with tools like Dirb or GoBuster (brute-force directory enumeration tools). However, it’s often worth checking to see if there are any backup files that have been left lying around. For example, backing up WordPress’s configuration where changes will be made. wp-config.php is WordPress’s configuration file and contains sensitive information such as SQL database credentials.
It’s possible that backup files will be created and left with the default name. For the wp-config.php file, the backup may therefore be called wp-config.php.bak. Identifying this backup file will allow you to download and read the full contents of the wp-config file, providing the attacker with highly sensitive information that could be used in a subsequent attack.
Testing for Access to Key Files
WordPress’s key files include its configuration file and installation file. These files should not be accessible to users. A common argument is that files like wp-config.php don’t need to be moved/blocked as you can’t read the PHP code when you browse to the file, and it’s true that you cannot directly view the contents of wp-config.php by making a request. That is unless this is aided by an external event. For example, if the PHP interpreter were to fail/get turned off or (the more likely scenario) where WordPress or one of the components in use allows an attacker to conduct directory traversal and indirectly view the file’s contents.
Similarly, installation files have been exploited in the past where a vulnerability has manifested and these files (which are needed for exploitation) have been exposed. Although these events aren’t common and are unlikely to result in the server’s compromise, it’s often best practice to move the wp-config.php file one directory lower than the web root (so it is not easily found and will be inaccessible via web app requests) and restrict access to all installation files.
- Ensure that any backup files have been removed from accessible web application directories. Changing the backup file’s name is not a suitable alternative.
- While the risk of exploitation is low, consider moving the wp-config.php file outside of the web root and either restricting access to installation files (for example using .htaccess where an Apache server is used) or by deleting these files where they are no longer needed.
5. Identify Technology Fingerprinting Markers
There are countless bots online probing web applications looking for the presence of common vulnerabilities that can be exploited. The bots are pre-programmed to spot certain identifiers in the site’s structure or HTML code in order to discover whether the application may be exploitable. These identifiers might disclose what components are running as well as their versions. It’s all but impossible to conceal that WordPress is the platform being used by the application, especially from someone manually probing the site. However, you may be able to conceal the version of WordPress, along with the versions of its themes and plugins.
This can be a difficult task, but the less information the web application provides potential attackers and bots, the less likely they will be to compromise the application. Making your web application more resilient to fingerprinting will also act as a natural deterrent for attackers looking for the low hanging fruit targets. WPScan, as we saw earlier, is a useful tool to begin enumerating the WordPress application and also relies heavily on these fingerprinting markers.
Plugins and Themes
The versions of plugins and themes can be found in the files located in its folder (and sometimes manifests in the site’s HTML). For example, in our subject WordPress blog there is a plugin called “WP-Optimize”. If we browse to the readme.txt file inside the plugin’s folder, we can see that the version is disclosed as 3.1.9:
Identifiers like the one above, can be scrubbed by removing the file from the folder. For themes, the version is also often derived from the style.css file in addition to readme.txt. Please note that when a plugin or theme is updated, the file may return, so you may want to create a script that checks for certain files within your plugins folders and removes them where they appear.
The version of WordPress Core appears in a few places (please see below a list of identifiers):
- The HTML source code of the index page (/) and the /wp-admin page. Found in the meta tags and appended to WordPress js scripts and style sheets.
- The RSS feed which can be obtained by browsing to /feed.
- The /readme.html file in some older versions of WordPress.
- Consider scrubbing version markers for web app components (particularly WordPress Core, plugins and themes). Please see this article for more information on hiding the version of WordPress Core.
Predatech offers a range of security testing services, including penetration testing. If you’d like to discuss the security of your WordPress web application, please contact us today and book a free consultation.