CryptPad is a self-hosted zero-knowledge collaborative office suite — while document content is end-to-end encrypted in the browser, critical server-side infrastructure components can be misconfigured. Key assessment areas: adminKeys in config.js controls admin panel access and must be configured correctly; MongoDB stores user session tokens and account metadata; open registration without invite codes allows unauthorized account creation; and blob storage contains encrypted document files accessible to anyone who knows the blob path. This guide covers systematic CryptPad security assessment.
# CryptPad — admin panel access and adminKeys configuration
CP_URL="https://cryptpad.example.com"
# Admin panel — CryptPad uses Ed25519 public key for admin auth
# /admin accessible only to users whose public key is in config.adminKeys
curl -s -o /dev/null -w "%{http_code}" "${CP_URL}/admin/" 2>/dev/null
# 200 = admin panel accessible (still requires key-based auth)
# /api/config — instance configuration (publicly accessible)
curl -s "${CP_URL}/api/config" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Instance name: {d.get(\"instanceTitle\")}')
print(f'Admin email: {d.get(\"adminEmail\")}')
print(f'Max upload size: {d.get(\"maxUploadSize\")}')
print(f'Registration allowed: {not d.get(\"restrictRegistration\",False)}')
print(f'Storage limit: {d.get(\"defaultStorageMB\")}MB')
# This endpoint reveals whether registration is open
# and the admin contact email
" 2>/dev/null
# /checkup — admin diagnostic page (requires admin key auth in browser)
# But the endpoint itself may reveal server configuration info
curl -s "${CP_URL}/checkup/" 2>/dev/null | head -20
# config.js — CryptPad server configuration (critical secrets)
cat /opt/cryptpad/config/config.js 2>/dev/null | \
grep -E "adminKeys|httpUnsafeOrigin|adminEmail|blockPath|blobPath|filePath|mongoUri|rpc" \
| head -20
# adminKeys: ['ed25519-public-key-here'] — admin panel access
# httpUnsafeOrigin: 'http://localhost:3000' — should be HTTPS production URL
# mongoUri: 'mongodb://localhost:27017/cryptpad' — MongoDB connection
# blockPath: '/path/to/block/storage'
# blobPath: '/path/to/blob/storage'
# Docker environment
docker inspect cryptpad 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for c in d:
for e in c.get('Config',{}).get('Env',[]):
if any(k in e for k in ['CPAD_', 'ADMIN', 'MONGO', 'SECRET']):
print(e)
" 2>/dev/null
# CryptPad MongoDB session and account metadata extraction
# MongoDB — CryptPad uses MongoDB for session and account data
mongo --host localhost cryptpad 2>/dev/null << 'EOF'
// List all collections
show collections
// Session tokens — active authentication tokens
db.sessions.find({}).limit(10).pretty()
// _id: session ID, token: auth token, user: user hash
// User account data
db.users.find({}).limit(10).pretty()
// _id: user hash, edPublic: Ed25519 public key, created: timestamp
// Registered users count
db.users.count()
// Active sessions
db.sessions.count()
EOF
# Check if MongoDB requires authentication
mongo --host localhost --eval "db.adminCommand({listDatabases:1})" 2>/dev/null
# If this works without password, MongoDB is unauthenticated
# List blob/block storage (encrypted document files)
ls /opt/cryptpad/data/blob/ 2>/dev/null | head -20
ls /opt/cryptpad/data/block/ 2>/dev/null | head -20
# Files are named by hash but are accessible if path is known
# Document content IS encrypted (zero-knowledge)
# But file existence reveals that documents exist
# Get encrypted blob (content is AES-256 encrypted)
BLOB_HASH="abc123def456" # from URL or leaked path
curl -s "${CP_URL}/blob/${BLOB_HASH:0:2}/${BLOB_HASH}" 2>/dev/null | xxd | head -5
# Returns binary encrypted content (cannot decrypt without user key)
# But access itself may be auditable
# CryptPad config.js assessment and instance security checks
# config.js — full configuration
cat /opt/cryptpad/config/config.js 2>/dev/null
# Key configuration items to check:
# 1. httpUnsafeOrigin — should match the public HTTPS URL
# If set to http:// or localhost, cookie security may be broken
# 2. adminKeys — list of Ed25519 public keys with admin access
# Empty array = NO admin panel access (locked out)
# Wrong key = admin panel inaccessible
# 3. maxWorkerCount — performance config (not security)
# 4. restrictRegistration — false = open registration
# 5. allowSubscriptions — Stripe payment integration
# 6. mongoUri — database connection (may include auth credentials)
# e.g., mongodb://user:password@localhost:27017/cryptpad
python3 -c "
import re, sys
content = open('/opt/cryptpad/config/config.js').read()
# Extract key values
patterns = {
'adminKeys': r\"adminKeys.*?\\['([^']+)'\]\",
'mongoUri': r\"mongodb://[^']+\",
'httpUnsafeOrigin': r\"httpUnsafeOrigin.*?'([^']+)'\",
'adminEmail': r\"adminEmail.*?'([^']+)'\",
}
for name, pattern in patterns.items():
m = re.search(pattern, content)
if m:
print(f'{name}: {m.group(0)[:80]}')
" 2>/dev/null
# Check registration status (unauthenticated)
curl -s "${CP_URL}/api/config" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
reg = d.get('restrictRegistration', False)
print(f'Open registration: {not reg}')
print(f'Admin email: {d.get(\"adminEmail\",\"\")}')
" 2>/dev/null
# Storage usage (public stats endpoint)
curl -s "${CP_URL}/api/usage" 2>/dev/null | python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
print(f'Registered users: {d.get(\"users\")}')
print(f'Documents: {d.get(\"pads\")}')
print(f'Files: {d.get(\"files\")}')
except: pass
" 2>/dev/null
| Security Test | Method | Risk |
|---|---|---|
| adminKeys misconfiguration and admin panel access | Verify config.adminKeys contents — empty array locks everyone out; wrong key blocks legitimate admin; test /admin/ panel accessibility and functionality with own keys | High |
| MongoDB unauthenticated access — session token extraction | mongo cryptpad db.sessions.find() — active session tokens for all users; use tokens to authenticate as any user without password | Critical |
| Open registration detection | GET /api/config — restrictRegistration: false reveals open registration; unauthorized account creation enables access to shared documents and storage consumption | Medium |
| mongoUri credential extraction from config.js | Read config.js mongoUri — MongoDB connection string with username and password; full database access | High |
| Blob storage mass enumeration | GET /blob/{prefix}/{hash} — encrypted document blobs accessible by hash; mass enumeration possible to map document existence without decrypting content | Low |
Ironimo tests CryptPad deployments for MongoDB unauthenticated access and session token extraction, adminKeys misconfiguration detection, open registration status via /api/config, mongoUri credential extraction from config.js, blob storage unauthenticated access assessment, /api/usage statistics disclosure, httpUnsafeOrigin misconfiguration detection, Docker CPAD_ environment variable exposure, block storage path traversal testing, and admin panel accessibility verification.
Start free scan