在 ClickHouse 中,存在一个功能,允许具有相应权限的用户创建字典,以便与不同的数据库交互并对其运行特定查询。对于 PostgreSQL,SELECT 查询在被发送到数据库执行之前,会被包装在 COPY( {QUERY} ) TO STDOUT 语句中。通过在创建的字典中添加一个括号,可以跳出 COPY( {QUERY} ) TO STDOUT 语句,进而运行任意 SQL 语句,这又可能导致在数据库服务器上运行任意命令。
PostgreSQL 字典使用以下结构创建:
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'
))
字典中使用的 PostgreSQL 引擎的文档提到了以下内容:
PostgreSQL 端的 SELECT 查询在只读 PostgreSQL 事务中以 COPY (SELECT ...) TO STDOUT 形式运行,并在每次 SELECT 查询后提交。
由于字典中定义的查询在发送到 PostgreSQL 实例之前没有额外的验证,因此可以通过以 ")" 开头的查询来跳出 "COPY(...) TO STDOUT",从而创建一个非只读的事务。例如,可以在 ClickHouse 中使用以下查询来创建一个"恶意"的 PostgreSQL 字典:
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())
字典创建完成后,可以在 ClickHouse 中通过引用该字典来加载它。ClickHouse 将返回一个错误,表明 ClickHouse 所预期的 COPY 函数执行失败。然而,查询的其余部分仍会在后端 PostgreSQL 数据库上执行。通过滥用 PostgreSQL 的 PROGRAM 功能,可以运行任意命令。
虽然最初的测试是在 25.8.10.7 版本上进行的,但报告于 2025 年 1 月 30 日提交时,当时的最新版本是 26.3.9.8,该漏洞仍然存在。经过审查,该问题于 2026 年 4 月 10 日被标记为"不适用",并附有以下声明:
不 - 这主要不是出于安全原因,而是出于效率考虑。拥有 PostgreSQL 凭据和远程表函数的用户已经可以做很多事情,或者直接连接到 PostgreSQL 数据库并执行这些查询。
因此,这不构成风险,这里的攻击者需要有效的 PostgreSQL 数据库凭据,以及 ClickHouse 上的有效用户和使用 PostgreSQL 表函数的权限。
至于保护 PostgreSQL 数据库,用户应该为 ClickHouse 用户使用的任何 PostgreSQL 凭据设置合理的权限 - 这意味着有限的权限和范围,而不是直接使用默认的 "postgres" 凭据。
因此,在撰写本文时,该问题仍然影响最新版本的 ClickHouse:
目前看来不太可能发布修复程序,这意味着所有当前版本以及可能未来的 ClickHouse 版本都将受到影响。