
Proof-of-concept and detailed writeup for CVE-2026-51992, an SQL injection vulnerability in ClickHouse PostgreSQL dictionaries allowing arbitrary command execution.
Within ClickHouse, a feature exists that allows a user with the correct permissions to create dictionaries in order to interact with different databases and run specific queries on them. For PostgreSQL, SELECT queries are wrapped in a COPY( {QUERY} ) TO STDOUT statement before being executed on database. By adding a parantheses to a created dictionary, the COPY( {QUERY} ) TO STDOUT statement can be escaped out of and arbitrary SQL statements can be run, which can in turn lead to running arbitrary commands on the database server.
PosgreSQL dictionaries are created with the following structure:
SOURCE(POSTGRESQL(
port 5432
host 'postgresql-hostname'
user 'postgres_user'
password 'postgres_password'
db 'db_name'
table 'table_name'
replica(host 'example01-1' port 5432 priority 1)
replica(host 'example01-2' port 5432 priority 2)
where 'id=10'
invalidate_query 'SQL_QUERY'
query 'SELECT id, value_1, value_2 FROM db_name.table_name'
))
The documentation for the PostgreSQL engines used in the dictionaries mentions the following:
SELECT queries on PostgreSQL side run as COPY (SELECT ...) TO STDOUT inside read-only PostgreSQL transaction with commit after each SELECT query.
Since there is no additional validation on the queries defined in the dictionary before being send to the Postgres instance, the "COPY(...) TO STDOUT" can be broken out of by starting the query with a ")", creating a transaction that is not read-only. As an example, the following query can be used in ClickHouse to create a "bad" Postgres dictionary:
CREATE DICTIONARY exec_dict(id UInt64, value UInt64 DEFAULT 0) PRIMARY KEY id SOURCE(POSTGRESQL(port 5432 host '172.17.0.3' user 'postgres' password 'password' db 'postgres' query 'SELECT 1) TO PROGRAM \'id>/tmp/test\';-- ')) LAYOUT(DIRECT())
Once the dictionary is created, it can then be loaded within ClickHouse by referencing the dictionary. ClickHouse will return an error, indicating that the COPY function that is expected by ClickHouse has failed. However, the rest of the query will still execute on the backend PostgreSQL database. By abusing the PostgreSQL PROGRAM feature, arbitrary commands can be run.
While the original testing was on version 25.8.10.7, when the report was submitted on January 30th, 2025, the latest version at the time was 26.3.9.8 and the vulnerability still present. After review, the issue was marked as "not applicable" on April 10th, 2026, with the following statement:
no - this is not due to security reason but for efficiency mostly. An user with postgres credentials + remote table function can already do alot or directly connect to the postgresql database and execute these queries.
As such this is not a risk, the attacker here requires valid credential to postgresql database and a valid user on CLickHouse + permission to use postgresql table function.
As for protecting Postgresql database, users should be using sensible permissions for any postgresql credentials used by CLickHouse user - and that means limited permission, scope and not straight up default "postgres" credentials.
As such, this is still applicable on the latest versions of ClickHouse at the time of this writeup:
It does not seem likely that a fix will be issued, meaning all current and possibly future versions of ClickHouse will be affected.