Listmonk v6.0.0, a self-hosted newsletter manager, ships with a session management flaw that leaves existing sessions active even after users reset or change their passwords. Attackers who snag a session cookie before the change retain full account access. This breaks a core security promise: password updates should lock out intruders.
The vulnerability hits two key flows. In password resets, the app consumes the reset token, updates the password, and issues a new session—but skips revoking old ones. Authenticated password changes via the profile endpoint do the same: credentials update, but prior sessions persist. I verified this on a fresh v6.0.0 install. Old passwords fail logins, new ones succeed, yet pre-change cookies authenticate against endpoints like /api/profile, returning user data with 200 OK.
Why does this matter? Listmonk admins handle sensitive subscriber lists, email templates, bounce data, and campaign stats—often millions of records. A stolen session lets attackers export lists, send spam, or delete queues without re-authenticating. Account recovery becomes useless if the victim doesn’t manually log out everywhere first, which most users skip. In security terms, this violates OWASP session management guidelines, where credential changes must invalidate all sessions. No CVSS score yet, but it’s high-impact: persistent access post-compromise.
Technical Breakdown
Source code review points to gaps in cmd/auth.go (reset flow), cmd/users.go (profile updates), and internal/core/users.go (password hashing). Resets enforce single-use tokens and TOTP where enabled, but neither touches active sessions. Listmonk stores sessions server-side, likely in PostgreSQL (default backend), tied to user IDs without versioned tokens or revocation lists. A fix needs session invalidation—e.g., bumping a user session counter and checking it on each request, or deleting all user sessions on change.
This isn’t rare in smaller projects. Listmonk, built in Go by knadh, prioritizes performance (handles 10M+ subscribers) over enterprise-grade auth. Compare to mature tools like Discourse or Mail-in-a-Box, which invalidate sessions on password changes. Skeptical take: open-source speed leaves edges rough. Repo has 1.5k stars, active issues—flag it there if unfixed.
Proof of Concept
Case 1: Password Reset Leaves Sessions Live
1. Log in as a user; capture session cookie.
2. Trigger forgot-password; use emailed link to set new password.
3. Confirm: old password rejected, new accepted.
4. Replay old cookie:
GET /api/profile HTTP/1.1
Host: 127.0.0.1:9000
Cookie: session=eyJ1c2VyX2lkIjoiMSIsImV4cCI6MTcyMDAwMDAwMH0...
Result: 200 OK with profile JSON. Access persists.
Case 2: Password Change Ignores Sister Sessions
1. Log in twice; save session A and B cookies.
2. With A, PATCH /api/profile to change password.
3. Verify passwords as above.
4. Replay B on /api/profile: still 200 OK.
Example change request (with A):
PATCH /api/profile HTTP/1.1
Host: 127.0.0.1:9000
Cookie: session=eyJ1c2VyX2lkIjoiMSIsImV4cCI6MTcyMDAwMDAwMH0...
Content-Type: application/json
{"password": "newstrongpass123"}
Fix and Mitigation
Upgrade if patched—check GitHub.com/knadh/listmonk issues/PRs. No fix in v6.0.0. Interim: enforce short session expiry (e.g., 1 hour via config), use HTTPS-only cookies with HttpOnly/Secure flags, and monitor logs for anomalous /api hits. Rotate passwords and log out all devices post-reset. For prod, proxy with auth middleware like OAuth2 Proxy to layer protection.
Bottom line: run listmonk? Test this now. Expose it publicly? Prioritize. Self-hosting trades control for risks like this—audit auth flows quarterly. Tools evolve; stay sharp.