Author: Duncan Jepson
22% of compromised WordPress websites occurred via vulnerabilities in their installed plugins. This was a staggering statistic to me when I first heard it. Because of this, I decided to take a bit of a dive into researching WordPress plugins and their vulnerabilities.
From a defensive position, you should clearly understand the plugins installed within your application and ensure that no known exploitable vulnerabilities exist within them.
From an offensive position, quickly identifying the plugins used within WordPress websites will provide you with a much broader attack surface to exploit.
This blog post will demonstrate a technique to quickly and easily enumerate the installed plugins (and their versions) across a range of target WordPress hosts.
In the previous blog post, we described a process for hunting for vulnerabilities in WordPress plugins at scale. We did this by automating downloading the top 1000 plugins from the WordPress plugin repository and using static source code analysis to identify common vulnerability anti-patterns, such as XSS.
Now that we have found vulnerabilities in different plugins, we need to be able to identify WordPress sites that have these plugins installed and may be exploitable. Ideally, we want to do this at scale too.
Before we fire off some tools, let’s take a minute to understand what we are looking for and how the WordPress architecture can assist us in our endeavors.
Quick look at WordPress Plugin Standard
All WordPress plugins are installed in a common location within the webroot “/wp-content/plugins/”. This makes sense as the WordPress system, and users must be able to readily access the plugin functionality.
Unfortunately, directory listing is very rarely enabled within WordPress deployments, meaning that we can’t just list every file and folder within that directory with one simple GET request (that would just be too easy). We are going to have to get creative (more on this soon).
Additionally, within each plugin’s directory should be a readme.txt file that adheres to the WordPress plugin Readme file standard (https://developer.wordpress.org/plugins/wordpress-org/how-your-readme-txt-works/ ). According to the standard:
The plugin’s name should be on the first line inside “==” tages:
=== Plugin Name ===
The plugin’s version should be listed directly under the changelog heading inside “=” tags:
== Changelog == = 1.0 = * A change since the previous version.
A word of caution, though – recall I said: “should be”. Not all plugins will strictly adhere to the standard, so if parsing the readme.txt file, be prepared for non-conformity and handle these errors gracefully. Trust me, this is a good life lesson when parsing any file!
Plan of attack
With this knowledge, we can hypothesize how we may enumerate a WordPress site to identify its installed plugins and their version number.
- Find a list of all WordPress plugin names,
- Luck for us these already exist, and have been incorporated into many of the tools we will be using,
- Check for the existence of /wp-content/plugins/{{pluginName}}/readme.txt against our list of plugins
- If the readme.txt file exists, then the plugin is installed (it may not be enabled).
- Parse the readme.txt file to extract the version number from the changelog.
Let’s get hunting!
Enumerating WordPress hosts
First, we need a list of targets (a common starting point for many bug hunters). For this, let’s use the target list from: https://github.com/arkadiyt/bounty-targets-data/blob/master/data/domains.txt
So as not to be fuzzing websites that are not using WordPress, we must first enumerate our list of targets to filter out those sites that we should not be testing.
A handy tool to do this is Project Discovery’s httpx (https://github.com/projectdiscovery/nuclei-templates).
httpx is a fast and multi-purpose HTTP toolkit that can probe websites for various information. For our specific use case we want to know if the application uses WordPress. Httpx can identify this through its tech-detect functionality (-td, –tech-detect).
Under the hood httpx tech-detect is using “wappalyzergo” (https://github.com/projectdiscovery/wappalyzergo) to identify technology stacks by comparing HTTP responses to a list of known technology responses or “Fingerprints”. for example WordPress fingerprints include:
- the javascript “wp_username” property, or
- a match on the regex “<link rel=[\”‘]stylesheet[\”‘] [^>]+/wp-(?:content|includes)/” within a page source.
Lets execute httpx with tech-detect against our target list and see what the results are:
To extract a more usable output from the tool we be execurt the below command:
httpx -td -ms WordPress -l domains.txt | sed -e 's/\s.*$//' > wp-targets.txt
breaking this down:
- httpx -td -ms WordPress -l domains.txt
- httpx : executes the httpx utility
- -td : using the tech-detect functionality
- -ms WordPress: matching the results against the string
- -l domains.txt: using the list of domains in the domains.txt file
- The standard output of httpx is piped (|) into the sed utility
- sed -e ‘s/\s.*$//’ > wp-targets.txt
- sed : using the sed utility
- e ‘s/\s.*$//’ : a sed script which replaces (s/) everything after and including the first space character ti the end of the line (\s.*$) with nothing (//)
- > wp-targets.txt : stdout being redirected to the file wp-targets.txt
Resulting in:
This has now given us a new filtered list of hosts in the wp-tagets.txt file that are expected to be using WordPress.
Enumerating WordPress sites Plugins
From this list we should now be able to enumerate each site’s list of installed WordPress plugins.
My tool of choice here is another Project Discovery open-source utility, Nuclei (https://github.com/projectdiscovery/nuclei). Nuclei is a fast and customizable host scanner that uses powerful and flexible templates to identify patterns. The Nuclei community has already created thousands of templates for people to use.
Lucky for us, a WordPress plugin fuzzing template already exists: “fuzzing/wordpress-plugins-detect.yaml”
Essentially what this plugin is doing is using a wordlist of know plugin names, it brute-forces this list against the target, looking to see if a 200 response is returned:
A word of warning. As this is a “fuzzing” scan, it will be making many thousand requests per target and will take a while to run.
when we run it against a single domain from our filtered target list, we get:
nuclei -u https://careers.marriott.com -itags fuzz -t ~/nuclei-templates/fuzzing/wordpress-plugins-detect.yaml
Success! well? maybe partial success… we have the plugins’ short names (“PluginSlug=”), but we don’t have the version number.
To demonstrate the power and flexibility of Nuclei, we can modify the Nuclei template to extract additional information about the plugin.
To get a plugin’s full name and version number from its readme.txt file, we can add “Extractors” to the Nuclei template.
What we have done here is add two regular expressions
- “===\s(.*)\s===” : extracts the plugins name
- “==\sChangelog\s==[\r\n]+=\s(?P<version>\\d.[\d.]*\d)\s=” : extracts the plugins version number
By the time you read this post I would have hopefully submitted a pull request to the nuclei-templates repository (https://github.com/projectdiscovery/nuclei-templates) with this improvement to the wordpress plugin.
When we rerun the Nuclei template with our updates, we now get:
nuclei -u https://careers.marriott.com -itags fuzz -t ~/nuclei-templates/fuzzing/wordpress-plugins-detect.yaml
Success! We now have the full name and version number of each plugin installed on our target.
To run this scan against our entire filtered target list we can provide the target list file directly to nuclei with the -l flag instead of a single url we provided with the -u flag:
nuclei -l wp-targets.txt -itags fuzz -t ~/nuclei-templates/fuzzing/wordpress-plugins-detect.yaml
Or if you want to get fancy with one single command we can also merge everything together with Unix Pipes (|):
cat domains.txt | httpx -td -ms WordPress | sed -e 's/\s.*$//' | nuclei -itags fuzz -t ~/nuclei-templates/fuzzing/wordpress-plugins-detect.yaml
Strong word or warning here – This may take an extremely long time to execute:
- 102 hosts
- x 89,810 WordPress Plugins
- = 9,160,620 GET requests
Conclusion
From here we can take our list of plugins and their versions and perform further static code analysis against them as we did in the previous blog post. This approach is more work than simply comparing to a list of known vulnerabilities, but will hopefully assist you in finding new vulnerabilities in plugins that are being actively used in the targets that you are interested in.
Nice writeup! Here are a few additional thoughts:
WordPress also stores plugins in the /wp-content/mu-plugins/ directory.
The location of the plugins directory can also be modified through the use of the
WP_CONTENT_URL global variable.
The readme file is sometimes named differently from the standard as ReadMe.txt or README.txt.
Many sites may block the display of readme files so testing for a 401 Forbidden response can sometimes indicate the presence of a blocked readme.
Rate limit your scans to be respectful of the site and to help avoid being blocked by WAFs.
All the best!