Apr 2, 2026·7 min read·0 visits
Listmonk prior to version 6.1.0 does not invalidate active session tokens upon password changes or resets. Attackers with stolen session cookies can maintain persistent authenticated access despite victim credential rotation.
Listmonk versions 4.1.0 through 6.0.x contain an Insufficient Session Expiration vulnerability (CWE-613) within the application's authentication lifecycle handlers. The software fails to revoke existing authenticated sessions when a user undergoes a password reset or performs an intentional password change. This oversight enables an attacker who has acquired a valid session cookie to maintain unauthorized, persistent access to the compromised account, successfully bypassing the primary defense mechanism of credential rotation.
Listmonk is an open-source, self-hosted newsletter and mailing list manager written in Go and backed by a PostgreSQL database. The software relies on server-side session management to handle administrator and user authentication. Sessions are generated upon successful login, stored persistently in the database, and tracked via an HTTP cookie delivered to the client.
CVE-2026-34828 identifies a structural flaw in how listmonk manages the lifecycle of these sessions during critical security events. Specifically, the application exhibits an Insufficient Session Expiration vulnerability (CWE-613). When an account password is changed or reset, the application updates the authentication credentials but fails to terminate preexisting sessions associated with that user.
This architecture fundamentally decouples the session state from the credential state. Consequently, the primary remediation strategy for suspected account compromise—rotating the account password—fails to neutralize the threat. An attacker who has previously compromised an active session token retains access until the original token reaches its natural Time-To-Live (TTL) expiration.
The attack surface requires the threat actor to have preemptively obtained a valid session cookie. This is typically achieved through secondary attack vectors, such as Cross-Site Scripting (XSS), physical access to an unlocked terminal, endpoint malware, or network traffic interception on insecure connections.
The root cause of CVE-2026-34828 resides in the application's user profile update and password reset routines. The application tracks user sessions via a dedicated sessions table in the PostgreSQL database. The authentication middleware validates incoming requests by extracting the session ID from the cookie, verifying its cryptographic signature, checking its TTL, and confirming its presence in the database.
During a password reset flow, orchestrated by the doResetPassword handler in cmd/auth.go, the application successfully receives the valid reset token and processes the new password. The backend then issues a SQL UPDATE statement targeting the users table, overwriting the existing password_hash with the newly generated bcrypt hash.
However, this routine lacks a corresponding cleanup operation against the sessions table. The internal/core/users.go library, which abstracts the database interactions, did not implement any logic to invalidate or purge session records tied to the affected user ID.
Because the session validation middleware strictly evaluates the token's validity against the sessions table without cross-referencing a password version identifier or checking a last_password_change timestamp, the preexisting session tokens remain entirely valid. The application logic assumes session validity based solely on the token's existence, ignoring state changes in the underlying credential store.
The vulnerability was resolved in commit db82035d619348949512dafdaf60c86037cafc9e. The fix introduces a unified mechanism to purge active sessions from the database when credential-altering events occur. The core addition is a new parameterized SQL query in queries/users.sql.
-- Pre-patch: No session deletion query existed for user updates.
-- Post-patch: Added to queries/users.sql
-- name: delete-user-sessions
DELETE FROM sessions WHERE data->>'user_id' = $1 AND ($2 = '' OR id != $2);This query targets the JSONB data column in the sessions table to match the specific user ID. It conditionally spares the currently active session if an ID is provided in parameter $2, preventing the user initiating the password change from being unexpectedly logged out of their current device.
The Go backend was updated to utilize this query. In cmd/users.go, the handler responsible for processing user profile updates was modified to invoke the new DeleteUserSessions wrapper function.
// Modified cmd/users.go - UpdateUserProfile handler
func (a *App) UpdateUserProfile(c echo.Context) error {
// ... (input validation and user update logic)
// Post-patch addition: Revoke all other sessions upon password change
if req.Password != "" {
currentSession := auth.GetSessionID(c)
if err := a.core.DeleteUserSessions(user.ID, currentSession); err != nil {
a.log.Errorf("failed to clear sessions: %v", err)
}
}
return c.JSON(http.StatusOK, true)
}A similar implementation was added to cmd/auth.go for the unauthenticated password reset flow, where parameter $2 is explicitly left empty ("") to indiscriminately terminate all sessions for that user ID.
Exploitation of CVE-2026-34828 requires an attacker to possess a stolen session cookie. The exploit lifecycle begins when the threat actor compromises the victim's session, typically through client-side vulnerabilities, local machine access, or session hijacking techniques.
Once the victim detects suspicious activity, the standard incident response protocol dictates changing the account password. The victim authenticates to listmonk and navigates to the Profile settings to execute a password change, or utilizes the "Forgot Password" feature to reset the credentials via email.
> [!NOTE] > The vulnerability execution occurs precisely when the server processes the password change. The application commits the new password hash to the database but leaves the attacker's active session undisturbed in the session store.
The attacker continues to interface with the listmonk API by injecting the originally stolen session cookie into subsequent HTTP requests. The server processes the session identifier, finds a matching and unexpired record in the database, and authorizes the transaction.
GET /api/profile HTTP/1.1
Host: listmonk.example.local
Cookie: session=a8b3c9d2...
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 1,
"username": "admin",
"role": "super-admin"
}The persistence is maintained until the stolen session reaches its absolute expiration time, granting the attacker uninterrupted administrative or user-level access to the mailing list infrastructure.
The exploitation of this vulnerability results in persistent, unauthorized access to the listmonk instance. The severity is highly dependent on the privileges associated with the compromised session. If the session belongs to a super-admin, the threat actor retains complete control over the application, including the ability to exfiltrate subscriber databases, modify mailing lists, alter application configurations, and distribute malicious campaigns.
The CVSS v3.1 vector evaluates to 7.1 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N). The network attack vector (AV:N) and low attack complexity (AC:L) reflect the ease of replaying a valid cookie over the network. The privileges required (PR:L) denote that the attacker acts within the context of the compromised user account. Confidentiality impact (C:H) is high due to the exposure of personal subscriber data.
This vulnerability fundamentally degrades the integrity of the application's incident response mechanisms. System administrators and users operating under the assumption that a password reset secures a compromised account remain unknowingly exposed to continuous exploitation.
The definitive remediation for CVE-2026-34828 is upgrading the listmonk application to version 6.1.0 or later. This release incorporates the DeleteUserSessions logic, ensuring that session lifecycles are correctly tied to credential validity.
For environments where immediate patching is administratively or technically unfeasible, a manual mitigation strategy must be employed. Administrators can enforce a global session invalidation by directly interacting with the PostgreSQL database. Executing DELETE FROM sessions; will immediately terminate all active sessions, forcing all users to re-authenticate using their current credentials.
To detect potential historical exploitation, security engineers should audit the application logs and database records. Evidence of exploitation may present as multiple distinct IP addresses utilizing the same user ID within the sessions table immediately following a password reset event recorded in the system audit logs.
Network defenders can additionally implement Web Application Firewall (WAF) rules designed to monitor for session anomalies. Detecting a single session cookie generating requests from geographically disparate locations within an impossibly short timeframe (impossible travel) serves as a robust indicator of session theft and concurrent usage.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
listmonk knadh | >= 4.1.0, < 6.1.0 | 6.1.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-613 |
| Attack Vector | Network |
| CVSS Score | 7.1 (High) |
| Exploit Status | Proof of Concept (PoC) available |
| KEV Status | Not Listed |
| Impact | Persistent Unauthorized Access |
The system does not adequately terminate a user session upon critical account lifecycle events, allowing an attacker to maintain persistent unauthorized access.