aniket/awwfensive

Exploiting CVE-2025-29744: When Prepared Statements Aren't Safe

snippet

This post analyzes a subtle SQL injection vulnerability — CVE-2025-29744 — in pg-promise, a popular PostgreSQL library for Node.js built on top of node-postgres.

Discovered by the Sonar team, this flaw affects how pg-promise handles certain prepared statements when using the simple query protocol. Under specific conditions, it may allow attackers to inject SQL—even if first-party code uses parameterized queries.

Exploitation requires:


Vulnerability Details

The vulnerability involved injecting inline comments in a situation where subtraction from a value was possible, such as:

const query = 'UPDATE users SET balance = balance-$1 WHERE id = $2';
await db.none(query, [amount, userId]);

In this JS snippet, $1 is replaced with the value of amount and $2 is replaced with userId in a prepared statement. Assuming $1 is user controlled input, if the user enters negative value say -5 the SQL statement becomes:

UPDATE users SET balance = balance--5 WHERE id = 1

This situation results in inline commenting a part of the statement from the point of user input, which means 5 WHERE id = 1 was commented out and ignored.

Usually prepared statements are considered a secure practice to prevent SQL injection, but different libraries might handle comments differently, just like MySQL requires whitespace after the -- start comment sequence.


Setting up a vulnerable lab; Exploitation; Impact

In practical applications, the requirements for exploiting this CVE are quite stringent. In this section, we’ll explore what conditions are necessary for exploitation, what worked in our testing, and what didn’t.

Setting up the vulnerable application

To test the exploitability of this CVE locally, I built a minimal inventory app where users can input an amount and a username. These inputs are used in a SQL statement that modifies the user’s balance, satisfying the vulnerability prerequisites.

Lab setup:

cvePlayground is a repository of intentionally vulnerable CVE labs with detailed writeups and reproducible environments for hands-on learning and security research.

[FOLDER] This writeup corresponds to the following lab: CVE-2025-29744 — pg-promise SQL Injection

You’ll find setup instructions and exploitation steps inside the linked repository folder.

snippet

Following is the snippet where the vulnerability exists, even thought the application uses prepared statements, the application is vulnerable to SQL injection because vulnerable library pg-promise v11.5.4 failed to implement required security standards which leads to commenting the query when a negative integer is entered.

app

Initial Testing

When submitting the -1 as the input 1 (amount) and cat as the input 2 which is an existing user in the database, the Node.js server throws an error.

snippet

The following error is a clear indication that the SQL statement failed due PostgreSQL encountering a syntax error because the query ended unexpectedly, likely due to an improperly handled comment (--).

The query contains:

SELECT * FROM pg_promise_example WHERE result=--1 OR name='cat';

and PostgreSQL interpreted it as:

SELECT * FROM pg_promise_example WHERE result=

resulting an error.

The error confirms that the inline comment sequence is indeed commenting out rest of the query.


Crafting the Impact: using PostgreSQL against PostgreSQL

The official POC mentioned, PostgreSQL supports multi literals, which means a new line character (\n) can be used to pass multiple inputs! When -1 is passed that comments rest of the statement, passing foo \n bar makes the query look like:

SELECT * FROM pg_promise_example WHERE result=--1 OR name='foo
bar';

As a result, when PostgreSQL parses this query, it will ignore the comments and treat bar'; as additional statement.

An attacker can craft malicious query such as:

SELECT * FROM pg_promise_example WHERE result=--1 OR name='foo
INSERT INTO users (id, username, balance) VALUES (1337,"BigCat",13370000)';--';

Exploitation Example

Moving back to our vulnerable application, let’s enumerate version of the database with the above discussed knowledge.

testinp

Again, -1 comments the statement, and after cat a new line is treated as multi literal which makes the query look like:

SELECT * FROM pg_promise_example WHERE result=--1 OR name='cat 
1 AND 1=0 UNION SELECT 1337, version(); --
queryresult

From this point on, we have significant control — the possibilities are wide open.


The Patch

To address the vulnerability, the pg-promise team introduced a utility function designed to safely format numeric values before injecting them into SQL queries—especially in contexts involving subtraction.

queryresult

This utility is responsible for converting numeric values into safe SQL-friendly strings, and here’s how it works:

Ensuring valid input

if (typeof num === 'bigint' || Number.isFinite(num))

This condition ensures that the function only operates on a valid numeric inputs:

To conclude, this block of code only run for valid BigInt or finite numbers.

Conversion to string

const s = num.toString();

Once confirmed as safe, the number is converted to a string for formatting and interpolation.

Safe Formatting: Parentheses

return num < 0 ? `(${s})` : s;

A classic ternary operator that returns:

This formatting cleverly avoid accidental Comment injection from input like --5, which would previously result in syntactically invalid or exploitable SQL like:

UPDATE users SET balance = balance--5 WHERE id = 1;
-- becomes → UPDATE users SET balance = balance [COMMENT START]

End Note

PostgreSQL treats anything after -- as a comment unless it’s wrapped correctly. By transforming negative numbers into parentheses, like (-5) instead of --5, the patch neutralizes the risk of inline comment injection, even when using string substitution inside unsafe queries.

This simple but effective patch showcases how even well-intentioned ORMs can become vulnerable when lower-level SQL syntax rules (like comment delimiters) aren’t handled precisely.

If you enjoyed reading this blog and are passionate about CVE reversing or building hands-on CVE labs for practicing real-world web hacking scenarios, consider contributing to cvePlayground — a collection of intentionally vulnerable applications designed for learning and experimentation.

Check it out on GitHub: https://github.com/awwfensive/cvePlayground


References