The Time Bomb in the Schema: Liferay Upgrade SQL Injection
Jan 13, 2026·7 min read
Executive Summary (TL;DR)
Liferay's upgrade tool for MS SQL Server trusts database metadata blindly. If an attacker can rename a Primary Key constraint to contain malicious SQL, the upgrade script—running with high privileges—will execute it. It's a classic 'Second-Order' SQL injection turned into a persistence time bomb.
A high-complexity but devastating SQL injection vulnerability in the Liferay Portal/DXP upgrade process allows attackers to plant malicious payloads in database metadata (primary keys), which are then executed with elevated privileges during system upgrades.
The Hook: The Call is Coming from Inside the House
We often think of SQL injection (SQLi) as a front-door attack: a user types ' OR 1=1 -- into a search box, and the web application trips over its own shoelaces. But what happens when the attack vector isn't user input, but the database itself? That's the insidious nature of CVE-2023-33945.
This isn't a vulnerability you exploit in real-time to dump a password hash. This is a time bomb. It resides in the Liferay Portal and DXP upgrade process—specifically when running on Microsoft SQL Server. The upgrade logic, designed to migrate your data to a shiny new version, has a fatal flaw: it assumes the database schema is pristine and trustworthy.
Imagine an attacker who gains a foothold—maybe a low-privileged SQL injection or a compromised account with just enough permission to rename database objects. They don't steal data immediately. Instead, they rename a Primary Key (PK) constraint to a malicious SQL payload. Then, they wait. Weeks or months later, when the diligent sysadmin runs the critical upgrade script with sa (System Administrator) privileges, the trap springs. The upgrade tool reads the malicious PK name and executes it, effectively letting the attacker hijack the admin's session to nuke the database or create a backdoor.
The Flaw: Unsanitized Metadata Trust
The root cause here is a failure to sanitize metadata. When Liferay performs an upgrade, it often needs to restructure tables. To do this, it has to drop existing constraints (like Primary Keys) before modifying columns. The logic flows like this: query the database for the name of the PK on Table_X, store that name in a variable, and then concatenate it into a DROP CONSTRAINT statement.
Developers constantly remember to sanitize HTTP inputs, but they frequently forget that database metadata is also input. In this specific case, the Liferay upgrade runner queries MS SQL Server's system views (like sys.indexes or sys.key_constraints) to find out what the PK is called.
Because the developers assumed a Primary Key would always be named something sane like PK_Users_01, they didn't bother wrapping the variable in quotes or brackets when building the next query. They took the string directly from the database and glued it into a raw SQL command. This allows for Second-Order SQL Injection, where the payload is stored first and executed later by a completely different process.
The Code: Anatomy of the Injection
While the exact proprietary source code isn't public, the mechanics described in the advisory allow us to reconstruct the vulnerable logic with high precision. The issue lies in the Java-based upgrade classes interacting with MS SQL via JDBC.
The Vulnerable Logic
Imagine the upgrader needs to drop the PK on the Users table. It runs logic effectively similar to this:
// Step 1: Get the PK name from metadata
String pkName = getPrimaryKeyName("Users");
// Step 2: Construct the SQL command (VULNERABLE)
// If pkName is "PK_Users", this runs: ALTER TABLE Users DROP CONSTRAINT PK_Users
String sql = "ALTER TABLE Users DROP CONSTRAINT " + pkName;
// Step 3: Execute with High Privileges
statement.execute(sql);The Payload Injection
If an attacker has previously renamed the PK using sp_rename to something like PK_Old]; DROP TABLE Users; --, the variable pkName becomes that malicious string.
The resulting SQL constructed in Step 2 becomes:
ALTER TABLE Users DROP CONSTRAINT PK_Old]; DROP TABLE Users; --MS SQL Server parses this as two distinct commands:
ALTER TABLE Users DROP CONSTRAINT PK_Old](Valid command)DROP TABLE Users(The attacker's payload)--(Comments out the rest)
The Fix
The remediation is simple but critical: delimiting identifiers. In MS SQL, identifiers should be wrapped in brackets [].
// Fixed Logic
String sql = "ALTER TABLE Users DROP CONSTRAINT [" + pkName + "]";Now, the database treats the entire malicious string as the name of the constraint, causing the query to fail harmlessly because no constraint with that long, weird name exists, rather than executing the injected code.
The Exploit: Planting the Trap
Let's walk through a realistic attack chain. This exploits the "High Complexity" aspect of the CVSS score, turning a minor foothold into a major compromise.
Prerequisite: The attacker needs the ability to execute sp_rename or modify schema definitions. This could come from a separate, lower-severity SQLi in a custom Liferay portlet.
Step 1: The Setup
The attacker identifies a table scheduled for modification in the next Liferay version (e.g., User_). They check the current PK name.
Step 2: The Injection The attacker renames the Primary Key. They don't delete data yet; they just change the label.
-- Renaming the primary key of the User_ table
-- Payload: Closes the bracket, injects a command to add a new sysadmin, and comments out the rest.
EXEC sp_rename
'PK_User_',
'PK_User_]; CREATE LOGIN hacker WITH PASSWORD = ''P@ssw0rd123!''; ALTER SERVER ROLE sysadmin ADD MEMBER hacker; --';Step 3: The Waiting Game The attacker does nothing. The application continues to function normally because the PK name rarely matters during standard runtime CRUD operations.
Step 4: The Trigger
Months later, the admin takes the system offline for a maintenance window to upgrade from Liferay 7.3 to 7.4. They run the upgrade tool, which connects as sa.
Step 5: Detonation
The upgrade tool reaches the User_ table migration step. It fetches the name we planted. It constructs the DROP CONSTRAINT query. The injection fires. The attacker now has a sysadmin account named hacker waiting for them on the database server.
The Impact: Why You Should Panic
The CVSS score of 6.4 is deceptive. It's lowered by the "High Complexity" and "High Privileges" requirements, but the Impact is catastrophic (Confidentiality, Integrity, Availability all High).
If successfully exploited, this is not just a data leak; it is total infrastructure compromise. Because upgrade scripts almost always require elevated permissions (often db_owner or sysadmin), the injected code runs with those same permissions.
This bypasses standard application-layer defenses. WAFs won't catch it (the payload is already inside). Runtime RASP won't catch it (it happens during a maintenance window). It is a persistent threat that turns your own operational procedures against you.
The Fix: Remediation & Hunting
Mitigation involves two steps: patching the code and scrubbing the database.
1. Patching Upgrade Liferay immediately to a fixed version where the upgrade logic properly quotes identifiers.
- Liferay Portal: 7.4.3.18+
- DXP 7.3: Update 6+
- DXP 7.4: Update 18+
2. Hunting for IoCs (Indicators of Compromise) Before you run any upgrade, you should audit your database schema. If an attacker has already planted a bomb, patching the tool might prevent execution, but it's better to know if you've been targeted.
Run this query on your MS SQL Server to find suspicious constraints:
SELECT
t.name AS TableName,
k.name AS ConstraintName,
k.type_desc
FROM sys.key_constraints k
JOIN sys.tables t ON k.parent_object_id = t.object_id
WHERE
k.name LIKE '%;%'
OR k.name LIKE '%--%'
OR k.name LIKE '%DROP%'
OR k.name LIKE '%UPDATE%';If this query returns results, do not proceed with the upgrade. Investigate the constraints immediately and check your transaction logs for when they were renamed.
Official Patches
Technical Appendix
CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:U/C:H/I:H/A:HAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
Liferay Portal Liferay | >= 7.3.1, <= 7.4.3.17 | 7.4.3.18 |
Liferay DXP 7.3 Liferay | < Update 6 | Update 6 |
Liferay DXP 7.4 Liferay | < Update 18 | Update 18 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-89 (Improper Neutralization of Special Elements in SQL Command) |
| Attack Vector | Network (Context Dependent) |
| CVSS v3.1 | 6.4 (Medium) |
| Impact | Critical (RCE/DBA via Upgrade) |
| EPSS Score | 0.00282 (Low Probability) |
| Exploit Status | No Public PoC / Weaponized Exploit |
MITRE ATT&CK Mapping
The software constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component.
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.