CVE-2021-33322
Description
In Liferay Portal 7.3.0 and earlier, and Liferay DXP 7.0 before fix pack 96, 7.1 before fix pack 18, and 7.2 before fix pack 5, password reset tokens are not invalidated after a user changes their password, which allows remote attackers to change the user’s password via the old password reset token.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Liferay Portal and DXP fail to invalidate password reset tokens after password change, allowing attackers to reuse an old token to reset the user's password.
Vulnerability
In Liferay Portal 7.3.0 and earlier, and Liferay DXP 7.0 before fix pack 96, 7.1 before fix pack 18, and 7.2 before fix pack 5, a flaw in the password reset flow leaves password reset tokens valid even after a user successfully changes their password. The _invalidateTicket method was not called in the updatePassword and updatePasswordManually functions prior to the fix [3][4]. This means an issued reset token remains active indefinitely, rather than being invalidated upon password change. The issue affects the ticket management system that handles password reset tokens [1].
Exploitation
An attacker who has obtained a valid password reset token (for example, through intercepting a password reset email, or by having prior access to the token via another vulnerability) can use that token to change the victim's password even after the legitimate user has already changed their password. No additional authentication is required once the attacker holds the token. The attacker only needs network access to the Liferay instance's password reset endpoint [1]. The attack sequence involves: obtaining the old reset token, sending a password reset request with that token, and setting a new password of the attacker's choosing.
Impact
Successful exploitation allows a remote attacker to arbitrarily change a user's password without knowing the current password. This results in a complete compromise of the victim's account, leading to unauthorized access to all data and functionality associated with that user within the Liferay portal. The impact is particularly severe for administrative accounts, as the attacker could gain elevated privileges [1].
Mitigation
Liferay released fixes for this vulnerability in Liferay DXP 7.0 fix pack 96, 7.1 fix pack 18, 7.2 fix pack 5, and Liferay Portal 7.3.1. The fix adds calls to _invalidateTicket within the updatePassword and updatePasswordManually methods to ensure all existing password reset tickets are expired when a password is changed [3][4]. Users should upgrade to the patched versions immediately. If upgrading is not possible, restricting network access to the password reset functionality and monitoring for suspicious password reset activity may reduce risk, but a full mitigation requires applying the patch.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
com.liferay.portal:com.liferay.portal.implMaven | < 5.7.3 | 5.7.3 |
com.liferay.portal:release.dxp.bomMaven | >= 7.0.0, < 7.0.10.fp96 | 7.0.10.fp96 |
com.liferay.portal:release.dxp.bomMaven | >= 7.1.0, < 7.1.10.fp18 | 7.1.10.fp18 |
com.liferay.portal:release.dxp.bomMaven | >= 7.2.0, < 7.2.10.fp5 | 7.2.10.fp5 |
Affected products
3- Liferay/Liferay Portaldescription
- ghsa-coords2 versions
< 5.7.3+ 1 more
- (no CPE)range: < 5.7.3
- (no CPE)range: >= 7.0.0, < 7.0.10.fp96
Patches
29fe453b34f58LPS-108642 SF simplify
1 file changed · +8 −10
portal-impl/src/com/liferay/portal/service/impl/UserLocalServiceImpl.java+8 −10 modified@@ -4808,7 +4808,7 @@ public User updatePassword( null, ServiceContextThreadLocal.getServiceContext()); } - _invalidateTicket(user.getCompanyId(), User.class.getName(), userId); + _invalidateTicket(user); return user; } @@ -4843,7 +4843,7 @@ public User updatePasswordManually( user = userPersistence.update(user); - _invalidateTicket(user.getCompanyId(), User.class.getName(), userId); + _invalidateTicket(user); return user; } @@ -6896,19 +6896,17 @@ private String _getLocalizedValue( return localizedValueMap.get(fallbackLocale); } - private void _invalidateTicket( - long companyId, String className, long classPK) - throws PortalException { - + private void _invalidateTicket(User user) throws PortalException { List<Ticket> tickets = ticketLocalService.getTickets( - companyId, className, classPK, TicketConstants.TYPE_PASSWORD); + user.getCompanyId(), User.class.getName(), user.getUserId(), + TicketConstants.TYPE_PASSWORD); for (Ticket ticket : tickets) { if (!ticket.isExpired()) { ticketLocalService.updateTicket( - ticket.getTicketId(), className, classPK, - TicketConstants.TYPE_PASSWORD, ticket.getExtraInfo(), - new Date()); + ticket.getTicketId(), User.class.getName(), + user.getUserId(), TicketConstants.TYPE_PASSWORD, + ticket.getExtraInfo(), new Date()); } } }
8f072ee8527aLPS-108642 Once change the password, we need to invalidate the password reset link
1 file changed · +24 −1
portal-impl/src/com/liferay/portal/service/impl/UserLocalServiceImpl.java+24 −1 modified@@ -4808,6 +4808,8 @@ public User updatePassword( null, ServiceContextThreadLocal.getServiceContext()); } + _invalidateTicket(user.getCompanyId(), User.class.getName(), userId); + return user; } @@ -4839,7 +4841,11 @@ public User updatePasswordManually( user.setPasswordModifiedDate(passwordModifiedDate); user.setDigest(user.getDigest(password)); - return userPersistence.update(user); + user = userPersistence.update(user); + + _invalidateTicket(user.getCompanyId(), User.class.getName(), userId); + + return user; } /** @@ -6890,6 +6896,23 @@ private String _getLocalizedValue( return localizedValueMap.get(fallbackLocale); } + private void _invalidateTicket( + long companyId, String className, long classPK) + throws PortalException { + + List<Ticket> tickets = ticketLocalService.getTickets( + companyId, className, classPK, TicketConstants.TYPE_PASSWORD); + + for (Ticket ticket : tickets) { + if (!ticket.isExpired()) { + ticketLocalService.updateTicket( + ticket.getTicketId(), className, classPK, + TicketConstants.TYPE_PASSWORD, ticket.getExtraInfo(), + new Date()); + } + } + } + private void _sendNotificationEmail( String fromAddress, String fromName, String toAddress, User toUser, String subject, String body,
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-vwj8-4grf-3r8vghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-33322ghsaADVISORY
- github.com/liferay/liferay-portal/commit/8f072ee8527a1dd5c0ffa91c4a78641d0e666b95ghsaWEB
- github.com/liferay/liferay-portal/commit/9fe453b34f58286a504d995be8ba50499adcf1b7ghsaWEB
- issues.liferay.com/browse/LPE-16981mitrex_refsource_CONFIRM
- liferay.atlassian.net/browse/LPE-16981ghsaWEB
- liferay.dev/portal/security/known-vulnerabilities/-/asset_publisher/jekt/content/cve-2021-33322-password-change-does-not-invalidate-password-reset-tokensghsaWEB
- portal.liferay.dev/learn/security/known-vulnerabilities/-/asset_publisher/HbL5mxmVrnXW/content/id/120748020mitrex_refsource_CONFIRM
News mentions
0No linked articles in our index yet.