WordPress
A popular Content Management System (CMS) for static content, with a visual UI
Last updated
A popular Content Management System (CMS) for static content, with a visual UI
Last updated
The state-of-the-art security scanner for WordPress is wpscan
, checking and enumerating many different vulnerabilities from plugins, backup files, and other WordPress-specific errors.
See the API Setup for instructions on how to use their API to get real-time updates of vulnerability data such as versions of plugins. This is highly recommended to make sure you find the newest CVEs.
The following command starts such a scan with extra options enabled and writes the output to a file:
The results of such a scan often reveal outdated plugins with vulnerabilities, and/or generic misconfigurations to exploit. Use a search engine here when unsure about exploiting a certain finding.
One vulnerability that is infamous with WordPress is the /xmlrpc.php
file being public. But what is the real risk you may ask? The main risk is the system.multicall()
function that you can interact with to send multiple XML RPC requests simultaneously, and the server will process them all separately.
You can imagine that for a heavy request, this can amplify one request into a ton of load on the server, possibly resulting in a Denial of Service (DoS). Another idea is using the fact that you can send lots of request at the same time to bypass a rate limit, for password attempts, for example. There exists an RPC call to log in with a username and password to the administrator panel, and with this technique you can do so hundreds of times in one request, significantly speeding up the process (more details).
The following tool implements this idea by guessing many passwords from a wordlist:
When authenticated as an admin, you can make any changes to the site. This also means you can edit the PHP code that is executed whenever a page is visited, allowing you to write code that executes shell commands.
You should be able to access Tools -> Theme File Editor to edit the current theme:
Then, select any .php
file you think will be executed when you visit a page. By default, there is a functions.php
file that every other file includes, so it will always be run. Edit such a file to include any PHP code you want to execute:
After saving, you should be able to access the page to run the code:
If this does not work for any reason, alternatives include the Tools -> Plugin File Editor with any plugin, then activate it at Plugins -> Installed Plugins to trigger the code:
As a last option, you can always upload your own malicious plugin like this: https://github.com/wetw0rk/malicious-wordpress-plugin
WordPress can be extended by installing plugins, either through the store or manually by adding them to the wp-content/plugins/
folder. Custom plugins may contain security vulnerabilities and are a very common source of WordPress issues that WPScan also searches for.
Plugins can add several new inputs to an application that may be vulnerable to all kinds of attacks. Important to know when auditing them is knowing how you can call them.
Starting with actions, these can be registered with add_action()
and their name must be prefixed with wp_ajax
to be accessible via the web ajax endpoint. By default, these actions require authentication of any (low-privilege) user. Registering them is done by passing a "callable" as the second argument, which may be a function name that PHP calls. See the following example:
Another more interesting action for hackers is an unauthenticated one. With the nopriv
prefix, this automatically allows any request without authentication to run the callback function:
Anyone can call such an API with the /wp-admin/admin-ajax.php
endpoint, which requires a ?action=
parameter set to the name after the prefix, for example:
Another type of input adding routes to the REST API at /wp-json
. These are often registered at the rest_api_init
action and use the register_rest_route()
function to give a namespace and endpoint to request. The callback function will run when a request passes the permission check:
Any unauthenticated user can request this endpoint because the permission_callback
always returns true. A request like the following would be parsed by the callback function:
Because any user can access authenticated actions, plugin developers should check the roles of the current user to prevent unauthorized access. The following example shows how both an administrator
and subscriber
may run this code:
Another interesting piece of code to look at is wp-login.php
from WordPress itself. The ?action=
parameter is used in a switch statement to execute various different pieces of logic involving user accounts:
In one vulnerability, the password reset token was generated in an insecure way, which allowed you to run the resetpass
action on wp-login.php
to choose a new password for the user. If you check the source code that handles this action you can see that it handles the key
and login
parameters for the reset key and username respectively:
Some easy mistakes to make when writing custom WordPress plugins. This ranges from unintuitive behaviour to some previous CVEs in other plugins.
is_admin()
as privilege checkFunctions like current_user_can
should be used to check the permissions of the currently logged-in user. A developer who doesn't fully read the documentation may encounter the is_admin()
function that sounds like it should check if the current user is an administrator.
However, this is not the case! It instead checks if the current path is to an administrator page. Any user can make a request to /wp-admin/
, the /wp-admin/admin-ajax.php
handler for example triggers this too.
Below is a list of all default permissions per role for reference:
Super Admin
Complete Control of Multi-Site Networks
Admin
Change Themes
Add and Remove Widgets from Sidebar
Activate and Deactivate Plugins
Add and Remove Other Users
Change Roles of Other Users
Editor
Edit, Delete, or Approve Comments
Add, Edit or Delete Tags
Add, Edit, or Delete Categories
Add and Remove Links
Edit or Delete Published Posts by Any User
Write Own Pages
Edit or Delete Published Pages by Any User
Edit or Delete Media Files
Author
Upload Media Files
Contributor
View Comments
Write Own Posts
Edit Own Posts
Subscriber
Edit Own Profile
A Reflected XSS vulnerability was reported in Appmaker <= 1.36.12 (details). One of its files looks like this, with a vulnerability in the hook_payment_footer()
method:
The ?payment_gateway=
parameter is placed directly into the DOM here. It is easily abused by closing the script tag, and then opening a new one with malicious JavaScript. This works from any page as the footer is always loaded.
The social-media-builder plugin is no longer available for download due to a "Security Issue". It turns out that this is an authenticated Insecure Deserialization vulnerability. When calling the import_buttons
action, the following code is triggered:
A URL inside ?attachmentUrl=
is fetched and unserialized. If the server allows it, you can use a data:
URI with base64 to return arbitrary content to be deserialized. A JavaScript snippet that triggers this is below:
Replace INSERT_DESERIALIZATION_EXPLOIT_HERE
with a PHP deserialization gadget chain that may require other outdated libraries or custom code.