CVE-2025-65091

Calendar of Doom: A Critical HQL Injection in XWiki

Alon Barad
Alon Barad
Software Engineer

Jan 11, 2026·4 min read

Executive Summary (TL;DR)

The XWiki Full Calendar Macro contained a 'Build-Your-Own-Query' vulnerability. By passing raw HQL into the `sql` URL parameter, unauthenticated attackers could dump the entire database or delete content. The fix involves dropping privileges and removing the ability to pass full queries.

A 10.0 CVSS critical vulnerability in the XWiki Full Calendar Macro allows unauthenticated attackers to execute arbitrary HQL queries. The vulnerability stems from a Velocity script that accepts raw SQL fragments as URL parameters, effectively exposing a database console to the internet.

The Hook: "Build Your Own Query" Interface

Calendars are usually the most boring part of any application. They show dates, maybe a meeting or two, and that's it. But in the world of XWiki, the Full Calendar Macro decided to spice things up by turning a simple JSON data feed into a high-stakes gambling game with your database.

Here is the setup: XWiki uses Hibernate to talk to its database. The Full Calendar Macro needs to fetch events, so it uses a Velocity script (Calendar.JSONService) to query the backend. Normally, a developer would write a specific query to fetch events.

In this case, however, the developers apparently decided that writing queries was too much work. Instead, they built what essentially amounts to a database proxy. They exposed parameters like request.sql, request.wheresql, and request.fromsql directly to the web. If you've ever thought, "Man, I wish I could just SELECT * FROM users via a URL parameter," this vulnerability is for you.

The Flaw: A Text-Book Injection

The vulnerability lies in how the Calendar.JSONService constructs its Hibernate Query Language (HQL) statement. HQL is an object-oriented dialect of SQL. It's powerful, but it still suffers from the same injection flaws as standard SQL if you treat it like a string concatenation party.

Here is the logic flaw: The script checks if the sql parameter is present in the HTTP request. If it is, it sets the HQL query directly to the value of that parameter. No filtering, no validation, no questions asked.

But wait, it gets worse. In XWiki, Velocity scripts run with the permissions of the last author who saved the document. If an administrator or a user with "Programming Rights" installed or updated this macro, that script runs with God-mode privileges.

So, you have an unauthenticated endpoint (accessible to Guests) that accepts raw database commands and executes them with the highest possible privileges. It's not just a hole in the wall; the entire wall is missing.

The Smoking Gun: Code Analysis

Let's look at the code before the patch. It is breathtaking in its simplicity and danger.

 

CVE-2025-65091 Vulnerable Code

#if ("$!{request.sql}" != '')

direct injection point 1: Just overwrite the whole query

#set ($hql = $request.sql) #else

direct injection point 2: Concatenate partial strings

#set ($hql =", BaseObject as obj $!{request.fromsql} where doc.fullName=obj.name and obj.className='${request.classname}' $!{request.wheresql}") #end

Execute the query

#foreach ($item in $services.query.hql($hql).addFilter('currentlanguage').execute())

... return data ...

#end


The code literally says: "If the user provided `sql`, run it. Otherwise, construct a query using other user inputs (`fromsql`, `classname`, `wheresql`)." Even the "safe" path is riddled with injection points because `$\{request.classname\}` is interpolated directly into the string without binding.

The Exploit: Total Database Compromise

Exploiting this is trivial. You don't need complex boolean-blind inferencing or time-based payloads (though those work too). You can just ask for the data you want.

Scenario: An attacker wants to dump all user credentials.

Vector: The sql parameter.

Payload:

GET /xwiki/bin/view/Calendar/JSONService?sql=select doc.fullName, doc.content from XWikiDocument doc where doc.fullName like '%Profile%'

Because the script iterates over the results and returns them as JSON, the attacker simply receives the database contents in the HTTP response. Since the script likely runs with Programming Rights, the attacker bypasses all document-level access controls. They can read private pages, password hashes (if stored in docs), and configuration secrets.

The Fix: Dropping the Hammer (and Privileges)

The remediation in version 2.4.5 (Commit 5fdcf06a05015786492fda69b4d9dea5460cc994) is a multi-layered approach. They didn't just fix the injection; they neutered the execution context.

Key Changes:

  1. Removed sql parameter: You can no longer replace the entire query string.
  2. Bind Parameters: The classname is now bound using .bindValue(), preventing string breakouts there.
  3. Drop Permissions: This is the critical part.
 

The Fix

#set ($discard = $xcontext.dropPermissions()) #foreach ($item in $services.query.hql($hqlStatement).bindValue("classname", "$!{request.classname}")...)


> [!NOTE]
> **Researcher Insight**: Notice that `$request.fromsql` and `$request.wheresql` are *still* concatenated into the query string in the fix! However, because `$xcontext.dropPermissions()` is called, the query now runs as the current user (e.g., Guest). Even if you inject SQL, you can only query documents you already have permission to see. This turns a Critical Data Exfiltration bug into a potential (but limited) Denial of Service annoyance.

Fix Analysis (1)

Technical Appendix

CVSS Score
10.0/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
EPSS Probability
0.03%
Top 92% most exploited

Affected Systems

XWiki Full Calendar Macro < 2.4.5

Affected Versions Detail

Product
Affected Versions
Fixed Version
macro-fullcalendar
xwiki-contrib
< 2.4.52.4.5
AttributeDetail
CVSS10.0 (Critical)
CWECWE-89 (SQL Injection)
Attack VectorNetwork (HQL Injection)
PrivilegesNone (Unauthenticated)
Exploit StatusHigh Probability (Trivial)
PatchVersion 2.4.5
CWE-89
SQL Injection

Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')

Vulnerability Timeline

CVE-2025-65091 Published
2025-01-20
Fix committed to GitHub
2025-01-16

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.