Drupal Discovery & Enumeration

🎯 Objective: Master the identification, enumeration, and intelligence gathering techniques for Drupal installations to complete comprehensive CMS security assessment capabilities across the three major content management platforms.

Overview

Drupal, launched in 2001, represents the third pillar of the CMS Trinity alongside WordPress and Joomla. While holding a smaller market share (2.4% of CMS market), Drupal powers critical infrastructure including 56% of government websites globally and 33 Fortune 500 companies. Its enterprise focus and robust architecture make it a high-value target requiring specialized enumeration techniques.

Key Drupal Statistics:

  • 1.5% of internet sites (over 1.1 million installations)

  • 5% of top 1 million websites worldwide

  • 7% of top 10,000 sites (enterprise concentration)

  • 950,000+ active instances (Update Status module data)

  • Available in 100 languages with global deployment

  • Major users: Tesla, Warner Bros Records, government agencies


Drupal Architecture & Fundamentals

Core Concepts & Structure

Content Management via Nodes

Node System Architecture:
/node/1     β†’ Blog post
/node/2     β†’ Article  
/node/3     β†’ Page content
/node/4     β†’ Poll/Survey
/node/[ID]  β†’ Any content type

Node = Universal content container in Drupal

User Role Hierarchy

Administrator      β†’ Complete system control and configuration
Authenticated User β†’ Login access with role-based permissions
Anonymous         β†’ Public visitors (read-only by default)

Custom Roles:
Editor            β†’ Content editing permissions
Moderator         β†’ Comment and user management
Content Manager   β†’ Specific content type management

Directory Structure Analysis

/core/               # Drupal 8+ core files
/modules/            # Contributed and custom modules
/themes/             # Site themes and templates
/sites/              # Multi-site configurations
/profiles/           # Installation profiles
/vendor/             # Third-party libraries (Composer)
/libraries/          # External libraries (Drupal 7)

Configuration Files:
sites/default/settings.php      # Database and site configuration
.htaccess                      # Apache configuration
robots.txt                     # Search engine directives

Discovery & Fingerprinting Techniques

Initial Identification Methods

Method 1: Generator Meta Tag Detection

# Search for Drupal generator meta tag
curl -s http://target.com | grep -i drupal

# Example outputs:
<meta name="Generator" content="Drupal 8 (https://www.drupal.org)" />
<meta name="generator" content="Drupal 7 (http://drupal.org)" />
# Look for Drupal attribution in page footer
curl -s http://target.com | grep -i "powered by"

# Typical findings:
<span>Powered by <a href="https://www.drupal.org">Drupal</a></span>

Method 3: Node-Based URL Pattern Recognition

# Test for node-based URL structure
curl -I http://target.com/node/1
curl -I http://target.com/node/2
curl -I http://target.com/?q=node/1

# Drupal-specific URL patterns:
/node/[number]          # Content nodes
/admin                  # Administrative interface
/user                   # User management
/user/login            # Login page
/?q=node/[number]      # Clean URLs disabled

Method 4: Standard File Detection

# Check for Drupal-specific files
curl -I http://target.com/CHANGELOG.txt
curl -I http://target.com/README.txt
curl -I http://target.com/INSTALL.txt
curl -I http://target.com/LICENSE.txt
curl -I http://target.com/MAINTAINERS.txt

# Look for robots.txt Drupal indicators
curl -s http://target.com/robots.txt | grep -E "(node|admin|user)"

# Example robots.txt content:
Disallow: /admin/
Disallow: /comment/reply/
Disallow: /filter/tips/
Disallow: /node/add/
Disallow: /search/
Disallow: /user/register/

Method 5: CSS/JavaScript Fingerprinting

# Search for Drupal-specific assets
curl -s http://target.com | grep -E "(drupal|misc/|sites/)"

# Common asset patterns:
/misc/drupal.css               # Drupal 6/7
/core/misc/drupal.css          # Drupal 8+
/sites/default/files/          # File uploads
/modules/system/system.css     # System styles
/themes/[theme]/css/          # Theme assets

Version Detection Strategies

Core Version Identification

Method 1: CHANGELOG.txt Analysis (Primary)

# Extract version from CHANGELOG.txt (most reliable)
curl -s http://target.com/CHANGELOG.txt | head -n 3

# Example outputs:
Drupal 7.57, 2018-02-21
Drupal 8.9.1, 2020-06-17
Drupal 9.3.6, 2022-02-16

# Automated version extraction
curl -s http://target.com/CHANGELOG.txt | grep -m1 "^Drupal" | awk '{print $2}' | tr -d ','

# Check if CHANGELOG.txt is blocked
curl -I http://target.com/CHANGELOG.txt
# HTTP/1.1 404 Not Found (indicates newer/hardened installation)

Method 2: Generator Meta Tag Version

# Extract version from HTML meta generator
curl -s http://target.com | grep -oP 'content="Drupal \K[0-9.]+'

# Example extraction:
curl -s http://target.com | grep generator | grep -oP 'Drupal \K[0-9.]+'

Method 3: Core JavaScript File Analysis

# Drupal 7 version detection via jQuery
curl -s http://target.com/misc/jquery.js | head -n 5

# Drupal 8+ version detection
curl -s http://target.com/core/misc/drupal.js | head -n 10

# Search for version strings in assets
curl -s http://target.com | grep -oP '/core/misc/drupal\.js\?v=\K[0-9.]+'

Method 4: CSS Timestamp Analysis

# Check CSS files for version indicators
curl -s http://target.com | grep -oP 'system\.css\?[a-z0-9]+'

# Extract modification timestamps
curl -s http://target.com/modules/system/system.css | head -n 3

Method 5: Update Status Module Detection

# Check for update.php (indicates version)
curl -I http://target.com/update.php

# Version information in installation profile
curl -s http://target.com/profiles/standard/standard.info

Version-Specific Indicators

Drupal 6 Characteristics

# Directory structure indicators
curl -I http://target.com/misc/drupal.js
curl -I http://target.com/includes/
curl -I http://target.com/modules/system/
curl -I http://target.com/themes/garland/    # Default theme

# jQuery version (typically 1.2.x)
curl -s http://target.com/misc/jquery.js | grep -oP 'jQuery \K[0-9.]+'

Drupal 7 Characteristics

# Drupal 7 specific files and paths
curl -I http://target.com/misc/            # Misc directory
curl -I http://target.com/sites/all/       # All sites directory
curl -I http://target.com/themes/bartik/   # Default Bartik theme

# jQuery version (typically 1.4.x)
curl -s http://target.com/misc/jquery.js | head -n 3

Drupal 8+ Characteristics

# Drupal 8+ modern structure
curl -I http://target.com/core/            # Core directory
curl -I http://target.com/vendor/          # Composer dependencies
curl -I http://target.com/themes/classy/   # Classy base theme

# Symfony integration detection
curl -s http://target.com | grep -i symfony

# Twig template engine indicators
curl -s http://target.com | grep -E "\.html\.twig"

Manual Enumeration Techniques

Content Discovery via Node Enumeration

Sequential Node Discovery

# Enumerate content nodes systematically
for i in {1..100}; do
    response=$(curl -s -o /dev/null -w "%{http_code}" "http://target.com/node/$i")
    if [ "$response" = "200" ]; then
        echo "[+] Found node: $i"
        curl -s "http://target.com/node/$i" | grep -oP '<title>\K[^<]+' | head -n 1
    fi
done

# Alternative with clean URLs disabled
for i in {1..50}; do
    curl -s -o /dev/null -w "Node $i: %{http_code}\n" "http://target.com/?q=node/$i"
done

Content Type Analysis

# Identify content types from node URLs
curl -s http://target.com/node/1 | grep -oP 'content-type-\K[a-z-]+'

# Common Drupal content types:
# - article (blog posts, news)
# - page (static pages)
# - webform (contact forms)
# - product (e-commerce)
# - event (calendar entries)

Administrative Interface Discovery

Admin Panel Enumeration

# Standard administrative paths
curl -I http://target.com/admin
curl -I http://target.com/admin/config
curl -I http://target.com/admin/content
curl -I http://target.com/admin/structure
curl -I http://target.com/admin/appearance
curl -I http://target.com/admin/modules
curl -I http://target.com/admin/people

# Check admin response codes
admin_paths=(
    "/admin"
    "/admin/config" 
    "/admin/content"
    "/admin/structure"
    "/admin/appearance"
    "/admin/modules"
    "/admin/people"
    "/admin/reports"
)

for path in "${admin_paths[@]}"; do
    echo "Checking: $path"
    curl -s -o /dev/null -w "%{http_code}" "http://target.com$path"
done

User Management Interface

# User-related paths
curl -I http://target.com/user
curl -I http://target.com/user/login
curl -I http://target.com/user/register
curl -I http://target.com/user/password

# User profile enumeration (if accessible)
for i in {1..10}; do
    curl -s -o /dev/null -w "User $i: %{http_code}\n" "http://target.com/user/$i"
done

Module & Theme Discovery

Active Module Enumeration

# Common module paths to test
modules=(
    "admin_menu"
    "block"
    "comment"
    "contact"
    "field"
    "file"
    "filter"
    "forum"
    "image"
    "menu"
    "node"
    "path"
    "search"
    "system"
    "taxonomy"
    "user"
    "views"
    "webform"
)

echo "[+] Enumerating Drupal modules:"
for module in "${modules[@]}"; do
    response=$(curl -s -o /dev/null -w "%{http_code}" "http://target.com/modules/$module/")
    if [ "$response" != "404" ]; then
        echo "[+] Module found: $module (HTTP $response)"
    fi
done

Theme Discovery & Analysis

# Enumerate active themes
curl -s http://target.com | grep -oP '/themes/[^/]+/' | sort -u

# Common Drupal themes to test
themes=(
    "bartik"        # Drupal 7 default
    "garland"       # Drupal 6 default  
    "seven"         # Admin theme
    "stark"         # Minimal theme
    "classy"        # Drupal 8 base
    "stable"        # Drupal 8 base
    "olivero"       # Drupal 9 default
    "claro"         # Drupal 9 admin
)

for theme in "${themes[@]}"; do
    curl -s -o /dev/null -w "Theme $theme: %{http_code}\n" "http://target.com/themes/$theme/"
done

Custom Module Discovery

# Look for custom modules in sites directory
curl -s http://target.com/sites/all/modules/ | grep -oP 'href="[^"]*"' | cut -d'"' -f2

# Check for development modules (dangerous if enabled)
dev_modules=(
    "devel"
    "stage_file_proxy"
    "reroute_email"
    "field_ui"
    "views_ui"
)

for module in "${dev_modules[@]}"; do
    response=$(curl -s -o /dev/null -w "%{http_code}" "http://target.com/admin/modules")
    echo "Checking dev module: $module"
done

Automated Enumeration Tools

DroopeScan - Advanced Drupal Scanner

Installation & Setup

# Install via pip (recommended)
sudo pip3 install droopescan

# Verify installation
droopescan --help

# Alternative: Manual installation
git clone https://github.com/droope/droopescan.git
cd droopescan
pip3 install -r requirements.txt
python3 droopescan -h

Basic Drupal Scanning

# Comprehensive Drupal enumeration
droopescan scan drupal -u http://target.com

# Example output interpretation:
[+] Plugins found:
    php http://target.com/modules/php/
    views http://target.com/modules/views/

[+] Themes found:
    bartik http://target.com/themes/bartik/

[+] Possible version(s):
    7.56
    7.57
    7.58

[+] Possible interesting urls found:
    Default admin - http://target.com/user/login

Advanced DroopeScan Options

# Enumerate specific components
droopescan scan drupal -u http://target.com --enumerate p    # plugins only
droopescan scan drupal -u http://target.com --enumerate t    # themes only
droopescan scan drupal -u http://target.com --enumerate v    # version only

# Control scan intensity
droopescan scan drupal -u http://target.com --threads 10 --timeout 30

# Scan multiple targets
droopescan scan drupal --url-file targets.txt

# Output to file
droopescan scan drupal -u http://target.com --output json > drupal_scan.json

DroopeScan Output Analysis

# Parse JSON output for specific information
cat drupal_scan.json | jq '.plugins[].name'
cat drupal_scan.json | jq '.themes[].name'  
cat drupal_scan.json | jq '.version[]'

# Filter high-confidence results
cat drupal_scan.json | jq '.plugins[] | select(.confidence > 75)'

Custom Drupal Enumeration Scripts

Comprehensive Module Brute Force

#!/bin/bash
# drupal-module-enum.sh

target="$1"
wordlist="/usr/share/seclists/Discovery/Web-Content/CMS/drupal_modules.txt"

if [ ! -f "$wordlist" ]; then
    echo "[!] Wordlist not found. Creating basic module list..."
    cat > drupal_modules.txt << 'EOF'
admin_menu
backup_migrate
captcha
cck
ctools
date
devel
entity
features
field_group
google_analytics
imageapi
imagefield
imce
jquery_ui
libraries
link
location
menu_block
module_filter
panels
pathauto
recaptcha
rules
token
transliteration
views
webform
wysiwyg
EOF
    wordlist="drupal_modules.txt"
fi

echo "[+] Enumerating Drupal modules on $target"
echo "[+] Using wordlist: $wordlist"

while IFS= read -r module; do
    # Check both Drupal 7 and 8+ paths
    for path in "/sites/all/modules/$module" "/modules/$module" "/modules/contrib/$module"; do
        response=$(curl -s -o /dev/null -w "%{http_code}" "$target$path/")
        if [ "$response" = "200" ] || [ "$response" = "403" ]; then
            echo "[+] Found module: $module ($path) - HTTP $response"
            
            # Try to get module info
            info_response=$(curl -s "$target$path/${module}.info")
            if [ -n "$info_response" ]; then
                version=$(echo "$info_response" | grep -oP 'version = "\K[^"]+')
                description=$(echo "$info_response" | grep -oP 'description = "\K[^"]+')
                echo "    Version: $version"
                echo "    Description: $description"
            fi
        fi
    done
done < "$wordlist"

Node Content Discovery Script

#!/bin/bash
# drupal-node-discovery.sh

target="$1"
max_nodes="${2:-100}"

echo "[+] Discovering Drupal content nodes on $target"
echo "[+] Testing up to $max_nodes nodes..."

found_nodes=()

for i in $(seq 1 $max_nodes); do
    # Test both clean URLs and query parameters
    for url_format in "/node/$i" "/?q=node/$i"; do
        response=$(curl -s -o /dev/null -w "%{http_code}" "$target$url_format")
        if [ "$response" = "200" ]; then
            echo "[+] Node $i: $target$url_format"
            found_nodes+=("$i")
            
            # Extract title and content type
            content=$(curl -s "$target$url_format")
            title=$(echo "$content" | grep -oP '<title>\K[^<]+' | head -n 1)
            content_type=$(echo "$content" | grep -oP 'content-type-\K[a-z-]+' | head -n 1)
            
            echo "    Title: $title"
            echo "    Type: $content_type"
            break
        fi
    done
done

echo "[+] Summary: Found ${#found_nodes[@]} accessible nodes"
printf '%s\n' "${found_nodes[@]}"

Configuration & Security Analysis

Settings.php Analysis

Database Configuration Discovery

# Attempt to access settings.php (usually protected)
curl -s http://target.com/sites/default/settings.php

# Common settings.php locations
settings_paths=(
    "/sites/default/settings.php"
    "/sites/all/settings.php"
    "/sites/example.com/settings.php"
)

# Look for backup/development files
backup_configs=(
    "/sites/default/settings.php.bak"
    "/sites/default/settings.php.old" 
    "/sites/default/settings.php~"
    "/sites/default/settings.local.php"
    "/sites/default/development.settings.php"
)

for config in "${backup_configs[@]}"; do
    response=$(curl -s -o /dev/null -w "%{http_code}" "http://target.com$config")
    if [ "$response" = "200" ]; then
        echo "[!] Accessible config found: $config"
        curl -s "http://target.com$config" | grep -E "(database|password|host)"
    fi
done

Multi-site Configuration Detection

# Check for multi-site setup
curl -s http://target.com/sites/ | grep -oP 'href="[^"]*"' | grep -v '\.\.'

# Common multi-site indicators
curl -I http://target.com/sites/sites.php
curl -I http://target.com/sites/example.sites.php

# Domain-specific configurations
curl -I http://target.com/sites/example.com/
curl -I http://target.com/sites/www.example.com/

Update Status & Security Headers

Update Status Analysis

# Check for update.php access
curl -I http://target.com/update.php

# Look for update status information
curl -s http://target.com/admin/reports/updates 2>/dev/null | grep -i "security update"

# Available updates endpoint (if accessible)
curl -s http://target.com/admin/modules/update

Security Header Analysis

# Analyze Drupal security headers
curl -I http://target.com

# Key headers to examine:
# X-Drupal-Cache: HIT/MISS (caching status)
# X-Generator: Drupal (version info)
# Set-Cookie: SESS* (session management)
# X-Frame-Options: (clickjacking protection)
# Content-Security-Policy: (XSS protection)

HTB Academy Lab Solutions

Lab: Drupal Version Detection

Question: "Identify the Drupal version number in use on http://drupal-qa.inlanefreight.local"

Solution Methodology:

Step 1: Environment Setup

# Add VHost entry to /etc/hosts
echo "STMIP drupal-qa.inlanefreight.local" >> /etc/hosts

# Verify connectivity
curl -I http://drupal-qa.inlanefreight.local/

Step 2: Primary Version Detection Method

# Method 1: CHANGELOG.txt (Most Reliable)
curl -s http://drupal-qa.inlanefreight.local/CHANGELOG.txt | head -n 5

# Extract first version entry
curl -s http://drupal-qa.inlanefreight.local/CHANGELOG.txt | grep -m1 "^Drupal"

# Expected output format:
Drupal 7.30, 2014-07-24

# Clean version extraction:
curl -s http://drupal-qa.inlanefreight.local/CHANGELOG.txt | grep -m1 "Drupal" | awk '{print $2}' | tr -d ','

Step 3: Alternative Detection Methods

# Method 2: Generator Meta Tag
curl -s http://drupal-qa.inlanefreight.local/ | grep -i generator

# Method 3: README.txt Analysis
curl -s http://drupal-qa.inlanefreight.local/README.txt | head -n 10

# Method 4: DroopeScan Verification
droopescan scan drupal -u http://drupal-qa.inlanefreight.local

# Method 5: Manual CSS/JS Analysis
curl -s http://drupal-qa.inlanefreight.local/misc/jquery.js | head -n 3

Step 4: Verify Answer Format

# HTB Lab Answer: 7.30
# Full version from CHANGELOG: Drupal 7.30, 2014-07-24
# Submit format: 7.30 (version number only)

# Verification command:
curl -s http://drupal-qa.inlanefreight.local/CHANGELOG.txt | grep -m1 "Drupal" | awk '{print $2}' | tr -d ','
# Output: 7.30

Expected Lab Answers

Target: http://drupal-qa.inlanefreight.local Answer: 7.30 Method: CHANGELOG.txt analysis Full Version String: Drupal 7.30, 2014-07-24


Version-Specific Vulnerability Research

Drupal 7 Security Landscape

Common Drupal 7 Vulnerabilities

# Research known vulnerabilities for identified version
searchsploit "drupal 7"
searchsploit "drupal 7.30"

# Key vulnerability classes:
# - SQL Injection (Drupalgeddon)
# - Remote Code Execution
# - Cross-Site Scripting
# - Authentication Bypass
# - Privilege Escalation

Drupalgeddon Vulnerability Series

# CVE-2014-3704 (Drupalgeddon 1) - SQL Injection
# Affects: Drupal 7.0 - 7.31
# Impact: Remote Code Execution

# CVE-2018-7600 (Drupalgeddon 2) - Remote Code Execution  
# Affects: Drupal 6.x, 7.x, 8.x
# Impact: Complete system compromise

# CVE-2018-7602 (Drupalgeddon 3) - Remote Code Execution
# Affects: Drupal 7.x, 8.x (partial fix bypass)
# Impact: Authentication bypass to RCE

Module-Specific Security Research

High-Risk Module Categories

# File upload modules (arbitrary file upload)
- imagefield
- filefield  
- media
- imce

# User input modules (injection vulnerabilities)
- webform
- contact
- comment
- forum

# Development modules (information disclosure)
- devel
- stage_file_proxy
- field_ui
- views_ui

# Third-party integrations (authentication bypass)
- ldap
- saml
- oauth

Module Vulnerability Research

# Research specific module vulnerabilities
module="webform"
version="7.x-3.20"

# Search exploit databases
searchsploit "drupal $module"
searchsploit "$module $version"

# Check Drupal security advisories
curl -s "https://www.drupal.org/security" | grep -i "$module"

# CVE database search
curl -s "https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=drupal+$module"

Intelligence Gathering Workflow

Comprehensive Enumeration Checklist

Phase 1: Initial Discovery

Phase 2: Content Analysis

Phase 3: Module & Theme Discovery

Phase 4: Configuration & Security Assessment


Defensive Considerations

Security Hardening Recommendations

Core Security Measures

# Essential Drupal security steps:
1. Remove/rename update.php after updates
2. Disable PHP module in production
3. Remove development modules (devel, field_ui, views_ui)
4. Block access to CHANGELOG.txt and README.txt
5. Implement proper file permissions (644 files, 755 directories)
6. Enable security updates and monitoring

File System Hardening

# Recommended file permissions
find /var/www/drupal/ -type f -exec chmod 644 {} \;
find /var/www/drupal/ -type d -exec chmod 755 {} \;
chmod 444 /var/www/drupal/sites/default/settings.php

# Block access to sensitive files via .htaccess
<Files "*.info">
  Order deny,allow
  Deny from all
</Files>

Monitoring and Detection

Attack Pattern Recognition

# Monitor for common attack patterns:
# - CHANGELOG.txt access attempts
# - Node enumeration (sequential /node/X requests)
# - Admin path brute forcing
# - Module directory enumeration
# - Settings.php access attempts

# Log analysis for Drupal attacks
tail -f /var/log/apache2/access.log | grep -E "(CHANGELOG|node/[0-9]+|admin|settings\.php)"

Security Monitoring Setup

# File integrity monitoring for key files
find /var/www/drupal/ -name "*.php" -type f -exec md5sum {} \; > drupal_hashes.txt

# Monitor configuration changes
inotifywait -m -r -e modify /var/www/drupal/sites/default/settings.php

Cross-Module Integration

Drupal in Multi-CMS Environments

CMS Fingerprinting Automation

#!/bin/bash
# cms-identify.sh

target="$1"

echo "[+] CMS Identification for $target"

# WordPress detection
if curl -s "$target/wp-admin/" | grep -q "WordPress"; then
    echo "[+] WordPress detected"
fi

# Joomla detection  
if curl -s "$target/" | grep -qi "joomla"; then
    echo "[+] Joomla detected"
fi

# Drupal detection
if curl -s "$target/" | grep -qi "drupal"; then
    echo "[+] Drupal detected"
    version=$(curl -s "$target/CHANGELOG.txt" | grep -m1 "Drupal" 2>/dev/null)
    echo "    Version: $version"
fi

Integration with Other Modules


Next Steps

After Drupal enumeration, proceed to:

  1. Drupal Attacks & Exploitation - Drupalgeddon and module vulnerabilities

  2. Servlet Containers - Java application attacks

  3. Development Tools - CI/CD and build system attacks

πŸ’‘ Key Takeaway: Drupal enumeration requires understanding of the node-based content system, version-specific file locations, and module architecture. While less common than WordPress, Drupal installations often power critical enterprise and government infrastructure, making thorough enumeration essential for comprehensive security assessments.

Last updated