Hacker News new | ask | show | jobs
by TheDong 464 days ago
The postgres escape function actually worked fine before this "CVE". It was documented as escaping something for use as part of a postgres query.

BeyondTrust used it as input to the 'psql' tool, which is an interactive tool you're not really supposed to programmatically invoke, and the documentation for the postgres escape function didn't say it escaped input for psql. Even though postgres was fine calling it a CVE and fixing it, I think this is 100% on BeyondTrust for assuming that escaping a string for a postgres query meant it was safe for psql.

If BeyondTrust had just used it as part of a postgres query string, the escape function would have been sufficient.

.... and that's also exactly the reason that using parameterized queries is better. With parameterized queries, the escaping and the query parsing are done in the same place, so there's no chance of confusion for the programming language's string library to get in the way, or for the psql tool's input parsing to re-interpret and alter the escaped string before sending it over the wire.

2 comments

> If BeyondTrust had just used it as part of a postgres query string, the escape function would have been sufficient.

That's completely false. The following (pseudo code because working with C strings is verbose and beside the point) with nothing to do with psql should be fine:

  PQexec(conn, "SELECT * FROM user WHERE nickname = '" + PQescapeString(user_input) + "';")
but thanks to the vulnerable PQescapeString(), the following user_input

  "\xc0'; DROP TABLE user"
would fuck it up. That's just the failed escape function leading to a classic SQL injection. Using psql makes it worse because psql can execute additional non-SQL commands, but this escape function is not "fine" at all with or without psql.

> With parameterized queries, the escaping and the query parsing are done in the same place

Again, wrong. For parametrized queries, params don't go through serialization because they don't need to hit the parser, there's no "escaping" whatsoever.

> but thanks to the vulnerable PQescapeString(), the following user_input would fuck it up

Nope. To quote: https://www.rapid7.com/blog/post/2025/02/13/cve-2025-1094-po...

> Because of how PostgreSQL string escaping routines handle invalid UTF-8 characters, in combination with how invalid byte sequences within the invalid UTF-8 characters are processed by psql

If you just pass it to postgres over a normal query, postgres will reject an invalid byte sequence in the query with an error, refuse to even parse the query, and thus you won't get a SQL injection. It's just that psql didn't hard-error on invalid utf-8, even though postgres did.

That's why the escape function was suitable for postgres, both the escape function and postgres's query parser assume invalid byte sequences are, you know, invalid.

> For parametrized queries, params don't go through serialization because they don't need to hit the parser, there's no "escaping" whatsoever.

You're right of course, I used imprecise language that everyone understands, and you're choosing to read critically in order to be combative.

You don't have to be so combative.

Sure, the blog post didn't mention PQexec would reject it, so I assumed it would be accepted. Turns out it's a narrowly dodged bullet, I'm wrong on that. But having to chain two vulnerabilities together to own the system doesn't make either vulnerability less of a vulnerability. The escape function was wrong, period, another level of defense helped in this case, but "the documentation for the postgres escape function didn't say it escaped input for psql" is a bullshit excuse (it definitely didn't achieve the documented goal of "escaping special characters so that they cannot cause any harm"), putting "CVE" in quotes and blaming it all on the user is wrong.

> I used imprecise language that everyone understands, and you're choosing to read critically in order to be combative.

No, your "imprecise language" is a fundamental and quite dangerous misunderstanding that could easily lead to more vulnerabilities like this one ("PQexecParams = PQexec + PQescapeString, amiright? I'll just use the latter"). Maybe you didn't misunderstand yourself, maybe you did, but it's 100% misleading for readers not familiar with db internals.

> The postgres escape function actually worked fine before this "CVE". It was documented as escaping something for use as part of a postgres query.

> BeyondTrust used it as input to the 'psql' tool, which is an interactive tool you're not really supposed to programmatically invoke, and the documentation for the postgres escape function didn't say it escaped input for psql.

But this is the documentation for psql:

> psql is a terminal-based front-end to PostgreSQL. It enables you to type in queries interactively, issue them to PostgreSQL, and see the query results. Alternatively, input can be from a file or from command line arguments. In addition, psql provides a number of meta-commands and various shell-like features to facilitate writing scripts and automating a wide variety of tasks.

We can learn two things from this:

1. You are definitely supposed to be able to invoke psql programmatically, or else the suggestion to "write scripts and automate a wide variety of tasks" would make no sense.

2. The input to psql is documented as a "query", and it seems fine to assume that a "query" for psql is the same thing as a "postgres query".