I found this issue while reviewing listmonk, an open-source newsletter and mailing list manager, with a simple security question in mind:
When a user changes or resets a password, does the application actually terminate already-issued sessions?
In this case, the answer was no.
Previously issued authenticated sessions remained valid after both:
That meant a stolen session cookie could survive the exact security events that users rely on to recover their account.
The issue was accepted and assigned CVE-2026-34828.
Project: listmonk on GitHub
CVE: CVE-2026-34828
This affected listmonk, a widely adopted project with 5M+ Docker pulls.
stolen authenticated session → victim resets or changes password → old session remains valid → attacker retains account access after credential recovery
listmonk is a self-hosted mailing list and newsletter manager.
It provides:
That means its session model is a real security boundary.
The important question here was not whether listmonk supports password reset.
The real question was:
Does password reset or password change actually revoke attacker persistence if a session has already been stolen?
In this case, it did not.
A lot of security reviews focus too narrowly on login bypasses and obvious privilege escalation.
That misses an important class of weaknesses:
recovery failures
If a user changes or resets a password, that action is supposed to be meaningful.
It is supposed to reduce trust in older credentials and old authentication state.
If an attacker already has a valid session and that session survives the recovery event, then the victim has not actually recovered the account fully.
That was the issue here.
This was not a login validation bug. It was not a crypto issue. It was not a password hashing failure.
It was a session lifecycle failure:
That is enough to create a real vulnerability.
I did not approach listmonk by randomly hitting endpoints and hoping one would fall over.
The stronger path was to identify the highest-value trust boundary first.
For authentication-heavy software, one of the best boundaries to test is this:
Do security-sensitive account changes revoke previously trusted sessions?
That question usually becomes interesting around:
In listmonk, the strongest signal came from the first two.
That is where the issue became clear.
The bug was not that password changes failed.
The bug was that sessions outlived them.
From source review, the password reset flow:
but there was no visible revocation of older sessions.
The same pattern appeared in the authenticated password change flow:
That behavior matched the live results exactly.
Relevant code areas I reviewed were:
cmd/auth.go for forgot/reset behaviorcmd/users.go for authenticated profile updatesinternal/core/users.go for password update handlingBecause session theft is a real attack condition.
Once an attacker obtains a valid authenticated session cookie through any means such as:
the victim should be able to terminate that attacker persistence by changing or resetting the password.
Here, they could not.
The attack chain was straightforward:
That is the whole vulnerability.
The important distinction is persistence after recovery.
Plenty of applications treat password change as a purely credential-level event. That is not enough.
The real question is not:
“Did the password value change in storage?”
The real question is:
“Did the trust relationship attached to older sessions get revoked?”
In listmonk, it did not.
That turns what could have been ordinary account maintenance into incomplete security recovery.
That is the difference between:
I validated the issue in two separate flows.
First, I created a normal test user and logged in, saving the authenticated session cookie.
Then I triggered the forgot-password flow, captured the reset link, and reset the password.
After reset:
A representative validation request looked like this:
And the server still returned:
with the authenticated profile.
That established the core claim:
I then validated the same class of bug in the authenticated password change flow.
I logged in twice as the same user and saved two valid authenticated sessions:
Using session A, I changed the password through the profile update endpoint.
Example request:
After that:
A follow-up request using session B still returned authenticated data from /api/profile.
That proved the issue was not limited to the forgot/reset path.
It affected normal authenticated password changes too.
One reproduction would already have been enough to show a problem.
But validating both flows mattered for two reasons.
It showed the bug was not isolated to one edge-case recovery path.
The same security property failed in:
It made the issue harder to dismiss as accidental business logic.
This was clearly a broader session management weakness:
That gave the issue much stronger security weight.
I also tested the reset flow on a TOTP-enabled account because I wanted to know whether password reset would silently weaken or bypass 2FA expectations.
What I confirmed was:
That was a useful boundary check.
It narrowed the issue correctly.
The vulnerability was not:
The real issue remained:
That is a cleaner and more defensible finding.
This issue was reasonably classified as High.
The key impact here is persistent unauthorized access after account security recovery actions.
The advisory classification was:
That makes sense.
The claim is not that an attacker can log in without credentials from nothing. The claim is that once an attacker has obtained a valid authenticated session, the victim cannot fully terminate that access by performing the exact security actions that are supposed to recover the account, namely password reset and password change.
That is a real and defensible session-management vulnerability.
Some people underrate session persistence bugs because they assume session theft is already “game over.”
That is too simplistic.
The real question is what happens after the victim notices something is wrong and takes action.
If:
then account recovery is incomplete.
That is not just awkward behavior. That is a security failure in the recovery model.
Especially in an admin-facing platform, that is a meaningful issue with strong confidentiality impact.
The maintainer fixed the issue in commit:
The core fix direction is exactly what this bug needed:
That is the correct remediation because it targets the real security property that failed:
older trust should die when credentials change
A good fix for this class of bug is not about changing password validation. It is about revoking previously active session state attached to the account.
That is the part that restores actual recovery.
This issue was reported privately through GitHub’s security reporting flow.
The maintainer:
CVE-2026-34828
One thing that came up during advisory handling was scope.
The original report included both:
GitHub initially treated these as independently fixable issues for CVE assignment purposes. That is a useful reminder that advisory scope matters even when the underlying weakness is conceptually similar.
The final result was CVE-2026-34828.
The key lesson here is simple:
changing credentials is not enough if old authenticated trust is still alive.
A lot of developers think in terms of:
Those things matter.
But the real security boundary is broader:
when a high-risk account event happens, what previously trusted state must stop being trusted?
In this case, the answer should have been:
And listmonk was not doing that.
That is the real takeaway.
This vulnerability was not about fancy payloads or clever parser tricks.
It was about asking the right trust-boundary question.
In listmonk, the password changed.
The recovery action completed.
But the attacker’s old session still lived.
That is why this became CVE-2026-34828.